Compare commits

...

52 Commits

Author SHA1 Message Date
pvictor 5f8389ff34 updated slope chart example 2024-05-22 11:15:58 +02:00
pvictor 85708897b0 v0.4.3 2024-05-15 11:23:59 +02:00
pvictor 9200d918ad fix news and version number 2024-05-15 11:07:42 +02:00
pvictor 58723475cb updated slope chart height 2024-05-13 11:52:39 +02:00
pvictor b313b45989 added slope charts 2024-05-13 11:20:25 +02:00
pvictor 7e4189366e updated ApexCharts.js to 3.49.1 2024-05-13 11:18:20 +02:00
pvictor 3a3a10369f updated life_expec data + long format 2024-05-13 11:17:46 +02:00
pvictor d0fac7c1ee Updated ApexCharts.js to 3.48.0 2024-03-20 10:25:34 +01:00
pvictor 1ac176f30c maj pkgdown + doc 2024-03-11 11:29:42 +01:00
pvictor 624ab8b901 update pkgdown 2024-03-09 12:00:13 +01:00
pvictor 57733add33 Updated ApexCharts.js to 3.47.0 2024-03-09 11:48:29 +01:00
pvictor c1560fc9c5 Updated ApexCharts.js to 3.46.0 2024-02-21 11:50:04 +01:00
dependabot[bot] e68f20930c
Bump postcss from 8.3.11 to 8.4.31 (#73)
Bumps [postcss](https://github.com/postcss/postcss) from 8.3.11 to 8.4.31.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.3.11...8.4.31)

---
updated-dependencies:
- dependency-name: postcss
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-20 11:07:15 +01:00
pvictor 6cff8a9f67 updated apexcharts.js to 3.45.0 2023-12-20 11:06:46 +01:00
pvictor 1d94f9d4a6 Updated ApexCharts.js to 3.44.0 2023-10-20 10:14:59 +02:00
pvictor eb228597d8 Updated ApexCharts.js to 3.43.0 2023-10-03 09:20:13 +02:00
pvictor bbda86de93 fix heatmap xaxis (force character) 2023-08-23 10:42:59 +02:00
pvictor 467be227fc Updated ApexCharts.js to 3.41.1 2023-08-23 10:13:17 +02:00
pvictor 11d244e992 prepare for cran 2023-06-14 14:05:06 +02:00
pvictor 5587cdbef4 added dumbbell example in vignette + area charts 2023-06-14 11:40:27 +02:00
pvictor be187e37f1 added example data 2023-06-14 11:40:03 +02:00
Victor Perrier 34aee9bc96
updated test-coverage github action 2023-06-13 18:36:14 +02:00
Victor Perrier 44ead44178
apex(): support for dumbbell charts 2023-06-13 18:02:54 +02:00
Victor Perrier 9baa753c3f
added parse_dumbbell_data() 2023-06-13 18:01:21 +02:00
pvictor 24c552ea68 Updated ApexCharts.js to 3.41.0 2023-06-12 09:51:06 +02:00
pvictor b7ed86e556 added ax_forecast_data_points() 2023-05-17 15:49:28 +02:00
pvictor 7705b91e88 Updated ApexCharts.js to 3.40.0 2023-05-05 10:04:44 +02:00
pvictor 70204162f4 Updated ApexCharts.js to 3.37.3 2023-04-05 11:08:55 +02:00
dependabot[bot] 11c6938935
Bump webpack from 5.64.4 to 5.76.0 (#69)
Bumps [webpack](https://github.com/webpack/webpack) from 5.64.4 to 5.76.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.64.4...v5.76.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-22 16:30:51 +01:00
pvictor f83cb879a8 apex(): update bar config 2023-02-22 10:52:04 +01:00
pvictor de477fec66 fix rcmdcheck 2023-02-22 10:49:40 +01:00
pvictor 896998074e added support for boxplot in apex() 2023-02-22 10:45:29 +01:00
pvictor b69b7b7b4d updated apexcharts to 3.37.0 2023-02-08 09:23:23 +01:00
pvictor 1b6a57df3a prepare for cran 2023-01-08 19:23:11 +01:00
pvictor 34d73e81ee updated github actions 2023-01-08 18:45:39 +01:00
pvictor 7d32381a65 CRAN status badge 2022-12-09 09:50:30 +01:00
pvictor 2174c8b238 New cran checks badge URL, fix #64 2022-12-09 09:48:13 +01:00
Victor Perrier 1fe6e97eee
Facets y2 (#65)
* decompose set scale

* set scale yaxis 2

* get global chart serie for fixed yaxis
2022-12-09 09:36:32 +01:00
pvictor cacfcde3ce bump version 2022-12-01 15:45:47 +01:00
pvictor 4ef564e605 facets: added grid_width arg 2022-12-01 15:38:15 +01:00
Victor Perrier 23dbb5e869
Merge pull request #59 from dreamRs/dependabot/npm_and_yarn/terser-5.14.2
Bump terser from 5.10.0 to 5.14.2
2022-12-01 15:25:28 +01:00
pvictor 4cbf760e29 facets: fix add_line usage 2022-12-01 15:24:57 +01:00
pvictor 1e3ad8fcc8 gh action pkgdown 2022-12-01 14:26:40 +01:00
pvictor 162b7874d6 gh actions pkgdown 2022-12-01 12:49:48 +01:00
pvictor ef470d1889 gh action pkgdown 2022-12-01 12:36:08 +01:00
pvictor 69a343fca0 updated pkgdown gh action 2022-12-01 12:26:15 +01:00
pvictor e848debf0a facets: manage yaxis2 correctly 2022-12-01 11:51:52 +01:00
pvictor f50e0064c2 updated apexcharts to 3.36.3 2022-11-09 09:51:05 +01:00
pvictor d0d234baee basic support for rangeArea charts 2022-10-25 22:20:14 +02:00
pvictor a9914aa702 updated Apexcharts to 3.36.0 2022-10-25 18:11:22 +02:00
dependabot[bot] f45efa78fd
Bump terser from 5.10.0 to 5.14.2
Bumps [terser](https://github.com/terser/terser) from 5.10.0 to 5.14.2.
- [Release notes](https://github.com/terser/terser/releases)
- [Changelog](https://github.com/terser/terser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/terser/terser/commits)

---
updated-dependencies:
- dependency-name: terser
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-20 10:04:33 +00:00
pvictor bc22af7e0c
Updated ApexCharts.js to 3.35.0 2022-04-02 18:34:43 +02:00
52 changed files with 1548 additions and 451 deletions

View File

@ -21,3 +21,4 @@
^webpack\.dev\.js$ ^webpack\.dev\.js$
^webpack\.prod\.js$ ^webpack\.prod\.js$
^webpack\.common\.js$ ^webpack\.common\.js$
^CRAN-SUBMISSION$

View File

@ -1,4 +1,4 @@
# Workflow derived from https://github.com/r-lib/actions/tree/master/examples # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
on: on:
push: push:
@ -18,7 +18,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
config: config:
- {os: macOS-latest, r: 'release'} - {os: macos-latest, r: 'release'}
- {os: windows-latest, r: 'release'} - {os: windows-latest, r: 'release'}
- {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'}
- {os: ubuntu-latest, r: 'release'} - {os: ubuntu-latest, r: 'release'}
@ -29,30 +29,21 @@ jobs:
R_KEEP_PKG_SOURCE: yes R_KEEP_PKG_SOURCE: yes
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: r-lib/actions/setup-pandoc@v1 - uses: r-lib/actions/setup-pandoc@v2
- uses: r-lib/actions/setup-r@v1 - uses: r-lib/actions/setup-r@v2
with: with:
r-version: ${{ matrix.config.r }} r-version: ${{ matrix.config.r }}
http-user-agent: ${{ matrix.config.http-user-agent }} http-user-agent: ${{ matrix.config.http-user-agent }}
use-public-rspm: true use-public-rspm: true
- uses: r-lib/actions/setup-r-dependencies@v1 - uses: r-lib/actions/setup-r-dependencies@v2
with: with:
extra-packages: rcmdcheck extra-packages: any::rcmdcheck
needs: check
- uses: r-lib/actions/check-r-package@v1 - uses: r-lib/actions/check-r-package@v2
- 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@main
with: with:
name: ${{ runner.os }}-r${{ matrix.config.r }}-results upload-snapshots: true
path: check

View File

@ -1,8 +1,10 @@
# Workflow derived from https://github.com/r-lib/actions/tree/master/examples # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
on: on:
push: push:
branches: [main, master] branches: [main, master]
pull_request:
branches: [main, master]
release: release:
types: [published] types: [published]
workflow_dispatch: workflow_dispatch:
@ -12,28 +14,34 @@ name: pkgdown
jobs: jobs:
pkgdown: pkgdown:
runs-on: ubuntu-latest runs-on: ubuntu-latest
# Only restrict concurrency for non-PR jobs
concurrency:
group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }}
env: env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: r-lib/actions/setup-pandoc@v1 - uses: r-lib/actions/setup-pandoc@v2
- uses: r-lib/actions/setup-r@v1 - uses: r-lib/actions/setup-r@v2
with: with:
use-public-rspm: true use-public-rspm: true
- uses: r-lib/actions/setup-r-dependencies@v1 - uses: r-lib/actions/setup-r-dependencies@v2
with: with:
extra-packages: | extra-packages: any::pkgdown, local::., any::dplyr, any::highcharter, any::gapminder
dplyr
highcharter
gapminder
pkgdown
needs: website needs: website
pak-version: devel
- name: Deploy package - name: Build site
run: | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE)
git config --local user.name "$GITHUB_ACTOR" shell: Rscript {0}
git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com"
Rscript -e 'pkgdown::deploy_to_branch(new_process = FALSE)' - name: Deploy to GitHub pages 🚀
if: github.event_name != 'pull_request'
uses: JamesIves/github-pages-deploy-action@v4.4.1
with:
clean: false
branch: gh-pages
folder: docs

View File

@ -1,48 +1,50 @@
# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
on: on:
push: push:
branches: branches: [main, master]
- main
- master
pull_request: pull_request:
branches: branches: [main, master]
- main
- master
name: test-coverage name: test-coverage
jobs: jobs:
test-coverage: test-coverage:
runs-on: macOS-latest runs-on: ubuntu-latest
env: env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: r-lib/actions/setup-r@v1 - uses: r-lib/actions/setup-r@v2
- uses: r-lib/actions/setup-pandoc@v1
- name: Query dependencies
run: |
install.packages('remotes')
saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2)
writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version")
shell: Rscript {0}
- name: Cache R packages
uses: actions/cache@v2
with: with:
path: ${{ env.R_LIBS_USER }} use-public-rspm: true
key: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-${{ hashFiles('.github/depends.Rds') }}
restore-keys: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-
- name: Install dependencies - uses: r-lib/actions/setup-r-dependencies@v2
run: | with:
install.packages(c("remotes")) extra-packages: any::covr
remotes::install_deps(dependencies = TRUE) needs: coverage
remotes::install_cran("covr")
shell: Rscript {0}
- name: Test coverage - name: Test coverage
run: covr::codecov() run: |
covr::codecov(
quiet = FALSE,
clean = FALSE,
install_path = file.path(Sys.getenv("RUNNER_TEMP"), "package")
)
shell: Rscript {0} shell: Rscript {0}
- name: Show testthat output
if: always()
run: |
## --------------------------------------------------------------------
find ${{ runner.temp }}/package -name 'testthat.Rout*' -exec cat '{}' \; || true
shell: bash
- name: Upload test results
if: failure()
uses: actions/upload-artifact@v3
with:
name: coverage-test-failures
path: ${{ runner.temp }}/package

1
.gitignore vendored
View File

@ -6,3 +6,4 @@ inst/doc
data-raw/*.csv data-raw/*.csv
node_modules node_modules
docs docs
data-raw/inputs/

View File

@ -1,5 +1,5 @@
Package: apexcharter Package: apexcharter
Version: 0.3.1 Version: 0.4.3.9000
Title: Create Interactive Chart with the JavaScript 'ApexCharts' Library Title: Create Interactive Chart with the JavaScript 'ApexCharts' Library
Description: Provides an 'htmlwidgets' interface to 'apexcharts.js'. Description: Provides an 'htmlwidgets' interface to 'apexcharts.js'.
'Apexcharts' is a modern JavaScript charting library to build interactive charts and visualizations with simple API. 'Apexcharts' is a modern JavaScript charting library to build interactive charts and visualizations with simple API.
@ -28,7 +28,7 @@ Suggests:
scales, scales,
rmarkdown, rmarkdown,
covr covr
RoxygenNote: 7.1.2 RoxygenNote: 7.3.1
Roxygen: list(markdown = TRUE) Roxygen: list(markdown = TRUE)
URL: https://github.com/dreamRs/apexcharter, https://dreamrs.github.io/apexcharter/ URL: https://github.com/dreamRs/apexcharter, https://dreamrs.github.io/apexcharter/
BugReports: https://github.com/dreamRs/apexcharter/issues BugReports: https://github.com/dreamRs/apexcharter/issues

View File

@ -29,6 +29,7 @@ export(ax_dataLabels)
export(ax_facet_grid) export(ax_facet_grid)
export(ax_facet_wrap) export(ax_facet_wrap)
export(ax_fill) export(ax_fill)
export(ax_forecast_data_points)
export(ax_grid) export(ax_grid)
export(ax_labels) export(ax_labels)
export(ax_labels2) export(ax_labels2)
@ -52,6 +53,7 @@ export(ax_xaxis)
export(ax_yaxis) export(ax_yaxis)
export(ax_yaxis2) export(ax_yaxis2)
export(bar_opts) export(bar_opts)
export(boxplot_opts)
export(bubble_opts) export(bubble_opts)
export(config_update) export(config_update)
export(events_opts) export(events_opts)
@ -81,10 +83,13 @@ export(vars)
importFrom(ggplot2,aes) importFrom(ggplot2,aes)
importFrom(ggplot2,label_value) importFrom(ggplot2,label_value)
importFrom(ggplot2,vars) importFrom(ggplot2,vars)
importFrom(graphics,boxplot)
importFrom(htmltools,css)
importFrom(htmltools,renderTags) importFrom(htmltools,renderTags)
importFrom(htmltools,resolveDependencies) importFrom(htmltools,resolveDependencies)
importFrom(htmltools,tagList) importFrom(htmltools,tagList)
importFrom(htmltools,tags) importFrom(htmltools,tags)
importFrom(htmltools,validateCssUnit)
importFrom(htmlwidgets,JS) importFrom(htmlwidgets,JS)
importFrom(htmlwidgets,JSEvals) importFrom(htmlwidgets,JSEvals)
importFrom(htmlwidgets,createWidget) importFrom(htmlwidgets,createWidget)
@ -99,8 +104,10 @@ importFrom(rlang,"%||%")
importFrom(rlang,as_label) importFrom(rlang,as_label)
importFrom(rlang,eval_tidy) importFrom(rlang,eval_tidy)
importFrom(rlang,is_function) importFrom(rlang,is_function)
importFrom(rlang,is_list)
importFrom(rlang,is_named) importFrom(rlang,is_named)
importFrom(rlang,is_null) importFrom(rlang,is_null)
importFrom(rlang,quo)
importFrom(rlang,quos) importFrom(rlang,quos)
importFrom(rlang,sym) importFrom(rlang,sym)
importFrom(rlang,syms) importFrom(rlang,syms)

33
NEWS.md
View File

@ -1,3 +1,36 @@
apexcharter 0.4.3
==================
* Updated ApexCharts.js to 3.49.1 (see https://github.com/apexcharts/apexcharts.js/releases).
* New chart type : slope charts.
apexcharter 0.4.2
==================
* Updated ApexCharts.js to 3.46.0 (see https://github.com/apexcharts/apexcharts.js/releases).
apexcharter 0.4.1
==================
* Updated ApexCharts.js to 3.41.0 (new charts type: dumbbell chart and funnel chart).
* `apex()` : added support for boxplot.
* New function `ax_forecast_data_points()` to mark points as forecasted values.
apexcharter 0.4.0
==================
* Updated ApexCharts.js to 3.36.3.
* New chart type : range area charts.
* Facets: correctly manage secondary y axis.
apexcharter 0.3.1 apexcharter 0.3.1
================== ==================

View File

@ -115,7 +115,7 @@ events_opts <- function(click = NULL,
#' @title Bar options #' @title Bar options
#' #'
#' @description Use these options in \code{\link{ax_plotOptions}}. #' @description Use these options in [ax_plotOptions()].
#' #'
#' @param horizontal Logical. This option will turn a column chart into a horizontal bar chart. #' @param horizontal Logical. This option will turn a column chart into a horizontal bar chart.
#' @param endingShape Available Options: \code{"flat"} or \code{"rounded"}. #' @param endingShape Available Options: \code{"flat"} or \code{"rounded"}.
@ -128,7 +128,7 @@ events_opts <- function(click = NULL,
#' #'
#' @note See \url{https://apexcharts.com/docs/options/plotoptions/bar/}. #' @note See \url{https://apexcharts.com/docs/options/plotoptions/bar/}.
#' #'
#' @return A \code{list} of options that can be used in \code{\link{ax_plotOptions}}. #' @return A \code{list} of options that can be used in [ax_plotOptions()].
#' #'
#' @export #' @export
#' #'
@ -170,7 +170,7 @@ bar_opts <- function(horizontal = NULL,
#' @title Heatmap options #' @title Heatmap options
#' #'
#' @description Use these options in \code{\link{ax_plotOptions}}. #' @description Use these options in [ax_plotOptions()].
#' #'
#' @param radius Numeric. Radius of the rectangle inside heatmap. #' @param radius Numeric. Radius of the rectangle inside heatmap.
#' @param enableShades Logical. Enable different shades of color depending on the value #' @param enableShades Logical. Enable different shades of color depending on the value
@ -180,7 +180,7 @@ bar_opts <- function(horizontal = NULL,
#' #'
#' @note See \url{https://apexcharts.com/docs/options/plotoptions/heatmap/}. #' @note See \url{https://apexcharts.com/docs/options/plotoptions/heatmap/}.
#' #'
#' @return A \code{list} of options that can be used in \code{\link{ax_plotOptions}}. #' @return A \code{list} of options that can be used in [ax_plotOptions()].
#' #'
#' @export #' @export
#' #'
@ -228,7 +228,7 @@ heatmap_opts <- function(radius = NULL,
#' @title Radial bar options #' @title Radial bar options
#' #'
#' @description Use these options in \code{\link{ax_plotOptions}}. #' @description Use these options in [ax_plotOptions()].
#' #'
#' @param size Numeric. Manual size of the radialBars instead of calculating automatically from default height / width. #' @param size Numeric. Manual size of the radialBars instead of calculating automatically from default height / width.
#' @param inverseOrder Logical. Whether to make the first value of series innermost or outermost. #' @param inverseOrder Logical. Whether to make the first value of series innermost or outermost.
@ -243,7 +243,7 @@ heatmap_opts <- function(radius = NULL,
#' #'
#' @note See \url{https://apexcharts.com/docs/options/plotoptions/radialbar/}. #' @note See \url{https://apexcharts.com/docs/options/plotoptions/radialbar/}.
#' #'
#' @return A \code{list} of options that can be used in \code{\link{ax_plotOptions}}. #' @return A \code{list} of options that can be used in [ax_plotOptions()].
#' #'
#' @export #' @export
#' #'
@ -301,7 +301,7 @@ radialBar_opts <- function(size = NULL,
#' @title Pie options #' @title Pie options
#' #'
#' @description Use these options in \code{\link{ax_plotOptions}}. #' @description Use these options in [ax_plotOptions()].
#' #'
#' @param size Numeric. Custom size of the pie which will override the default size calculations. #' @param size Numeric. Custom size of the pie which will override the default size calculations.
#' @param donut List with two fields \code{size} (Donut / ring size in percentage relative to the total pie area.) #' @param donut List with two fields \code{size} (Donut / ring size in percentage relative to the total pie area.)
@ -314,7 +314,7 @@ radialBar_opts <- function(size = NULL,
#' #'
#' @note See \url{https://apexcharts.com/docs/options/plotoptions/pie/}. #' @note See \url{https://apexcharts.com/docs/options/plotoptions/pie/}.
#' #'
#' @return A \code{list} of options that can be used in \code{\link{ax_plotOptions}}. #' @return A \code{list} of options that can be used in [ax_plotOptions()].
#' #'
#' @export #' @export
#' #'
@ -351,7 +351,7 @@ pie_opts <- function(size = NULL,
#' @title Bubble options #' @title Bubble options
#' #'
#' @description Use these options in \code{\link{ax_plotOptions}}. #' @description Use these options in [ax_plotOptions()].
#' #'
#' @param minBubbleRadius Minimum radius size of a bubble. #' @param minBubbleRadius Minimum radius size of a bubble.
#' If a bubble value is too small to be displayed, this size will be used. #' If a bubble value is too small to be displayed, this size will be used.
@ -361,7 +361,7 @@ pie_opts <- function(size = NULL,
#' #'
#' @note See \url{https://apexcharts.com/docs/options/plotoptions/bubble/}. #' @note See \url{https://apexcharts.com/docs/options/plotoptions/bubble/}.
#' #'
#' @return A \code{list} of options that can be used in \code{\link{ax_plotOptions}}. #' @return A \code{list} of options that can be used in [ax_plotOptions()].
#' @export #' @export
#' #'
#' @examples #' @examples
@ -386,3 +386,36 @@ bubble_opts <- function(minBubbleRadius, maxBubbleRadius, ...) {
) )
} }
#' @title Boxplot options
#'
#' @description Use these options in [ax_plotOptions()].
#'
#' @param color.upper Color for the upper quartile (Q3 to median) of the box plot.
#' @param color.lower Color for the lower quartile (median to Q1) of the box plot.
#' @param ... Additional parameters.
#'
#' @note See \url{https://apexcharts.com/docs/options/plotoptions/boxplot/}.
#'
#' @return A \code{list} of options that can be used in [ax_plotOptions()].
#' @export
#'
#' @examples
#' data("mpg", package = "ggplot2")
#' apex(mpg, aes(class, hwy), "boxplot") %>%
#' ax_plotOptions(
#' boxPlot = boxplot_opts(color.upper = "#848484", color.lower = "#848484" )
#' )
boxplot_opts <- function(color.upper, color.lower, ...) {
dropNulls(
list(
colors = dropNulls(list(
upper = color.upper,
lower = color.lower
)),
...
)
)
}

View File

@ -185,11 +185,12 @@ ax_chart <- function(ax,
#' Specific options for chart #' Specific options for chart
#' #'
#' @template ax-default #' @template ax-default
#' @param bar See \code{\link{bar_opts}}. #' @param bar See [bar_opts()].
#' @param heatmap See \code{\link{heatmap_opts}}. #' @param heatmap See [heatmap_opts()].
#' @param radialBar See \code{\link{radialBar_opts}}. #' @param radialBar See [radialBar_opts()].
#' @param pie See \code{\link{pie_opts}}. #' @param pie See [pie_opts()].
#' @param bubble See \code{\link{bubble_opts}}. #' @param bubble See [bubble_opts()].
#' @param boxPlot See [boxplot_opts()].
#' @param ... Additional parameters. #' @param ... Additional parameters.
#' #'
#' #'
@ -234,6 +235,7 @@ ax_plotOptions <- function(ax,
radialBar = NULL, radialBar = NULL,
pie = NULL, pie = NULL,
bubble = NULL, bubble = NULL,
boxPlot = NULL,
...) { ...) {
params <- c(as.list(environment()), list(...))[-1] params <- c(as.list(environment()), list(...))[-1]
.ax_opt2(ax, "plotOptions", l = dropNulls(params)) .ax_opt2(ax, "plotOptions", l = dropNulls(params))
@ -1271,9 +1273,44 @@ ax_nodata <- function(ax,
#' Forecast data points
#'
#' @template ax-default
#' @param count Number of ending data-points you want to indicate as a forecast or prediction values.
#' The ending line/bar will result into a dashed border with a distinct look to differentiate from the rest of the data-points.
#' @param fillOpacity Opacity of the fill attribute.
#' @param strokeWidth Sets the width of the points.
#' @param dashArray Creates dashes in borders of svg path. Higher number creates more space between dashes in the border.
#' @param ... Additional arguments (not used).
#'
#' @export
#'
#' @examples
#' # add 5 predictions to data then plot it
#' data.frame(
#' time = seq_len(53),
#' lh = c(
#' as.vector(lh),
#' as.vector(predict(arima(lh, order = c(1,0,1)), 5)$pred)
#' )
#' ) %>%
#' apex(aes(time, lh), type = "line") %>%
#' ax_xaxis(type = "numeric") %>%
#' ax_forecast_data_points(count = 5)
ax_forecast_data_points <- function(ax,
count = NULL,
fillOpacity = NULL,
strokeWidth = NULL,
dashArray = NULL,
...) {
params <- list(
count = count,
fillOpacity = fillOpacity,
strokeWidth = strokeWidth,
dashArray = dashArray
)
.ax_opt2(ax, "forecastDataPoints", l = dropNulls(params))
}

View File

@ -14,7 +14,7 @@
#' `"pie"`, `"donut"`, #' `"pie"`, `"donut"`,
#' `"radialBar"`, `"radar"`, `"scatter"`, #' `"radialBar"`, `"radar"`, `"scatter"`,
#' `"heatmap"`, `"treemap"`, #' `"heatmap"`, `"treemap"`,
#' `"timeline"`. #' `"timeline"`, `"dumbbell"` and `"slope"`.
#' @param ... Other arguments passed on to methods. Not currently used. #' @param ... Other arguments passed on to methods. Not currently used.
#' @param synchronize Give a common id to charts to synchronize them (tooltip and zoom). #' @param synchronize Give a common id to charts to synchronize them (tooltip and zoom).
#' @param serie_name Name for the serie displayed in tooltip, #' @param serie_name Name for the serie displayed in tooltip,
@ -43,8 +43,10 @@ apex <- function(data, mapping,
arg = type, arg = type,
choices = c( choices = c(
"column", "bar", "column", "bar",
"line", "spline", "step", "rangeBar", "dumbbell",
"line", "spline", "step", "slope",
"area", "area-spline", "area-step", "area", "area-spline", "area-step",
"rangeArea",
"pie", "donut", "pie", "donut",
"radialBar", "radialBar",
"radar", "radar",
@ -53,7 +55,8 @@ apex <- function(data, mapping,
"heatmap", "heatmap",
"treemap", "treemap",
"timeline", "timeline",
"candlestick" "candlestick",
"boxplot"
) )
) )
data <- as.data.frame(data) data <- as.data.frame(data)
@ -66,7 +69,8 @@ apex <- function(data, mapping,
type <- "bubble" type <- "bubble"
} }
mapdata <- lapply(mapping, rlang::eval_tidy, data = data) mapdata <- lapply(mapping, rlang::eval_tidy, data = data)
if (is.null(mapdata$y) & !type %in% c("candlestick", "timeline", "heatmap")) { type_no_compute <- c("candlestick", "boxplot", "timeline", "heatmap", "rangeArea", "rangeBar", "dumbbell", "slope")
if (is.null(mapdata$y) & !type %in% type_no_compute) {
mapdata <- compute_count(mapdata) mapdata <- compute_count(mapdata)
} }
if (type %in% c("pie", "donut", "radialBar", "polarArea")) { if (type %in% c("pie", "donut", "radialBar", "polarArea")) {
@ -117,7 +121,9 @@ apex <- function(data, mapping,
# Construct series # Construct series
#' @importFrom rlang %||% #' @importFrom rlang %||%
make_series <- function(mapdata, mapping, type = NULL, serie_name = NULL, force_datetime_names = FALSE) { make_series <- function(mapdata, mapping, type = NULL, serie_name = NULL, force_datetime_names = FALSE) {
if (identical(type, "candlestick")) { if (identical(type, "boxplot")) {
series <- parse_boxplot_data(mapdata, serie_name = serie_name)
} else if (identical(type, "candlestick")) {
if (!all(c("x", "open", "high", "low", "close") %in% names(mapping))) if (!all(c("x", "open", "high", "low", "close") %in% names(mapping)))
stop("For candlestick charts 'x', 'open', 'high', 'low', and 'close' aesthetics must be provided.", call. = FALSE) stop("For candlestick charts 'x', 'open', 'high', 'low', and 'close' aesthetics must be provided.", call. = FALSE)
if (!is.null(mapdata$group)) if (!is.null(mapdata$group))
@ -130,8 +136,23 @@ make_series <- function(mapdata, mapping, type = NULL, serie_name = NULL, force_
if (is.null(mapdata$group)) if (is.null(mapdata$group))
mapdata$group <- serie_name %||% rlang::as_label(mapping$x) mapdata$group <- serie_name %||% rlang::as_label(mapping$x)
series <- parse_timeline_data(mapdata) series <- parse_timeline_data(mapdata)
} else if (isTRUE(type %in% c("dumbbell"))) {
if (!all(c("y", "x", "xend") %in% names(mapping)))
stop("For dumbbell charts 'x', 'xend', and 'y' aesthetics must be provided.", call. = FALSE)
if (is.null(mapdata$group))
mapdata$group <- serie_name %||% rlang::as_label(mapping$x)
series <- parse_dumbbell_data(mapdata)
} else { } else {
mapdata <- as.data.frame(mapdata, stringsAsFactors = FALSE) mapdata <- as.data.frame(mapdata, stringsAsFactors = FALSE)
if (all(rlang::has_name(mapdata, c("ymin", "ymax")))) {
mapdata$y <- lapply(
X = seq_len(nrow(mapdata)),
FUN = function(i) {
list(mapdata$ymin[i], mapdata$ymax[i])
}
)
mapdata$ymin <- mapdata$ymax <- NULL
}
if (isTRUE(type %in% c("scatter", "bubble"))) { if (isTRUE(type %in% c("scatter", "bubble"))) {
complete <- complete.cases(mapdata[c("x", "y")]) complete <- complete.cases(mapdata[c("x", "y")])
n_missing <- sum(!complete) n_missing <- sum(!complete)
@ -143,7 +164,7 @@ make_series <- function(mapdata, mapping, type = NULL, serie_name = NULL, force_
if (is.character(mapdata$x)) if (is.character(mapdata$x))
mapdata$x[is.na(mapdata$x)] <- "NA" mapdata$x[is.na(mapdata$x)] <- "NA"
x_order <- unique(mapdata$x) x_order <- unique(mapdata$x)
if (is_x_datetime(mapdata)) { if (is_x_datetime(mapdata) & !identical(type, "rangeArea")) {
add_names <- force_datetime_names add_names <- force_datetime_names
x_order <- sort(x_order) x_order <- sort(x_order)
} else { } else {
@ -158,7 +179,7 @@ make_series <- function(mapdata, mapping, type = NULL, serie_name = NULL, force_
))) )))
if (is_grouped(mapping)) { if (is_grouped(mapping)) {
mapdata <- rename_aes(mapdata) mapdata <- rename_aes(mapdata)
len_grp <- tapply(mapdata$group, mapdata$group, length) len_grp <- tapply(as.character(mapdata$group), as.character(mapdata$group), length)
if (length(unique(len_grp)) > 1 & !isTRUE(type %in% c("scatter", "bubble"))) { if (length(unique(len_grp)) > 1 & !isTRUE(type %in% c("scatter", "bubble"))) {
warning("apex: all groups must have same length! You can use `tidyr::complete` for this.") warning("apex: all groups must have same length! You can use `tidyr::complete` for this.")
} }
@ -191,8 +212,11 @@ is_sized <- function(x) {
any(c("size", "z") %in% names(x)) any(c("size", "z") %in% names(x))
} }
#' @importFrom rlang quo
rename_aes_heatmap <- function(mapping) { rename_aes_heatmap <- function(mapping) {
if (is.null(mapping["x"]))
stop("apex(..., type = 'heatmap') must have an 'x' aesthetic", call. = FALSE)
mapping[["x"]] <- quo(as.character(!!mapping[["x"]]))
n_mapping <- names(mapping) n_mapping <- names(mapping)
n_mapping[n_mapping == "y"] <- "group" n_mapping[n_mapping == "y"] <- "group"
if ("fill" %in% n_mapping) { if ("fill" %in% n_mapping) {
@ -232,14 +256,16 @@ list1 <- function(x) {
# Change type of charts for helpers type # Change type of charts for helpers type
correct_type <- function(type) { correct_type <- function(type) {
if (identical(type, "column")) { if (isTRUE(type %in% c("column"))) {
"bar" "bar"
} else if (isTRUE(type %in% c("spline", "step"))) { } else if (isTRUE(type %in% c("spline", "step", "slope"))) {
"line" "line"
} else if (isTRUE(type %in% c("area-spline", "area-step"))) { } else if (isTRUE(type %in% c("area-spline", "area-step"))) {
"area" "area"
} else if (identical(type, "timeline")) { } else if (isTRUE(type %in% c("timeline", "dumbbell"))) {
"rangeBar" "rangeBar"
} else if (identical(type, "boxplot")) {
"boxPlot"
} else { } else {
type type
} }
@ -248,7 +274,7 @@ correct_type <- function(type) {
multi_type <- function(x) { multi_type <- function(x) {
multis <- c("column", "area", "line", multis <- c("column", "area", "line",
"spline", "step", "scatter", "spline", "step", "scatter",
"bubble") "bubble", "rangeArea")
if (isTRUE(x %in% multis)) { if (isTRUE(x %in% multis)) {
correct_type(x) correct_type(x)
} else { } else {
@ -298,12 +324,17 @@ choose_config <- function(type, mapdata) {
datetime <- is_x_datetime(mapdata) datetime <- is_x_datetime(mapdata)
range_x <- range_num(mapdata$x) range_x <- range_num(mapdata$x)
range_y <- range_num(mapdata$y) range_y <- range_num(mapdata$y)
if (identical(type, "boxplot")) {
box_horiz <- !is.numeric(mapdata$y) & is.numeric(mapdata$x)
}
switch( switch(
type, type,
"bar" = config_bar(horizontal = TRUE), "bar" = config_bar(horizontal = TRUE),
"dumbbell" = config_bar(horizontal = TRUE, isDumbbell = TRUE),
"column" = config_bar(horizontal = FALSE, datetime = datetime), "column" = config_bar(horizontal = FALSE, datetime = datetime),
"line" = config_line(datetime = datetime), "line" = config_line(datetime = datetime),
"area" = config_line(datetime = datetime), "area" = config_line(datetime = datetime),
"rangeArea" = config_line(datetime = datetime),
"spline" = config_line(curve = "smooth", datetime = datetime), "spline" = config_line(curve = "smooth", datetime = datetime),
"step" = config_line(curve = "stepline", datetime = datetime), "step" = config_line(curve = "stepline", datetime = datetime),
"area-spline" = config_line(curve = "smooth", datetime = datetime), "area-spline" = config_line(curve = "smooth", datetime = datetime),
@ -312,34 +343,33 @@ choose_config <- function(type, mapdata) {
"bubble" = config_scatter(range_x = range_x, range_y = range_y, datetime = datetime), "bubble" = config_scatter(range_x = range_x, range_y = range_y, datetime = datetime),
"timeline" = config_timeline(), "timeline" = config_timeline(),
"candlestick" = config_candlestick(), "candlestick" = config_candlestick(),
"boxplot" = config_boxplot(horizontal = box_horiz),
"slope" = config_slope(),
list() list()
) )
} }
# Config for column & bar charts # Config for column & bar charts
config_bar <- function(horizontal = FALSE, datetime = FALSE) { config_bar <- function(horizontal = FALSE, datetime = FALSE, isDumbbell = FALSE) {
config <- list( config <- list(
dataLabels = list(enabled = FALSE), dataLabels = list(enabled = FALSE),
plotOptions = list( plotOptions = list(
bar = list( bar = list(
horizontal = horizontal horizontal = horizontal,
isDumbbell = isDumbbell
) )
), ),
tooltip = list( tooltip = list(
shared = TRUE, shared = TRUE,
intersect = FALSE, intersect = FALSE,
followCursor = TRUE followCursor = TRUE
),
grid = list(
yaxis = list(lines = list(show = !isTRUE(horizontal))),
xaxis = list(lines = list(show = isTRUE(horizontal)))
) )
) )
if (isTRUE(horizontal)) {
config <- c(config, list(
grid = list(
yaxis = list(lines = list(show = FALSE)),
xaxis = list(lines = list(show = TRUE))
)
))
}
if (isTRUE(datetime)) { if (isTRUE(datetime)) {
config$xaxis$type <- "datetime" config$xaxis$type <- "datetime"
} }
@ -425,3 +455,22 @@ config_candlestick <- function() {
) )
} }
config_boxplot <- function(horizontal = FALSE) {
list(
plotOptions = list(
bar = list(
horizontal = horizontal
)
)
)
}
config_slope <- function() {
list(
plotOptions = list(
line = list(
isSlopeChart = TRUE
)
)
)
}

View File

@ -5,9 +5,8 @@
#' to create interactive and modern SVG charts. #' to create interactive and modern SVG charts.
#' #'
#' @name apexcharter-package #' @name apexcharter-package
#' @docType package
#' @author Victor Perrier (@@dreamRs_fr) #' @author Victor Perrier (@@dreamRs_fr)
NULL "_PACKAGE"
#' apexcharter exported operators and S3 methods #' apexcharter exported operators and S3 methods
#' #'

View File

@ -42,6 +42,7 @@ apexchart <- function(ax_opts = list(),
preRenderHook = function(widget) { preRenderHook = function(widget) {
widget$x$data <- NULL widget$x$data <- NULL
widget$x$mapping <- NULL widget$x$mapping <- NULL
widget$x$add_line <- NULL
add_locale_apex(widget) add_locale_apex(widget)
}, },
sizingPolicy = htmlwidgets::sizingPolicy( sizingPolicy = htmlwidgets::sizingPolicy(

View File

@ -11,7 +11,7 @@
#' \item{\code{continent_origin}}{Continent of residence of population.} #' \item{\code{continent_origin}}{Continent of residence of population.}
#' \item{\code{n}}{Number of people concerned.} #' \item{\code{n}}{Number of people concerned.}
#' } #' }
#' @source UNHCR (The UN Refugee Agency) (\url{https://www.unhcr.org/}) #' @source UNHCR (The UN Refugee Agency) (\url{https://data.unhcr.org/})
"unhcr_ts" "unhcr_ts"
@ -58,3 +58,43 @@
#' } #' }
#' @source Wikipedia (\url{https://fr.wikipedia.org/wiki/Climat_de_Paris}) #' @source Wikipedia (\url{https://fr.wikipedia.org/wiki/Climat_de_Paris})
"climate_paris" "climate_paris"
#' @title eco2mix data
#'
#' @description The dataset contains data about electricity consumption and production in France between 2012 and 2022.
#'
#' @format A data frame with 3,033 observations and 3 variables.
#'
#' @source Rte (Réseau et transport d'électricité) (\url{https://www.rte-france.com/eco2mix} and \url{https://opendata.reseaux-energies.fr/})
"eco2mix"
#' @title Temperature data
#'
#' @description The dataset contains data about temperatures in France between 2018 and 2022.
#'
#' @format A data frame with 365 observations and 6 variables.
#'
#' @source Enedis (\url{https://data.enedis.fr/explore/dataset/donnees-de-temperature-et-de-pseudo-rayonnement/})
"temperatures"
#' @title Life expectancy data
#'
#' @description The dataset contains data about life expectancy in 1972 and 2007 for 10 countries.
#'
#' @format A data frame with 10 observations and 4 variables.
#'
#' @source gapminder package (\url{https://jennybc.github.io/gapminder/} and \url{https://www.gapminder.org/data/})
"life_expec"
#' @title Life expectancy data (long format)
#'
#' @description The dataset contains data about life expectancy in 1972 and 2007 for 10 countries.
#'
#' @format A data frame with 20 observations and 3 variables.
#'
#' @source gapminder package (\url{https://jennybc.github.io/gapminder/} and \url{https://www.gapminder.org/data/})
"life_expec_long"

180
R/facets-utils.R Normal file
View File

@ -0,0 +1,180 @@
#' @importFrom rlang eval_tidy
get_facets <- function(data, rows, cols, type = c("wrap", "grid")) {
type <- match.arg(type)
byrows <- lapply(X = rows, FUN = eval_tidy, data = data)
bycols <- lapply(X = cols, FUN = eval_tidy, data = data)
facets <- split(x = data, f = c(bycols, byrows), sep = "|__|")
facets <- lapply(
X = seq_along(facets),
FUN = function(i) {
facet <- facets[[i]]
attr(facet, "keys") <- strsplit(
x = names(facets)[i],
split = "|__|", fixed = TRUE
)[[1]]
facet
}
)
label_row <- lapply(byrows, unique)
label_row <- lapply(label_row, sort)
label_row <- apply(expand.grid(label_row), 1, paste, collapse = "*")
label_col <- lapply(bycols, unique)
label_col <- lapply(label_col, sort)
label_col <- apply(expand.grid(label_col), 1, paste, collapse = "*")
list(
facets = facets,
nrow = if (identical(type, "grid")) n_facet(byrows) else NULL,
ncol = if (identical(type, "grid")) n_facet(bycols) else NULL,
label_row = label_row,
label_col = label_col
)
}
n_facet <- function(l) {
l <- lapply(l, function(x) {
length(unique(x))
})
Reduce(`*`, l)
}
#' @importFrom rlang %||% is_list is_named
set_scale <- function(ax, values, scales = c("fixed", "free", "free_y", "free_x"), axis = c("x", "y", "y2")) {
if (is.null(scales))
return(ax)
scales <- match.arg(scales)
axis <- match.arg(axis)
if (identical(axis, "y2")) {
axis <- "y"
wyaxis <- 2
} else {
wyaxis <- 1
}
if (is.null(values))
return(ax)
if (inherits(values, c("numeric", "integer", "Date", "POSIXt"))) {
range_vals <- range(pretty(values, n = 10), na.rm = TRUE)
} else {
range_vals <- NULL
}
waxis <- switch(
axis,
"x" = "xaxis",
"y" = "yaxis"
)
this_axis <- ax$x$ax_opts[[waxis]]
if (inherits(this_axis, "yaxis2")) {
ax$x$ax_opts[[waxis]][[wyaxis]] <- set_scale_axis(
this_axis[[wyaxis]],
range_vals = range_vals,
scales = scales,
axis = axis
)
# ax$x$ax_opts[[waxis]][[2]] <- set_scale_axis(
# this_axis[[2]],
# range_vals = range_vals,
# scales = scales,
# axis = axis
# )
} else {
ax$x$ax_opts[[waxis]] <- set_scale_axis(
this_axis,
range_vals = range_vals,
scales = scales,
axis = axis
)
}
return(ax)
}
scale_fmt <- function(x, time = inherits(x, c("Date", "POSIXt"))) {
if (is.null(x))
return(NULL)
if (time)
x <- format_date(x)
x
}
set_scale_axis <- function(this_axis,
range_vals,
scales = c("fixed", "free", "free_y", "free_x"),
axis = c("x", "y")) {
scales <- match.arg(scales)
axis <- match.arg(axis)
if (scales == "fixed") {
this_axis$min <- this_axis$min %||% scale_fmt(range_vals[1])
this_axis$max <- this_axis$max %||% scale_fmt(range_vals[2])
} else if (scales == "free") {
this_axis$min <- NULL
this_axis$max <- NULL
} else if (scales == "free_x") {
if (axis == "y") {
this_axis$min <- this_axis$min %||% scale_fmt(range_vals[1])
this_axis$max <- this_axis$max %||% scale_fmt(range_vals[2])
} else {
this_axis$min <- NULL
this_axis$max <- NULL
}
} else if (scales == "free_y") {
if (axis == "x") {
this_axis$min <- this_axis$min %||% scale_fmt(range_vals[1])
this_axis$max <- this_axis$max %||% scale_fmt(range_vals[2])
} else {
this_axis$min <- NULL
this_axis$max <- NULL
}
}
return(this_axis)
}
get_option <- function(ax, opt1, opt2 = NULL) {
if (is.null(opt2)) {
ax$x$ax_opts[[opt1]]
} else {
ax$x$ax_opts[[opt1]][[opt2]]
}
}
remove_option <- function(ax, opt1, opt2 = NULL) {
if (is.null(opt2)) {
ax$x$ax_opts[[opt1]] <- NULL
} else {
ax$x$ax_opts[[opt1]][[opt2]] <- NULL
}
ax
}
get_yaxis_serie <- function(ax, which = 1) {
series <- ax$x$ax_opts$series
yaxis <- ax$x$ax_opts$yaxis
if (inherits(yaxis, c("yaxis", "yaxis2"))) {
yaxis <- yaxis[[which]]
name <- yaxis$serieName
if (!is.null(name)) {
series_names <- vapply(series, FUN = `[[`, "name", FUN.VALUE = character(1))
indice <- which(name == series_names)
} else {
indice <- which
}
unlist(lapply(series[[indice]]$data, FUN = `[[`, "y"))
} else {
unlist(lapply(
X = seq_along(series),
FUN = function(indice) {
unlist(lapply(series[[indice]]$data, FUN = `[[`, "y"))
}
))
}
}
has_yaxis2 <- function(ax) {
inherits(ax$x$ax_opts$yaxis, "yaxis2")
}

View File

@ -1,114 +1,4 @@
#' @importFrom rlang eval_tidy
get_facets <- function(data, rows, cols, type = c("wrap", "grid")) {
type <- match.arg(type)
byrows <- lapply(X = rows, FUN = eval_tidy, data = data)
bycols <- lapply(X = cols, FUN = eval_tidy, data = data)
facets <- split(x = data, f = c(bycols, byrows), sep = "|__|")
facets <- lapply(
X = seq_along(facets),
FUN = function(i) {
facet <- facets[[i]]
attr(facet, "keys") <- strsplit(
x = names(facets)[i],
split = "|__|", fixed = TRUE
)[[1]]
facet
}
)
label_row <- lapply(byrows, unique)
label_row <- lapply(label_row, sort)
label_row <- apply(expand.grid(label_row), 1, paste, collapse = "*")
label_col <- lapply(bycols, unique)
label_col <- lapply(label_col, sort)
label_col <- apply(expand.grid(label_col), 1, paste, collapse = "*")
list(
facets = facets,
nrow = if (identical(type, "grid")) n_facet(byrows) else NULL,
ncol = if (identical(type, "grid")) n_facet(bycols) else NULL,
label_row = label_row,
label_col = label_col
)
}
n_facet <- function(l) {
l <- lapply(l, function(x) {
length(unique(x))
})
Reduce(`*`, l)
}
#' @importFrom rlang %||%
set_scale <- function(ax, values, scales = c("fixed", "free", "free_y", "free_x"), axis = c("x", "y")) {
if (is.null(scales))
return(ax)
scales <- match.arg(scales)
axis <- match.arg(axis)
if (is.null(values))
return(ax)
if (inherits(values, c("numeric", "integer", "Date", "POSIXt"))) {
range_vals <- range(pretty(values, n = 10), na.rm = TRUE)
} else {
range_vals <- NULL
}
fmt <- function(x, time = inherits(values, c("Date", "POSIXt"))) {
if (is.null(x))
return(NULL)
if (time)
x <- format_date(x)
x
}
waxis <- switch(
axis,
"x" = "xaxis",
"y" = "yaxis"
)
if (scales == "fixed") {
ax$x$ax_opts[[waxis]]$min <- ax$x$ax_opts[[waxis]]$min %||% fmt(range_vals[1])
ax$x$ax_opts[[waxis]]$max <- ax$x$ax_opts[[waxis]]$max %||% fmt(range_vals[2])
} else if (scales == "free") {
ax$x$ax_opts[[waxis]]$min <- NULL
ax$x$ax_opts[[waxis]]$max <- NULL
} else if (scales == "free_x") {
if (axis == "y") {
ax$x$ax_opts[[waxis]]$min <- ax$x$ax_opts[[waxis]]$min %||% fmt(range_vals[1])
ax$x$ax_opts[[waxis]]$max <- ax$x$ax_opts[[waxis]]$max %||% fmt(range_vals[2])
} else {
ax$x$ax_opts[[waxis]]$min <- NULL
ax$x$ax_opts[[waxis]]$max <- NULL
}
} else if (scales == "free_y") {
if (axis == "x") {
ax$x$ax_opts[[waxis]]$min <- ax$x$ax_opts[[waxis]]$min %||% fmt(range_vals[1])
ax$x$ax_opts[[waxis]]$max <- ax$x$ax_opts[[waxis]]$max %||% fmt(range_vals[2])
} else {
ax$x$ax_opts[[waxis]]$min <- NULL
ax$x$ax_opts[[waxis]]$max <- NULL
}
}
return(ax)
}
get_option <- function(ax, opt1, opt2 = NULL) {
if (is.null(opt2)) {
ax$x$ax_opts[[opt1]]
} else {
ax$x$ax_opts[[opt1]][[opt2]]
}
}
remove_option <- function(ax, opt1, opt2 = NULL) {
if (is.null(opt2)) {
ax$x$ax_opts[[opt1]] <- NULL
} else {
ax$x$ax_opts[[opt1]][[opt2]] <- NULL
}
ax
}
#' @importFrom rlang eval_tidy is_null is_function #' @importFrom rlang eval_tidy is_null is_function
build_facets <- function(chart) { build_facets <- function(chart) {
@ -144,6 +34,14 @@ build_facets <- function(chart) {
byrow = TRUE byrow = TRUE
) )
lrow <- get_last_row(grid) lrow <- get_last_row(grid)
facet_data_add_line <- if (!is.null(chart$x$add_line)) {
get_facets(
data = chart$x$add_line$data,
rows = chart$x$facet$facets_row,
cols = chart$x$facet$facets_col,
type = chart$x$facet$type
)$facets
}
facets <- lapply( facets <- lapply(
X = nums, X = nums,
FUN = function(i) { FUN = function(i) {
@ -176,7 +74,37 @@ build_facets <- function(chart) {
if (!is.null(new$x$colors_manual)) { if (!is.null(new$x$colors_manual)) {
new <- ax_colors_manual(ax = new, values = new$x$colors_manual) new <- ax_colors_manual(ax = new, values = new$x$colors_manual)
} }
new$height <- chart$x$facet$chart_height if (!is.null(facet_data_add_line)) {
maplinedata <- lapply(chart$x$add_line$mapping, eval_tidy, data = facet_data_add_line[[i]])
if (chart$x$facet$scales %in% c("fixed", "free_y") & chart$x$type %in% c("bar")) {
maplinedata <- complete_mapdata(maplinedata, mapall)
}
if (chart$x$facet$scales %in% c("fixed", "free_x") & chart$x$type %in% c("column")) {
maplinedata <- complete_mapdata(maplinedata, mapall)
}
new$x$ax_opts$series <- c(
new$x$ax_opts$series,
make_series(
mapdata = maplinedata,
mapping = chart$x$add_line$mapping,
type = chart$x$add_line$type,
serie_name = chart$x$add_line$serie_name,
force_datetime_names = c("x", "y")
)
)
# new <- add_line(
# ax = new,
# mapping = chart$x$add_line$mapping,
# data = facet_data_add_line[[i]],
# type = chart$x$add_line$type,
# serie_name = chart$x$add_line$serie_name
# )
}
if (has_yaxis2(new)) {
values <- get_yaxis_serie(chart, 2)
new <- set_scale(new, values, scales = chart$x$facet$scales, axis = "y2")
}
new$height <- chart$height %||% chart$x$facet$chart_height
new$x$facet <- NULL new$x$facet <- NULL
class(new) <- setdiff(class(new), "apex_facet") class(new) <- setdiff(class(new), "apex_facet")
return(new) return(new)
@ -207,23 +135,24 @@ get_last_row <- function(mat) {
#' @title Facets for ApexCharts #' @title Facets for ApexCharts
#' #'
#' @description Create matrix of charts by row and column faceting variable (`ax_facet_grid`), #' @description Create matrix of charts by row and column faceting variable (`ax_facet_grid`),
#' or by specified number of row and column for faceting variable(s) (`ax_facet_wrap`). #' or by specified number of row and column for faceting variable(s) (`ax_facet_wrap`).
#' #'
#' @param ax An [apexchart()] `htmlwidget` object. #' @param ax An [apexchart()] `htmlwidget` object.
#' @param facets Variable(s) to use for facetting, wrapped in `vars(...)`. #' @param facets Variable(s) to use for facetting, wrapped in `vars(...)`.
#' @param nrow,ncol Number of row and column in output matrix. #' @param nrow,ncol Number of row and column in output matrix.
#' @param scales Should scales be fixed (`"fixed"`, the default), #' @param scales Should scales be fixed (`"fixed"`, the default),
#' free (`"free"`), or free in one dimension (`"free_x"`, `"free_y"`)? #' free (`"free"`), or free in one dimension (`"free_x"`, `"free_y"`)?
#' @param labeller A function with one argument containing for each facet the value of the faceting variable. #' @param labeller A function with one argument containing for each facet the value of the faceting variable.
#' @param chart_height Individual chart height. #' @param chart_height Individual chart height, ignored if an height is defined in `apex()` or `apexcharter()`.
#' @param grid_width Total width for the grid, regardless of the number of column.
#' #'
#' @return An [apexchart()] `htmlwidget` object with an additionnal class `"apex_facet"`. #' @return An [apexchart()] `htmlwidget` object with an additionnal class `"apex_facet"`.
#' #'
#' @details # Warning #' @details # Warning
#' To properly render in Shiny applications, use [apexfacetOutput()] (in UI) and [renderApexfacet()] (in Server). #' To properly render in Shiny applications, use [apexfacetOutput()] (in UI) and [renderApexfacet()] (in Server).
#' #'
#' @export #' @export
#' #'
#' @name apex-facets #' @name apex-facets
@ -237,7 +166,8 @@ ax_facet_wrap <- function(ax,
ncol = NULL, ncol = NULL,
scales = c("fixed", "free", "free_y", "free_x"), scales = c("fixed", "free", "free_y", "free_x"),
labeller = label_value, labeller = label_value,
chart_height = "300px") { chart_height = "300px",
grid_width = "100%") {
if (!inherits(ax, "apex")) if (!inherits(ax, "apex"))
stop("ax_facet_wrap only works with charts generated with apex()", call. = FALSE) stop("ax_facet_wrap only works with charts generated with apex()", call. = FALSE)
scales <- match.arg(scales) scales <- match.arg(scales)
@ -250,6 +180,7 @@ ax_facet_wrap <- function(ax,
scales = scales, scales = scales,
labeller = labeller, labeller = labeller,
chart_height = chart_height, chart_height = chart_height,
grid_width = grid_width,
type = "wrap" type = "wrap"
) )
class(ax) <- c("apex_facet", class(ax)) class(ax) <- c("apex_facet", class(ax))
@ -269,7 +200,8 @@ ax_facet_grid <- function(ax,
cols = NULL, cols = NULL,
scales = c("fixed", "free", "free_y", "free_x"), scales = c("fixed", "free", "free_y", "free_x"),
labeller = label_value, labeller = label_value,
chart_height = "300px") { chart_height = "300px",
grid_width = "100%") {
if (!inherits(ax, "apex")) if (!inherits(ax, "apex"))
stop("ax_facet_wrap only works with charts generated with apex()", call. = FALSE) stop("ax_facet_wrap only works with charts generated with apex()", call. = FALSE)
scales <- match.arg(scales) scales <- match.arg(scales)
@ -285,6 +217,7 @@ ax_facet_grid <- function(ax,
scales = scales, scales = scales,
labeller = labeller, labeller = labeller,
chart_height = chart_height, chart_height = chart_height,
grid_width = grid_width,
type = "grid" type = "grid"
) )
class(ax) <- c("apex_facet", class(ax)) class(ax) <- c("apex_facet", class(ax))
@ -298,6 +231,7 @@ ax_facet_grid <- function(ax,
# Tag --------------------------------------------------------------------- # Tag ---------------------------------------------------------------------
#' @importFrom rlang %||% #' @importFrom rlang %||%
#' @importFrom htmltools tags css validateCssUnit
build_facet_tag <- function(x) { build_facet_tag <- function(x) {
facets <- build_facets(x) facets <- build_facets(x)
content <- facets$facets content <- facets$facets
@ -359,10 +293,10 @@ build_facet_tag <- function(x) {
} }
if (identical(facets$type, "wrap")) { if (identical(facets$type, "wrap")) {
TAG <- build_grid( TAG <- build_grid(
content = content, content = content,
nrow = d$nrow, nrow = d$nrow,
ncol = d$ncol, ncol = d$ncol,
row_after = row_after, row_after = row_after,
col_before = col_before col_before = col_before
) )
} else if (identical(facets$type, "grid")) { } else if (identical(facets$type, "grid")) {
@ -398,7 +332,7 @@ build_facet_tag <- function(x) {
col_after = if (!is.null(facets$nrow)) "30px", col_after = if (!is.null(facets$nrow)) "30px",
row_gap = "3px", row_gap = "3px",
col_gap = "3px", col_gap = "3px",
row_after = row_after, row_after = row_after,
col_before = col_before col_before = col_before
) )
} else { } else {
@ -424,6 +358,11 @@ build_facet_tag <- function(x) {
TAG TAG
) )
} }
TAG <- tags$div(
style = css(width = validateCssUnit(x$x$facet$grid_width)),
class = "apexcharter-facet",
TAG
)
return(TAG) return(TAG)
} }
@ -462,8 +401,8 @@ apexfacetOutput <- function(outputId) {
#' @param env The environment in which to evaluate `expr`. #' @param env The environment in which to evaluate `expr`.
#' @param quoted Is `expr` a quoted expression (with `quote()`)? This #' @param quoted Is `expr` a quoted expression (with `quote()`)? This
#' is useful if you want to save an expression in a variable. #' is useful if you want to save an expression in a variable.
#' #'
#' @seealso [ax_facet_wrap()], [ax_facet_grid()] #' @seealso [ax_facet_wrap()], [ax_facet_grid()]
#' #'
#' @export #' @export
#' #'
@ -524,8 +463,8 @@ complete_mapdata <- function(mapdata, mapall) {
data <- as.data.frame(mapdata) data <- as.data.frame(mapdata)
full_x <- unique(mapall$x) full_x <- unique(mapall$x)
full_data <- data.frame( full_data <- data.frame(
xorder = seq_along(full_x), xorder = seq_along(full_x),
x = full_x, x = full_x,
stringsAsFactors = FALSE stringsAsFactors = FALSE
) )
full_data <- merge( full_data <- merge(
@ -557,4 +496,3 @@ complete_data <- function(data, vars, fill_var, fill_value = 0) {
return(full_data) return(full_data)
} }

View File

@ -1,6 +1,6 @@
#' @title Add a line to a chart #' @title Add a line to a chart
#' #'
#' @description Add a line to an existing chart (bar, scatter and line types supported). #' @description Add a line to an existing chart (bar, scatter and line types supported).
#' On scatter charts you can also add a smooth line. #' On scatter charts you can also add a smooth line.
#' #'
@ -12,14 +12,14 @@
#' @param serie_name Name for the serie displayed in tooltip and legend. #' @param serie_name Name for the serie displayed in tooltip and legend.
#' #'
#' @export #' @export
#' #'
#' @name add-line #' @name add-line
#' #'
#' @example examples/mixed-charts.R #' @example examples/mixed-charts.R
add_line <- function(ax, add_line <- function(ax,
mapping, mapping,
data = NULL, data = NULL,
type = c("line", "spline"), type = c("line", "spline"),
serie_name = NULL) { serie_name = NULL) {
type <- match.arg(type) type <- match.arg(type)
if (!inherits(ax, "apex")) if (!inherits(ax, "apex"))
@ -30,9 +30,10 @@ add_line <- function(ax,
} else { } else {
apex_type <- ax$x$mixed_type apex_type <- ax$x$mixed_type
} }
if (!isTRUE(apex_type %in% c("line", "bar", "scatter", "candlestick"))) if (!isTRUE(apex_type %in% c("line", "bar", "scatter", "candlestick", "rangeArea")))
stop("add_line: apex() must be a column, scatter or candlestick chart.", call. = FALSE) stop("add_line: apex() must be a column, scatter or candlestick chart.", call. = FALSE)
ax$x$ax_opts$chart$type <- "line" if (!identical(apex_type, "rangeArea"))
ax$x$ax_opts$chart$type <- "line"
if (is.null(data)) if (is.null(data))
data <- ax$x$data data <- ax$x$data
data <- as.data.frame(data) data <- as.data.frame(data)
@ -41,6 +42,12 @@ add_line <- function(ax,
ax$x$ax_opts$series, ax$x$ax_opts$series,
make_series(mapdata, mapping, type, serie_name, force_datetime_names = c("x", "y")) make_series(mapdata, mapping, type, serie_name, force_datetime_names = c("x", "y"))
) )
ax$x$add_line <- list(
data = data,
mapping = mapping,
type = type,
serie_name = serie_name
)
if (identical(apex_type, "scatter")) { if (identical(apex_type, "scatter")) {
if (is.null(ax$x$ax_opts$markers$size)) { if (is.null(ax$x$ax_opts$markers$size)) {
ax$x$ax_opts$markers$size <- c(6, 0) ax$x$ax_opts$markers$size <- c(6, 0)
@ -77,14 +84,14 @@ add_line <- function(ax,
#' @param model Model to use between \code{\link{lm}} or \code{\link{loess}}. #' @param model Model to use between \code{\link{lm}} or \code{\link{loess}}.
#' @param n Number of points used for predictions. #' @param n Number of points used for predictions.
#' @param ... Arguments passed to \code{model}. #' @param ... Arguments passed to \code{model}.
#' #'
#' @export #' @export
#' #'
#' @importFrom stats lm loess predict #' @importFrom stats lm loess predict
#' @importFrom rlang !! sym #' @importFrom rlang !! sym
#' #'
#' @name add-line #' @name add-line
add_smooth_line <- function(ax, add_smooth_line <- function(ax,
formula = y ~ x, formula = y ~ x,
model = c("lm", "loess"), model = c("lm", "loess"),
n = 100, n = 100,
@ -114,14 +121,14 @@ add_smooth_line <- function(ax,
} }
new_data <- data.frame(x = seq( new_data <- data.frame(x = seq(
from = min(mapdata$x, na.rm = TRUE), from = min(mapdata$x, na.rm = TRUE),
to = max(mapdata$x, na.rm = TRUE), to = max(mapdata$x, na.rm = TRUE),
length.out = n length.out = n
)) ))
new_data$smooth <- predict(model_results, new_data) new_data$smooth <- predict(model_results, new_data)
add_line( add_line(
ax = ax, ax = ax,
mapping = aes(x = `!!`(sym("x")), y = `!!`(sym("smooth"))), mapping = aes(x = `!!`(sym("x")), y = `!!`(sym("smooth"))),
data = new_data, data = new_data,
type = type, type = type,
serie_name = serie_name serie_name = serie_name
) )

View File

@ -110,6 +110,44 @@ parse_timeline_data <- function(.list) {
} }
parse_dumbbell_data <- function(.list) {
if (is.null(.list$group)) {
lapply(
X = seq_len(length(.list[[1]])),
FUN = function(i) {
val <- lapply(.list, `[[`, i)
l <- list(
x = as.character(val$y),
y = list(val$x, val$xend)
)
if (!is.null(val$fill)) {
l$fillColor <- val$fill
}
l
}
)
} else {
grouped <- as.data.frame(.list, stringsAsFactors = FALSE)
grouped$group <- NULL
grouped <- split(
x = grouped,
f = .list$group
)
grouped <- lapply(grouped, as.list)
lapply(
X = names(grouped),
FUN = function(name) {
list(
name = name,
data = parse_dumbbell_data(grouped[[name]])
)
}
)
}
}
parse_candlestick_data <- function(.list) { parse_candlestick_data <- function(.list) {
list(list( list(list(
type = "candlestick", type = "candlestick",
@ -127,3 +165,30 @@ parse_candlestick_data <- function(.list) {
)) ))
} }
#' @importFrom graphics boxplot
parse_boxplot_data <- function(.list, serie_name = NULL) {
if (!is.numeric(.list$y) & is.numeric(.list$x)) {
.list[c("x", "y")] <- .list[c("y", "x")]
}
boxed <- boxplot(y ~ x, data = .list, plot = FALSE)
list(dropNulls(list(
serie_name = serie_name,
type = "boxPlot",
data = lapply(
X = seq_along(boxed$names),
FUN = function(i) {
list(
x = boxed$names[i],
y = c(
boxed$stats[1, i],
boxed$stats[2, i],
boxed$stats[3, i],
boxed$stats[4, i],
boxed$stats[5, i]
)
)
}
)
)))
}

View File

@ -5,7 +5,10 @@ null_or_empty <- function(x) {
dropNullsOrEmpty <- function(x) { dropNullsOrEmpty <- function(x) {
x[!vapply(x, null_or_empty, FUN.VALUE = logical(1))] clss <- class(x)
x <- x[!vapply(x, null_or_empty, FUN.VALUE = logical(1))]
class(x) <- clss
return(x)
} }
dropNulls <- function(x) { dropNulls <- function(x) {

View File

@ -1,16 +1,15 @@
# apexcharter # apexcharter
> Htmlwidget for [apexcharts.js](https://github.com/apexcharts/apexcharts.js) : A modern JavaScript charting library to build interactive charts and visualizations with simple API. See the [online demo](https://dreamrs.github.io/apexcharter/) for examples. > Htmlwidget for [apexcharts.js](https://github.com/apexcharts/apexcharts.js) : A modern JavaScript charting library to build interactive charts and visualizations with simple API. See the [online documentation](https://dreamrs.github.io/apexcharter/) for examples.
<!-- badges: start --> <!-- badges: start -->
[![version](http://www.r-pkg.org/badges/version/apexcharter)](https://CRAN.R-project.org/package=apexcharter) [![CRAN status](https://www.r-pkg.org/badges/version/apexcharter)](https://CRAN.R-project.org/package=apexcharter)
[![cran checks](https://cranchecks.info/badges/worst/apexcharter)](https://cranchecks.info/pkgs/apexcharter) [![cran checks](https://badges.cranchecks.info/worst/apexcharter.svg)](https://cran.r-project.org/web/checks/check_results_apexcharter.html)
[![Codecov test coverage](https://codecov.io/gh/dreamRs/apexcharter/branch/master/graph/badge.svg)](https://app.codecov.io/gh/dreamRs/apexcharter?branch=master) [![Codecov test coverage](https://codecov.io/gh/dreamRs/apexcharter/branch/master/graph/badge.svg)](https://app.codecov.io/gh/dreamRs/apexcharter?branch=master)
[![R-CMD-check](https://github.com/dreamRs/apexcharter/workflows/R-CMD-check/badge.svg)](https://github.com/dreamRs/apexcharter/actions) [![R-CMD-check](https://github.com/dreamRs/apexcharter/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/dreamRs/apexcharter/actions/workflows/R-CMD-check.yaml)
<!-- badges: end --> <!-- badges: end -->
## Installation ## Installation
Install from [CRAN](https://CRAN.R-project.org/package=apexcharter) with: Install from [CRAN](https://CRAN.R-project.org/package=apexcharter) with:

View File

@ -6,6 +6,9 @@ template:
bslib: bslib:
base_font: {google: "Poppins"} base_font: {google: "Poppins"}
primary: "#112446" primary: "#112446"
navbar-dark-color: "#FFFFFF"
secondary: "#DFDFDF"
navbar-dark-active-color: "#DFDFDF"
navbar: navbar:
bg: primary bg: primary

View File

@ -1,6 +1,6 @@
## Test environments ## Test environments
* local OS Widows 10 install, R 4.1.2 * local Ubuntu 22.04 install, R 4.2.2
* ubuntu 20.04, Windows 10, macOS (on GitHub Actions), R 4.1.2 * ubuntu 22.04, Windows 10, macOS (on GitHub Actions), R 4.2.2
* win-builder (devel and release) * win-builder (devel and release)
## R CMD check results ## R CMD check results

131
data-raw/eco2mix.R Normal file
View File

@ -0,0 +1,131 @@
# ------------------------------------------------------------------------
#
# eCO2mix data
# https://www.rte-france.com/eco2mix
#
# ------------------------------------------------------------------------
# Packages ----------------------------------------------------------------
library(data.table)
library(fasttime)
complete <- function(data, vars, fill = list()) {
data <- data[do.call(CJ, c(
lapply(
X = mget(vars),
FUN = function(var) {
if (inherits(var, "factor")) {
if (anyNA(var)) {
factor(c(levels(var), NA_character_), levels = levels(var), ordered = is.ordered(var))
} else {
factor(levels(var), levels = levels(var), ordered = is.ordered(var))
}
} else {
unique(var)
}
}
),
list(sorted = FALSE)
)), on = vars]
if (length(fill) > 0 && all(nzchar(names(fill)))) {
for (fillvar in names(fill)) {
data[is.na(get(fillvar)), (fillvar) := fill[[fillvar]]]
}
}
data[]
}
# Download data -----------------------------------------------------------
# Source: https://odre.opendatasoft.com/explore/dataset/eco2mix-national-cons-def/
# and https://odre.opendatasoft.com/explore/dataset/eco2mix-national-tr
# Read & transform data ---------------------------------------------------
# eco2mix <- fread(file = "data-raw/inputs/eco2mix-national-cons-def.csv")
# eco2mix <- eco2mix[, c(5, 6, 9:17)]
# setnames(eco2mix, c("datetime", "consumption", "fuel", "coal", "gas", "nuclear", "wind", "solar", "hydraulic", "pumping", "bioenergies"))
eco2mix_tr <- fread(file = "data-raw/inputs/eco2mix-national-tr.csv")
eco2mix_tr <- eco2mix_tr[, c(5, 6, 9:17)]
setnames(eco2mix_tr, c("datetime", "consumption", "fuel", "coal", "gas", "nuclear", "wind", "solar", "hydraulic", "pumping", "bioenergies"))
eco2mix <- copy(eco2mix_tr)
# eco2mix <- rbind(eco2mix, eco2mix_tr)
eco2mix <- eco2mix[!is.na(consumption)]
eco2mix[, consumption := NULL]
# eco2mix[, date := as.Date(format(datetime, format = "%Y-%m-%d"))]
# eco2mix[, datetime := NULL]
# setcolorder(eco2mix, "date")
eco2mix <- eco2mix[minute(datetime) != 15]
eco2mix <- eco2mix[minute(datetime) != 45]
eco2mix <- eco2mix[datetime >= (max(datetime) - 24*60*60*7)]
eco2mix <- melt(
data = eco2mix,
id.vars = 1,
variable.name = "source",
value.name = "production",
na.rm = TRUE,
variable.factor = FALSE
)
eco2mix <- eco2mix[, list(production = round(mean(production))), by = list(datetime, source)]
eco2mix[, source := factor(
x = source,
levels = c("pumping", "wind", "solar", "nuclear", "hydraulic", "gas", "coal", "fuel", "bioenergies"),
ordered = TRUE
)]
eco2mix <- complete(eco2mix, c("datetime", "source"), list(production = 0))
setorder(eco2mix, source, datetime)
eco2mix[]
# Use data ----------------------------------------------------------------
setDF(eco2mix)
usethis::use_data(eco2mix, internal = FALSE, overwrite = TRUE, compress = "xz")
# Test example ------------------------------------------------------------
apex(eco2mix[source == "consumption"], aes(date, production), type = "line")
# data("eco2mix", package = "apexcharter")
apex(eco2mix, aes(datetime, production, fill = source), type = "area") %>%
ax_chart(animations = list(enabled = FALSE), stacked = TRUE) %>%
ax_stroke(width = 1) %>%
ax_fill(opacity = 1, type = "solid") %>%
ax_tooltip(x = list(format = "dd MMM, HH:mm")) %>%
ax_yaxis(labels = list(formatter = format_num("~", suffix = "MW"))) %>%
ax_colors_manual(
list(
"bioenergies" = "#156956",
"fuel" = "#80549f",
"coal" = "#a68832",
"solar" = "#d66b0d",
"gas" = "#f20809",
"wind" = "#72cbb7",
"hydraulic" = "#2672b0",
"nuclear" = "#e4a701",
"pumping" = "#0e4269"
)
) %>%
ax_labs(
title = "Electricity generation by sector in France",
subtitle = "Data from \u00e9CO\u2082mix"
)

View File

@ -0,0 +1,70 @@
# Package -----------------------------------------------------------------
library(data.table)
library(gapminder)
# Data --------------------------------------------------------------------
life_expec_long <- as.data.table(gapminder::gapminder)
life_expec_long <- life_expec_long[year %in% c(1972, 2007), list(country, year, lifeExp)]
# life_expec <- life_expec[country %in% sample(unique(country), 10)]
life_expec_long <- life_expec_long[country %in% c("Botswana", "Ghana", "Iran", "Liberia", "Malaysia", "Mexico",
"Nigeria", "Pakistan", "Philippines", "Zambia")]
life_expec_long[, country := as.character(country)]
life_expec_long[, lifeExp := round(lifeExp, 1)]
life_expec <- dcast(life_expec_long, country ~ year, value.var = "lifeExp")
life_expec[, type := fifelse(`1972` > `2007`, "decreased", "increased")]
life_expec_long <- melt(data = life_expec, id.vars = c("country", "type"), variable.name = "year", value.name = "lifeExp")
# Use data ----------------------------------------------------------------
setDF(life_expec)
usethis::use_data(life_expec, internal = FALSE, overwrite = TRUE, compress = "xz")
setDF(life_expec_long)
usethis::use_data(life_expec_long, internal = FALSE, overwrite = TRUE, compress = "xz")
# Test example ------------------------------------------------------------
pkgload::load_all()
apex(life_expec, aes(country, x = `1972`, xend = `2007`), type = "dumbbell") %>%
ax_plotOptions(
bar = bar_opts(
dumbbellColors = list(list("#3d85c6", "#fb6003"))
)
) %>%
ax_colors("#BABABA") %>%
ax_labs(
title = "Life expectancy : 1972 vs. 2007",
subtitle = "Data from Gapminder dataset",
x = "Life expectancy at birth, in years"
)
apex(life_expec, aes(country, x = `1972`, xend = `2007`, group = type), type = "dumbbell") %>%
ax_xaxis(type = "category", categories = unique(life_expec$country)) %>%
ax_plotOptions(
bar = bar_opts(
dumbbellColors = list(list("#3d85c6", "#fb6003"), list("#3d85c6", "#fb6003"))
)
) %>%
ax_colors(c("#3d85c6", "#fb6003")) %>%
ax_labs(
title = "Life expectancy : 1972 vs. 2007",
subtitle = "Data from Gapminder dataset",
x = "Life expectancy at birth, in years"
)

63
data-raw/temperature.R Normal file
View File

@ -0,0 +1,63 @@
# ------------------------------------------------------------------------
#
# temperature data for France
# https://data.enedis.fr/explore/dataset/donnees-de-temperature-et-de-pseudo-rayonnement
#
# ------------------------------------------------------------------------
# Packages ----------------------------------------------------------------
library(data.table)
library(fasttime)
# Data --------------------------------------------------------------------
temperatures <- fread(file = "data-raw/inputs/donnees-de-temperature-et-de-pseudo-rayonnement.csv")
temperatures <- temperatures[, c(6, 7, 8, 2)]
setnames(temperatures, c("year", "month", "day", "temperature"))
temperatures <- temperatures[year > 2017]
temperatures <- temperatures[, list(temperature = round(mean(temperature, na.rm = TRUE), 1)), by = c("year", "month", "day")]
temperatures <- dcast(data = temperatures, formula = month + day ~ year, value.var = "temperature")
temperatures <- temperatures[!(month == 2 & day == 29)]
temperatures[, low := do.call(pmin, c(as.list(.SD), na.rm = TRUE)), .SDcols = as.character(2018:2021)]
temperatures[, high := do.call(pmax, c(as.list(.SD), na.rm = TRUE)), .SDcols = as.character(2018:2021)]
temperatures[, average := rowMeans(.SD, na.rm = TRUE), .SDcols = as.character(2018:2021)]
temperatures[, (as.character(2018:2021)) := NULL]
# setnames(temperatures, "2022", "temperature")
temperatures[, date := as.Date("2022-01-01") + (seq_len(.N) - 1)]
temperatures[, (c("month", "day")) := NULL]
setcolorder(temperatures, "date")
temperatures[]
# Save --------------------------------------------------------------------
setDF(temperatures)
usethis::use_data(temperatures, internal = FALSE, overwrite = TRUE, compress = "xz")
# Test example ------------------------------------------------------------
pkgload::load_all()
apex(temperatures, aes(x = date, ymin = low, ymax = high), type = "rangeArea", serie_name = "Low/High (2018-2021)") %>%
add_line(aes(date, `2023`)) %>%
ax_chart(animations = list(enabled = FALSE)) %>%
ax_yaxis(tickAmount = 7, labels = list(formatter = format_num("~", suffix = "°C"))) %>%
ax_colors(c("#8485854D", "#FF0000")) %>%
ax_stroke(width = c(1, 2)) %>%
ax_fill(opacity = 1, type = "solid") %>%
ax_labs(
title = "Temperatures in 2023 with range from 2018 to 2021",
subtitle = "Data from ENEDIS"
)

BIN
data/eco2mix.rda Normal file

Binary file not shown.

BIN
data/life_expec.rda Normal file

Binary file not shown.

BIN
data/life_expec_long.rda Normal file

Binary file not shown.

BIN
data/temperatures.rda Normal file

Binary file not shown.

View File

@ -3,7 +3,7 @@ library(apexcharter)
data("presidential", package = "ggplot2") data("presidential", package = "ggplot2")
# Basic (with formated date in tooltip) # Basic (with formated date in tooltip)
apex(presidential, aes(x = name, start = start, end = end), "timeline") %>% apex(presidential, aes(x = name, start = start, end = end), "timeline") %>%
ax_tooltip( ax_tooltip(
x = list( x = list(
format = "yyyy" format = "yyyy"
@ -11,20 +11,37 @@ apex(presidential, aes(x = name, start = start, end = end), "timeline") %>%
) )
# With groups # With groups
apex(presidential, apex(
aes(x = name, start = start, end = end, group = party), presidential,
"timeline") aes(x = name, start = start, end = end, group = party),
"timeline"
)
# With groups but force position # With groups but force position
apex(presidential, apex(
aes(x = name, start = start, end = end, group = party), presidential,
"timeline") %>% aes(x = name, start = start, end = end, group = party),
ax_xaxis(categories = presidential$name) "timeline"
# Bush appears twice ) %>%
ax_plotOptions(
bar = bar_opts(rangeBarGroupRows = TRUE)
) %>%
ax_xaxis(categories = unique(presidential$name))
# With custom colors # With custom colors
presidential$color <- ifelse(presidential$party == "Democratic", "#00355f", "#c51c22")
apex(presidential, apex(
aes(x = name, start = start, end = end, fill = color), presidential,
"timeline") aes(x = name, start = start, end = end, group = party),
"timeline"
) %>%
ax_plotOptions(
bar = bar_opts(rangeBarGroupRows = TRUE)
) %>%
ax_xaxis(categories = unique(presidential$name)) %>%
ax_colors_manual(list(
Democratic = "#00355f",
Republican = "#c51c22"
))

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
/*! /*!
* ApexCharts v3.33.1 * ApexCharts v3.49.1
* (c) 2018-2022 ApexCharts * (c) 2018-2024 ApexCharts
* Released under the MIT License. * Released under the MIT License.
*/ */

View File

@ -13,7 +13,8 @@ ax_facet_wrap(
ncol = NULL, ncol = NULL,
scales = c("fixed", "free", "free_y", "free_x"), scales = c("fixed", "free", "free_y", "free_x"),
labeller = label_value, labeller = label_value,
chart_height = "300px" chart_height = "300px",
grid_width = "100\%"
) )
ax_facet_grid( ax_facet_grid(
@ -22,7 +23,8 @@ ax_facet_grid(
cols = NULL, cols = NULL,
scales = c("fixed", "free", "free_y", "free_x"), scales = c("fixed", "free", "free_y", "free_x"),
labeller = label_value, labeller = label_value,
chart_height = "300px" chart_height = "300px",
grid_width = "100\%"
) )
} }
\arguments{ \arguments{
@ -37,7 +39,9 @@ free (\code{"free"}), or free in one dimension (\code{"free_x"}, \code{"free_y"}
\item{labeller}{A function with one argument containing for each facet the value of the faceting variable.} \item{labeller}{A function with one argument containing for each facet the value of the faceting variable.}
\item{chart_height}{Individual chart height.} \item{chart_height}{Individual chart height, ignored if an height is defined in \code{apex()} or \code{apexcharter()}.}
\item{grid_width}{Total width for the grid, regardless of the number of column.}
\item{rows, cols}{A set of variables or expressions quoted by \code{vars()} \item{rows, cols}{A set of variables or expressions quoted by \code{vars()}
and defining faceting groups on the rows or columns dimension.} and defining faceting groups on the rows or columns dimension.}

View File

@ -30,7 +30,7 @@ a \code{data.frame}, it will be coerced to with \code{as.data.frame}.}
\code{"pie"}, \code{"donut"}, \code{"pie"}, \code{"donut"},
\code{"radialBar"}, \code{"radar"}, \code{"scatter"}, \code{"radialBar"}, \code{"radar"}, \code{"scatter"},
\code{"heatmap"}, \code{"treemap"}, \code{"heatmap"}, \code{"treemap"},
\code{"timeline"}.} \code{"timeline"}, \code{"dumbbell"} and \code{"slope"}.}
\item{...}{Other arguments passed on to methods. Not currently used.} \item{...}{Other arguments passed on to methods. Not currently used.}
@ -43,9 +43,7 @@ use \code{\link[=config_update]{config_update()}} for more control.}
\item{serie_name}{Name for the serie displayed in tooltip, \item{serie_name}{Name for the serie displayed in tooltip,
only used for single serie.} only used for single serie.}
\item{width}{A numeric input in pixels.} \item{width, height}{A numeric input in pixels.}
\item{height}{A numeric input in pixels.}
\item{elementId}{Use an explicit element ID for the widget.} \item{elementId}{Use an explicit element ID for the widget.}
} }

View File

@ -2,12 +2,22 @@
% Please edit documentation in R/apexcharter-package.R % Please edit documentation in R/apexcharter-package.R
\docType{package} \docType{package}
\name{apexcharter-package} \name{apexcharter-package}
\alias{apexcharter}
\alias{apexcharter-package} \alias{apexcharter-package}
\title{An \code{htmlwidget} interface to the \title{An \code{htmlwidget} interface to the
ApexCharts javascript chart library} ApexCharts javascript chart library}
\description{ \description{
This package allow you to use ApexCharts.js (\url{https://apexcharts.com/}), This package allow you to use ApexCharts.js (\url{https://apexcharts.com/}),
to create interactive and modern SVG charts. to create interactive and modern SVG charts.
}
\seealso{
Useful links:
\itemize{
\item \url{https://github.com/dreamRs/apexcharter}
\item \url{https://dreamrs.github.io/apexcharter/}
\item Report bugs at \url{https://github.com/dreamRs/apexcharter/issues}
}
} }
\author{ \author{
Victor Perrier (@dreamRs_fr) Victor Perrier (@dreamRs_fr)

View File

@ -0,0 +1,48 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/apex-utils.R
\name{ax_forecast_data_points}
\alias{ax_forecast_data_points}
\title{Forecast data points}
\usage{
ax_forecast_data_points(
ax,
count = NULL,
fillOpacity = NULL,
strokeWidth = NULL,
dashArray = NULL,
...
)
}
\arguments{
\item{ax}{An \code{\link[=apexchart]{apexchart()}} \code{htmlwidget} object.}
\item{count}{Number of ending data-points you want to indicate as a forecast or prediction values.
The ending line/bar will result into a dashed border with a distinct look to differentiate from the rest of the data-points.}
\item{fillOpacity}{Opacity of the fill attribute.}
\item{strokeWidth}{Sets the width of the points.}
\item{dashArray}{Creates dashes in borders of svg path. Higher number creates more space between dashes in the border.}
\item{...}{Additional arguments (not used).}
}
\value{
An \code{\link[=apexchart]{apexchart()}} \code{htmlwidget} object.
}
\description{
Forecast data points
}
\examples{
# add 5 predictions to data then plot it
data.frame(
time = seq_len(53),
lh = c(
as.vector(lh),
as.vector(predict(arima(lh, order = c(1,0,1)), 5)$pred)
)
) \%>\%
apex(aes(time, lh), type = "line") \%>\%
ax_xaxis(type = "numeric") \%>\%
ax_forecast_data_points(count = 5)
}

View File

@ -11,21 +11,24 @@ ax_plotOptions(
radialBar = NULL, radialBar = NULL,
pie = NULL, pie = NULL,
bubble = NULL, bubble = NULL,
boxPlot = NULL,
... ...
) )
} }
\arguments{ \arguments{
\item{ax}{An \code{\link[=apexchart]{apexchart()}} \code{htmlwidget} object.} \item{ax}{An \code{\link[=apexchart]{apexchart()}} \code{htmlwidget} object.}
\item{bar}{See \code{\link{bar_opts}}.} \item{bar}{See \code{\link[=bar_opts]{bar_opts()}}.}
\item{heatmap}{See \code{\link{heatmap_opts}}.} \item{heatmap}{See \code{\link[=heatmap_opts]{heatmap_opts()}}.}
\item{radialBar}{See \code{\link{radialBar_opts}}.} \item{radialBar}{See \code{\link[=radialBar_opts]{radialBar_opts()}}.}
\item{pie}{See \code{\link{pie_opts}}.} \item{pie}{See \code{\link[=pie_opts]{pie_opts()}}.}
\item{bubble}{See \code{\link{bubble_opts}}.} \item{bubble}{See \code{\link[=bubble_opts]{bubble_opts()}}.}
\item{boxPlot}{See \code{\link[=boxplot_opts]{boxplot_opts()}}.}
\item{...}{Additional parameters.} \item{...}{Additional parameters.}
} }

View File

@ -33,10 +33,10 @@ bar_opts(
\item{...}{Additional parameters.} \item{...}{Additional parameters.}
} }
\value{ \value{
A \code{list} of options that can be used in \code{\link{ax_plotOptions}}. A \code{list} of options that can be used in \code{\link[=ax_plotOptions]{ax_plotOptions()}}.
} }
\description{ \description{
Use these options in \code{\link{ax_plotOptions}}. Use these options in \code{\link[=ax_plotOptions]{ax_plotOptions()}}.
} }
\note{ \note{
See \url{https://apexcharts.com/docs/options/plotoptions/bar/}. See \url{https://apexcharts.com/docs/options/plotoptions/bar/}.

31
man/boxplot_opts.Rd Normal file
View File

@ -0,0 +1,31 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/apex-options.R
\name{boxplot_opts}
\alias{boxplot_opts}
\title{Boxplot options}
\usage{
boxplot_opts(color.upper, color.lower, ...)
}
\arguments{
\item{color.upper}{Color for the upper quartile (Q3 to median) of the box plot.}
\item{color.lower}{Color for the lower quartile (median to Q1) of the box plot.}
\item{...}{Additional parameters.}
}
\value{
A \code{list} of options that can be used in \code{\link[=ax_plotOptions]{ax_plotOptions()}}.
}
\description{
Use these options in \code{\link[=ax_plotOptions]{ax_plotOptions()}}.
}
\note{
See \url{https://apexcharts.com/docs/options/plotoptions/boxplot/}.
}
\examples{
data("mpg", package = "ggplot2")
apex(mpg, aes(class, hwy), "boxplot") \%>\%
ax_plotOptions(
boxPlot = boxplot_opts(color.upper = "#848484", color.lower = "#848484" )
)
}

View File

@ -16,10 +16,10 @@ If a bubble value is too large to cover the chart, this size will be used.}
\item{...}{Additional parameters.} \item{...}{Additional parameters.}
} }
\value{ \value{
A \code{list} of options that can be used in \code{\link{ax_plotOptions}}. A \code{list} of options that can be used in \code{\link[=ax_plotOptions]{ax_plotOptions()}}.
} }
\description{ \description{
Use these options in \code{\link{ax_plotOptions}}. Use these options in \code{\link[=ax_plotOptions]{ax_plotOptions()}}.
} }
\note{ \note{
See \url{https://apexcharts.com/docs/options/plotoptions/bubble/}. See \url{https://apexcharts.com/docs/options/plotoptions/bubble/}.

19
man/eco2mix.Rd Normal file
View File

@ -0,0 +1,19 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/data.R
\docType{data}
\name{eco2mix}
\alias{eco2mix}
\title{eco2mix data}
\format{
A data frame with 3,033 observations and 3 variables.
}
\source{
Rte (Réseau et transport d'électricité) (\url{https://www.rte-france.com/eco2mix} and \url{https://opendata.reseaux-energies.fr/})
}
\usage{
eco2mix
}
\description{
The dataset contains data about electricity consumption and production in France between 2012 and 2022.
}
\keyword{datasets}

View File

@ -24,10 +24,10 @@ heatmap_opts(
\item{...}{Additional parameters.} \item{...}{Additional parameters.}
} }
\value{ \value{
A \code{list} of options that can be used in \code{\link{ax_plotOptions}}. A \code{list} of options that can be used in \code{\link[=ax_plotOptions]{ax_plotOptions()}}.
} }
\description{ \description{
Use these options in \code{\link{ax_plotOptions}}. Use these options in \code{\link[=ax_plotOptions]{ax_plotOptions()}}.
} }
\note{ \note{
See \url{https://apexcharts.com/docs/options/plotoptions/heatmap/}. See \url{https://apexcharts.com/docs/options/plotoptions/heatmap/}.

19
man/life_expec.Rd Normal file
View File

@ -0,0 +1,19 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/data.R
\docType{data}
\name{life_expec}
\alias{life_expec}
\title{Life expectancy data}
\format{
A data frame with 10 observations and 4 variables.
}
\source{
gapminder package (\url{https://jennybc.github.io/gapminder/} and \url{https://www.gapminder.org/data/})
}
\usage{
life_expec
}
\description{
The dataset contains data about life expectancy in 1972 and 2007 for 10 countries.
}
\keyword{datasets}

19
man/life_expec_long.Rd Normal file
View File

@ -0,0 +1,19 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/data.R
\docType{data}
\name{life_expec_long}
\alias{life_expec_long}
\title{Life expectancy data (long format)}
\format{
A data frame with 20 observations and 3 variables.
}
\source{
gapminder package (\url{https://jennybc.github.io/gapminder/} and \url{https://www.gapminder.org/data/})
}
\usage{
life_expec_long
}
\description{
The dataset contains data about life expectancy in 1972 and 2007 for 10 countries.
}
\keyword{datasets}

View File

@ -31,10 +31,10 @@ and \code{background} (The background color of the pie).}
\item{...}{Additional parameters.} \item{...}{Additional parameters.}
} }
\value{ \value{
A \code{list} of options that can be used in \code{\link{ax_plotOptions}}. A \code{list} of options that can be used in \code{\link[=ax_plotOptions]{ax_plotOptions()}}.
} }
\description{ \description{
Use these options in \code{\link{ax_plotOptions}}. Use these options in \code{\link[=ax_plotOptions]{ax_plotOptions()}}.
} }
\note{ \note{
See \url{https://apexcharts.com/docs/options/plotoptions/pie/}. See \url{https://apexcharts.com/docs/options/plotoptions/pie/}.

View File

@ -39,10 +39,10 @@ radialBar_opts(
\item{...}{Additional parameters.} \item{...}{Additional parameters.}
} }
\value{ \value{
A \code{list} of options that can be used in \code{\link{ax_plotOptions}}. A \code{list} of options that can be used in \code{\link[=ax_plotOptions]{ax_plotOptions()}}.
} }
\description{ \description{
Use these options in \code{\link{ax_plotOptions}}. Use these options in \code{\link[=ax_plotOptions]{ax_plotOptions()}}.
} }
\note{ \note{
See \url{https://apexcharts.com/docs/options/plotoptions/radialbar/}. See \url{https://apexcharts.com/docs/options/plotoptions/radialbar/}.

19
man/temperatures.Rd Normal file
View File

@ -0,0 +1,19 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/data.R
\docType{data}
\name{temperatures}
\alias{temperatures}
\title{Temperature data}
\format{
A data frame with 365 observations and 6 variables.
}
\source{
Enedis (\url{https://data.enedis.fr/explore/dataset/donnees-de-temperature-et-de-pseudo-rayonnement/})
}
\usage{
temperatures
}
\description{
The dataset contains data about temperatures in France between 2018 and 2022.
}
\keyword{datasets}

View File

@ -15,7 +15,7 @@ Returned IDPs, Stateless persons, Others of concern.}
} }
} }
\source{ \source{
UNHCR (The UN Refugee Agency) (\url{https://www.unhcr.org/}) UNHCR (The UN Refugee Agency) (\url{https://data.unhcr.org/})
} }
\usage{ \usage{
unhcr_ts unhcr_ts

402
package-lock.json generated
View File

@ -9,13 +9,13 @@
"version": "1.0.0", "version": "1.0.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"apexcharts": "^3.33.1", "apexcharts": "^3.49.1",
"d3-format": "^3.0.1" "d3-format": "^3.0.1"
}, },
"devDependencies": { "devDependencies": {
"css-loader": "^6.5.1", "css-loader": "^6.5.1",
"style-loader": "^3.3.1", "style-loader": "^3.3.1",
"webpack": "^5.64.4", "webpack": "^5.76.0",
"webpack-cli": "^4.9.1", "webpack-cli": "^4.9.1",
"webpack-merge": "^5.8.0" "webpack-merge": "^5.8.0"
} }
@ -29,10 +29,68 @@
"node": ">=10.0.0" "node": ">=10.0.0"
} }
}, },
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
"dev": true,
"dependencies": {
"@jridgewell/set-array": "^1.0.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
"@jridgewell/trace-mapping": "^0.3.9"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
"dev": true,
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/set-array": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
"dev": true,
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/source-map": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
"integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
"dev": true,
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.0",
"@jridgewell/trace-mapping": "^0.3.9"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.14",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
"dev": true
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.14",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz",
"integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==",
"dev": true,
"dependencies": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"node_modules/@types/eslint": { "node_modules/@types/eslint": {
"version": "8.2.0", "version": "8.21.2",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.2.0.tgz", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.21.2.tgz",
"integrity": "sha512-74hbvsnc+7TEDa1z5YLSe4/q8hGYB3USNvCuzHUJrjPV6hXaq8IXcngCrHkuvFt0+8rFz7xYXrHgNayIX0UZvQ==", "integrity": "sha512-EMpxUyystd3uZVByZap1DACsMXvb82ypQnGn89e1Y0a+LYu3JJscUd/gqhRsVFDkaD2MIiWo0MT8EfXr3DGRKw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@types/estree": "*", "@types/estree": "*",
@ -40,9 +98,9 @@
} }
}, },
"node_modules/@types/eslint-scope": { "node_modules/@types/eslint-scope": {
"version": "3.7.1", "version": "3.7.4",
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.1.tgz", "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz",
"integrity": "sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g==", "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@types/eslint": "*", "@types/eslint": "*",
@ -50,9 +108,9 @@
} }
}, },
"node_modules/@types/estree": { "node_modules/@types/estree": {
"version": "0.0.50", "version": "0.0.51",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz",
"integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==",
"dev": true "dev": true
}, },
"node_modules/@types/json-schema": { "node_modules/@types/json-schema": {
@ -261,10 +319,15 @@
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
"dev": true "dev": true
}, },
"node_modules/@yr/monotone-cubic-spline": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@yr/monotone-cubic-spline/-/monotone-cubic-spline-1.0.3.tgz",
"integrity": "sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA=="
},
"node_modules/acorn": { "node_modules/acorn": {
"version": "8.6.0", "version": "8.8.2",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
"integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==", "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
"dev": true, "dev": true,
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
@ -308,10 +371,11 @@
} }
}, },
"node_modules/apexcharts": { "node_modules/apexcharts": {
"version": "3.33.1", "version": "3.49.1",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.33.1.tgz", "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.49.1.tgz",
"integrity": "sha512-5aVzrgJefd8EH4w7oRmuOhA3+cxJxQg27cYg3ANVGvPCOB4AY3mVVNtFHRFaIq7bv8ws4GRaA9MWfzoWQw3MPQ==", "integrity": "sha512-MqGtlq/KQuO8j0BBsUJYlRG8VBctKwYdwuBtajHgHTmSgUU3Oai+8oYN/rKCXwXzrUlYA+GiMgotAIbXY2BCGw==",
"dependencies": { "dependencies": {
"@yr/monotone-cubic-spline": "^1.0.3",
"svg.draggable.js": "^2.2.2", "svg.draggable.js": "^2.2.2",
"svg.easing.js": "^2.0.0", "svg.easing.js": "^2.0.0",
"svg.filter.js": "^2.0.2", "svg.filter.js": "^2.0.2",
@ -455,9 +519,9 @@
"dev": true "dev": true
}, },
"node_modules/enhanced-resolve": { "node_modules/enhanced-resolve": {
"version": "5.8.3", "version": "5.12.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz",
"integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==", "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"graceful-fs": "^4.2.4", "graceful-fs": "^4.2.4",
@ -625,9 +689,9 @@
"dev": true "dev": true
}, },
"node_modules/graceful-fs": { "node_modules/graceful-fs": {
"version": "4.2.8", "version": "4.2.10",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
"integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
"dev": true "dev": true
}, },
"node_modules/has": { "node_modules/has": {
@ -762,10 +826,10 @@
"node": ">= 10.13.0" "node": ">= 10.13.0"
} }
}, },
"node_modules/json-parse-better-errors": { "node_modules/json-parse-even-better-errors": {
"version": "1.0.2", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
"dev": true "dev": true
}, },
"node_modules/json-schema-traverse": { "node_modules/json-schema-traverse": {
@ -853,10 +917,16 @@
} }
}, },
"node_modules/nanoid": { "node_modules/nanoid": {
"version": "3.2.0", "version": "3.3.6",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
"integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
"dev": true, "dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"bin": { "bin": {
"nanoid": "bin/nanoid.cjs" "nanoid": "bin/nanoid.cjs"
}, },
@ -982,21 +1052,31 @@
} }
}, },
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.3.11", "version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.11.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
"integrity": "sha512-hCmlUAIlUiav8Xdqw3Io4LcpA1DOt7h3LSTAC4G6JGHFFaWzI6qvFt9oilvl8BmkbBRX1IhM90ZAmpk68zccQA==", "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
"dev": true, "dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"dependencies": { "dependencies": {
"nanoid": "^3.1.30", "nanoid": "^3.3.6",
"picocolors": "^1.0.0", "picocolors": "^1.0.0",
"source-map-js": "^0.6.2" "source-map-js": "^1.0.2"
}, },
"engines": { "engines": {
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
} }
}, },
"node_modules/postcss-modules-extract-imports": { "node_modules/postcss-modules-extract-imports": {
@ -1252,9 +1332,9 @@
} }
}, },
"node_modules/source-map-js": { "node_modules/source-map-js": {
"version": "0.6.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
@ -1403,13 +1483,14 @@
} }
}, },
"node_modules/terser": { "node_modules/terser": {
"version": "5.10.0", "version": "5.14.2",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz", "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz",
"integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==", "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@jridgewell/source-map": "^0.3.2",
"acorn": "^8.5.0",
"commander": "^2.20.0", "commander": "^2.20.0",
"source-map": "~0.7.2",
"source-map-support": "~0.5.20" "source-map-support": "~0.5.20"
}, },
"bin": { "bin": {
@ -1417,14 +1498,6 @@
}, },
"engines": { "engines": {
"node": ">=10" "node": ">=10"
},
"peerDependencies": {
"acorn": "^8.5.0"
},
"peerDependenciesMeta": {
"acorn": {
"optional": true
}
} }
}, },
"node_modules/terser-webpack-plugin": { "node_modules/terser-webpack-plugin": {
@ -1461,15 +1534,6 @@
} }
} }
}, },
"node_modules/terser/node_modules/source-map": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
"dev": true,
"engines": {
"node": ">= 8"
}
},
"node_modules/uri-js": { "node_modules/uri-js": {
"version": "4.4.1", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@ -1486,9 +1550,9 @@
"dev": true "dev": true
}, },
"node_modules/watchpack": { "node_modules/watchpack": {
"version": "2.3.0", "version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.0.tgz", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
"integrity": "sha512-MnN0Q1OsvB/GGHETrFeZPQaOelWh/7O+EiFlj8sM9GPjtQkis7k01aAxrg/18kTfoIVcLL+haEVFlXDaSRwKRw==", "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"glob-to-regexp": "^0.4.1", "glob-to-regexp": "^0.4.1",
@ -1499,35 +1563,35 @@
} }
}, },
"node_modules/webpack": { "node_modules/webpack": {
"version": "5.64.4", "version": "5.76.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.64.4.tgz", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.0.tgz",
"integrity": "sha512-LWhqfKjCLoYJLKJY8wk2C3h77i8VyHowG3qYNZiIqD6D0ZS40439S/KVuc/PY48jp2yQmy0mhMknq8cys4jFMw==", "integrity": "sha512-l5sOdYBDunyf72HW8dF23rFtWq/7Zgvt/9ftMof71E/yUb1YLOBmTgA2K4vQthB3kotMrSj609txVE0dnr2fjA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@types/eslint-scope": "^3.7.0", "@types/eslint-scope": "^3.7.3",
"@types/estree": "^0.0.50", "@types/estree": "^0.0.51",
"@webassemblyjs/ast": "1.11.1", "@webassemblyjs/ast": "1.11.1",
"@webassemblyjs/wasm-edit": "1.11.1", "@webassemblyjs/wasm-edit": "1.11.1",
"@webassemblyjs/wasm-parser": "1.11.1", "@webassemblyjs/wasm-parser": "1.11.1",
"acorn": "^8.4.1", "acorn": "^8.7.1",
"acorn-import-assertions": "^1.7.6", "acorn-import-assertions": "^1.7.6",
"browserslist": "^4.14.5", "browserslist": "^4.14.5",
"chrome-trace-event": "^1.0.2", "chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^5.8.3", "enhanced-resolve": "^5.10.0",
"es-module-lexer": "^0.9.0", "es-module-lexer": "^0.9.0",
"eslint-scope": "5.1.1", "eslint-scope": "5.1.1",
"events": "^3.2.0", "events": "^3.2.0",
"glob-to-regexp": "^0.4.1", "glob-to-regexp": "^0.4.1",
"graceful-fs": "^4.2.4", "graceful-fs": "^4.2.9",
"json-parse-better-errors": "^1.0.2", "json-parse-even-better-errors": "^2.3.1",
"loader-runner": "^4.2.0", "loader-runner": "^4.2.0",
"mime-types": "^2.1.27", "mime-types": "^2.1.27",
"neo-async": "^2.6.2", "neo-async": "^2.6.2",
"schema-utils": "^3.1.0", "schema-utils": "^3.1.0",
"tapable": "^2.1.1", "tapable": "^2.1.1",
"terser-webpack-plugin": "^5.1.3", "terser-webpack-plugin": "^5.1.3",
"watchpack": "^2.3.0", "watchpack": "^2.4.0",
"webpack-sources": "^3.2.2" "webpack-sources": "^3.2.3"
}, },
"bin": { "bin": {
"webpack": "bin/webpack.js" "webpack": "bin/webpack.js"
@ -1617,9 +1681,9 @@
} }
}, },
"node_modules/webpack-sources": { "node_modules/webpack-sources": {
"version": "3.2.2", "version": "3.2.3",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.2.tgz", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
"integrity": "sha512-cp5qdmHnu5T8wRg2G3vZZHoJPN14aqQ89SyQ11NpGH5zEMDCclt49rzo+MaRazk7/UeILhAI+/sEtcM+7Fr0nw==", "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">=10.13.0" "node": ">=10.13.0"
@ -1660,10 +1724,59 @@
"integrity": "sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==", "integrity": "sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==",
"dev": true "dev": true
}, },
"@jridgewell/gen-mapping": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
"dev": true,
"requires": {
"@jridgewell/set-array": "^1.0.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
"@jridgewell/trace-mapping": "^0.3.9"
}
},
"@jridgewell/resolve-uri": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
"dev": true
},
"@jridgewell/set-array": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
"dev": true
},
"@jridgewell/source-map": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
"integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
"dev": true,
"requires": {
"@jridgewell/gen-mapping": "^0.3.0",
"@jridgewell/trace-mapping": "^0.3.9"
}
},
"@jridgewell/sourcemap-codec": {
"version": "1.4.14",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
"dev": true
},
"@jridgewell/trace-mapping": {
"version": "0.3.14",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz",
"integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==",
"dev": true,
"requires": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"@types/eslint": { "@types/eslint": {
"version": "8.2.0", "version": "8.21.2",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.2.0.tgz", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.21.2.tgz",
"integrity": "sha512-74hbvsnc+7TEDa1z5YLSe4/q8hGYB3USNvCuzHUJrjPV6hXaq8IXcngCrHkuvFt0+8rFz7xYXrHgNayIX0UZvQ==", "integrity": "sha512-EMpxUyystd3uZVByZap1DACsMXvb82ypQnGn89e1Y0a+LYu3JJscUd/gqhRsVFDkaD2MIiWo0MT8EfXr3DGRKw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/estree": "*", "@types/estree": "*",
@ -1671,9 +1784,9 @@
} }
}, },
"@types/eslint-scope": { "@types/eslint-scope": {
"version": "3.7.1", "version": "3.7.4",
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.1.tgz", "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz",
"integrity": "sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g==", "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/eslint": "*", "@types/eslint": "*",
@ -1681,9 +1794,9 @@
} }
}, },
"@types/estree": { "@types/estree": {
"version": "0.0.50", "version": "0.0.51",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz",
"integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==",
"dev": true "dev": true
}, },
"@types/json-schema": { "@types/json-schema": {
@ -1879,10 +1992,15 @@
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
"dev": true "dev": true
}, },
"@yr/monotone-cubic-spline": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@yr/monotone-cubic-spline/-/monotone-cubic-spline-1.0.3.tgz",
"integrity": "sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA=="
},
"acorn": { "acorn": {
"version": "8.6.0", "version": "8.8.2",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
"integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==", "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
"dev": true "dev": true
}, },
"acorn-import-assertions": { "acorn-import-assertions": {
@ -1912,10 +2030,11 @@
"requires": {} "requires": {}
}, },
"apexcharts": { "apexcharts": {
"version": "3.33.1", "version": "3.49.1",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.33.1.tgz", "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.49.1.tgz",
"integrity": "sha512-5aVzrgJefd8EH4w7oRmuOhA3+cxJxQg27cYg3ANVGvPCOB4AY3mVVNtFHRFaIq7bv8ws4GRaA9MWfzoWQw3MPQ==", "integrity": "sha512-MqGtlq/KQuO8j0BBsUJYlRG8VBctKwYdwuBtajHgHTmSgUU3Oai+8oYN/rKCXwXzrUlYA+GiMgotAIbXY2BCGw==",
"requires": { "requires": {
"@yr/monotone-cubic-spline": "^1.0.3",
"svg.draggable.js": "^2.2.2", "svg.draggable.js": "^2.2.2",
"svg.easing.js": "^2.0.0", "svg.easing.js": "^2.0.0",
"svg.filter.js": "^2.0.2", "svg.filter.js": "^2.0.2",
@ -2017,9 +2136,9 @@
"dev": true "dev": true
}, },
"enhanced-resolve": { "enhanced-resolve": {
"version": "5.8.3", "version": "5.12.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz",
"integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==", "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"graceful-fs": "^4.2.4", "graceful-fs": "^4.2.4",
@ -2147,9 +2266,9 @@
"dev": true "dev": true
}, },
"graceful-fs": { "graceful-fs": {
"version": "4.2.8", "version": "4.2.10",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
"integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
"dev": true "dev": true
}, },
"has": { "has": {
@ -2243,10 +2362,10 @@
"supports-color": "^8.0.0" "supports-color": "^8.0.0"
} }
}, },
"json-parse-better-errors": { "json-parse-even-better-errors": {
"version": "1.0.2", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
"dev": true "dev": true
}, },
"json-schema-traverse": { "json-schema-traverse": {
@ -2313,9 +2432,9 @@
"dev": true "dev": true
}, },
"nanoid": { "nanoid": {
"version": "3.2.0", "version": "3.3.6",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
"integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
"dev": true "dev": true
}, },
"neo-async": { "neo-async": {
@ -2408,14 +2527,14 @@
} }
}, },
"postcss": { "postcss": {
"version": "8.3.11", "version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.11.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
"integrity": "sha512-hCmlUAIlUiav8Xdqw3Io4LcpA1DOt7h3LSTAC4G6JGHFFaWzI6qvFt9oilvl8BmkbBRX1IhM90ZAmpk68zccQA==", "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"nanoid": "^3.1.30", "nanoid": "^3.3.6",
"picocolors": "^1.0.0", "picocolors": "^1.0.0",
"source-map-js": "^0.6.2" "source-map-js": "^1.0.2"
} }
}, },
"postcss-modules-extract-imports": { "postcss-modules-extract-imports": {
@ -2591,9 +2710,9 @@
"dev": true "dev": true
}, },
"source-map-js": { "source-map-js": {
"version": "0.6.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"dev": true "dev": true
}, },
"source-map-support": { "source-map-support": {
@ -2699,22 +2818,15 @@
"dev": true "dev": true
}, },
"terser": { "terser": {
"version": "5.10.0", "version": "5.14.2",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz", "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz",
"integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==", "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@jridgewell/source-map": "^0.3.2",
"acorn": "^8.5.0",
"commander": "^2.20.0", "commander": "^2.20.0",
"source-map": "~0.7.2",
"source-map-support": "~0.5.20" "source-map-support": "~0.5.20"
},
"dependencies": {
"source-map": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
"dev": true
}
} }
}, },
"terser-webpack-plugin": { "terser-webpack-plugin": {
@ -2746,9 +2858,9 @@
"dev": true "dev": true
}, },
"watchpack": { "watchpack": {
"version": "2.3.0", "version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.0.tgz", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
"integrity": "sha512-MnN0Q1OsvB/GGHETrFeZPQaOelWh/7O+EiFlj8sM9GPjtQkis7k01aAxrg/18kTfoIVcLL+haEVFlXDaSRwKRw==", "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
"dev": true, "dev": true,
"requires": { "requires": {
"glob-to-regexp": "^0.4.1", "glob-to-regexp": "^0.4.1",
@ -2756,35 +2868,35 @@
} }
}, },
"webpack": { "webpack": {
"version": "5.64.4", "version": "5.76.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.64.4.tgz", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.0.tgz",
"integrity": "sha512-LWhqfKjCLoYJLKJY8wk2C3h77i8VyHowG3qYNZiIqD6D0ZS40439S/KVuc/PY48jp2yQmy0mhMknq8cys4jFMw==", "integrity": "sha512-l5sOdYBDunyf72HW8dF23rFtWq/7Zgvt/9ftMof71E/yUb1YLOBmTgA2K4vQthB3kotMrSj609txVE0dnr2fjA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/eslint-scope": "^3.7.0", "@types/eslint-scope": "^3.7.3",
"@types/estree": "^0.0.50", "@types/estree": "^0.0.51",
"@webassemblyjs/ast": "1.11.1", "@webassemblyjs/ast": "1.11.1",
"@webassemblyjs/wasm-edit": "1.11.1", "@webassemblyjs/wasm-edit": "1.11.1",
"@webassemblyjs/wasm-parser": "1.11.1", "@webassemblyjs/wasm-parser": "1.11.1",
"acorn": "^8.4.1", "acorn": "^8.7.1",
"acorn-import-assertions": "^1.7.6", "acorn-import-assertions": "^1.7.6",
"browserslist": "^4.14.5", "browserslist": "^4.14.5",
"chrome-trace-event": "^1.0.2", "chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^5.8.3", "enhanced-resolve": "^5.10.0",
"es-module-lexer": "^0.9.0", "es-module-lexer": "^0.9.0",
"eslint-scope": "5.1.1", "eslint-scope": "5.1.1",
"events": "^3.2.0", "events": "^3.2.0",
"glob-to-regexp": "^0.4.1", "glob-to-regexp": "^0.4.1",
"graceful-fs": "^4.2.4", "graceful-fs": "^4.2.9",
"json-parse-better-errors": "^1.0.2", "json-parse-even-better-errors": "^2.3.1",
"loader-runner": "^4.2.0", "loader-runner": "^4.2.0",
"mime-types": "^2.1.27", "mime-types": "^2.1.27",
"neo-async": "^2.6.2", "neo-async": "^2.6.2",
"schema-utils": "^3.1.0", "schema-utils": "^3.1.0",
"tapable": "^2.1.1", "tapable": "^2.1.1",
"terser-webpack-plugin": "^5.1.3", "terser-webpack-plugin": "^5.1.3",
"watchpack": "^2.3.0", "watchpack": "^2.4.0",
"webpack-sources": "^3.2.2" "webpack-sources": "^3.2.3"
} }
}, },
"webpack-cli": { "webpack-cli": {
@ -2832,9 +2944,9 @@
} }
}, },
"webpack-sources": { "webpack-sources": {
"version": "3.2.2", "version": "3.2.3",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.2.tgz", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
"integrity": "sha512-cp5qdmHnu5T8wRg2G3vZZHoJPN14aqQ89SyQ11NpGH5zEMDCclt49rzo+MaRazk7/UeILhAI+/sEtcM+7Fr0nw==", "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
"dev": true "dev": true
}, },
"which": { "which": {

View File

@ -30,12 +30,12 @@
"devDependencies": { "devDependencies": {
"css-loader": "^6.5.1", "css-loader": "^6.5.1",
"style-loader": "^3.3.1", "style-loader": "^3.3.1",
"webpack": "^5.64.4", "webpack": "^5.76.0",
"webpack-cli": "^4.9.1", "webpack-cli": "^4.9.1",
"webpack-merge": "^5.8.0" "webpack-merge": "^5.8.0"
}, },
"dependencies": { "dependencies": {
"apexcharts": "^3.33.1", "apexcharts": "^3.49.1",
"d3-format": "^3.0.1" "d3-format": "^3.0.1"
} }
} }

View File

@ -252,3 +252,21 @@ test_that("apexfacetOutput works", {
test_that("get_yaxis_serie works", {
mydata <- data.frame(
x = 1:10,
y = c(1:5, (16:20) * 10),
fill = rep(c("a", "b"), each = 5)
)
ax <- apex(mydata, aes(x, y), "line")
expect_equal(get_yaxis_serie(ax, 1), c(1:5, (16:20) * 10))
ax <- apex(mydata, aes(x, y, fill = fill), "line")
expect_equal(get_yaxis_serie(ax, 1), c(1:5, (16:20) * 10))
ax <- apex(mydata, aes(x, y, fill = fill), "line") %>%
ax_yaxis(title = list(text = "Y1")) %>%
ax_yaxis2(title = list(text = "Y2"))
expect_equal(get_yaxis_serie(ax, 1), c(1:5))
expect_equal(get_yaxis_serie(ax, 2), c((16:20) * 10))
})

View File

@ -82,16 +82,62 @@ apex(data = economics_long, type = "line", mapping = aes(x = date, y = value01,
``` ```
## Area charts
Create area charts with `type = "area"`: Create area charts with `type = "area"`:
```{r area} ```{r area}
apex(data = economics_long, type = "area", mapping = aes(x = date, y = value01, fill = variable)) %>% data("eco2mix", package = "apexcharter")
ax_yaxis(decimalsInFloat = 2) %>% # number of decimals to keep
ax_chart(stacked = TRUE) %>% apex(eco2mix, aes(datetime, production, fill = source), type = "area") %>%
ax_yaxis(max = 4, tickAmount = 4) ax_chart(animations = list(enabled = FALSE), stacked = TRUE) %>%
ax_stroke(width = 1) %>%
ax_fill(opacity = 1, type = "solid") %>%
ax_tooltip(x = list(format = "dd MMM, HH:mm")) %>%
ax_yaxis(labels = list(formatter = format_num("~", suffix = "MW"))) %>%
ax_colors_manual(
list(
"bioenergies" = "#156956",
"fuel" = "#80549f",
"coal" = "#a68832",
"solar" = "#d66b0d",
"gas" = "#f20809",
"wind" = "#72cbb7",
"hydraulic" = "#2672b0",
"nuclear" = "#e4a701",
"pumping" = "#0e4269"
)
) %>%
ax_labs(
title = "Electricity generation by sector in France",
subtitle = "Data from \u00e9CO\u2082mix"
)
``` ```
You can create ribbon charts using `ymin` and `ymax` aesthetics :
```{r ribbon}
data("temperatures", package = "apexcharter")
apex(
temperatures,
aes(x = date, ymin = low, ymax = high),
type = "rangeArea",
serie_name = "Low/High (2018-2021)"
) %>%
add_line(aes(date, `2023`)) %>%
ax_chart(animations = list(enabled = FALSE)) %>%
ax_yaxis(tickAmount = 7, labels = list(formatter = format_num("~", suffix = "°C"))) %>%
ax_colors(c("#8485854D", "#FF0000")) %>%
ax_stroke(width = c(1, 2)) %>%
ax_fill(opacity = 1, type = "solid") %>%
ax_labs(
title = "Temperatures in 2023 with range from 2018 to 2021",
subtitle = "Data from ENEDIS"
)
```
@ -118,7 +164,7 @@ apex(data = mtcars, type = "scatter", mapping = aes(x = wt, y = mpg, z = scales:
## Pie charts ## Pie & donut charts
Simple pie charts can be created with: Simple pie charts can be created with:
@ -131,6 +177,12 @@ poll <- data.frame(
apex(data = poll, type = "pie", mapping = aes(x = answer, y = n)) apex(data = poll, type = "pie", mapping = aes(x = answer, y = n))
``` ```
It's also possible to make donut chart:
```{r donut}
apex(data = poll, type = "donut", mapping = aes(x = answer, y = n))
```
## Radial charts ## Radial charts
@ -244,4 +296,71 @@ apex(
## Boxplot
Create boxplot (without outliers for now) with:
```{r boxplot}
data("mpg", package = "ggplot2")
apex(mpg, aes(hwy, class), "boxplot") %>%
ax_plotOptions(
boxPlot = boxplot_opts(color.upper = "#8BB0A6", color.lower = "#8BB0A6" )
) %>%
ax_stroke(colors = list("#2A5769")) %>%
ax_grid(
xaxis = list(lines = list(show = TRUE)),
yaxis = list(lines = list(show = FALSE))
)
```
## Dumbbell charts
Create Dumbbell chart with:
```{r dumbbell}
data("life_expec", package = "apexcharter")
apex(life_expec, aes(country, x = `1972`, xend = `2007`), type = "dumbbell") %>%
ax_plotOptions(
bar = bar_opts(
dumbbellColors = list(list("#3d85c6", "#fb6003"))
)
) %>%
ax_colors("#BABABA") %>%
ax_labs(
title = "Life expectancy : 1972 vs. 2007",
subtitle = "Data from Gapminder dataset",
x = "Life expectancy at birth, in years"
)
```
## Slope charts
Create a slope chart with:
```{r slope}
data("life_expec_long", package = "apexcharter")
apex(
life_expec_long,
mapping = aes(x = year, y = lifeExp, fill = country),
type = "slope",
height = "700px"
) %>%
ax_chart(animations = list(enabled = FALSE)) %>%
# aurora nord12 = #d08770 / aurora nord14 = #a3be8c -> darken colorspace::darken(, amount = 0.3)
ax_colors(ifelse(unique(life_expec_long[, c("country", "type")])$type == "decreased", "#955945", "#6A8354")) %>%
ax_labs(
title = "Life expectancy : 1972 vs. 2007",
subtitle = "Data from Gapminder dataset",
x = "Life expectancy at birth, in years"
) %>%
# ax_dataLabels(enabled = FALSE) %>% # show or note the labels + values
ax_xaxis(position = "bottom")
```