├── .Rbuildignore ├── .github ├── .gitignore └── workflows │ └── check-standard.yaml ├── .gitignore ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── NEWS.md ├── R ├── add_alternate_layout.R ├── array_table_to_matrix.R ├── bootstrap-background-colors.R ├── check_for_area_mismatches.R ├── get_elements.R ├── get_info.R ├── grid_card.R ├── grid_card_old.R ├── grid_card_text.R ├── grid_container.R ├── grid_nested.R ├── grid_page.R ├── grid_place.R ├── grid_plot.R ├── gridlayout-package.R ├── helper-demo-apps.R ├── md_table_to_matrix.R ├── md_to_gridlayout.R ├── new_gridlayout.R ├── parse_layout_matrix.R ├── parsing-utils.R ├── tag-utils.R ├── to_css.R ├── to_md.R ├── use_gridlayout_rmd.R ├── use_gridlayout_shiny.R ├── utils-pipe.R └── utils.R ├── README.Rmd ├── README.md ├── _pkgdown.yml ├── docs ├── 404.html ├── LICENSE-text.html ├── LICENSE.html ├── ace-1.2.3 │ ├── ace.js │ ├── mode-css.js │ ├── mode-html.js │ ├── mode-javascript.js │ ├── mode-markdown.js │ ├── mode-plain_text.js │ ├── mode-r.js │ ├── mode-rdoc.js │ ├── mode-rhtml.js │ ├── mode-text.js │ ├── mode-xml.js │ └── theme-textmate.js ├── articles │ ├── alternate-layouts.html │ ├── alternate-layouts_files │ │ └── header-attrs-2.7.1 │ │ │ └── header-attrs.js │ ├── alternate-layouts_resizing.gif │ ├── alternate-layouts_w1000_h1000.png │ ├── alternate-layouts_w2000_h1000.png │ ├── alternate-layouts_w500_h1000.png │ ├── defining-a-layout.html │ ├── index.html │ ├── layout-examples.html │ ├── layout-examples_files │ │ └── header-attrs-2.7.1 │ │ │ └── header-attrs.js │ ├── layout-examples_focal_chart_side.png │ ├── layout-examples_focal_chart_top.png │ ├── layout-examples_four_panel.png │ ├── layout-examples_scrolling_stack.png │ ├── layout-examples_stack.png │ ├── use_gridlayout_rmd.png │ ├── use_gridlayout_rmd_base.png │ ├── use_gridlayout_rmd_extra_child_styles.png │ ├── use_gridlayout_rmd_only_grid_panel.png │ ├── using_with_rmd.html │ └── using_with_rmd_files │ │ └── header-attrs-2.7.1 │ │ └── header-attrs.js ├── authors.html ├── bootstrap-toc.css ├── bootstrap-toc.js ├── docsearch.css ├── docsearch.js ├── holder-2.9.0 │ └── holder.min.js ├── index.html ├── link.svg ├── news │ └── index.html ├── pkgdown.css ├── pkgdown.js ├── pkgdown.yml ├── reference │ ├── Rplot001.png │ ├── add_alternate_layout.gridlayout.html │ ├── add_alternate_layout.html │ ├── build_css_rule.html │ ├── card_plot_output.html │ ├── figures │ │ ├── basic_markdown.png │ │ ├── geyser_demo.png │ │ ├── geyser_w_grided.png │ │ ├── grided_default.png │ │ ├── lifecycle-archived.svg │ │ ├── lifecycle-defunct.svg │ │ ├── lifecycle-deprecated.svg │ │ ├── lifecycle-experimental.svg │ │ ├── lifecycle-maturing.svg │ │ ├── lifecycle-questioning.svg │ │ ├── lifecycle-stable.svg │ │ ├── lifecycle-superseded.svg │ │ └── nested_demo.png │ ├── flex_stack.html │ ├── get_element_ids.html │ ├── get_elements.html │ ├── grid_card.html │ ├── grid_card_old.html │ ├── grid_card_plot.html │ ├── grid_card_text.html │ ├── grid_container.html │ ├── grid_nested.html │ ├── grid_page.html │ ├── grid_panel.html │ ├── grid_panel_nested.html │ ├── grid_panel_plot.html │ ├── grid_panel_stack.html │ ├── grid_panel_text.html │ ├── grid_place.html │ ├── grid_plot.html │ ├── grided_create_new_app.html │ ├── grided_edit_existing_layout.html │ ├── gridlayout-package.html │ ├── index.html │ ├── make_bg_class.html │ ├── md_to_gridlayout.html │ ├── nested_grid_panel.html │ ├── new_gridlayout.html │ ├── pipe.html │ ├── run_with_grided.html │ ├── text_panel.html │ ├── title_panel.html │ ├── to_app_template.html │ ├── to_css.html │ ├── to_matrix.html │ ├── to_md.html │ ├── use_gridlayout_rmd.html │ ├── use_gridlayout_shiny.html │ └── vertical_stack_panel.html ├── rmarkdown.png ├── rmd.css ├── sitemap.xml ├── snippets │ └── snippets.js ├── tidyverse-2.css └── tidyverse.css ├── gridlayout.Rproj ├── inst ├── demo_apps │ ├── alternate_layouts │ │ ├── README.md │ │ ├── app.R │ │ └── tests │ │ │ ├── testthat.R │ │ │ └── testthat │ │ │ ├── _snaps │ │ │ ├── mac-4.1 │ │ │ │ └── shinytest2 │ │ │ │ │ ├── alternate_layouts-001.png │ │ │ │ │ ├── alternate_layouts-002.png │ │ │ │ │ └── alternate_layouts-003.png │ │ │ └── mac-4.2 │ │ │ │ └── shinytest2 │ │ │ │ ├── alternate_layouts-001.png │ │ │ │ ├── alternate_layouts-002.png │ │ │ │ └── alternate_layouts-003.png │ │ │ ├── setup.R │ │ │ └── test-shinytest2.R │ ├── geyser │ │ ├── README.md │ │ └── app.R │ ├── nested_grids │ │ ├── README.md │ │ ├── app.R │ │ └── tests │ │ │ ├── testthat.R │ │ │ └── testthat │ │ │ ├── _snaps │ │ │ ├── mac-4.1 │ │ │ │ └── shinytest2 │ │ │ │ │ └── nested_grids-001.png │ │ │ └── mac-4.2 │ │ │ │ └── shinytest2 │ │ │ │ └── nested_grids-001.png │ │ │ ├── setup.R │ │ │ └── test-shinytest2.R │ ├── recursive_nesting │ │ ├── app.R │ │ └── tests │ │ │ ├── testthat.R │ │ │ └── testthat │ │ │ ├── _snaps │ │ │ ├── mac-4.1 │ │ │ │ └── shinytest2 │ │ │ │ │ └── recursive_nesting-001.png │ │ │ └── mac-4.2 │ │ │ │ └── shinytest2 │ │ │ │ └── recursive_nesting-001.png │ │ │ ├── setup.R │ │ │ └── test-shinytest2.R │ ├── rmarkdown_demo │ │ ├── README.md │ │ ├── grid_markdown.Rmd │ │ ├── grid_markdown_options.Rmd │ │ └── grid_markdown_options.html │ ├── setupScreenshots.R │ ├── simple │ │ ├── README.md │ │ └── app.R │ └── tabset │ │ └── app.R └── resources │ ├── gridlayout.css │ ├── gridlayout.js │ └── gridlayout_rmd_styles.css ├── man ├── add_alternate_layout.Rd ├── build_css_rule.Rd ├── card_plot_output.Rd ├── examples │ ├── grid_place_app.R │ ├── nested_app.R │ ├── simple_app.R │ └── simple_app_old.R ├── figures │ ├── basic_markdown.png │ ├── geyser_demo.png │ ├── geyser_w_grided.png │ ├── grided_default.png │ ├── lifecycle-archived.svg │ ├── lifecycle-defunct.svg │ ├── lifecycle-deprecated.svg │ ├── lifecycle-experimental.svg │ ├── lifecycle-maturing.svg │ ├── lifecycle-questioning.svg │ ├── lifecycle-stable.svg │ ├── lifecycle-superseded.svg │ └── nested_demo.png ├── flex_stack.Rd ├── get_element_ids.Rd ├── get_elements.Rd ├── grid_card.Rd ├── grid_card_old.Rd ├── grid_card_plot.Rd ├── grid_card_text.Rd ├── grid_container.Rd ├── grid_nested.Rd ├── grid_page.Rd ├── grid_place.Rd ├── gridlayout-package.Rd ├── make_bg_class.Rd ├── md_to_gridlayout.Rd ├── new_gridlayout.Rd ├── pipe.Rd ├── to_css.Rd ├── to_matrix.Rd ├── to_md.Rd ├── use_gridlayout_rmd.Rd └── use_gridlayout_shiny.Rd ├── tests ├── testthat.R └── testthat │ ├── _snaps │ ├── grid_page.md │ ├── markdown-layout-parsing.md │ ├── nested-grids.md │ ├── new_gridlayout.md │ └── to_css.md │ ├── test-alternate_layouts.R │ ├── test-array_table_to_matrix.R │ ├── test-demo-apps.R │ ├── test-grid_container.R │ ├── test-grid_page.R │ ├── test-markdown-layout-parsing.R │ ├── test-md_table_to_matrix.R │ ├── test-nested-grids.R │ ├── test-new_gridlayout.R │ ├── test-parse_layout_matrix.R │ ├── test-to_css.R │ └── test-to_md.R └── vignettes ├── .gitignore ├── alternate-layouts.Rmd ├── alternate-layouts_resizing.gif ├── alternate-layouts_w1000_h1000.png ├── alternate-layouts_w2000_h1000.png ├── alternate-layouts_w500_h1000.png ├── defining-a-layout.Rmd ├── layout-examples.Rmd ├── layout-examples_focal_chart_side.png ├── layout-examples_focal_chart_top.png ├── layout-examples_four_panel.png ├── layout-examples_scrolling_stack.png ├── layout-examples_stack.png ├── use_gridlayout_rmd.png ├── use_gridlayout_rmd_base.png ├── use_gridlayout_rmd_extra_child_styles.png ├── use_gridlayout_rmd_only_grid_panel.png └── using_with_rmd.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^README\.Rmd$ 4 | ^_pkgdown\.yml$ 5 | ^docs$ 6 | ^pkgdown$ 7 | ^LICENSE\.md$ 8 | ^inst/grided/www/rmd_layout_editing_files$ 9 | ^tests/run-screenshot-tests\.R$ 10 | ^tests/screenshot-tests$ 11 | ^inst/grided/www/\.cache$ 12 | ^inst/grided/www/node_modules$ 13 | ^inst/demo_apps/.*.html 14 | ^inst/demo_apps/.*_files$ 15 | ^doc$ 16 | ^Meta$ 17 | ^\.github$ 18 | ^data-raw$ 19 | ^inst\/demo_apps\/\w+\/tests/testthat/_snaps 20 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/workflows/check-standard.yaml: -------------------------------------------------------------------------------- 1 | # For help debugging build failures open an issue on the RStudio community with the 'github-actions' tag. 2 | # https://community.rstudio.com/new-topic?category=Package%20development&tags=github-actions 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | name: R-CMD-check 12 | 13 | jobs: 14 | website: 15 | uses: rstudio/shiny-workflows/.github/workflows/website.yaml@v1 16 | R-CMD-check: 17 | runs-on: ${{ matrix.config.os }} 18 | 19 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | config: 25 | - {os: windows-latest, r: 'release'} 26 | - {os: macOS-latest, r: 'release'} 27 | - {os: ubuntu-20.04, r: 'release', rspm: "https://packagemanager.rstudio.com/cran/__linux__/focal/latest"} 28 | - {os: ubuntu-20.04, r: 'devel', rspm: "https://packagemanager.rstudio.com/cran/__linux__/focal/latest"} 29 | 30 | env: 31 | R_REMOTES_NO_ERRORS_FROM_WARNINGS: true 32 | RSPM: ${{ matrix.config.rspm }} 33 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 34 | 35 | steps: 36 | - uses: actions/checkout@v2 37 | 38 | - uses: r-lib/actions/setup-r@v1 39 | with: 40 | r-version: ${{ matrix.config.r }} 41 | 42 | - uses: r-lib/actions/setup-pandoc@v1 43 | 44 | - name: Query dependencies 45 | run: | 46 | install.packages('remotes') 47 | saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2) 48 | writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version") 49 | shell: Rscript {0} 50 | 51 | - name: Restore R package cache 52 | if: runner.os != 'Windows' 53 | uses: actions/cache@v2 54 | with: 55 | path: ${{ env.R_LIBS_USER }} 56 | key: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-${{ hashFiles('.github/depends.Rds') }} 57 | restore-keys: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1- 58 | 59 | - name: Install system dependencies 60 | if: runner.os == 'Linux' 61 | run: | 62 | while read -r cmd 63 | do 64 | eval sudo $cmd 65 | done < <(Rscript -e 'writeLines(remotes::system_requirements("ubuntu", "20.04"))') 66 | 67 | - name: Install dependencies 68 | run: | 69 | remotes::install_deps(dependencies = TRUE) 70 | remotes::install_cran("rcmdcheck") 71 | shell: Rscript {0} 72 | 73 | - name: Check 74 | env: 75 | _R_CHECK_CRAN_INCOMING_REMOTE_: false 76 | run: | 77 | options(crayon.enabled = TRUE) 78 | rcmdcheck::rcmdcheck(args = c("--no-manual", "--as-cran"), error_on = "warning", check_dir = "check") 79 | shell: Rscript {0} 80 | 81 | - name: Upload check results 82 | if: failure() 83 | uses: actions/upload-artifact@main 84 | with: 85 | name: ${{ runner.os }}-r${{ matrix.config.r }}-results 86 | path: check 87 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | 6 | */.DS_Store 7 | .DS_Store 8 | 9 | inst/griditor/www/.cache/ 10 | inst/grided/www/.cache/ 11 | 12 | # Ignore all the stuff in the dist folder 13 | inst/grided/dist/* 14 | inst/grided/www/dist/* 15 | # Except index.js becuse that's the bundled js we actually want 16 | !inst/grided/dist/index.js 17 | 18 | inst/rmarkdown_demo/grid_markdown.html 19 | inst/doc 20 | 21 | inst/grided/www/rmd_layout_editing_files/ 22 | 23 | inst/rmarkdown_demo/rmd_layout_editing_files/ 24 | 25 | inst/rmarkdown_demo/rmd_layout_editing.html 26 | 27 | inst/demo_apps/rmarkdown_demo/rmd_layout_editing_files/ 28 | 29 | *.mov 30 | doc 31 | Meta 32 | 33 | inst/grided/www/node_modules/ 34 | 35 | inst/grided/www/.parcel-cache/ 36 | 37 | inst/grided/www/yarn-error.log 38 | 39 | inst/demo_apps/rmarkdown_demo/remarker_test.html 40 | 41 | inst/demo_apps/rmarkdown_demo/grid_markdown.html 42 | 43 | inst/demo_apps/remarker/app.html 44 | /doc/ 45 | /Meta/ 46 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: gridlayout 2 | Type: Package 3 | Title: Build Layouts for R-Webapps Using CSS-Grid 4 | Version: 0.2.1 5 | Authors@R: c(person("Nick", "Strayer", role = c("aut", "cre"), email = "nick.strayer@rstudio.com")) 6 | Description: Makes it easy to layout your Shiny app or other R-derived HTML documents using CSS grid. 7 | License: MIT + file LICENSE 8 | Encoding: UTF-8 9 | Suggests: 10 | testthat (>= 3.0.0), 11 | shiny, 12 | bslib, 13 | crayon, 14 | knitr, 15 | rmarkdown, 16 | fontawesome, 17 | here, 18 | rstudioapi, 19 | fs, 20 | glue, 21 | ggplot2, 22 | gt, 23 | shinytest2 24 | Config/testthat/edition: 3 25 | RoxygenNote: 7.2.3 26 | Roxygen: list(markdown = TRUE) 27 | VignetteBuilder: knitr 28 | Imports: 29 | magrittr, 30 | htmltools, 31 | rlang, 32 | lifecycle 33 | Config/Needs/website: 34 | rstudio/quillt 35 | Depends: 36 | R (>= 2.10) 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2021 2 | COPYRIGHT HOLDER: gridlayout authors 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2021 gridlayout authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(format,gridlayout) 4 | S3method(format,gridlayout_template) 5 | S3method(get_element_ids,gridlayout) 6 | S3method(get_element_ids,gridlayout_template) 7 | S3method(get_elements,default) 8 | S3method(get_elements,gridlayout) 9 | S3method(get_info,gridlayout) 10 | S3method(get_info,gridlayout_template) 11 | S3method(print,gridlayout) 12 | S3method(print,gridlayout_template) 13 | S3method(to_md,default) 14 | S3method(to_md,gridlayout) 15 | export("%>%") 16 | export(card_plot_output) 17 | export(get_element_ids) 18 | export(get_elements) 19 | export(grid_card) 20 | export(grid_card_old) 21 | export(grid_card_plot) 22 | export(grid_card_text) 23 | export(grid_container) 24 | export(grid_nested) 25 | export(grid_page) 26 | export(grid_place) 27 | export(md_to_gridlayout) 28 | export(new_gridlayout) 29 | export(to_css) 30 | export(to_matrix) 31 | export(to_md) 32 | export(use_gridlayout_rmd) 33 | importFrom(lifecycle,deprecated) 34 | importFrom(magrittr,"%>%") 35 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # Development 2 | 3 | ### Minor new features and improvements 4 | - Now the default container height is 100% for any container other than `grid_page()`. This helps avoid confusing sizing when layout is embedded in other containers like a card. 5 | 6 | # gridlayout 0.2.1 7 | 8 | ### Minor new features and improvements 9 | 10 | - Replace internal usage of `bslib::card_body_fill()` with unsufixed `bslib::card_body()` to reflect updates in API. Will fix confusing warning messages about `card_body_fill` being depreciated even though it isn't used in user code. 11 | 12 | ### Bug fixes 13 | - Short cards could have squished contents (15c0397483529b34688f8f76bbc57a06ac9d9764) 14 | 15 | # gridlayout 0.2.0 16 | 17 | ### Major changes 18 | 19 | - `grid_card()` now wraps `bslib::card()` directly. This means that there are some api changes such as depreciated the `title` argument for using `bslib::card_header(title)` instead. Old `grid_card()` arguments that are present in code will get flagged with a warning to allow users to know when/how to update their code with the new version. 20 | - `card_plot_output()` is now depreciated in favor of the new `bslib` cards smart fill behavior. Simple plots can still be done with `grid_card_plot()`. 21 | - The function `grid_card_old()` has been added to ease transition to the new card api and to keep some features such as collapsibility are not available on the new api. 22 | 23 | # gridlayout 0.1.0 24 | 25 | ### Major new features and improvements 26 | 27 | - `grid_card()` is now the main method of placing items onto the grid 28 | - Added ability to add full-bleed header and sidebar with special child functions. These sit outside the main grid and can simplify layouts to not require them to constantly be added. 29 | - General interface for cards is a lighter in favor of outputting most non-layout logic to the coming `bslib::card()` and friends. 30 | 31 | ### Minor new features and improvements 32 | 33 | - Website has been updated with better resources 34 | 35 | ### Bug fixes 36 | 37 | ### Known bugs 38 | -------------------------------------------------------------------------------- /R/array_table_to_matrix.R: -------------------------------------------------------------------------------- 1 | array_table_to_matrix <- function(array_table) { 2 | 3 | 4 | no_sizes <- array_table %>% 5 | lapply(function(line) str_trim(str_remove_all(line, CSS_VALUE_REGEX))) %>% 6 | Filter(f = function(line) line != "") %>% 7 | flatten() %>% 8 | strsplit(split = "\\s+") 9 | 10 | if (length(unique(get_num_cols_by_row(no_sizes))) > 1) { 11 | stop("Layout appears to be malformed. Make sure each row has the same number of columns") 12 | } 13 | 14 | layout_matrix <- flatten(no_sizes) %>% 15 | matrix( 16 | nrow = length(no_sizes), 17 | ncol = length(no_sizes[[1]]), 18 | byrow = TRUE 19 | ) 20 | 21 | n_rows <- nrow(layout_matrix) 22 | n_cols <- ncol(layout_matrix) 23 | 24 | has_col_sizes <- length(array_table) > n_rows && any( 25 | has_css_value( 26 | vapply( 27 | split_on_space(array_table), 28 | FUN = function(line) line[1] , 29 | FUN.VALUE = character(1L) 30 | ) 31 | ) 32 | ) 33 | 34 | first_column_of_rows <- vapply( 35 | split_on_space(if (has_col_sizes) array_table[-1] else array_table), 36 | FUN = function(line) line[1] , 37 | FUN.VALUE = character(1L) 38 | ) 39 | has_row_sizes <- any( 40 | has_css_value(first_column_of_rows) 41 | ) 42 | 43 | if (has_col_sizes){ 44 | layout_matrix <- rbind("", layout_matrix) 45 | } 46 | 47 | if (has_row_sizes){ 48 | layout_matrix <- cbind("", layout_matrix) 49 | } 50 | 51 | 52 | if (has_col_sizes){ 53 | col_sizes <- strsplit(str_trim(array_table[[1]]), split = "\\s+")[[1]] 54 | 55 | if (length(col_sizes) < n_cols) { 56 | stop("If supplying column sizes in layout array, every column must be given a size.") 57 | } 58 | 59 | if (length(col_sizes) == n_cols && has_row_sizes) { 60 | # No gap size given so pad col_sizes with empty gap size space 61 | col_sizes <- c("", col_sizes) 62 | } 63 | 64 | layout_matrix[1,] <- col_sizes 65 | } 66 | 67 | if (has_row_sizes){ 68 | 69 | # We can fill in empty values for row sizes, however, so do that here 70 | first_column_of_rows[!has_css_value(first_column_of_rows)] = "" 71 | 72 | if (has_col_sizes) { 73 | layout_matrix[-1,1] <- first_column_of_rows 74 | } else { 75 | layout_matrix[,1] <- first_column_of_rows 76 | } 77 | } 78 | 79 | 80 | layout_matrix 81 | 82 | } 83 | 84 | -------------------------------------------------------------------------------- /R/bootstrap-background-colors.R: -------------------------------------------------------------------------------- 1 | # https://getbootstrap.com/docs/5.0/utilities/background/#background-color 2 | bslib_bg_colors <- c( 3 | "primary", 4 | "secondary", 5 | "success", 6 | "danger", 7 | "warning", 8 | "info", 9 | "light", 10 | "dark" 11 | ) 12 | 13 | 14 | #' Build a bootstrap background class 15 | #' 16 | #' Info on possible colors from the [bootstrap 17 | #' website](https://getbootstrap.com/docs/5.0/utilities/background/#background-color) 18 | #' 19 | #' @param bgColor Color of background in terms of bootstrap variable names: 20 | #' Options are `"primary"`, `"secondary"`, `"success"`, `"danger"`, 21 | #' `"warning"`, `"info"`, `"light"`, and `"dark`. 22 | #' @param bgGradient Should a gradient be applied to the background instead of a 23 | #' solid color? 24 | #' 25 | #' @return A string containing the bootstrap class name to apply the requested 26 | #' background color 27 | #' 28 | #' @keywords internal 29 | #' 30 | #' @examples 31 | #' 32 | #' gridlayout:::make_bg_class("warning") 33 | #' # A gradient can be applied as well 34 | #' gridlayout:::make_bg_class("primary", TRUE) 35 | #' 36 | make_bg_class <- function(bgColor = NULL, bgGradient = FALSE) { 37 | 38 | if (is.null(bgColor)) return("") 39 | if (!bgColor %in% bslib_bg_colors) { 40 | stop("The requested color of ", bgColor, " is not one of the possible ", 41 | "options. Possible values are ", 42 | paste0("\"", bslib_bg_colors, "\"", collapse = ", "), ".") 43 | } 44 | paste0("bg-",bgColor, if (bgGradient) " bg-gradient" else "") 45 | } 46 | 47 | 48 | -------------------------------------------------------------------------------- /R/check_for_area_mismatches.R: -------------------------------------------------------------------------------- 1 | 2 | # elements <- htmltools::tagList( 3 | # htmltools::div("hi", style="grid-area:test; background-color:green;"), 4 | # htmltools::div("there", style="background-color: red;"), 5 | # htmltools::div("world") 6 | # ) 7 | get_provided_grid_areas <- function(elements){ 8 | areas <- lapply(elements, FUN=get_grid_area) 9 | simplify2array( 10 | Filter(function(x) !is.null(x), x = areas) 11 | ) 12 | } 13 | 14 | get_grid_area <- function(el){ 15 | inline_styles <- el$attribs$style 16 | if(is.null(inline_styles)) { 17 | return(NULL) 18 | } 19 | 20 | grid_area <- str_extract( 21 | inline_styles, 22 | pattern = "(?<=grid-area:)\\s*\\w+(?=;)" 23 | ) 24 | 25 | # Account for potential spaces in the grid area definition 26 | grid_area <- str_trim(grid_area) 27 | 28 | if(length(grid_area) == 0) { 29 | return(NULL) 30 | } 31 | 32 | grid_area 33 | } 34 | 35 | 36 | check_for_area_mismatches <- function(provided_areas, layout_areas){ 37 | 38 | error_msg <- c() 39 | 40 | not_in_provided <- layout_areas[!layout_areas %in% provided_areas] 41 | if (length(not_in_provided) > 0) { 42 | 43 | missing_areas <- paste0("\"", not_in_provided,"\"", collapse = ', ') 44 | error_msg <- c( 45 | error_msg, 46 | paste( 47 | "Grid areas", missing_areas, "are specified in the layout but not present in the children:\n", 48 | "If the grid-area css property for these areas is being provided in a custom", 49 | " way, set `check_for_mismatches = FALSE` to avoid this error message." 50 | ) 51 | ) 52 | } 53 | 54 | 55 | not_in_layout <- provided_areas[!provided_areas %in% layout_areas] 56 | if (length(not_in_layout) > 0) { 57 | missing_areas <- paste0("\"", not_in_layout,"\"", collapse = ', ') 58 | error_msg <- c( 59 | error_msg, 60 | paste( 61 | "Grid areas", missing_areas, 62 | "are present in the children of grid container but not specified in the layout:\n" 63 | ) 64 | ) 65 | } 66 | 67 | if (length(error_msg) == 0) { return(NULL) } 68 | 69 | 70 | stop(paste(error_msg, collapse = "\n"), call. = FALSE) 71 | } 72 | -------------------------------------------------------------------------------- /R/get_elements.R: -------------------------------------------------------------------------------- 1 | #' Get elements and positions out of a `gridlayout` object 2 | #' 3 | #' @param layout Object of class `"gridlayout"` 4 | #' 5 | #' @return List of all unique elements in the layout and their `id`, 6 | #' `{start,end}_row`, and `{start,end}_col`. Positions are indexed starting at 7 | #' 1 8 | #' 9 | #' @seealso [get_element_ids] 10 | #' @examples 11 | #' grid_obj <- md_to_gridlayout( 12 | #' layout_table = " 13 | #' | |120px |1fr |1fr | 14 | #' |------|--------|-------|-------| 15 | #' |100px |header |header |header | 16 | #' |1fr |sidebar |plot_a |plot_a | 17 | #' |1fr |sidebar |plot_b |plot_c |" 18 | #' ) 19 | #' 20 | #' get_elements(grid_obj) 21 | #' 22 | #' @export 23 | get_elements <- function(layout){ 24 | UseMethod("get_elements") 25 | } 26 | 27 | #' @export 28 | get_elements.default <- function(layout){ 29 | warning("The get_elements function is only defined for objects of the gridlayout class") 30 | } 31 | 32 | #' @export 33 | get_elements.gridlayout <- function(layout){ 34 | # Just remove the gridlayout class so printing doesn't confuse people 35 | get_info(layout, "elements") 36 | } 37 | 38 | #' Get ids of element in `gridlayout` object 39 | #' 40 | #' @param x Object of class `"gridlayout"`. 41 | #' 42 | #' @return Character vector of ids of all elements in layout 43 | #' @export 44 | #' 45 | #' @seealso [get_elements] 46 | #' @examples 47 | #' 48 | #' grid_obj <- md_to_gridlayout( 49 | #' layout_table = " 50 | #' | |120px |1fr |1fr | 51 | #' |------|--------|-------|-------| 52 | #' |100px |header |header |header | 53 | #' |1fr |sidebar |plot_a |plot_a | 54 | #' |1fr |sidebar |plot_b |plot_c |" 55 | #' ) 56 | #' 57 | #' get_element_ids(grid_obj) 58 | get_element_ids <- function(x){ 59 | UseMethod("get_element_ids") 60 | } 61 | 62 | get_element_ids.default <- function(x){ 63 | cat("get_element_ids generic") 64 | } 65 | 66 | #' @export 67 | get_element_ids.gridlayout <- function(x){ 68 | get_info(x, "element_ids") 69 | } 70 | 71 | #' @export 72 | get_element_ids.gridlayout_template <- function(x){ 73 | extract_chr(get_info(x, "elements"), "id") 74 | } 75 | 76 | -------------------------------------------------------------------------------- /R/get_info.R: -------------------------------------------------------------------------------- 1 | get_info <- function(x, prop){ 2 | UseMethod("get_info") 3 | } 4 | 5 | get_info.default <- function(x, prop){ 6 | cat("get_info generic") 7 | } 8 | 9 | #' @export 10 | get_info.gridlayout <- function(x, prop){ 11 | if (is_gridlayout_prop(prop)) { 12 | return (x[[prop]]) 13 | } 14 | get_info.gridlayout_template(x$layout, prop) 15 | } 16 | 17 | #' @export 18 | get_info.gridlayout_template <- function(x, prop){ 19 | if (is.null(x[[prop]])) { 20 | if (is_gridlayout_prop(prop)) { 21 | stop("Try to access property of gridlayout from gridlayout_template object") 22 | } 23 | stop("That property doesn't exist") 24 | } 25 | x[[prop]] 26 | } 27 | 28 | is_gridlayout_prop <- function(prop) prop %in% c("element_ids", "alternates") 29 | 30 | dump_all_info <- function(x) { 31 | list( 32 | grid = list( 33 | rows = get_info(x, "row_sizes"), 34 | cols = get_info(x, "col_sizes"), 35 | gap = get_info(x, "gap_size") 36 | ), 37 | elements = get_elements(x) 38 | ) 39 | } 40 | 41 | -------------------------------------------------------------------------------- /R/grid_card.R: -------------------------------------------------------------------------------- 1 | #' Grid-positioned card element 2 | #' 3 | #' The standard element for placing elements on the grid in a simple card 4 | #' container 5 | #' 6 | #' @param area Name of grid area, should match an area defined in the layout 7 | #' section of the wrapping `grid_page()` or `grid_container()`. 8 | #' @inheritDotParams bslib::card 9 | #' 10 | #' @seealso [bslib::card] for underlying function. [bslib::card_header], 11 | #' [bslib::card_body], [bslib::card_footer]. 12 | #' [grid_card_text()] for a card with just text content, 13 | #' [grid_card_plot()] for a card with just plot content, [grid_place()] to 14 | #' place any tag onto the grid without needing to wrap in a card, and 15 | #' [card_plot_output()] for including a smart-sized plot within a card. 16 | #' 17 | #' @example man/examples/simple_app.R 18 | #' @export 19 | grid_card <- function(area, ...) { 20 | warn_about_new_api(...) 21 | grid_place(area = area, bslib::card(...)) 22 | } 23 | 24 | 25 | warn_about_new_api <- function(...) { 26 | card_args <- list(...) 27 | args_names <- names(card_args) 28 | ignored_args <- args_names[args_names %in% old_api_args] 29 | if (length(ignored_args) > 0) { 30 | warning( 31 | paste0( 32 | "The grid_card() api updated with version 0.1.1 and ", 33 | "the following args are no longer supported: ", 34 | paste0("\"", ignored_args, "\"", collapse = ","), 35 | ". Look at help for bslib::card() to find alternatives! Alternatively ", 36 | "you can switch to grid_card_old() to keep the same api ", 37 | "(although updating to the new bslib version is recomended!)" 38 | ) 39 | ) 40 | } 41 | } 42 | old_api_args <- c( 43 | "title", 44 | "scrollable", 45 | "collapsible", 46 | "has_border", 47 | "item_gap" 48 | ) 49 | -------------------------------------------------------------------------------- /R/grid_card_text.R: -------------------------------------------------------------------------------- 1 | 2 | #' Grid-positioned card with only text 3 | #' 4 | #' Makes a grid_card that contains just text that is vertically centered within 5 | #' the panel. Useful for app titles or displaying text-based statistics. 6 | #' 7 | #' @inheritParams grid_card 8 | #' @inheritDotParams grid_card 9 | #' @param content Whatever you want the title to say. Typically just text but 10 | #' any tag or tag-list is possible. All will get wrapped in an `h3` tag. 11 | #' @param wrapping_tag What tag should the text be wrapped in. Takes either an 12 | #' `htmltools` tag function or the string of a tag accessible via 13 | #' `htmltools::tags[[wrapping_tag]]`. 14 | #' @param icon Optional icon/image for left of text. Supports image locations 15 | #' (those ending in `.png, .jpg, .jpeg` or `.svg`), ids of font-awesome icons 16 | #' (i.e. that works with `fontawesome::fa(icon)]`, or `fontawesome` icons as 17 | #' returned by [fontawesome::fa()] (if customization of icon style is 18 | #' desired.) 19 | #' @param alignment Horizontal alignment of text. Typical options include 20 | #' `start", "center", "end"`. For full list of options, see the [css-spec for 21 | #' `align-items`.](https://developer.mozilla.org/en-US/docs/Web/CSS/align-items) 22 | #' 23 | #' @param is_title Should the text of this panel be passed on as the title of 24 | #' the page? This will make the text show up in the browser tab or when you 25 | #' bookmark the app etc.. 26 | #' @param img_height If the passed icon is a path to an image, how tall should 27 | #' that image be rendered (preserves aspect ratio.) 28 | #' @inheritParams grid_card 29 | #' 30 | #' @examples 31 | #' 32 | #' # Typically you'll just pass a character string to the function 33 | #' grid_card_text(area = "header", "This is my header") 34 | #' 35 | #' # Icons from `fontawesome` can be used: 36 | #' 37 | #' # Either with just the id 38 | #' grid_card_text(area = "header", "Here's my text", icon = "r-project") 39 | #' 40 | #' # Or by directly passing the icon object if you want more customization 41 | #' grid_card_text( 42 | #' "header", 43 | #' "Here's my text", 44 | #' icon = fontawesome::fa("r-project", fill = "steelblue") 45 | #' ) 46 | #' 47 | #' # You can also pass arbitrary image locations for the icon 48 | #' grid_card_text( 49 | #' "header", 50 | #' "Here's my text", 51 | #' icon = "https://cran.r-project.org/Rlogo.svg" 52 | #' ) 53 | #' 54 | #' # These images can have their size controlled 55 | #' grid_card_text( 56 | #' "header", 57 | #' "Here's my text", 58 | #' icon = "https://cran.r-project.org/Rlogo.svg", 59 | #' img_height = "20px" 60 | #' ) 61 | #' 62 | #' 63 | #' # Commonly you may want to use the text panel text as the title of your app 64 | #' grid_card_text(area = "header", "My App Name", is_title = TRUE) 65 | #' 66 | #' @example man/examples/simple_app.R 67 | #' 68 | #' @export 69 | grid_card_text <- function(area, 70 | content = NULL, 71 | ..., 72 | alignment = "start", 73 | img_height = "55px", 74 | icon = NULL, 75 | wrapping_tag = "h2", 76 | is_title = FALSE) { 77 | if (notNull(icon)) { 78 | if (str_detect(icon, "\\.png|\\.jpg|\\.jpeg|\\.svg")) { 79 | icon <- shiny::img(src = icon, height = htmltools::validateCssUnit(img_height)) 80 | } else { 81 | requireNamespace("fontawesome", quietly = TRUE) 82 | # Test against a font awesome tag in case the class changes 83 | already_fa_obj <- identical(class(icon), class(fontawesome::fa("r-project"))) 84 | 85 | if (!already_fa_obj) { 86 | # Attempt to get icon from font-awesome 87 | icon <- fontawesome::fa(icon) 88 | } 89 | } 90 | } 91 | 92 | if (is.character(wrapping_tag)) { 93 | wrapping_tag <- htmltools::tags[[wrapping_tag]] 94 | } 95 | 96 | grid_place( 97 | area = area, 98 | htmltools::tags$div( 99 | class = "card grid_card_text", 100 | style = htmltools::css( 101 | `align-items` = alignment 102 | ), 103 | wrapping_tag( 104 | htmltools::tagList( 105 | icon, 106 | content 107 | ) 108 | ), 109 | if (is_title) htmltools::tags$head(htmltools::tags$title(content)) 110 | ) 111 | ) 112 | } 113 | -------------------------------------------------------------------------------- /R/grid_nested.R: -------------------------------------------------------------------------------- 1 | #' Grid-positioned card with `gridlayout` positioned content 2 | #' 3 | #' Creates a panel for a layout with its own internal gridlayout 4 | #' 5 | #' @inheritParams grid_card 6 | #' @param title Optional title for the card. Gets wrapped in 7 | #' `bslib::card_header()` 8 | #' @inheritParams grid_container 9 | #' 10 | #' @seealso [grid_card], [grid_container] 11 | #' 12 | #' @return A `grid_card` with a nested layout within it 13 | #' 14 | #' @seealso [grid_page()] for using a gridlayout to entirely define the page. 15 | #' [grid_container()] for placing a gridlayout in non-gridlayout parent 16 | #' elements. [grid_card()] for placing content inside your layout. See 17 | #' `vignette("defining-a-layout", package = "gridlayout")` for more info on 18 | #' defining your layout. 19 | #' 20 | #' @example man/examples/nested_app.R 21 | #' @export 22 | grid_nested <- function(area, 23 | layout, 24 | ..., 25 | id = NULL, 26 | title = NULL, 27 | flag_mismatches = TRUE) { 28 | 29 | nested_grid <- grid_container( 30 | layout = new_gridlayout(layout, container_height = "100%"), 31 | flag_mismatches = flag_mismatches, 32 | id = id, 33 | ... 34 | ) 35 | 36 | grid_card( 37 | area = area, 38 | if (!is.null(title)) { 39 | bslib::card_header(title) 40 | }, 41 | bslib::card_body( 42 | class = "p-0", 43 | nested_grid 44 | ) 45 | ) 46 | } 47 | -------------------------------------------------------------------------------- /R/grid_page.R: -------------------------------------------------------------------------------- 1 | #' Use `gridlayout` for entire app layout 2 | #' 3 | #' This is the typical way to use `gridlayout` in your `Shiny` app. `grid_page` 4 | #' will make up the entire `ui` declaration of the app. Under the hood it uses 5 | #' [shiny::fluidPage()] and [grid_container]. Elements are placed in the layout 6 | #' by wrapping in a `grid_card()` with the `area` set to the area in the layout 7 | #' the element should be placed in. 8 | #' 9 | #' @inheritParams grid_container 10 | #' @param container_height Optional parameter to control height of page. 11 | #' Defaults to `"viewport"` so app takes up full vertical space of page. 12 | #' See argument of same name in `new_gridlayout()` for more options. 13 | #' @param theme Optional argument to pass to `theme` argument of 14 | #' \code{\link[shiny]{fluidPage}}. 15 | #' @param flag_mismatches Should mismatches between the named arguments and 16 | #' layout elements trigger an error? 17 | #' 18 | #' @return A UI definition that can be passed to the 19 | #' \code{\link[shiny]{shinyUI}} function. 20 | #' @export 21 | #' 22 | #' @seealso See `vignette("defining-a-layout", package = "gridlayout")` for more info on defining your layout. [grid_container()] for using gridlayout without also setting up the 23 | #' root page layout. [grid_nested()] for placing a grid container within 24 | #' another gridlayout. [grid_card()] for placing content inside your layout. 25 | #' 26 | #' @example man/examples/simple_app.R 27 | #' 28 | grid_page <- function(layout, 29 | ..., 30 | row_sizes = NULL, 31 | col_sizes = NULL, 32 | gap_size = NULL, 33 | container_height = "viewport", 34 | theme = bslib::bs_theme(version = 5), 35 | flag_mismatches = FALSE) { 36 | dot_args <- list(...) 37 | 38 | body_elements <- Filter( 39 | f = function(x) !(is_grid_page_header(x) || is_grid_page_sidebar(x)), 40 | dot_args 41 | ) 42 | 43 | header <- Filter( 44 | f = function(x) is_grid_page_header(x), 45 | dot_args 46 | ) 47 | 48 | sidebar <- Filter( 49 | f = function(x) is_grid_page_sidebar(x), 50 | dot_args 51 | ) 52 | 53 | container_args <- c( 54 | list( 55 | layout = layout, 56 | flag_mismatches = flag_mismatches, 57 | row_sizes = row_sizes, 58 | col_sizes = col_sizes, 59 | gap_size = gap_size, 60 | container_height=container_height, 61 | # This is used to prevent a randomly generated grid container id data 62 | # attribute which stabilizes tests and makes it easier to target with 63 | # custom css 64 | id = "gridlayout-grid-page-container" 65 | ), 66 | body_elements 67 | ) 68 | 69 | container <- do.call(grid_container, container_args) 70 | 71 | shiny::fluidPage( 72 | theme = theme, 73 | htmltools::tags$div( 74 | id = "gridlayout-grid-page", 75 | header, 76 | sidebar, 77 | container 78 | ) 79 | ) 80 | } 81 | 82 | 83 | grid_page_header <- function(..., bgColor = "primary", bgGradient = FALSE, height = NULL) { 84 | update_el( 85 | htmltools::tags$div(...), 86 | classes = c( 87 | "grid-page-header", 88 | make_bg_class(bgColor, bgGradient) 89 | ), 90 | styles = list(height = height) 91 | ) 92 | } 93 | 94 | is_grid_page_header <- function(x) { 95 | has_class(x, "grid-page-header") 96 | } 97 | 98 | grid_page_sidebar <- function(..., side = "left", bgColor = "light", bgGradient = FALSE, width = NULL) { 99 | update_el( 100 | htmltools::tags$div(...), 101 | classes = c( 102 | make_bg_class(bgColor, bgGradient), 103 | paste0("grid-page-sidebar-", side) 104 | ), 105 | styles = list( 106 | width = width 107 | ) 108 | ) 109 | } 110 | 111 | is_grid_page_sidebar <- function(x) { 112 | has_class(x, "grid-page-sidebar") 113 | } 114 | -------------------------------------------------------------------------------- /R/grid_place.R: -------------------------------------------------------------------------------- 1 | #' Place any `shiny.tag` element on the grid 2 | #' 3 | #' Allows you to place any item directly on the grid without having to wrap it 4 | #' in `grid_card()` or other containers. This is done by adding a css property 5 | #' and data-attribute to the tag object. 6 | #' 7 | #' @param area Area of grid corresponding to the wrapping grid containers grid 8 | #' definition 9 | #' @param element Element (html tag) to be placed. 10 | #' 11 | #' @return The `element` updated with a `gridlayout-area` data-attribute and the 12 | #' `grid-area` property added to its styles 13 | #' 14 | #' @seealso [grid_card()] to place content on the grid wrapped in a bootstrap 15 | #' card. 16 | #' @example man/examples/grid_place_app.R 17 | #' @export 18 | grid_place <- function(area, element) { 19 | if (!inherits(element, "shiny.tag")) { 20 | stop("element needs to be a valid tag object") 21 | } 22 | 23 | htmltools::tagAppendAttributes( 24 | add_styles( 25 | element, 26 | "grid-area" = area 27 | ), 28 | "data-gridlayout-area" = area, 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /R/grid_plot.R: -------------------------------------------------------------------------------- 1 | #' Grid-positioned plot output 2 | #' 3 | #' A light wrapper for `shiny::plotOutput()` that uses gridlayout-friendly 4 | #' sizing defaults. 5 | #' 6 | #' 7 | #' @inheritParams grid_card 8 | #' @param outputId Output id of the plot output. Used to link to server code 9 | #' generating plot. If left unset this will use the same value as the `area` 10 | #' argument. 11 | #' @inheritDotParams shiny::plotOutput 12 | #' 13 | #' @return A grid panel filled with plot output 14 | #' 15 | #' @seealso [bslib::card_body], [shiny::plotOutput()] 16 | #' @example man/examples/simple_app.R 17 | #' 18 | #' @export 19 | grid_card_plot <- function(area, outputId = area,...) { 20 | grid_card( 21 | area = area, 22 | height = "100%", 23 | bslib::card_body( 24 | shiny::plotOutput(outputId = outputId, ...) 25 | ) 26 | ) 27 | } 28 | 29 | 30 | #' Plot output with smart sizing for use inside a `grid_card` - Depreciated 31 | #' 32 | #' `r lifecycle::badge('deprecated')` 33 | #' 34 | #' No longer necessary. Use plain `shiny::plotOutput()` wrapped with 35 | #' `bslib::card_body()`. 36 | #' 37 | #' @inheritParams shiny::plotOutput 38 | #' @param ... Named arguments become attributes on the div containing the plot. 39 | #' @param height height in valid css units (see [htmltools::validateCssUnit()] 40 | #' for more details.) Most use-cases will leave this unset and the plot will 41 | #' fill the card as best it can. 42 | #' @param stretch Set to `TRUE` if this `card_body` is eager to use any extra 43 | #' vertical space is available in the card. 44 | #' 45 | #' @seealso [grid_card_plot()] for a simpler way of placing just a plot on the 46 | #' grid 47 | #' @export 48 | card_plot_output <- function(outputId, 49 | click = NULL, 50 | dblclick = NULL, 51 | hover = NULL, 52 | brush = NULL, 53 | height = NULL, 54 | stretch = TRUE, 55 | ...) { 56 | 57 | lifecycle::deprecate_warn()("card_plot_output() is no longer needed with bslib card api. Simply use regular shiny::plotOutput()") 58 | 59 | plot_div <- shiny::plotOutput( 60 | outputId, 61 | height = height, 62 | click = click, 63 | dblclick = dblclick, 64 | hover = hover, 65 | brush = brush 66 | ) 67 | 68 | # TODO: card-img-* needs to go on the itself, not the containing
69 | htmltools::tagAppendAttributes( 70 | plot_div, 71 | style = htmltools::css( 72 | flex = if (stretch) "1 1", 73 | `-webkit-flex` = if (stretch) "1 1", 74 | # May be NULL 75 | `flex-basis` = htmltools::validateCssUnit(height), 76 | `-webkit-flex-basis` = htmltools::validateCssUnit(height), 77 | ), 78 | !!!rlang::list2(...) 79 | ) 80 | } 81 | -------------------------------------------------------------------------------- /R/gridlayout-package.R: -------------------------------------------------------------------------------- 1 | #' @keywords internal 2 | "_PACKAGE" 3 | 4 | ## usethis namespace: start 5 | #' @importFrom lifecycle deprecated 6 | ## usethis namespace: end 7 | NULL 8 | -------------------------------------------------------------------------------- /R/helper-demo-apps.R: -------------------------------------------------------------------------------- 1 | 2 | demo_apps <- c( 3 | "alternate_layouts", 4 | "nested_grids", 5 | "recursive_nesting", 6 | "scrolling_panels" 7 | ) 8 | 9 | accept_demo_snapshots <- function(){ 10 | for (demo_app in demo_apps) { 11 | path_to_app <- system.file(package = "gridlayout", paste0("demo_apps/", demo_app)) 12 | 13 | testthat::snapshot_accept(path=file.path(path_to_app, "tests/testthat")) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /R/md_table_to_matrix.R: -------------------------------------------------------------------------------- 1 | 2 | md_table_to_matrix <- function(md_layout){ 3 | # Split into rows 4 | by_rows <- strsplit(md_layout, "\\n")[[1]] %>% str_trim() 5 | by_rows <- by_rows[by_rows != ""] 6 | 7 | is_valid_md_table <- all( 8 | grepl( 9 | x = by_rows, 10 | pattern = "^\\s*\\|.*\\|\\s*$", 11 | perl = TRUE 12 | ) 13 | ) 14 | 15 | if (!is_valid_md_table){ 16 | stop("Layout table is malformed. Check syntax.") 17 | } 18 | 19 | nested_arrays <- by_rows %>% 20 | # Remove any decorative header lines: e.g. "|---:|" 21 | Filter(f = function(x) !grepl(x, pattern = "^[\\s\\|\\-|:]+$", perl = TRUE)) %>% 22 | # Remove leading pipes 23 | str_remove_all("^\\|\\s*") %>% 24 | # Split into cells on internal pipes 25 | strsplit("\\s*\\|\\s*") %>% 26 | Filter(f = function(x) {length(x) > 0}) 27 | 28 | 29 | layout_matrix <- matrix( 30 | do.call(c, nested_arrays), 31 | nrow = length(nested_arrays), 32 | byrow = TRUE 33 | ) 34 | 35 | 36 | layout_matrix 37 | } 38 | -------------------------------------------------------------------------------- /R/md_to_gridlayout.R: -------------------------------------------------------------------------------- 1 | #' Build `gridlayout` object from markdown table 2 | #' 3 | #' This is just a wrapper around [new_gridlayout] that is more explicit about 4 | #' its input format. Also adds the ability to absorb errors for use when parsing 5 | #' free text that may or may not represent a layout table. 6 | #' 7 | #' @param layout_table Character string with a markdown table. First row and 8 | #' column are reserved for sizing (any valid css sizing works). An optional 9 | #' grid-gap can be provided using the very first cell. 10 | #' @param null_instead_of_error When the input fails to be parsed as a layout, 11 | #' should the function return `NULL` instead of throwing an error? Useful for 12 | #' situations where you're parsing multiple tables simply want to check if a 13 | #' table can be parsed instead of relying on it being parsable. 14 | #' @inheritDotParams new_gridlayout 15 | #' 16 | #' @return An object of class "grid_layout", which stores the layout as a 17 | #' matrix. This can be passed to other functions such as `layout_to_css()`. 18 | #' 19 | #' @seealso [new_gridlayout] 20 | #' @export 21 | #' 22 | #' @examples 23 | #' md_to_gridlayout( 24 | #' layout_table = " 25 | #' | |120px |1fr |1fr | 26 | #' |:-----|:-------|:------|:------| 27 | #' |100px |header |header |header | 28 | #' |1fr |sidebar |plot_a |plot_c | 29 | #' |1fr |sidebar |plot_b |plot_b |" 30 | #' ) 31 | #' 32 | #' 33 | #' # Can specify gap size in upper left cell 34 | #' md_to_gridlayout( 35 | #' layout_table = " 36 | #' |25px |120px |1fr | 37 | #' |:-----|:------|:------| 38 | #' |100px |header |header | 39 | #' |1fr |plot |table | 40 | #' |1fr |footer |footer |" 41 | #' ) 42 | #' 43 | #' # Don't need to follow full md table with 44 | #' # header row if not desired 45 | #' md_to_gridlayout( 46 | #' layout_table = " 47 | #' |25px |120px |1fr | 48 | #' |100px |header |header | 49 | #' |1fr |plot |table | 50 | #' |1fr |footer |footer |" 51 | #' ) 52 | #' 53 | #' # Can omit sizing as well if desired 54 | #' md_to_gridlayout( 55 | #' layout_table = " 56 | #' |header |header | 57 | #' |plot |table | 58 | #' |footer |footer |" 59 | #' ) 60 | #' 61 | md_to_gridlayout <- function(layout_table, null_instead_of_error = FALSE, ...) { 62 | tryCatch( 63 | { 64 | new_gridlayout(layout_table, ...) 65 | }, 66 | error = function(err) { 67 | if (null_instead_of_error) { 68 | NULL 69 | } else { 70 | stop(err) 71 | } 72 | } 73 | ) 74 | } 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /R/parse_layout_matrix.R: -------------------------------------------------------------------------------- 1 | DEFAULT_SIZE_CHAR <- "." 2 | 3 | parse_layout_matrix <- function(layout_matrix){ 4 | 5 | # Missing column sizes 6 | if (!all_css_or_empty(layout_matrix[1,])) { 7 | layout_matrix <- rbind( 8 | "", 9 | layout_matrix 10 | ) 11 | } 12 | 13 | # Missing row sizes 14 | if (!all_css_or_empty(layout_matrix[,1])){ 15 | layout_matrix <- cbind( 16 | "", 17 | layout_matrix 18 | ) 19 | } 20 | 21 | # Subtract the size row and column to get layout dimensions 22 | n_rows <- nrow(layout_matrix) - 1L 23 | n_cols <- ncol(layout_matrix) - 1L 24 | 25 | # Fill in the empty or missing sizes with defaults 26 | column_sizes <- fill_missing_w_default(layout_matrix[1,-1]) 27 | row_sizes <- fill_missing_w_default(layout_matrix[-1,1]) 28 | gap_size <- fill_missing_w_default(layout_matrix[1,1]) 29 | 30 | # Strip away sizes to get the main layout matrix 31 | items_matrix <- matrix( 32 | layout_matrix[-1,-1], 33 | nrow = n_rows, 34 | ncol = n_cols 35 | ) 36 | 37 | item_ids <- unique(as.vector(items_matrix)) 38 | 39 | validate_item_ids(item_ids) 40 | 41 | elements <- lapply( 42 | item_ids, 43 | function(id){ 44 | all_pos <- which( items_matrix == id, arr.ind = TRUE ) 45 | list( 46 | id = id, 47 | start_row = min(all_pos[,"row"]), 48 | end_row = max(all_pos[,"row"]), 49 | start_col = min(all_pos[,"col"]), 50 | end_col = max(all_pos[,"col"]) 51 | ) 52 | } 53 | ) 54 | 55 | list( 56 | areas = items_matrix, 57 | elements = elements, 58 | column_sizes = column_sizes, 59 | row_sizes = row_sizes, 60 | gap_size = gap_size 61 | ) 62 | } 63 | 64 | VALID_ITEM_ID_REGEX = "^[a-zA-Z]+\\w*|\\.$" 65 | validate_item_ids <- function(item_ids){ 66 | 67 | is_valid_item_id <- str_detect(item_ids, pattern = VALID_ITEM_ID_REGEX) 68 | 69 | if (all(is_valid_item_id)) { return(TRUE) } 70 | 71 | bad_ids <- item_ids[!is_valid_item_id] 72 | 73 | stop( 74 | "Layout has the following invalid item ids in it:\n", 75 | list_in_quotes(bad_ids), "\n", 76 | "Only simple words and (non-starting) numbers are allowed.", 77 | call. = FALSE 78 | ) 79 | } 80 | -------------------------------------------------------------------------------- /R/parsing-utils.R: -------------------------------------------------------------------------------- 1 | CSS_VALUE_REGEX <- "([0-9]\\d*)(\\.\\d+)?(px|rem|fr)|auto" 2 | has_css_value <- function(x) grepl(x, pattern = CSS_VALUE_REGEX, perl = TRUE) 3 | 4 | 5 | 6 | replace_x_with_y<- function(vec, x, y){ 7 | vec[vec == x] <- y 8 | vec 9 | } 10 | 11 | 12 | fill_missing_w_default <- function(vec){ 13 | replace_x_with_y(vec, x = "", y = DEFAULT_SIZE_CHAR) 14 | } 15 | 16 | replace_default_with_value <- function(vec, val) { 17 | replace_x_with_y(vec, x = DEFAULT_SIZE_CHAR, y = val) 18 | } 19 | 20 | 21 | all_css_or_empty <- function(vals) { 22 | all(has_css_value(vals) | vals == "") 23 | } 24 | 25 | 26 | flatten <- function(x) { do.call(what = c, args = x)} 27 | 28 | get_num_cols_by_row <- function(nested_array){ 29 | vapply( 30 | nested_array, 31 | FUN = length, 32 | FUN.VALUE = numeric(1L) 33 | ) 34 | } 35 | 36 | split_on_space <- function(x){ 37 | strsplit(str_trim(x), split = "\\s+") 38 | } 39 | -------------------------------------------------------------------------------- /R/tag-utils.R: -------------------------------------------------------------------------------- 1 | 2 | add_styles <- function(el, ...) { 3 | el$attribs$style <- paste0( 4 | el$attribs$style, 5 | htmltools::css(...) 6 | ) 7 | 8 | el 9 | } 10 | 11 | add_class <- function(el, class) { 12 | htmltools::tagAppendAttributes(el, class = paste(class, collapse = " ")) 13 | } 14 | 15 | update_el <- function(el, classes = list(), styles = list()) { 16 | el <- htmltools::tagAppendAttributes( 17 | el, 18 | class = paste(classes, collapse = " ") 19 | ) 20 | 21 | el$attribs$style <- paste0( 22 | el$attribs$style, 23 | do.call(htmltools::css, styles) 24 | ) 25 | 26 | el 27 | } 28 | 29 | 30 | has_class <- function(el, class) { 31 | if (!inherits(el, "shiny.tag")) { 32 | return(FALSE) 33 | } 34 | 35 | any( 36 | grepl( 37 | x = htmltools::tagGetAttribute(el, "class"), 38 | pattern = class, 39 | fixed = TRUE 40 | ) 41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /R/use_gridlayout_rmd.R: -------------------------------------------------------------------------------- 1 | #' Enable `gridlayout` usage in RMarkdown documents 2 | #' 3 | #' Adds required hooks to RMarkdown to process `gridlayout` chunks and style 4 | #' document accordingly. Layout will be generated from a chunk identified with 5 | #' the syntax ```` ```{gridlayout} ``` ```` 6 | #' 7 | #' See `vignette("using_with_rmd", package = "gridlayout")` for a more in-depth 8 | #' overview. 9 | #' 10 | #' @param container_query The CSS selector used to access the main grid 11 | #' container. This is typically left at the default of `'.main-container'` as 12 | #' that's the parent of each section in the standard rmd template. 13 | #' @param selector_prefix CSS prefix used to target grid elements. This will 14 | #' change if you're integrating grid with a system that you don't want to use 15 | #' ids (the `"#"` prefix) with because they are not available or are used for 16 | #' other reasons. 17 | #' 18 | #' @return NULL 19 | #' @export 20 | #' 21 | use_gridlayout_rmd <- function(container_query = ".main-container", 22 | selector_prefix = "#") { 23 | requireNamespace("knitr", quietly = TRUE) 24 | 25 | accessory_css <- get_accessory_css("gridlayout.css") 26 | 27 | 28 | knitr::knit_engines$set(gridlayout = function(options) { 29 | code <- paste(options$code, collapse = "\n") 30 | if (options$eval) { 31 | layout <- md_to_gridlayout(code) 32 | areas <- get_element_ids(layout) 33 | 34 | css_for_layout <- paste( 35 | "", 48 | sep = "\n" 49 | ) 50 | } 51 | options$results <- "asis" 52 | options$echo <- FALSE 53 | knitr::engine_output( 54 | options, 55 | code = options$code, 56 | out = css_for_layout 57 | ) 58 | }) 59 | } 60 | 61 | # Dump css file to string for inlining while still keeping in a .css file for 62 | # easier editing 63 | get_accessory_css <- function(file) { 64 | paste( 65 | readLines(system.file(paste0("resources/", file), package = "gridlayout")), 66 | collapse = "\n" 67 | ) 68 | } 69 | -------------------------------------------------------------------------------- /R/use_gridlayout_shiny.R: -------------------------------------------------------------------------------- 1 | 2 | #' Convert layout to css for Shiny 3 | #' 4 | #' This simply wraps the output of `to_css()` in a `style` tag and escapes HTML 5 | #' characters to simplify using in Shiny. Most of the time you'll want to use 6 | #' \code{\link{grid_page}} or \code{\link{grid_container}} instead of manually 7 | #' adding this css though. 8 | #' 9 | #' @seealso \code{\link{to_css}}, \code{\link{grid_page}}, \code{\link{grid_container}} 10 | #' @inheritParams grid_container 11 | #' @inheritDotParams to_css -layout 12 | #' 13 | #' @return Character string of css used to setup grid layout and place elements 14 | #' (referenced by id) into correct locations 15 | #' 16 | #' @keywords internal 17 | #' 18 | #' @examples 19 | #' # Only run these examples in interactive R sessions 20 | #' if (interactive()) { 21 | #' library(shiny) 22 | #' my_layout <- " 23 | #' | | | | 24 | #' |------|--------|---------| 25 | #' |2rem |200px |1fr | 26 | #' |100px |header |header | 27 | #' |500px |sidebar |distPlot |" 28 | #' 29 | #' # The classic Geyser app with grid layout 30 | #' shinyApp( 31 | #' ui = fluidPage( 32 | #' use_gridlayout_shiny(my_layout, "#app-container"), 33 | #' div( 34 | #' id = "app-container", 35 | #' div( 36 | #' style = "grid-area: header;", 37 | #' h2(id = "app-title", "Old Faithful Geyser Data") 38 | #' ), 39 | #' div( 40 | #' style = "grid-area: sidebar;", 41 | #' sliderInput("bins", "Number of bins:", 42 | #' min = 1, max = 50, value = 30 43 | #' ) 44 | #' ), 45 | #' div( 46 | #' style = "grid-area:distPlot", 47 | #' plotOutput("distPlot", height = "100%") 48 | #' ) 49 | #' ) 50 | #' ), 51 | #' server = function(input, output) { 52 | #' output$distPlot <- renderPlot({ 53 | #' x <- faithful[, 2] 54 | #' bins <- seq(min(x), max(x), length.out = input$bins + 1) 55 | #' hist(x, breaks = bins, col = "darkgray", border = "white") 56 | #' }) 57 | #' } 58 | #' ) 59 | #' 60 | #' } 61 | use_gridlayout_shiny <- function(layout, ...) { 62 | requireNamespace("htmltools", quietly = TRUE) 63 | layout <- as_gridlayout(layout) 64 | htmltools::tags$head( 65 | htmltools::tags$style( 66 | htmltools::HTML(to_css(layout, ...)) 67 | ) 68 | ) 69 | } 70 | -------------------------------------------------------------------------------- /R/utils-pipe.R: -------------------------------------------------------------------------------- 1 | #' Pipe operator 2 | #' 3 | #' See \code{magrittr::\link[magrittr:pipe]{\%>\%}} for details. 4 | #' 5 | #' @name %>% 6 | #' @rdname pipe 7 | #' @keywords internal 8 | #' @export 9 | #' @importFrom magrittr %>% 10 | #' @usage lhs \%>\% rhs 11 | #' @param lhs A value or the magrittr placeholder. 12 | #' @param rhs A function call using the magrittr semantics. 13 | #' @return The result of calling `rhs(lhs)`. 14 | NULL 15 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | template: 2 | package: quillt 3 | 4 | development: 5 | mode: auto 6 | 7 | home: 8 | strip_header: false 9 | 10 | navbar: 11 | title: ~ 12 | type: default 13 | structure: 14 | left: [setting-up, examples, articles, reference] 15 | # right: [examples, github] 16 | components: 17 | home: ~ 18 | examples: 19 | text: Layout Examples 20 | href: articles/layout-examples.html 21 | setting-up: 22 | text: Defining a layout 23 | href: articles/defining-a-layout.html 24 | reference: 25 | text: Reference 26 | href: reference/index.html 27 | github: 28 | icon: fab fa-github fa-lg 29 | href: https://github.com/rstudio/gridlayout 30 | 31 | destination: docs 32 | 33 | reference: 34 | - title: Layouts 35 | desc: > 36 | Setup `gridlayout` containers 37 | - contents: 38 | - grid_page 39 | - grid_container 40 | - use_gridlayout_rmd 41 | - title: Content containers 42 | desc: > 43 | Place content on the grid. 44 | - contents: 45 | - starts_with("grid_card") 46 | - grid_card_plot 47 | - grid_nested 48 | - card_plot_output 49 | - grid_place 50 | - title: Working with gridlayout objects 51 | desc: > 52 | Functions for creating, editing, or generating content with `gridlayout` objects. For advanced use-cases. 53 | - contents: 54 | - new_gridlayout 55 | - md_to_gridlayout 56 | - get_elements 57 | - get_element_ids 58 | - to_md 59 | - to_matrix 60 | - to_css 61 | -------------------------------------------------------------------------------- /docs/ace-1.2.3/mode-plain_text.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/mode/plain_text",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/text_highlight_rules","ace/mode/behaviour"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./text_highlight_rules").TextHighlightRules,o=e("./behaviour").Behaviour,u=function(){this.HighlightRules=s,this.$behaviour=new o};r.inherits(u,i),function(){this.type="text",this.getNextLineIndent=function(e,t,n){return""},this.$id="ace/mode/plain_text"}.call(u.prototype),t.Mode=u}) -------------------------------------------------------------------------------- /docs/ace-1.2.3/mode-r.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/mode/tex_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=function(e){e||(e="text"),this.$rules={start:[{token:"comment",regex:"%.*$"},{token:e,regex:"\\\\[$&%#\\{\\}]"},{token:"keyword",regex:"\\\\(?:documentclass|usepackage|newcounter|setcounter|addtocounter|value|arabic|stepcounter|newenvironment|renewenvironment|ref|vref|eqref|pageref|label|cite[a-zA-Z]*|tag|begin|end|bibitem)\\b",next:"nospell"},{token:"keyword",regex:"\\\\(?:[a-zA-z0-9]+|[^a-zA-z0-9])"},{token:"paren.keyword.operator",regex:"[[({]"},{token:"paren.keyword.operator",regex:"[\\])}]"},{token:e,regex:"\\s+"}],nospell:[{token:"comment",regex:"%.*$",next:"start"},{token:"nospell."+e,regex:"\\\\[$&%#\\{\\}]"},{token:"keyword",regex:"\\\\(?:documentclass|usepackage|newcounter|setcounter|addtocounter|value|arabic|stepcounter|newenvironment|renewenvironment|ref|vref|eqref|pageref|label|cite[a-zA-Z]*|tag|begin|end|bibitem)\\b"},{token:"keyword",regex:"\\\\(?:[a-zA-z0-9]+|[^a-zA-z0-9])",next:"start"},{token:"paren.keyword.operator",regex:"[[({]"},{token:"paren.keyword.operator",regex:"[\\])]"},{token:"paren.keyword.operator",regex:"}",next:"start"},{token:"nospell."+e,regex:"\\s+"},{token:"nospell."+e,regex:"\\w+"}]}};r.inherits(o,s),t.TexHighlightRules=o}),ace.define("ace/mode/r_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules","ace/mode/tex_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=e("./tex_highlight_rules").TexHighlightRules,u=function(){var e=i.arrayToMap("function|if|in|break|next|repeat|else|for|return|switch|while|try|tryCatch|stop|warning|require|library|attach|detach|source|setMethod|setGeneric|setGroupGeneric|setClass".split("|")),t=i.arrayToMap("NULL|NA|TRUE|FALSE|T|F|Inf|NaN|NA_integer_|NA_real_|NA_character_|NA_complex_".split("|"));this.$rules={start:[{token:"comment.sectionhead",regex:"#+(?!').*(?:----|====|####)\\s*$"},{token:"comment",regex:"#+'",next:"rd-start"},{token:"comment",regex:"#.*$"},{token:"string",regex:'["]',next:"qqstring"},{token:"string",regex:"[']",next:"qstring"},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+[Li]?\\b"},{token:"constant.numeric",regex:"\\d+L\\b"},{token:"constant.numeric",regex:"\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d*)?i?\\b"},{token:"constant.numeric",regex:"\\.\\d+(?:[eE][+\\-]?\\d*)?i?\\b"},{token:"constant.language.boolean",regex:"(?:TRUE|FALSE|T|F)\\b"},{token:"identifier",regex:"`.*?`"},{onMatch:function(n){return e[n]?"keyword":t[n]?"constant.language":n=="..."||n.match(/^\.\.\d+$/)?"variable.language":"identifier"},regex:"[a-zA-Z.][a-zA-Z0-9._]*\\b"},{token:"keyword.operator",regex:"%%|>=|<=|==|!=|\\->|<\\-|\\|\\||&&|=|\\+|\\-|\\*|/|\\^|>|<|!|&|\\||~|\\$|:"},{token:"keyword.operator",regex:"%.*?%"},{token:"paren.keyword.operator",regex:"[[({]"},{token:"paren.keyword.operator",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],qqstring:[{token:"string",regex:'(?:(?:\\\\.)|(?:[^"\\\\]))*?"',next:"start"},{token:"string",regex:".+"}],qstring:[{token:"string",regex:"(?:(?:\\\\.)|(?:[^'\\\\]))*?'",next:"start"},{token:"string",regex:".+"}]};var n=(new o("comment")).getRules();for(var r=0;r :first-child"); 5 | var i, h, a; 6 | for (i = 0; i < hs.length; i++) { 7 | h = hs[i]; 8 | if (!/^h[1-6]$/i.test(h.tagName)) continue; // it should be a header h1-h6 9 | a = h.attributes; 10 | while (a.length > 0) h.removeAttribute(a[0].name); 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /docs/articles/alternate-layouts_resizing.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/docs/articles/alternate-layouts_resizing.gif -------------------------------------------------------------------------------- /docs/articles/alternate-layouts_w1000_h1000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/docs/articles/alternate-layouts_w1000_h1000.png -------------------------------------------------------------------------------- /docs/articles/alternate-layouts_w2000_h1000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/docs/articles/alternate-layouts_w2000_h1000.png -------------------------------------------------------------------------------- /docs/articles/alternate-layouts_w500_h1000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/docs/articles/alternate-layouts_w500_h1000.png -------------------------------------------------------------------------------- /docs/articles/layout-examples_files/header-attrs-2.7.1/header-attrs.js: -------------------------------------------------------------------------------- 1 | // Pandoc 2.9 adds attributes on both header and div. We remove the former (to 2 | // be compatible with the behavior of Pandoc < 2.8). 3 | document.addEventListener('DOMContentLoaded', function(e) { 4 | var hs = document.querySelectorAll("div.section[class*='level'] > :first-child"); 5 | var i, h, a; 6 | for (i = 0; i < hs.length; i++) { 7 | h = hs[i]; 8 | if (!/^h[1-6]$/i.test(h.tagName)) continue; // it should be a header h1-h6 9 | a = h.attributes; 10 | while (a.length > 0) h.removeAttribute(a[0].name); 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /docs/articles/layout-examples_focal_chart_side.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/docs/articles/layout-examples_focal_chart_side.png -------------------------------------------------------------------------------- /docs/articles/layout-examples_focal_chart_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/docs/articles/layout-examples_focal_chart_top.png -------------------------------------------------------------------------------- /docs/articles/layout-examples_four_panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/docs/articles/layout-examples_four_panel.png -------------------------------------------------------------------------------- /docs/articles/layout-examples_scrolling_stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/docs/articles/layout-examples_scrolling_stack.png -------------------------------------------------------------------------------- /docs/articles/layout-examples_stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/docs/articles/layout-examples_stack.png -------------------------------------------------------------------------------- /docs/articles/use_gridlayout_rmd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/docs/articles/use_gridlayout_rmd.png -------------------------------------------------------------------------------- /docs/articles/use_gridlayout_rmd_base.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/docs/articles/use_gridlayout_rmd_base.png -------------------------------------------------------------------------------- /docs/articles/use_gridlayout_rmd_extra_child_styles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/docs/articles/use_gridlayout_rmd_extra_child_styles.png -------------------------------------------------------------------------------- /docs/articles/use_gridlayout_rmd_only_grid_panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/docs/articles/use_gridlayout_rmd_only_grid_panel.png -------------------------------------------------------------------------------- /docs/articles/using_with_rmd_files/header-attrs-2.7.1/header-attrs.js: -------------------------------------------------------------------------------- 1 | // Pandoc 2.9 adds attributes on both header and div. We remove the former (to 2 | // be compatible with the behavior of Pandoc < 2.8). 3 | document.addEventListener('DOMContentLoaded', function(e) { 4 | var hs = document.querySelectorAll("div.section[class*='level'] > :first-child"); 5 | var i, h, a; 6 | for (i = 0; i < hs.length; i++) { 7 | h = hs[i]; 8 | if (!/^h[1-6]$/i.test(h.tagName)) continue; // it should be a header h1-h6 9 | a = h.attributes; 10 | while (a.length > 0) h.removeAttribute(a[0].name); 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /docs/bootstrap-toc.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Table of Contents v0.4.1 (http://afeld.github.io/bootstrap-toc/) 3 | * Copyright 2015 Aidan Feldman 4 | * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */ 5 | 6 | /* modified from https://github.com/twbs/bootstrap/blob/94b4076dd2efba9af71f0b18d4ee4b163aa9e0dd/docs/assets/css/src/docs.css#L548-L601 */ 7 | 8 | /* All levels of nav */ 9 | nav[data-toggle='toc'] .nav > li > a { 10 | display: block; 11 | padding: 4px 20px; 12 | font-size: 13px; 13 | font-weight: 500; 14 | color: #767676; 15 | } 16 | nav[data-toggle='toc'] .nav > li > a:hover, 17 | nav[data-toggle='toc'] .nav > li > a:focus { 18 | padding-left: 19px; 19 | color: #563d7c; 20 | text-decoration: none; 21 | background-color: transparent; 22 | border-left: 1px solid #563d7c; 23 | } 24 | nav[data-toggle='toc'] .nav > .active > a, 25 | nav[data-toggle='toc'] .nav > .active:hover > a, 26 | nav[data-toggle='toc'] .nav > .active:focus > a { 27 | padding-left: 18px; 28 | font-weight: bold; 29 | color: #563d7c; 30 | background-color: transparent; 31 | border-left: 2px solid #563d7c; 32 | } 33 | 34 | /* Nav: second level (shown on .active) */ 35 | nav[data-toggle='toc'] .nav .nav { 36 | display: none; /* Hide by default, but at >768px, show it */ 37 | padding-bottom: 10px; 38 | } 39 | nav[data-toggle='toc'] .nav .nav > li > a { 40 | padding-top: 1px; 41 | padding-bottom: 1px; 42 | padding-left: 30px; 43 | font-size: 12px; 44 | font-weight: normal; 45 | } 46 | nav[data-toggle='toc'] .nav .nav > li > a:hover, 47 | nav[data-toggle='toc'] .nav .nav > li > a:focus { 48 | padding-left: 29px; 49 | } 50 | nav[data-toggle='toc'] .nav .nav > .active > a, 51 | nav[data-toggle='toc'] .nav .nav > .active:hover > a, 52 | nav[data-toggle='toc'] .nav .nav > .active:focus > a { 53 | padding-left: 28px; 54 | font-weight: 500; 55 | } 56 | 57 | /* from https://github.com/twbs/bootstrap/blob/e38f066d8c203c3e032da0ff23cd2d6098ee2dd6/docs/assets/css/src/docs.css#L631-L634 */ 58 | nav[data-toggle='toc'] .nav > .active > ul { 59 | display: block; 60 | } 61 | -------------------------------------------------------------------------------- /docs/docsearch.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | // register a handler to move the focus to the search bar 4 | // upon pressing shift + "/" (i.e. "?") 5 | $(document).on('keydown', function(e) { 6 | if (e.shiftKey && e.keyCode == 191) { 7 | e.preventDefault(); 8 | $("#search-input").focus(); 9 | } 10 | }); 11 | 12 | $(document).ready(function() { 13 | // do keyword highlighting 14 | /* modified from https://jsfiddle.net/julmot/bL6bb5oo/ */ 15 | var mark = function() { 16 | 17 | var referrer = document.URL ; 18 | var paramKey = "q" ; 19 | 20 | if (referrer.indexOf("?") !== -1) { 21 | var qs = referrer.substr(referrer.indexOf('?') + 1); 22 | var qs_noanchor = qs.split('#')[0]; 23 | var qsa = qs_noanchor.split('&'); 24 | var keyword = ""; 25 | 26 | for (var i = 0; i < qsa.length; i++) { 27 | var currentParam = qsa[i].split('='); 28 | 29 | if (currentParam.length !== 2) { 30 | continue; 31 | } 32 | 33 | if (currentParam[0] == paramKey) { 34 | keyword = decodeURIComponent(currentParam[1].replace(/\+/g, "%20")); 35 | } 36 | } 37 | 38 | if (keyword !== "") { 39 | $(".contents").unmark({ 40 | done: function() { 41 | $(".contents").mark(keyword); 42 | } 43 | }); 44 | } 45 | } 46 | }; 47 | 48 | mark(); 49 | }); 50 | }); 51 | 52 | /* Search term highlighting ------------------------------*/ 53 | 54 | function matchedWords(hit) { 55 | var words = []; 56 | 57 | var hierarchy = hit._highlightResult.hierarchy; 58 | // loop to fetch from lvl0, lvl1, etc. 59 | for (var idx in hierarchy) { 60 | words = words.concat(hierarchy[idx].matchedWords); 61 | } 62 | 63 | var content = hit._highlightResult.content; 64 | if (content) { 65 | words = words.concat(content.matchedWords); 66 | } 67 | 68 | // return unique words 69 | var words_uniq = [...new Set(words)]; 70 | return words_uniq; 71 | } 72 | 73 | function updateHitURL(hit) { 74 | 75 | var words = matchedWords(hit); 76 | var url = ""; 77 | 78 | if (hit.anchor) { 79 | url = hit.url_without_anchor + '?q=' + escape(words.join(" ")) + '#' + hit.anchor; 80 | } else { 81 | url = hit.url + '?q=' + escape(words.join(" ")); 82 | } 83 | 84 | return url; 85 | } 86 | -------------------------------------------------------------------------------- /docs/link.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/pkgdown.js: -------------------------------------------------------------------------------- 1 | /* http://gregfranko.com/blog/jquery-best-practices/ */ 2 | (function($) { 3 | $(function() { 4 | 5 | $('.navbar-fixed-top').headroom(); 6 | 7 | $('body').css('padding-top', $('.navbar').height() + 10); 8 | $(window).resize(function(){ 9 | $('body').css('padding-top', $('.navbar').height() + 10); 10 | }); 11 | 12 | $('[data-toggle="tooltip"]').tooltip(); 13 | 14 | var cur_path = paths(location.pathname); 15 | var links = $("#navbar ul li a"); 16 | var max_length = -1; 17 | var pos = -1; 18 | for (var i = 0; i < links.length; i++) { 19 | if (links[i].getAttribute("href") === "#") 20 | continue; 21 | // Ignore external links 22 | if (links[i].host !== location.host) 23 | continue; 24 | 25 | var nav_path = paths(links[i].pathname); 26 | 27 | var length = prefix_length(nav_path, cur_path); 28 | if (length > max_length) { 29 | max_length = length; 30 | pos = i; 31 | } 32 | } 33 | 34 | // Add class to parent
  • , and enclosing
  • if in dropdown 35 | if (pos >= 0) { 36 | var menu_anchor = $(links[pos]); 37 | menu_anchor.parent().addClass("active"); 38 | menu_anchor.closest("li.dropdown").addClass("active"); 39 | } 40 | }); 41 | 42 | function paths(pathname) { 43 | var pieces = pathname.split("/"); 44 | pieces.shift(); // always starts with / 45 | 46 | var end = pieces[pieces.length - 1]; 47 | if (end === "index.html" || end === "") 48 | pieces.pop(); 49 | return(pieces); 50 | } 51 | 52 | // Returns -1 if not found 53 | function prefix_length(needle, haystack) { 54 | if (needle.length > haystack.length) 55 | return(-1); 56 | 57 | // Special case for length-0 haystack, since for loop won't run 58 | if (haystack.length === 0) { 59 | return(needle.length === 0 ? 0 : -1); 60 | } 61 | 62 | for (var i = 0; i < haystack.length; i++) { 63 | if (needle[i] != haystack[i]) 64 | return(i); 65 | } 66 | 67 | return(haystack.length); 68 | } 69 | 70 | /* Clipboard --------------------------*/ 71 | 72 | function changeTooltipMessage(element, msg) { 73 | var tooltipOriginalTitle=element.getAttribute('data-original-title'); 74 | element.setAttribute('data-original-title', msg); 75 | $(element).tooltip('show'); 76 | element.setAttribute('data-original-title', tooltipOriginalTitle); 77 | } 78 | 79 | if(ClipboardJS.isSupported()) { 80 | $(document).ready(function() { 81 | var copyButton = ""; 82 | 83 | $("div.sourceCode").addClass("hasCopyButton"); 84 | 85 | // Insert copy buttons: 86 | $(copyButton).prependTo(".hasCopyButton"); 87 | 88 | // Initialize tooltips: 89 | $('.btn-copy-ex').tooltip({container: 'body'}); 90 | 91 | // Initialize clipboard: 92 | var clipboardBtnCopies = new ClipboardJS('[data-clipboard-copy]', { 93 | text: function(trigger) { 94 | return trigger.parentNode.textContent.replace(/\n#>[^\n]*/g, ""); 95 | } 96 | }); 97 | 98 | clipboardBtnCopies.on('success', function(e) { 99 | changeTooltipMessage(e.trigger, 'Copied!'); 100 | e.clearSelection(); 101 | }); 102 | 103 | clipboardBtnCopies.on('error', function() { 104 | changeTooltipMessage(e.trigger,'Press Ctrl+C or Command+C to copy'); 105 | }); 106 | }); 107 | } 108 | })(window.jQuery || window.$) 109 | -------------------------------------------------------------------------------- /docs/pkgdown.yml: -------------------------------------------------------------------------------- 1 | pandoc: 2.19.2 2 | pkgdown: 2.0.5 3 | pkgdown_sha: ~ 4 | articles: 5 | alternate-layouts: alternate-layouts.html 6 | defining-a-layout: defining-a-layout.html 7 | layout-examples: layout-examples.html 8 | using_with_rmd: using_with_rmd.html 9 | last_built: 2023-02-28T15:43Z 10 | 11 | -------------------------------------------------------------------------------- /docs/reference/Rplot001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/docs/reference/Rplot001.png -------------------------------------------------------------------------------- /docs/reference/figures/basic_markdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/docs/reference/figures/basic_markdown.png -------------------------------------------------------------------------------- /docs/reference/figures/geyser_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/docs/reference/figures/geyser_demo.png -------------------------------------------------------------------------------- /docs/reference/figures/geyser_w_grided.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/docs/reference/figures/geyser_w_grided.png -------------------------------------------------------------------------------- /docs/reference/figures/grided_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/docs/reference/figures/grided_default.png -------------------------------------------------------------------------------- /docs/reference/figures/lifecycle-archived.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclearchivedarchived -------------------------------------------------------------------------------- /docs/reference/figures/lifecycle-defunct.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecycledefunctdefunct -------------------------------------------------------------------------------- /docs/reference/figures/lifecycle-deprecated.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecycledeprecateddeprecated -------------------------------------------------------------------------------- /docs/reference/figures/lifecycle-experimental.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecycleexperimentalexperimental -------------------------------------------------------------------------------- /docs/reference/figures/lifecycle-maturing.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclematuringmaturing -------------------------------------------------------------------------------- /docs/reference/figures/lifecycle-questioning.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclequestioningquestioning -------------------------------------------------------------------------------- /docs/reference/figures/lifecycle-stable.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclestablestable -------------------------------------------------------------------------------- /docs/reference/figures/lifecycle-superseded.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclesupersededsuperseded -------------------------------------------------------------------------------- /docs/reference/figures/nested_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/docs/reference/figures/nested_demo.png -------------------------------------------------------------------------------- /docs/rmarkdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/docs/rmarkdown.png -------------------------------------------------------------------------------- /docs/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | /404.html 5 | 6 | 7 | /LICENSE-text.html 8 | 9 | 10 | /LICENSE.html 11 | 12 | 13 | /articles/alternate-layouts.html 14 | 15 | 16 | /articles/defining-a-layout.html 17 | 18 | 19 | /articles/index.html 20 | 21 | 22 | /articles/layout-examples.html 23 | 24 | 25 | /articles/using_with_rmd.html 26 | 27 | 28 | /authors.html 29 | 30 | 31 | /index.html 32 | 33 | 34 | /news/index.html 35 | 36 | 37 | /reference/add_alternate_layout.gridlayout.html 38 | 39 | 40 | /reference/add_alternate_layout.html 41 | 42 | 43 | /reference/build_css_rule.html 44 | 45 | 46 | /reference/card_plot_output.html 47 | 48 | 49 | /reference/flex_stack.html 50 | 51 | 52 | /reference/get_element_ids.html 53 | 54 | 55 | /reference/get_elements.html 56 | 57 | 58 | /reference/grid_card.html 59 | 60 | 61 | /reference/grid_card_old.html 62 | 63 | 64 | /reference/grid_card_plot.html 65 | 66 | 67 | /reference/grid_card_text.html 68 | 69 | 70 | /reference/grid_container.html 71 | 72 | 73 | /reference/grid_nested.html 74 | 75 | 76 | /reference/grid_page.html 77 | 78 | 79 | /reference/grid_panel.html 80 | 81 | 82 | /reference/grid_panel_nested.html 83 | 84 | 85 | /reference/grid_panel_plot.html 86 | 87 | 88 | /reference/grid_panel_stack.html 89 | 90 | 91 | /reference/grid_panel_text.html 92 | 93 | 94 | /reference/grid_place.html 95 | 96 | 97 | /reference/grid_plot.html 98 | 99 | 100 | /reference/grided_create_new_app.html 101 | 102 | 103 | /reference/grided_edit_existing_layout.html 104 | 105 | 106 | /reference/gridlayout-package.html 107 | 108 | 109 | /reference/index.html 110 | 111 | 112 | /reference/make_bg_class.html 113 | 114 | 115 | /reference/md_to_gridlayout.html 116 | 117 | 118 | /reference/nested_grid_panel.html 119 | 120 | 121 | /reference/new_gridlayout.html 122 | 123 | 124 | /reference/pipe.html 125 | 126 | 127 | /reference/run_with_grided.html 128 | 129 | 130 | /reference/text_panel.html 131 | 132 | 133 | /reference/title_panel.html 134 | 135 | 136 | /reference/to_app_template.html 137 | 138 | 139 | /reference/to_css.html 140 | 141 | 142 | /reference/to_matrix.html 143 | 144 | 145 | /reference/to_md.html 146 | 147 | 148 | /reference/use_gridlayout_rmd.html 149 | 150 | 151 | /reference/use_gridlayout_shiny.html 152 | 153 | 154 | /reference/vertical_stack_panel.html 155 | 156 | 157 | -------------------------------------------------------------------------------- /docs/snippets/snippets.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | function loadSnippet(snippet, mode) { 4 | mode = mode || "markdown"; 5 | $("#" + snippet).addClass("snippet"); 6 | var editor = ace.edit(snippet); 7 | editor.setHighlightActiveLine(false); 8 | editor.setShowPrintMargin(false); 9 | editor.setReadOnly(true); 10 | editor.setShowFoldWidgets(false); 11 | editor.renderer.setDisplayIndentGuides(false); 12 | editor.setTheme("ace/theme/textmate"); 13 | editor.$blockScrolling = Infinity; 14 | editor.session.setMode("ace/mode/" + mode); 15 | editor.session.getSelection().clearSelection(); 16 | 17 | $.get("snippets/" + snippet + ".md", function(data) { 18 | editor.setValue(data, -1); 19 | editor.setOptions({ 20 | maxLines: editor.session.getLength() 21 | }); 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /docs/tidyverse-2.css: -------------------------------------------------------------------------------- 1 | body {font-size: 16px;} 2 | h1 {font-size: 40px;} 3 | h2 {font-size: 30px;} 4 | h3 {font-size: 23px;} 5 | 6 | .contents .page-header { 7 | margin-top: 10px; 8 | } 9 | 10 | /* reduce h3 margin for proper nesting under h2 */ 11 | .contents h3 {margin-top: -60px} 12 | 13 | .ref-arguments th {vertical-align: top;} 14 | 15 | /* navbar ----------------------------------------------- */ 16 | 17 | .navbar .info { 18 | float: left; 19 | height: 50px; 20 | width: 140px; 21 | font-size: 80%; 22 | position: relative; 23 | margin-left: 5px; 24 | } 25 | .navbar .info .partof { 26 | position: absolute; 27 | top: 0; 28 | } 29 | .navbar .info .version { 30 | position: absolute; 31 | bottom: 0; 32 | } 33 | .navbar .info .version-danger { 34 | font-weight: bold; 35 | color: orange; 36 | } 37 | 38 | .navbar-form { 39 | margin-top: 3px; 40 | margin-bottom: 0; 41 | } 42 | 43 | .navbar-toggle { 44 | margin-top: 8px; 45 | margin-bottom: 5px; 46 | } 47 | 48 | .navbar-nav li a { 49 | padding-bottom: 10px; 50 | } 51 | .navbar-default .navbar-nav > .active > a, 52 | .navbar-default .navbar-nav > .active > a:hover, 53 | .navbar-default .navbar-nav > .active > a:focus { 54 | background-color: #eee; 55 | border-radius: 3px; 56 | } 57 | 58 | /* syntax highlighting ----------------------------------- */ 59 | 60 | .co { 61 | color: #333; 62 | } 63 | 64 | /* footer ------------------------------------------------ */ 65 | 66 | footer { 67 | margin-top: 45px; 68 | padding: 35px 0 36px; 69 | border-top: 1px solid #e5e5e5; 70 | 71 | display: flex; 72 | color: #666; 73 | } 74 | footer p { 75 | margin-bottom: 0; 76 | } 77 | footer .tidyverse { 78 | flex: 1; 79 | margin-right: 1em; 80 | } 81 | footer .author { 82 | flex: 1; 83 | text-align: right; 84 | margin-left: 1em; 85 | } 86 | 87 | /* sidebar ------------------------------------------------ */ 88 | 89 | #sidebar h2 { 90 | font-size: 1.6em; 91 | margin-top: 1em; 92 | margin-bottom: 0.25em; 93 | } 94 | 95 | #sidebar .list-unstyled li { 96 | margin-bottom: 0.5em; 97 | line-height: 1.4; 98 | } 99 | 100 | #sidebar small { 101 | color: #777; 102 | } 103 | 104 | #sidebar .nav { 105 | padding-left: 0px; 106 | list-style-type: none; 107 | color: #5a9ddb; 108 | } 109 | 110 | #sidebar .nav > li { 111 | padding: 10px 0 0px 20px; 112 | display: list-item; 113 | line-height: 20px; 114 | background-image: url(./tocBullet.svg); 115 | background-repeat: no-repeat; 116 | background-size: 16px 280px; 117 | background-position: left 0px; 118 | } 119 | 120 | #sidebar .nav > li.active { 121 | background-position: left -240px; 122 | } 123 | 124 | #sidebar a { 125 | padding: 0px; 126 | color: #5a9ddb; 127 | background-color: transparent; 128 | } 129 | 130 | #sidebar a:hover { 131 | background-color: transparent; 132 | text-decoration: underline; 133 | } 134 | -------------------------------------------------------------------------------- /gridlayout.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageRoxygenize: rd,collate,namespace 22 | -------------------------------------------------------------------------------- /inst/demo_apps/alternate_layouts/README.md: -------------------------------------------------------------------------------- 1 | ## Alternate Layouts 2 | 3 | ### Purpose 4 | Demonstrates how multiple layouts can be declared for a veriety of screen sizes. Shows that these layouts can be used for large screens in addition to just providing mobile views. 5 | 6 | ### Screenshots 7 | - `tests/screenshot-tests/_snaps/demo-apps/alternate-layouts-mobile.png` 8 | - `tests/screenshot-tests/_snaps/demo-apps/alternate-layouts-normal.png` 9 | - `tests/screenshot-tests/_snaps/demo-apps/alternate-layouts-wide.png` 10 | -------------------------------------------------------------------------------- /inst/demo_apps/alternate_layouts/app.R: -------------------------------------------------------------------------------- 1 | # The geyser app... but in grid! 2 | library(gridlayout) 3 | library(shiny) 4 | library(bslib) 5 | 6 | main_layout <- c( 7 | "10px 200px 1fr ", 8 | "70px header header", 9 | "1fr sidebar plot " 10 | ) 11 | 12 | mobile_layout <- c( 13 | "10px 1fr ", 14 | "100px header ", 15 | "250px sidebar", 16 | "400px plot " 17 | ) 18 | 19 | big_screen_layout <- c( 20 | "10px 250px 250px 1fr ", 21 | "1fr header sidebar plot" 22 | ) 23 | 24 | my_layout <- new_gridlayout( 25 | main_layout, 26 | alternate_layouts = list( 27 | list( 28 | layout = mobile_layout, 29 | width_bounds = c(max = 600) 30 | ), 31 | list( 32 | layout = big_screen_layout, 33 | width_bounds = c(min = 1600) 34 | ) 35 | ) 36 | ) 37 | 38 | 39 | # The classic Geyser app with grid layout 40 | shinyApp( 41 | ui = grid_page( 42 | layout = my_layout, 43 | theme = bs_theme(), 44 | grid_card_text("header", "Geysers!"), 45 | grid_card( 46 | "sidebar", 47 | card_header("Settings"), 48 | sliderInput("bins","Number of bins:", min = 1, max = 50, value = 30, width = "100%") 49 | ), 50 | grid_card( 51 | "plot", 52 | card_body( 53 | plotOutput("distPlot") 54 | ) 55 | ) 56 | ), 57 | server = function(input, output) { 58 | output$distPlot <- renderPlot({ 59 | x <- faithful[, 2] 60 | bins <- seq(min(x), max(x), length.out = input$bins + 1) 61 | hist(x, breaks = bins, col = 'darkgray', border = 'white') 62 | }) 63 | } 64 | ) 65 | -------------------------------------------------------------------------------- /inst/demo_apps/alternate_layouts/tests/testthat.R: -------------------------------------------------------------------------------- 1 | shinytest2::test_app() 2 | -------------------------------------------------------------------------------- /inst/demo_apps/alternate_layouts/tests/testthat/_snaps/mac-4.1/shinytest2/alternate_layouts-001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/inst/demo_apps/alternate_layouts/tests/testthat/_snaps/mac-4.1/shinytest2/alternate_layouts-001.png -------------------------------------------------------------------------------- /inst/demo_apps/alternate_layouts/tests/testthat/_snaps/mac-4.1/shinytest2/alternate_layouts-002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/inst/demo_apps/alternate_layouts/tests/testthat/_snaps/mac-4.1/shinytest2/alternate_layouts-002.png -------------------------------------------------------------------------------- /inst/demo_apps/alternate_layouts/tests/testthat/_snaps/mac-4.1/shinytest2/alternate_layouts-003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/inst/demo_apps/alternate_layouts/tests/testthat/_snaps/mac-4.1/shinytest2/alternate_layouts-003.png -------------------------------------------------------------------------------- /inst/demo_apps/alternate_layouts/tests/testthat/_snaps/mac-4.2/shinytest2/alternate_layouts-001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/inst/demo_apps/alternate_layouts/tests/testthat/_snaps/mac-4.2/shinytest2/alternate_layouts-001.png -------------------------------------------------------------------------------- /inst/demo_apps/alternate_layouts/tests/testthat/_snaps/mac-4.2/shinytest2/alternate_layouts-002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/inst/demo_apps/alternate_layouts/tests/testthat/_snaps/mac-4.2/shinytest2/alternate_layouts-002.png -------------------------------------------------------------------------------- /inst/demo_apps/alternate_layouts/tests/testthat/_snaps/mac-4.2/shinytest2/alternate_layouts-003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/inst/demo_apps/alternate_layouts/tests/testthat/_snaps/mac-4.2/shinytest2/alternate_layouts-003.png -------------------------------------------------------------------------------- /inst/demo_apps/alternate_layouts/tests/testthat/setup.R: -------------------------------------------------------------------------------- 1 | # Load application support files into testing environment 2 | shinytest2::load_app_env() 3 | -------------------------------------------------------------------------------- /inst/demo_apps/alternate_layouts/tests/testthat/test-shinytest2.R: -------------------------------------------------------------------------------- 1 | library(shinytest2) 2 | 3 | test_that("{shinytest2} recording: alternate_layouts", { 4 | app <- AppDriver$new(variant = platform_variant(), name = "alternate_layouts", 5 | height = 2024, width = 1619) 6 | app$expect_screenshot() 7 | app$set_window_size(width = 1348, height = 2024) 8 | app$expect_screenshot() 9 | app$set_window_size(width = 575, height = 2024) 10 | app$expect_screenshot() 11 | }) 12 | -------------------------------------------------------------------------------- /inst/demo_apps/geyser/README.md: -------------------------------------------------------------------------------- 1 | ## Geyser app 2 | 3 | ### Purpose 4 | This is a kind-of "Minimum viable example", where we take the classic geyser app and recreate it using `gridlayout::grid_page()` 5 | 6 | ### Screenshots 7 | - `tests/screenshot-tests/_snaps/demo-apps/geyser-app.png` 8 | -------------------------------------------------------------------------------- /inst/demo_apps/geyser/app.R: -------------------------------------------------------------------------------- 1 | # The geyser app... but in grid! 2 | 3 | library(gridlayout) 4 | library(shiny) 5 | library(bslib) 6 | 7 | shinyApp( 8 | ui = grid_page( 9 | layout = c( 10 | "header header", 11 | "sidebar distPlot" 12 | ), 13 | row_sizes = c("50px", "1fr"), 14 | col_sizes = c("200px", "1fr"), 15 | grid_card_text("header", "This is my header", is_title = TRUE), 16 | grid_card( 17 | "sidebar", 18 | card_header("Settings"), 19 | sliderInput("bins","Number of bins:", 1, 50, 30, width = "100%") 20 | ), 21 | grid_card_plot("distPlot") 22 | ), 23 | server = function(input, output) { 24 | output$distPlot <- renderPlot({ 25 | x <- faithful[, 2] 26 | bins <- seq(min(x), max(x), length.out = input$bins + 1) 27 | hist(x, breaks = bins, col = 'darkgray', border = 'white') 28 | }) 29 | } 30 | ) 31 | -------------------------------------------------------------------------------- /inst/demo_apps/nested_grids/README.md: -------------------------------------------------------------------------------- 1 | ## Nested Grid Layouts 2 | 3 | ### Purpose 4 | 5 | Shows how you can nest a grid layout within another using the `grid_container()` UI function. The app has two identical nested grids, sitting in basic grid_card and the other wrapped in a `grid_card()` call with a title. They both should sit nicely contained and not overflow their bounds. 6 | 7 | ### Screenshots 8 | 9 | - `tests/screenshot-tests/_snaps/demo-apps/nested-app.png` 10 | -------------------------------------------------------------------------------- /inst/demo_apps/nested_grids/app.R: -------------------------------------------------------------------------------- 1 | # Demonstrating the ability to nest grid layouts within other gridlayouts 2 | library(gridlayout) 3 | library(shiny) 4 | library(bslib) 5 | 6 | ui <- grid_page( 7 | layout = c( 8 | " 250px 1fr ", 9 | "50px header header", 10 | "1fr sidebar plots " 11 | ), 12 | grid_card_text("header", "This is my header"), 13 | grid_card( 14 | "sidebar", 15 | card_header("Settings"), 16 | sliderInput("bins", "Number of bins:", min = 1, max = 50, value = 30, width = "100%") 17 | ), 18 | grid_nested( 19 | "plots", 20 | title = "Plots - in technicolor", 21 | layout = c( 22 | "distPlot distPlot distPlot", 23 | "redPlot bluePlot greenPlot" 24 | ), 25 | grid_card_plot("distPlot"), 26 | grid_card_plot("redPlot"), 27 | grid_card_plot("bluePlot"), 28 | grid_card_plot("greenPlot") 29 | ) 30 | ) 31 | 32 | 33 | drawHist <- function(nbins, color) { 34 | x <- faithful[, 2] 35 | bins <- seq(min(x), max(x), length.out = nbins + 1) 36 | hist(x, breaks = bins, col = color, border = "white") 37 | } 38 | 39 | server <- function(input, output) { 40 | output$distPlot <- renderPlot(drawHist(input$bins, "darkgray")) 41 | output$redPlot <- renderPlot(drawHist(input$bins, "orangered")) 42 | output$bluePlot <- renderPlot(drawHist(input$bins, "steelblue")) 43 | output$greenPlot <- renderPlot(drawHist(input$bins, "forestgreen")) 44 | } 45 | 46 | shinyApp(ui, server) 47 | 48 | -------------------------------------------------------------------------------- /inst/demo_apps/nested_grids/tests/testthat.R: -------------------------------------------------------------------------------- 1 | shinytest2::test_app() 2 | -------------------------------------------------------------------------------- /inst/demo_apps/nested_grids/tests/testthat/_snaps/mac-4.1/shinytest2/nested_grids-001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/inst/demo_apps/nested_grids/tests/testthat/_snaps/mac-4.1/shinytest2/nested_grids-001.png -------------------------------------------------------------------------------- /inst/demo_apps/nested_grids/tests/testthat/_snaps/mac-4.2/shinytest2/nested_grids-001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/inst/demo_apps/nested_grids/tests/testthat/_snaps/mac-4.2/shinytest2/nested_grids-001.png -------------------------------------------------------------------------------- /inst/demo_apps/nested_grids/tests/testthat/setup.R: -------------------------------------------------------------------------------- 1 | # Load application support files into testing environment 2 | shinytest2::load_app_env() 3 | -------------------------------------------------------------------------------- /inst/demo_apps/nested_grids/tests/testthat/test-shinytest2.R: -------------------------------------------------------------------------------- 1 | library(shinytest2) 2 | 3 | test_that("{shinytest2} recording: nested_grids", { 4 | app <- AppDriver$new(variant = platform_variant(), name = "nested_grids", height = 1470, 5 | width = 1684) 6 | app$expect_screenshot() 7 | }) 8 | -------------------------------------------------------------------------------- /inst/demo_apps/recursive_nesting/app.R: -------------------------------------------------------------------------------- 1 | library(gridlayout) 2 | library(shiny) 3 | library(fontawesome) 4 | 5 | max_depth <- 2 6 | 7 | emoji_panel <- function(area, emoji){ 8 | grid_card_text( 9 | area = area, 10 | content = emoji, 11 | alignment = "center" 12 | ) 13 | } 14 | 15 | make_nested_panels <- function(level = 0) { 16 | 17 | is_nested <- level > 0 18 | 19 | container_fn <- if (is_nested) grid_nested else grid_page 20 | 21 | container_fn( 22 | layout = new_gridlayout( 23 | c( 24 | "top top right", 25 | "left nested right", 26 | "left bottom bottom" 27 | ), 28 | row_sizes = c("1fr", "5fr", "1fr"), 29 | col_sizes = c("1fr", "5fr", "1fr"), 30 | gap_size = "30px" 31 | ), 32 | emoji_panel("top", "↓"), 33 | emoji_panel("bottom", "↑"), 34 | emoji_panel("left", "→"), 35 | emoji_panel("right", "←"), 36 | if(level < max_depth) make_nested_panels(level + 1) else emoji_panel("nested", "🐢"), 37 | area = if(is_nested) "nested" 38 | ) 39 | } 40 | 41 | shinyApp( 42 | ui = make_nested_panels(), 43 | server = function(input, output) {} 44 | ) 45 | -------------------------------------------------------------------------------- /inst/demo_apps/recursive_nesting/tests/testthat.R: -------------------------------------------------------------------------------- 1 | shinytest2::test_app() 2 | -------------------------------------------------------------------------------- /inst/demo_apps/recursive_nesting/tests/testthat/_snaps/mac-4.1/shinytest2/recursive_nesting-001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/inst/demo_apps/recursive_nesting/tests/testthat/_snaps/mac-4.1/shinytest2/recursive_nesting-001.png -------------------------------------------------------------------------------- /inst/demo_apps/recursive_nesting/tests/testthat/_snaps/mac-4.2/shinytest2/recursive_nesting-001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/inst/demo_apps/recursive_nesting/tests/testthat/_snaps/mac-4.2/shinytest2/recursive_nesting-001.png -------------------------------------------------------------------------------- /inst/demo_apps/recursive_nesting/tests/testthat/setup.R: -------------------------------------------------------------------------------- 1 | # Load application support files into testing environment 2 | shinytest2::load_app_env() 3 | -------------------------------------------------------------------------------- /inst/demo_apps/recursive_nesting/tests/testthat/test-shinytest2.R: -------------------------------------------------------------------------------- 1 | library(shinytest2) 2 | 3 | test_that("{shinytest2} recording: recursive_nesting", { 4 | app <- AppDriver$new(variant = platform_variant(), name = "recursive_nesting", 5 | height = 1121, width = 1126) 6 | app$expect_screenshot() 7 | }) 8 | -------------------------------------------------------------------------------- /inst/demo_apps/rmarkdown_demo/README.md: -------------------------------------------------------------------------------- 1 | ## Gridlayout in RMarkdown 2 | 3 | ### Purpose 4 | 5 | A simple RMarkdown document that uses the function `gridlayout::use_gridlayout_rmd()` to allow the user to define a gridlayout using a chunk of type `gridlayout`. 6 | 7 | The main article of interest here is `grid_markdown.Rmd` which is self-contained and should knit as normal. 8 | 9 | `grid_markdown_options.Rmd` is a parameterized version of `grid_markdown.Rmd` that is used to generate the vignette `vignettes/using_with_rmd.Rmd`. 10 | 11 | `rmd_layout_editing.Rmd` is a test file for developing the `grided` layout editor. It can be ignored. 12 | 13 | ### Screenshots 14 | 15 | - For `grid_markdown.Rmd` 16 | - `tests/screenshot-tests/_snaps/demo-apps/rmarkdown-doc.png` 17 | - For `grid_markdown_options.Rmd` 18 | - `vignettes/use_gridlayout_rmd_base.png` 19 | - `vignettes/use_gridlayout_rmd_extra_child_styles.png` 20 | - `vignettes/use_gridlayout_rmd_only_grid_card.png` 21 | -------------------------------------------------------------------------------- /inst/demo_apps/rmarkdown_demo/grid_markdown.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "`gridlayout` in Rmarkdown" 3 | author: "Nick Strayer" 4 | date: "3/9/2021" 5 | output: 6 | html_document: 7 | theme: 8 | version: 5 9 | --- 10 | 11 | ```{r setup, include=FALSE} 12 | library(gridlayout) 13 | 14 | knitr::opts_chunk$set(echo = TRUE) 15 | 16 | use_gridlayout_rmd() 17 | ``` 18 | 19 | ```{css echo=FALSE} 20 | .section { 21 | outline: 2px solid silver; 22 | padding: 10px; 23 | } 24 | ``` 25 | 26 | 27 | # main {.no-header} 28 | 29 | Main content! 30 | 31 | ```{gridlayout} 32 | | | | | 33 | |------|--------|---------| 34 | | |200px |1fr | 35 | |170px |header |header | 36 | |1fr |sidebar |main | 37 | |120px |footer |footer | 38 | ``` 39 | 40 | 41 | # Sidebar 42 | 43 | Here is some content for the sidebar 44 | 45 | # Footer 46 | 47 | Anything you want could go in the footer. 48 | -------------------------------------------------------------------------------- /inst/demo_apps/rmarkdown_demo/grid_markdown_options.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "`gridlayout` in Rmarkdown" 3 | author: "Nick Strayer" 4 | date: "3/9/2021" 5 | output: html_document 6 | params: 7 | mode: base 8 | --- 9 | 10 | ```{r setup, include=FALSE} 11 | library(gridlayout) 12 | 13 | knitr::opts_chunk$set(echo = TRUE) 14 | use_gridlayout_rmd() 15 | ``` 16 | 17 | ## Main 18 | 19 | ```{gridlayout} 20 | | | | | 21 | |------|--------|---------| 22 | |2rem |200px |1fr | 23 | |150px |header |header | 24 | |1fr |sidebar |main | 25 | |120px |footer |footer | 26 | ``` 27 | 28 | 29 | ## Sidebar 30 | 31 | Here is some content for the sidebar 32 | 33 | ## Footer 34 | 35 | Anything you want could go in the footer. 36 | -------------------------------------------------------------------------------- /inst/demo_apps/simple/README.md: -------------------------------------------------------------------------------- 1 | ## Geyser app 2 | 3 | ### Purpose 4 | This is a kind-of "Minimum viable example", where we take the classic geyser app and recreate it using `gridlayout::grid_page()` 5 | 6 | ### Screenshots 7 | - `tests/screenshot-tests/_snaps/demo-apps/geyser-app.png` 8 | -------------------------------------------------------------------------------- /inst/demo_apps/simple/app.R: -------------------------------------------------------------------------------- 1 | # Experiment with how little we can do but still get layout 2 | 3 | # pak::pkg_install('rstudio/bslib@joe/feature/cards') 4 | # pak::pkg_install('rstudio/bslib') 5 | library(gridlayout) 6 | library(shiny) 7 | library(bslib) 8 | 9 | shinyApp( 10 | ui = grid_page( 11 | layout = c("A B B"), 12 | grid_place( 13 | "A", 14 | sliderInput("bins","Number of bins:", min = 1, max = 50, value = 30, width = "100%") 15 | ), 16 | grid_place( 17 | "B", 18 | plotOutput("distPlot", height = "100%") 19 | ) 20 | ), 21 | server = function(input, output, session) { 22 | output$distPlot <- renderPlot({ 23 | x <- faithful[, 2] 24 | bins <- seq(min(x), max(x), length.out = input$bins + 1) 25 | hist(x, breaks = bins, col = 'darkgray', border = 'white') 26 | }) 27 | } 28 | ) 29 | -------------------------------------------------------------------------------- /inst/demo_apps/tabset/app.R: -------------------------------------------------------------------------------- 1 | library(gridlayout) 2 | library(shiny) 3 | 4 | ui <- grid_page( 5 | layout = c( 6 | "tabs tabs cookies", 7 | "bottom bottom bottom" 8 | ), 9 | grid_card( 10 | area = "tabs", 11 | tabsetPanel( 12 | tabPanel("Tab 1"), 13 | tabPanel("Tab 2"), 14 | tabPanel("Tab 3") 15 | )), 16 | grid_card_plot(area = "cookies"), 17 | grid_card(area = "bottom", a(href="https://github.com/rstudio/flexdashboard/issues/37", "flexdashboard issue #37")) 18 | ) 19 | 20 | shinyApp( 21 | ui = ui, 22 | server = function(input, output) { 23 | output$cookies <- renderPlot({ 24 | plot( 25 | x = c(1,3,4,6), 26 | y = c(0,3,2,4), 27 | "b", 28 | main = "Cookies Stolen", 29 | col="steelblue", 30 | xlab = "", 31 | ylab = "" 32 | ) 33 | }) 34 | } 35 | ) 36 | 37 | 38 | -------------------------------------------------------------------------------- /inst/resources/gridlayout.js: -------------------------------------------------------------------------------- 1 | // Watch for cards being hidden or shown and alert shiny of this 2 | function watch_cards() { 3 | const panel_visibility_map = new Map(); 4 | const resizeObserver = new ResizeObserver((entries) => { 5 | for (let entry of entries) { 6 | const el = entry.target; 7 | 8 | const content_height = getComputedStyle(el) 9 | .getPropertyValue("grid-template-rows") 10 | .split(" ")[1]; 11 | 12 | // An item is visible if the grid-row for the panel content is not set to 0px 13 | const is_visible = content_height != "0px"; 14 | 15 | if (panel_visibility_map.get(el) !== is_visible) { 16 | // Trigger shown or hidden events on all the bound ouputs so 17 | // Shiny knows to update them or not 18 | el.querySelectorAll(".shiny-bound-output").forEach((el) => { 19 | const $el = $(el); 20 | if (is_visible) { 21 | $el.trigger("show"); 22 | $el.show(); 23 | $el.trigger("shown"); 24 | } else { 25 | $el.trigger("hide"); 26 | $el.hide(); 27 | $el.trigger("hidden"); 28 | } 29 | }); 30 | panel_visibility_map.set(el, "seen"); 31 | } 32 | } 33 | }); 34 | 35 | document 36 | .querySelectorAll(".grid_card") 37 | .forEach((el) => resizeObserver.observe(el)); 38 | } 39 | 40 | window.onload = function () { 41 | watch_cards(); 42 | }; 43 | -------------------------------------------------------------------------------- /inst/resources/gridlayout_rmd_styles.css: -------------------------------------------------------------------------------- 1 | .section.no-header > h1:first-child { 2 | display: none; 3 | } 4 | 5 | .section > h1, 6 | .section > h2, 7 | .section > h3 { 8 | margin: 0; 9 | } 10 | 11 | .section.v_start, 12 | .section.v_center, 13 | .section.v_end, 14 | .section.h_start, 15 | .section.h_center, 16 | .section.h_end { 17 | display: grid; 18 | } 19 | 20 | .section.v_start { align-items: start; } 21 | .section.v_center { align-items: center; } 22 | .section.v_end { align-items: end; } 23 | 24 | .section.h_start { justify-items: start; } 25 | .section.h_center { justify-items: center; } 26 | .section.h_end { justify-items: end; } 27 | -------------------------------------------------------------------------------- /man/add_alternate_layout.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/add_alternate_layout.R 3 | \name{add_alternate_layout} 4 | \alias{add_alternate_layout} 5 | \title{Add alternate layout options} 6 | \usage{ 7 | add_alternate_layout( 8 | layout, 9 | alternate_layout, 10 | width_bounds = NULL, 11 | container_height = NULL 12 | ) 13 | } 14 | \arguments{ 15 | \item{layout}{Main (or default) layout object} 16 | 17 | \item{alternate_layout}{A \code{gridlayout} object or layout-as-markdown-table 18 | defining layout.} 19 | 20 | \item{width_bounds}{A named vector with at least one element of name \code{min}, 21 | or \code{max}. These two values are used to set when the layout occurs. For 22 | instance, \code{c(max = 600)} means the layout will occur up until the page is 23 | wider than \verb{600px}.} 24 | 25 | \item{container_height}{How tall the layout should be. If left as \code{NULL} this 26 | will simply inherit the main layout's height. Often this should be set to 27 | \code{"auto"} to allow mobile user's to scroll. Beware though, you will want to 28 | absolutely size any plot outputs in these cases.} 29 | } 30 | \description{ 31 | Adds a layout that will be triggered based upon the size of the viewport. 32 | This is typically used to add a mobile view (or a desktop view if your app is 33 | mobile first.) 34 | } 35 | \keyword{internal} 36 | -------------------------------------------------------------------------------- /man/build_css_rule.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/to_css.R 3 | \name{build_css_rule} 4 | \alias{build_css_rule} 5 | \title{Build css properties named or unnamed list of property values} 6 | \usage{ 7 | build_css_rule(selector, prop_list) 8 | } 9 | \arguments{ 10 | \item{selector}{valid css selector to target. E.g. \code{body} or 11 | \code{div.blue_boxes}... For inline styles where no selector is desired use 12 | \code{"inline"}.} 13 | 14 | \item{prop_list}{A list of property-value pairs for additional styles to be 15 | added to each element. Pairs can be given as named elements: e.g. 16 | \code{prop_list = c("background" = "blue")}. See \link[htmltools:css]{htmltools::css} for rules 17 | on formatting.} 18 | } 19 | \value{ 20 | A concatenated string of property values to be used inside a css 21 | selector. If the \code{prop_list} is empty, an empty string (\code{""}) is returned 22 | to avoid placing empty css rules on the webpage. 23 | } 24 | \description{ 25 | Build css properties named or unnamed list of property values 26 | } 27 | \keyword{internal} 28 | -------------------------------------------------------------------------------- /man/card_plot_output.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grid_plot.R 3 | \name{card_plot_output} 4 | \alias{card_plot_output} 5 | \title{Plot output with smart sizing for use inside a \code{grid_card} - Depreciated} 6 | \usage{ 7 | card_plot_output( 8 | outputId, 9 | click = NULL, 10 | dblclick = NULL, 11 | hover = NULL, 12 | brush = NULL, 13 | height = NULL, 14 | stretch = TRUE, 15 | ... 16 | ) 17 | } 18 | \arguments{ 19 | \item{outputId}{output variable to read the plot/image from.} 20 | 21 | \item{click}{This can be \code{NULL} (the default), a string, or an object 22 | created by the \code{\link[shiny:clickOpts]{clickOpts()}} function. If you use a value like 23 | \code{"plot_click"} (or equivalently, \code{clickOpts(id="plot_click")}), 24 | the plot will send coordinates to the server whenever it is clicked, and 25 | the value will be accessible via \code{input$plot_click}. The value will be 26 | a named list with \code{x} and \code{y} elements indicating the mouse 27 | position.} 28 | 29 | \item{dblclick}{This is just like the \code{click} argument, but for 30 | double-click events.} 31 | 32 | \item{hover}{Similar to the \code{click} argument, this can be \code{NULL} 33 | (the default), a string, or an object created by the 34 | \code{\link[shiny:hoverOpts]{hoverOpts()}} function. If you use a value like 35 | \code{"plot_hover"} (or equivalently, \code{hoverOpts(id="plot_hover")}), 36 | the plot will send coordinates to the server pauses on the plot, and the 37 | value will be accessible via \code{input$plot_hover}. The value will be a 38 | named list with \code{x} and \code{y} elements indicating the mouse 39 | position. To control the hover time or hover delay type, you must use 40 | \code{\link[shiny:hoverOpts]{hoverOpts()}}.} 41 | 42 | \item{brush}{Similar to the \code{click} argument, this can be \code{NULL} 43 | (the default), a string, or an object created by the 44 | \code{\link[shiny:brushOpts]{brushOpts()}} function. If you use a value like 45 | \code{"plot_brush"} (or equivalently, \code{brushOpts(id="plot_brush")}), 46 | the plot will allow the user to "brush" in the plotting area, and will send 47 | information about the brushed area to the server, and the value will be 48 | accessible via \code{input$plot_brush}. Brushing means that the user will 49 | be able to draw a rectangle in the plotting area and drag it around. The 50 | value will be a named list with \code{xmin}, \code{xmax}, \code{ymin}, and 51 | \code{ymax} elements indicating the brush area. To control the brush 52 | behavior, use \code{\link[shiny:brushOpts]{brushOpts()}}. Multiple 53 | \code{imageOutput}/\code{plotOutput} calls may share the same \code{id} 54 | value; brushing one image or plot will cause any other brushes with the 55 | same \code{id} to disappear.} 56 | 57 | \item{height}{height in valid css units (see \code{\link[htmltools:validateCssUnit]{htmltools::validateCssUnit()}} 58 | for more details.) Most use-cases will leave this unset and the plot will 59 | fill the card as best it can.} 60 | 61 | \item{stretch}{Set to \code{TRUE} if this \code{card_body} is eager to use any extra 62 | vertical space is available in the card.} 63 | 64 | \item{...}{Named arguments become attributes on the div containing the plot.} 65 | } 66 | \description{ 67 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} 68 | } 69 | \details{ 70 | No longer necessary. Use plain \code{shiny::plotOutput()} wrapped with 71 | \code{bslib::card_body()}. 72 | } 73 | \seealso{ 74 | \code{\link[=grid_card_plot]{grid_card_plot()}} for a simpler way of placing just a plot on the 75 | grid 76 | } 77 | -------------------------------------------------------------------------------- /man/examples/grid_place_app.R: -------------------------------------------------------------------------------- 1 | if (FALSE) { 2 | library(gridlayout) 3 | library(shiny) 4 | library(bslib) 5 | 6 | shinyApp( 7 | ui = grid_page( 8 | layout = c("A B B"), 9 | grid_place( 10 | "A", 11 | sliderInput("bins","Number of bins:", min = 1, max = 50, value = 30, width = "100%") 12 | ), 13 | grid_place( 14 | "B", 15 | plotOutput("distPlot", height = "100%") 16 | ) 17 | ), 18 | server = function(input, output, session) { 19 | output$distPlot <- renderPlot({ 20 | x <- faithful[, 2] 21 | bins <- seq(min(x), max(x), length.out = input$bins + 1) 22 | hist(x, breaks = bins, col = 'darkgray', border = 'white') 23 | }) 24 | } 25 | ) 26 | 27 | } 28 | -------------------------------------------------------------------------------- /man/examples/nested_app.R: -------------------------------------------------------------------------------- 1 | # Demonstrating the ability to nest grid layouts within other gridlayouts 2 | library(gridlayout) 3 | library(shiny) 4 | library(bslib) 5 | 6 | ui <- grid_page( 7 | layout = c( 8 | "header header", 9 | "sidebar plots" 10 | ), 11 | row_sizes = c("50px", "1fr"), 12 | col_sizes = c("250px", "1fr"), 13 | grid_card_text("header", "This is my header"), 14 | grid_card( 15 | "sidebar", 16 | card_header("Settings"), 17 | sliderInput("bins","Number of bins:", 1, 50, 30, width = "100%") 18 | ), 19 | grid_nested( 20 | "plots", 21 | title = "Plots - in technicolor", 22 | layout = c( 23 | "distPlot distPlot distPlot", 24 | "redPlot bluePlot greenPlot" 25 | ), 26 | grid_card_plot("distPlot"), 27 | grid_card_plot("redPlot"), 28 | grid_card_plot("bluePlot"), 29 | grid_card_plot("greenPlot") 30 | ) 31 | ) 32 | 33 | 34 | drawHist <- function(nbins, color) { 35 | x <- faithful[, 2] 36 | bins <- seq(min(x), max(x), length.out = nbins + 1) 37 | hist(x, breaks = bins, col = color, border = "white") 38 | } 39 | 40 | server <- function(input, output) { 41 | output$distPlot <- renderPlot(drawHist(input$bins, "darkgray")) 42 | output$redPlot <- renderPlot(drawHist(input$bins, "orangered")) 43 | output$bluePlot <- renderPlot(drawHist(input$bins, "steelblue")) 44 | output$greenPlot <- renderPlot(drawHist(input$bins, "forestgreen")) 45 | } 46 | 47 | if(FALSE){ 48 | shinyApp(ui, server) 49 | } 50 | -------------------------------------------------------------------------------- /man/examples/simple_app.R: -------------------------------------------------------------------------------- 1 | if (FALSE) { 2 | library(gridlayout) 3 | library(shiny) 4 | library(bslib) 5 | 6 | shinyApp( 7 | ui = grid_page( 8 | layout = c( 9 | "header header", 10 | "sidebar distPlot" 11 | ), 12 | row_sizes = c("50px", "1fr"), 13 | col_sizes = c("200px", "1fr"), 14 | grid_card_text("header", "This is my header"), 15 | grid_card( 16 | area = "sidebar", 17 | card_header("Settings"), 18 | card_body( 19 | sliderInput("bins", "Number of bins:", 1, 50, 30, width = "100%") 20 | ) 21 | ), 22 | grid_card_plot("distPlot") 23 | ), 24 | server = function(input, output) { 25 | output$distPlot <- renderPlot({ 26 | x <- faithful[, 2] 27 | bins <- seq(min(x), max(x), length.out = input$bins + 1) 28 | hist(x, breaks = bins, col = 'darkgray', border = 'white') 29 | }) 30 | } 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /man/examples/simple_app_old.R: -------------------------------------------------------------------------------- 1 | if (FALSE) { 2 | library(gridlayout) 3 | library(shiny) 4 | library(bslib) 5 | 6 | shinyApp( 7 | ui = grid_page( 8 | layout = c( 9 | "header header", 10 | "sidebar distPlot" 11 | ), 12 | row_sizes = c("50px", "1fr"), 13 | col_sizes = c("200px", "1fr"), 14 | grid_card_text("header", "This is my header"), 15 | grid_card_old( 16 | "sidebar", 17 | title = "Settings", 18 | sliderInput("bins","Number of bins:", 1, 50, 30, width = "100%") 19 | ), 20 | grid_card_plot("distPlot") 21 | ), 22 | server = function(input, output) { 23 | output$distPlot <- renderPlot({ 24 | x <- faithful[, 2] 25 | bins <- seq(min(x), max(x), length.out = input$bins + 1) 26 | hist(x, breaks = bins, col = 'darkgray', border = 'white') 27 | }) 28 | } 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /man/figures/basic_markdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/man/figures/basic_markdown.png -------------------------------------------------------------------------------- /man/figures/geyser_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/man/figures/geyser_demo.png -------------------------------------------------------------------------------- /man/figures/geyser_w_grided.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/man/figures/geyser_w_grided.png -------------------------------------------------------------------------------- /man/figures/grided_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/man/figures/grided_default.png -------------------------------------------------------------------------------- /man/figures/lifecycle-archived.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclearchivedarchived -------------------------------------------------------------------------------- /man/figures/lifecycle-defunct.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecycledefunctdefunct -------------------------------------------------------------------------------- /man/figures/lifecycle-deprecated.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecycledeprecateddeprecated -------------------------------------------------------------------------------- /man/figures/lifecycle-experimental.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecycleexperimentalexperimental -------------------------------------------------------------------------------- /man/figures/lifecycle-maturing.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclematuringmaturing -------------------------------------------------------------------------------- /man/figures/lifecycle-questioning.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclequestioningquestioning -------------------------------------------------------------------------------- /man/figures/lifecycle-stable.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclestablestable -------------------------------------------------------------------------------- /man/figures/lifecycle-superseded.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclesupersededsuperseded -------------------------------------------------------------------------------- /man/figures/nested_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/man/figures/nested_demo.png -------------------------------------------------------------------------------- /man/flex_stack.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grid_card_old.R 3 | \name{flex_stack} 4 | \alias{flex_stack} 5 | \title{Div with vertically stacked elements} 6 | \usage{ 7 | flex_stack(..., title, collapsible, scrollable, item_gap = "10px") 8 | } 9 | \arguments{ 10 | \item{...}{Arguments (typically children) passed to the \code{htmltools::div()} 11 | that contain the card's contents} 12 | 13 | \item{title}{Character string to go across top of panel with label. If left 14 | blank the card contents will take up entire space.} 15 | 16 | \item{collapsible}{Should the card be able to be collapsed (\code{TRUE} or 17 | \code{FALSE})? Gridlayout will only show collapser if the layout allows it 18 | (panel is entirely positioned within "auto" sized rows, and has a title). 19 | Setting this to \code{FALSE} will mean collapsibility of the panel will never be 20 | enabled, regardless of layout.} 21 | 22 | \item{scrollable}{Should scroll-bars be added so content that is larger than 23 | the panel can be seen?} 24 | 25 | \item{item_gap}{How much space should there be between consecutive items?} 26 | } 27 | \value{ 28 | Elements from \code{...} wrapped in a \code{shiny::div()} with styles for 29 | vertical stacking applied. 30 | } 31 | \description{ 32 | Contain a series of ui elements with each vertically stacked on top of 33 | eachother. Internally uses css flexbox to align items. 34 | } 35 | \seealso{ 36 | \link{grid_card} 37 | } 38 | \keyword{internal} 39 | -------------------------------------------------------------------------------- /man/get_element_ids.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/get_elements.R 3 | \name{get_element_ids} 4 | \alias{get_element_ids} 5 | \title{Get ids of element in \code{gridlayout} object} 6 | \usage{ 7 | get_element_ids(x) 8 | } 9 | \arguments{ 10 | \item{x}{Object of class \code{"gridlayout"}.} 11 | } 12 | \value{ 13 | Character vector of ids of all elements in layout 14 | } 15 | \description{ 16 | Get ids of element in \code{gridlayout} object 17 | } 18 | \examples{ 19 | 20 | grid_obj <- md_to_gridlayout( 21 | layout_table = " 22 | | |120px |1fr |1fr | 23 | |------|--------|-------|-------| 24 | |100px |header |header |header | 25 | |1fr |sidebar |plot_a |plot_a | 26 | |1fr |sidebar |plot_b |plot_c |" 27 | ) 28 | 29 | get_element_ids(grid_obj) 30 | } 31 | \seealso{ 32 | \link{get_elements} 33 | } 34 | -------------------------------------------------------------------------------- /man/get_elements.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/get_elements.R 3 | \name{get_elements} 4 | \alias{get_elements} 5 | \title{Get elements and positions out of a \code{gridlayout} object} 6 | \usage{ 7 | get_elements(layout) 8 | } 9 | \arguments{ 10 | \item{layout}{Object of class \code{"gridlayout"}} 11 | } 12 | \value{ 13 | List of all unique elements in the layout and their \code{id}, 14 | \verb{\{start,end\}_row}, and \verb{\{start,end\}_col}. Positions are indexed starting at 15 | 1 16 | } 17 | \description{ 18 | Get elements and positions out of a \code{gridlayout} object 19 | } 20 | \examples{ 21 | grid_obj <- md_to_gridlayout( 22 | layout_table = " 23 | | |120px |1fr |1fr | 24 | |------|--------|-------|-------| 25 | |100px |header |header |header | 26 | |1fr |sidebar |plot_a |plot_a | 27 | |1fr |sidebar |plot_b |plot_c |" 28 | ) 29 | 30 | get_elements(grid_obj) 31 | 32 | } 33 | \seealso{ 34 | \link{get_element_ids} 35 | } 36 | -------------------------------------------------------------------------------- /man/grid_card.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grid_card.R 3 | \name{grid_card} 4 | \alias{grid_card} 5 | \title{Grid-positioned card element} 6 | \usage{ 7 | grid_card(area, ...) 8 | } 9 | \arguments{ 10 | \item{area}{Name of grid area, should match an area defined in the layout 11 | section of the wrapping \code{grid_page()} or \code{grid_container()}.} 12 | 13 | \item{...}{ 14 | Arguments passed on to \code{\link[bslib:card]{bslib::card}} 15 | \describe{ 16 | \item{\code{full_screen}}{If \code{TRUE}, an icon will appear when hovering over the card 17 | body. Clicking the icon expands the card to fit viewport size.} 18 | \item{\code{height}}{Any valid \link[htmltools:validateCssUnit]{CSS unit} (e.g., 19 | \code{height="200px"}). Doesn't apply when a card is made \code{full_screen} 20 | (in this case, consider setting a \code{height} in \code{\link[bslib:card_body]{card_body()}}).} 21 | \item{\code{max_height,min_height}}{Any valid \link[htmltools:validateCssUnit]{CSS unit} (e.g., 22 | \code{max_height="200px"}). Doesn't apply when a card is made \code{full_screen} 23 | (in this case, consider setting a \code{max_height} in \code{\link[bslib:card_body]{card_body()}}).} 24 | \item{\code{fill}}{Whether or not to allow the card to grow/shrink to fit a 25 | fillable container with an opinionated height (e.g., \code{page_fillable()}).} 26 | \item{\code{class}}{Additional CSS classes for the returned UI element.} 27 | \item{\code{wrapper}}{A function (which returns a UI element) to call on unnamed 28 | arguments in \code{...} which are not already card item(s) (like 29 | \code{\link[bslib:card_header]{card_header()}}, \code{\link[bslib:card_body]{card_body()}}, etc.). Note that non-card items are grouped 30 | together into one \code{wrapper} call (e.g. given \code{card("a", "b", card_body("c"), "d")}, \code{wrapper} would be called twice, once with \code{"a"} and 31 | \code{"b"} and once with \code{"d"}).} 32 | }} 33 | } 34 | \description{ 35 | The standard element for placing elements on the grid in a simple card 36 | container 37 | } 38 | \examples{ 39 | if (FALSE) { 40 | library(gridlayout) 41 | library(shiny) 42 | library(bslib) 43 | 44 | shinyApp( 45 | ui = grid_page( 46 | layout = c( 47 | "header header", 48 | "sidebar distPlot" 49 | ), 50 | row_sizes = c("50px", "1fr"), 51 | col_sizes = c("200px", "1fr"), 52 | grid_card_text("header", "This is my header"), 53 | grid_card( 54 | area = "sidebar", 55 | card_header("Settings"), 56 | card_body( 57 | sliderInput("bins", "Number of bins:", 1, 50, 30, width = "100\%") 58 | ) 59 | ), 60 | grid_card_plot("distPlot") 61 | ), 62 | server = function(input, output) { 63 | output$distPlot <- renderPlot({ 64 | x <- faithful[, 2] 65 | bins <- seq(min(x), max(x), length.out = input$bins + 1) 66 | hist(x, breaks = bins, col = 'darkgray', border = 'white') 67 | }) 68 | } 69 | ) 70 | } 71 | } 72 | \seealso{ 73 | \link[bslib:card]{bslib::card} for underlying function. \link[bslib:card_body]{bslib::card_header}, 74 | \link[bslib:card_body]{bslib::card_body}, \link[bslib:card_body]{bslib::card_footer}. 75 | \code{\link[=grid_card_text]{grid_card_text()}} for a card with just text content, 76 | \code{\link[=grid_card_plot]{grid_card_plot()}} for a card with just plot content, \code{\link[=grid_place]{grid_place()}} to 77 | place any tag onto the grid without needing to wrap in a card, and 78 | \code{\link[=card_plot_output]{card_plot_output()}} for including a smart-sized plot within a card. 79 | } 80 | -------------------------------------------------------------------------------- /man/grid_card_old.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grid_card_old.R 3 | \name{grid_card_old} 4 | \alias{grid_card_old} 5 | \title{Grid-positioned card element - old version} 6 | \usage{ 7 | grid_card_old( 8 | area, 9 | ..., 10 | title = NULL, 11 | scrollable = FALSE, 12 | collapsible = TRUE, 13 | has_border = TRUE, 14 | item_gap = "10px", 15 | class = NULL 16 | ) 17 | } 18 | \arguments{ 19 | \item{area}{Name of grid area, should match an area defined in the layout 20 | section of the wrapping \code{grid_page()} or \code{grid_container()}.} 21 | 22 | \item{...}{Arguments (typically children) passed to the \code{htmltools::div()} 23 | that contain the card's contents} 24 | 25 | \item{title}{Character string to go across top of panel with label. If left 26 | blank the card contents will take up entire space.} 27 | 28 | \item{scrollable}{Should scroll-bars be added so content that is larger than 29 | the panel can be seen?} 30 | 31 | \item{collapsible}{Should the card be able to be collapsed (\code{TRUE} or 32 | \code{FALSE})? Gridlayout will only show collapser if the layout allows it 33 | (panel is entirely positioned within "auto" sized rows, and has a title). 34 | Setting this to \code{FALSE} will mean collapsibility of the panel will never be 35 | enabled, regardless of layout.} 36 | 37 | \item{has_border}{Should the card be surrounded by a border? Set to \code{FALSE} 38 | to turn off.} 39 | 40 | \item{item_gap}{How much vertical space should there be between children of 41 | card?} 42 | 43 | \item{class}{Additional CSS classes to include on the card div.} 44 | } 45 | \description{ 46 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} 47 | } 48 | \details{ 49 | Note that this version of grid card has been replaced with a newer one based 50 | on \code{bslib::card()}. That is the recomended way of using cards in your layout. 51 | This version is kept for compatibility with old apps while they are switched 52 | to the new interface. The standard element for placing elements on the grid 53 | in a simple card container 54 | } 55 | \examples{ 56 | if (FALSE) { 57 | library(gridlayout) 58 | library(shiny) 59 | library(bslib) 60 | 61 | shinyApp( 62 | ui = grid_page( 63 | layout = c( 64 | "header header", 65 | "sidebar distPlot" 66 | ), 67 | row_sizes = c("50px", "1fr"), 68 | col_sizes = c("200px", "1fr"), 69 | grid_card_text("header", "This is my header"), 70 | grid_card_old( 71 | "sidebar", 72 | title = "Settings", 73 | sliderInput("bins","Number of bins:", 1, 50, 30, width = "100\%") 74 | ), 75 | grid_card_plot("distPlot") 76 | ), 77 | server = function(input, output) { 78 | output$distPlot <- renderPlot({ 79 | x <- faithful[, 2] 80 | bins <- seq(min(x), max(x), length.out = input$bins + 1) 81 | hist(x, breaks = bins, col = 'darkgray', border = 'white') 82 | }) 83 | } 84 | ) 85 | } 86 | } 87 | \seealso{ 88 | \code{\link[=grid_card_text]{grid_card_text()}} for a card with just text content, 89 | \code{\link[=grid_card_plot]{grid_card_plot()}} for a card with just plot content, \code{\link[=grid_place]{grid_place()}} to 90 | place any tag onto the grid without needing to wrap in a card, and 91 | \code{\link[=card_plot_output]{card_plot_output()}} for including a smart-sized plot within a card. 92 | } 93 | -------------------------------------------------------------------------------- /man/grid_card_text.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grid_card_text.R 3 | \name{grid_card_text} 4 | \alias{grid_card_text} 5 | \title{Grid-positioned card with only text} 6 | \usage{ 7 | grid_card_text( 8 | area, 9 | content = NULL, 10 | ..., 11 | alignment = "start", 12 | img_height = "55px", 13 | icon = NULL, 14 | wrapping_tag = "h2", 15 | is_title = FALSE 16 | ) 17 | } 18 | \arguments{ 19 | \item{area}{Name of grid area, should match an area defined in the layout 20 | section of the wrapping \code{grid_page()} or \code{grid_container()}.} 21 | 22 | \item{content}{Whatever you want the title to say. Typically just text but 23 | any tag or tag-list is possible. All will get wrapped in an \code{h3} tag.} 24 | 25 | \item{...}{ 26 | Arguments passed on to \code{\link[=grid_card]{grid_card}} 27 | \describe{ 28 | \item{\code{}}{} 29 | }} 30 | 31 | \item{alignment}{Horizontal alignment of text. Typical options include 32 | \verb{start", "center", "end"}. For full list of options, see the \href{https://developer.mozilla.org/en-US/docs/Web/CSS/align-items}{css-spec for \code{align-items}.}} 33 | 34 | \item{img_height}{If the passed icon is a path to an image, how tall should 35 | that image be rendered (preserves aspect ratio.)} 36 | 37 | \item{icon}{Optional icon/image for left of text. Supports image locations 38 | (those ending in \verb{.png, .jpg, .jpeg} or \code{.svg}), ids of font-awesome icons 39 | (i.e. that works with \verb{fontawesome::fa(icon)]}, or \code{fontawesome} icons as 40 | returned by \code{\link[fontawesome:fa]{fontawesome::fa()}} (if customization of icon style is 41 | desired.)} 42 | 43 | \item{wrapping_tag}{What tag should the text be wrapped in. Takes either an 44 | \code{htmltools} tag function or the string of a tag accessible via 45 | \code{htmltools::tags[[wrapping_tag]]}.} 46 | 47 | \item{is_title}{Should the text of this panel be passed on as the title of 48 | the page? This will make the text show up in the browser tab or when you 49 | bookmark the app etc..} 50 | } 51 | \description{ 52 | Makes a grid_card that contains just text that is vertically centered within 53 | the panel. Useful for app titles or displaying text-based statistics. 54 | } 55 | \examples{ 56 | 57 | # Typically you'll just pass a character string to the function 58 | grid_card_text(area = "header", "This is my header") 59 | 60 | # Icons from `fontawesome` can be used: 61 | 62 | # Either with just the id 63 | grid_card_text(area = "header", "Here's my text", icon = "r-project") 64 | 65 | # Or by directly passing the icon object if you want more customization 66 | grid_card_text( 67 | "header", 68 | "Here's my text", 69 | icon = fontawesome::fa("r-project", fill = "steelblue") 70 | ) 71 | 72 | # You can also pass arbitrary image locations for the icon 73 | grid_card_text( 74 | "header", 75 | "Here's my text", 76 | icon = "https://cran.r-project.org/Rlogo.svg" 77 | ) 78 | 79 | # These images can have their size controlled 80 | grid_card_text( 81 | "header", 82 | "Here's my text", 83 | icon = "https://cran.r-project.org/Rlogo.svg", 84 | img_height = "20px" 85 | ) 86 | 87 | 88 | # Commonly you may want to use the text panel text as the title of your app 89 | grid_card_text(area = "header", "My App Name", is_title = TRUE) 90 | 91 | if (FALSE) { 92 | library(gridlayout) 93 | library(shiny) 94 | library(bslib) 95 | 96 | shinyApp( 97 | ui = grid_page( 98 | layout = c( 99 | "header header", 100 | "sidebar distPlot" 101 | ), 102 | row_sizes = c("50px", "1fr"), 103 | col_sizes = c("200px", "1fr"), 104 | grid_card_text("header", "This is my header"), 105 | grid_card( 106 | area = "sidebar", 107 | card_header("Settings"), 108 | card_body( 109 | sliderInput("bins", "Number of bins:", 1, 50, 30, width = "100\%") 110 | ) 111 | ), 112 | grid_card_plot("distPlot") 113 | ), 114 | server = function(input, output) { 115 | output$distPlot <- renderPlot({ 116 | x <- faithful[, 2] 117 | bins <- seq(min(x), max(x), length.out = input$bins + 1) 118 | hist(x, breaks = bins, col = 'darkgray', border = 'white') 119 | }) 120 | } 121 | ) 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /man/grid_container.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grid_container.R 3 | \name{grid_container} 4 | \alias{grid_container} 5 | \title{\code{gridlayout} container for use within other elements} 6 | \usage{ 7 | grid_container( 8 | layout, 9 | ..., 10 | id = NULL, 11 | flag_mismatches = TRUE, 12 | row_sizes = NULL, 13 | col_sizes = NULL, 14 | gap_size = NULL, 15 | container_height = NULL 16 | ) 17 | } 18 | \arguments{ 19 | \item{layout}{Either a markdown table representation (see 20 | \code{\link{md_to_gridlayout}}) or a \code{gridlayout} object defining the 21 | desired layout for your Shiny app.} 22 | 23 | \item{...}{\code{grid_card()} (or similar) arguments that will fill the grid 24 | layout. Note the areas should match the those provided in \code{layout}.} 25 | 26 | \item{id}{ID unique to this container (note that the HTML will be prefixed 27 | with \verb{grid-} to avoid namespace clashes)} 28 | 29 | \item{flag_mismatches}{Should a mismatch between supplied \code{elements} ui 30 | definitions and layout trigger a warning? In advanced cases you may want to 31 | dynamically set your layout and sometimes omit panels.} 32 | 33 | \item{row_sizes}{A character vector of valid css sizes for the height of each 34 | row in your grid as given by the main layout definition. If a single value 35 | is passed, it will be repeated for all columns. If sizes are provided both 36 | here and in the main layout then these sizes will be the ones used.} 37 | 38 | \item{col_sizes}{Same as \code{row_sizes}, but for column widths} 39 | 40 | \item{gap_size}{Valid css sizing for gap to be left between each element in your 41 | grid. Like \code{row_sizes} and \code{col_sizes}, this will win-out over a gap size 42 | provided in the main layout table.} 43 | 44 | \item{container_height}{Valid css unit determining how tall the containing 45 | element should be for this layout. Defaults to \verb{100\%}. Special value of 46 | \code{"viewport"} for full-page height maps to the CSS value of \verb{100vh} if any 47 | relative units (e.g. \code{fr} or \code{auto}) are included in row sizes and \code{auto} 48 | otherwise. Values such as \code{"auto"} will let the page grow to as large as it needs to be to fit all 49 | content. This should most likely be avoided when using row heights in 50 | relative units.} 51 | } 52 | \value{ 53 | A taglist with grid elements wrapped inside a container div of class 54 | \code{id}. 55 | } 56 | \description{ 57 | Builds a gridlayout within a div of specified id. Not typically called 58 | directly but can be used to create nested grids 59 | } 60 | \examples{ 61 | 62 | if (FALSE) { 63 | library(gridlayout) 64 | library(shiny) 65 | 66 | # The classic Geyser app with grid layout 67 | shinyApp( 68 | ui = fluidPage( 69 | grid_container( 70 | layout = " 71 | |2rem |200px |1fr | 72 | |85px |header |header | 73 | |1fr |sidebar |plot |", 74 | grid_card_text("header", "Geysers!"), 75 | grid_card( 76 | "sidebar", 77 | card_header("Settings"), 78 | sliderInput("bins", "Number of bins:", 79 | min = 1, 80 | max = 50, 81 | value = 30, 82 | width = "100\%") 83 | ), 84 | grid_card_plot("plot") 85 | ) 86 | ), 87 | server = function(input, output) { 88 | output$plot <- renderPlot({ 89 | x <- faithful[, 2] 90 | bins <- seq(min(x), max(x), length.out = input$bins + 1) 91 | hist(x, breaks = bins, col = 'darkgray', border = 'white') 92 | }) 93 | } 94 | ) 95 | } 96 | 97 | } 98 | \seealso{ 99 | \code{\link[=grid_page]{grid_page()}} for using a gridlayout to entirely define the page. 100 | \code{\link[=grid_nested]{grid_nested()}} for placing a grid container within another gridlayout. 101 | \code{\link[=grid_card]{grid_card()}} for placing content inside your layout. See 102 | \code{vignette("defining-a-layout", package = "gridlayout")} for more info on 103 | defining your layout. 104 | } 105 | -------------------------------------------------------------------------------- /man/grid_nested.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grid_nested.R 3 | \name{grid_nested} 4 | \alias{grid_nested} 5 | \title{Grid-positioned card with \code{gridlayout} positioned content} 6 | \usage{ 7 | grid_nested(area, layout, ..., id = NULL, title = NULL, flag_mismatches = TRUE) 8 | } 9 | \arguments{ 10 | \item{area}{Name of grid area, should match an area defined in the layout 11 | section of the wrapping \code{grid_page()} or \code{grid_container()}.} 12 | 13 | \item{layout}{Either a markdown table representation (see 14 | \code{\link{md_to_gridlayout}}) or a \code{gridlayout} object defining the 15 | desired layout for your Shiny app.} 16 | 17 | \item{...}{\code{grid_card()} (or similar) arguments that will fill the grid 18 | layout. Note the areas should match the those provided in \code{layout}.} 19 | 20 | \item{id}{ID unique to this container (note that the HTML will be prefixed 21 | with \verb{grid-} to avoid namespace clashes)} 22 | 23 | \item{title}{Optional title for the card. Gets wrapped in 24 | \code{bslib::card_header()}} 25 | 26 | \item{flag_mismatches}{Should a mismatch between supplied \code{elements} ui 27 | definitions and layout trigger a warning? In advanced cases you may want to 28 | dynamically set your layout and sometimes omit panels.} 29 | } 30 | \value{ 31 | A \code{grid_card} with a nested layout within it 32 | } 33 | \description{ 34 | Creates a panel for a layout with its own internal gridlayout 35 | } 36 | \examples{ 37 | # Demonstrating the ability to nest grid layouts within other gridlayouts 38 | library(gridlayout) 39 | library(shiny) 40 | library(bslib) 41 | 42 | ui <- grid_page( 43 | layout = c( 44 | "header header", 45 | "sidebar plots" 46 | ), 47 | row_sizes = c("50px", "1fr"), 48 | col_sizes = c("250px", "1fr"), 49 | grid_card_text("header", "This is my header"), 50 | grid_card( 51 | "sidebar", 52 | card_header("Settings"), 53 | sliderInput("bins","Number of bins:", 1, 50, 30, width = "100\%") 54 | ), 55 | grid_nested( 56 | "plots", 57 | title = "Plots - in technicolor", 58 | layout = c( 59 | "distPlot distPlot distPlot", 60 | "redPlot bluePlot greenPlot" 61 | ), 62 | grid_card_plot("distPlot"), 63 | grid_card_plot("redPlot"), 64 | grid_card_plot("bluePlot"), 65 | grid_card_plot("greenPlot") 66 | ) 67 | ) 68 | 69 | 70 | drawHist <- function(nbins, color) { 71 | x <- faithful[, 2] 72 | bins <- seq(min(x), max(x), length.out = nbins + 1) 73 | hist(x, breaks = bins, col = color, border = "white") 74 | } 75 | 76 | server <- function(input, output) { 77 | output$distPlot <- renderPlot(drawHist(input$bins, "darkgray")) 78 | output$redPlot <- renderPlot(drawHist(input$bins, "orangered")) 79 | output$bluePlot <- renderPlot(drawHist(input$bins, "steelblue")) 80 | output$greenPlot <- renderPlot(drawHist(input$bins, "forestgreen")) 81 | } 82 | 83 | if(FALSE){ 84 | shinyApp(ui, server) 85 | } 86 | } 87 | \seealso{ 88 | \link{grid_card}, \link{grid_container} 89 | 90 | \code{\link[=grid_page]{grid_page()}} for using a gridlayout to entirely define the page. 91 | \code{\link[=grid_container]{grid_container()}} for placing a gridlayout in non-gridlayout parent 92 | elements. \code{\link[=grid_card]{grid_card()}} for placing content inside your layout. See 93 | \code{vignette("defining-a-layout", package = "gridlayout")} for more info on 94 | defining your layout. 95 | } 96 | -------------------------------------------------------------------------------- /man/grid_page.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grid_page.R 3 | \name{grid_page} 4 | \alias{grid_page} 5 | \title{Use \code{gridlayout} for entire app layout} 6 | \usage{ 7 | grid_page( 8 | layout, 9 | ..., 10 | row_sizes = NULL, 11 | col_sizes = NULL, 12 | gap_size = NULL, 13 | container_height = "viewport", 14 | theme = bslib::bs_theme(version = 5), 15 | flag_mismatches = FALSE 16 | ) 17 | } 18 | \arguments{ 19 | \item{layout}{Either a markdown table representation (see 20 | \code{\link{md_to_gridlayout}}) or a \code{gridlayout} object defining the 21 | desired layout for your Shiny app.} 22 | 23 | \item{...}{\code{grid_card()} (or similar) arguments that will fill the grid 24 | layout. Note the areas should match the those provided in \code{layout}.} 25 | 26 | \item{row_sizes}{A character vector of valid css sizes for the height of each 27 | row in your grid as given by the main layout definition. If a single value 28 | is passed, it will be repeated for all columns. If sizes are provided both 29 | here and in the main layout then these sizes will be the ones used.} 30 | 31 | \item{col_sizes}{Same as \code{row_sizes}, but for column widths} 32 | 33 | \item{gap_size}{Valid css sizing for gap to be left between each element in your 34 | grid. Like \code{row_sizes} and \code{col_sizes}, this will win-out over a gap size 35 | provided in the main layout table.} 36 | 37 | \item{container_height}{Optional parameter to control height of page. 38 | Defaults to \code{"viewport"} so app takes up full vertical space of page. 39 | See argument of same name in \code{new_gridlayout()} for more options.} 40 | 41 | \item{theme}{Optional argument to pass to \code{theme} argument of 42 | \code{\link[shiny]{fluidPage}}.} 43 | 44 | \item{flag_mismatches}{Should mismatches between the named arguments and 45 | layout elements trigger an error?} 46 | } 47 | \value{ 48 | A UI definition that can be passed to the 49 | \code{\link[shiny]{shinyUI}} function. 50 | } 51 | \description{ 52 | This is the typical way to use \code{gridlayout} in your \code{Shiny} app. \code{grid_page} 53 | will make up the entire \code{ui} declaration of the app. Under the hood it uses 54 | \code{\link[shiny:fluidPage]{shiny::fluidPage()}} and \link{grid_container}. Elements are placed in the layout 55 | by wrapping in a \code{grid_card()} with the \code{area} set to the area in the layout 56 | the element should be placed in. 57 | } 58 | \examples{ 59 | if (FALSE) { 60 | library(gridlayout) 61 | library(shiny) 62 | library(bslib) 63 | 64 | shinyApp( 65 | ui = grid_page( 66 | layout = c( 67 | "header header", 68 | "sidebar distPlot" 69 | ), 70 | row_sizes = c("50px", "1fr"), 71 | col_sizes = c("200px", "1fr"), 72 | grid_card_text("header", "This is my header"), 73 | grid_card( 74 | area = "sidebar", 75 | card_header("Settings"), 76 | card_body( 77 | sliderInput("bins", "Number of bins:", 1, 50, 30, width = "100\%") 78 | ) 79 | ), 80 | grid_card_plot("distPlot") 81 | ), 82 | server = function(input, output) { 83 | output$distPlot <- renderPlot({ 84 | x <- faithful[, 2] 85 | bins <- seq(min(x), max(x), length.out = input$bins + 1) 86 | hist(x, breaks = bins, col = 'darkgray', border = 'white') 87 | }) 88 | } 89 | ) 90 | } 91 | } 92 | \seealso{ 93 | See \code{vignette("defining-a-layout", package = "gridlayout")} for more info on defining your layout. \code{\link[=grid_container]{grid_container()}} for using gridlayout without also setting up the 94 | root page layout. \code{\link[=grid_nested]{grid_nested()}} for placing a grid container within 95 | another gridlayout. \code{\link[=grid_card]{grid_card()}} for placing content inside your layout. 96 | } 97 | -------------------------------------------------------------------------------- /man/grid_place.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grid_place.R 3 | \name{grid_place} 4 | \alias{grid_place} 5 | \title{Place any \code{shiny.tag} element on the grid} 6 | \usage{ 7 | grid_place(area, element) 8 | } 9 | \arguments{ 10 | \item{area}{Area of grid corresponding to the wrapping grid containers grid 11 | definition} 12 | 13 | \item{element}{Element (html tag) to be placed.} 14 | } 15 | \value{ 16 | The \code{element} updated with a \code{gridlayout-area} data-attribute and the 17 | \code{grid-area} property added to its styles 18 | } 19 | \description{ 20 | Allows you to place any item directly on the grid without having to wrap it 21 | in \code{grid_card()} or other containers. This is done by adding a css property 22 | and data-attribute to the tag object. 23 | } 24 | \examples{ 25 | if (FALSE) { 26 | library(gridlayout) 27 | library(shiny) 28 | library(bslib) 29 | 30 | shinyApp( 31 | ui = grid_page( 32 | layout = c("A B B"), 33 | grid_place( 34 | "A", 35 | sliderInput("bins","Number of bins:", min = 1, max = 50, value = 30, width = "100\%") 36 | ), 37 | grid_place( 38 | "B", 39 | plotOutput("distPlot", height = "100\%") 40 | ) 41 | ), 42 | server = function(input, output, session) { 43 | output$distPlot <- renderPlot({ 44 | x <- faithful[, 2] 45 | bins <- seq(min(x), max(x), length.out = input$bins + 1) 46 | hist(x, breaks = bins, col = 'darkgray', border = 'white') 47 | }) 48 | } 49 | ) 50 | 51 | } 52 | } 53 | \seealso{ 54 | \code{\link[=grid_card]{grid_card()}} to place content on the grid wrapped in a bootstrap 55 | card. 56 | } 57 | -------------------------------------------------------------------------------- /man/gridlayout-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/gridlayout-package.R 3 | \docType{package} 4 | \name{gridlayout-package} 5 | \alias{gridlayout} 6 | \alias{gridlayout-package} 7 | \title{gridlayout: Build Layouts for R-Webapps Using CSS-Grid} 8 | \description{ 9 | Makes it easy to layout your Shiny app or other R-derived HTML documents using CSS grid. 10 | } 11 | \author{ 12 | \strong{Maintainer}: Nick Strayer \email{nick.strayer@rstudio.com} 13 | 14 | } 15 | \keyword{internal} 16 | -------------------------------------------------------------------------------- /man/make_bg_class.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/bootstrap-background-colors.R 3 | \name{make_bg_class} 4 | \alias{make_bg_class} 5 | \title{Build a bootstrap background class} 6 | \usage{ 7 | make_bg_class(bgColor = NULL, bgGradient = FALSE) 8 | } 9 | \arguments{ 10 | \item{bgColor}{Color of background in terms of bootstrap variable names: 11 | Options are \code{"primary"}, \code{"secondary"}, \code{"success"}, \code{"danger"}, 12 | \code{"warning"}, \code{"info"}, \code{"light"}, and \verb{"dark}.} 13 | 14 | \item{bgGradient}{Should a gradient be applied to the background instead of a 15 | solid color?} 16 | } 17 | \value{ 18 | A string containing the bootstrap class name to apply the requested 19 | background color 20 | } 21 | \description{ 22 | Info on possible colors from the \href{https://getbootstrap.com/docs/5.0/utilities/background/#background-color}{bootstrap website} 23 | } 24 | \examples{ 25 | 26 | gridlayout:::make_bg_class("warning") 27 | # A gradient can be applied as well 28 | gridlayout:::make_bg_class("primary", TRUE) 29 | 30 | } 31 | \keyword{internal} 32 | -------------------------------------------------------------------------------- /man/md_to_gridlayout.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/md_to_gridlayout.R 3 | \name{md_to_gridlayout} 4 | \alias{md_to_gridlayout} 5 | \title{Build \code{gridlayout} object from markdown table} 6 | \usage{ 7 | md_to_gridlayout(layout_table, null_instead_of_error = FALSE, ...) 8 | } 9 | \arguments{ 10 | \item{layout_table}{Character string with a markdown table. First row and 11 | column are reserved for sizing (any valid css sizing works). An optional 12 | grid-gap can be provided using the very first cell.} 13 | 14 | \item{null_instead_of_error}{When the input fails to be parsed as a layout, 15 | should the function return \code{NULL} instead of throwing an error? Useful for 16 | situations where you're parsing multiple tables simply want to check if a 17 | table can be parsed instead of relying on it being parsable.} 18 | 19 | \item{...}{ 20 | Arguments passed on to \code{\link[=new_gridlayout]{new_gridlayout}} 21 | \describe{ 22 | \item{\code{layout_def}}{Either a list of elements with the \code{id}, \code{start_row}, 23 | \code{end_row}, \code{start_col}, and \code{end_col} format, or a markdown table defining a 24 | layout.} 25 | \item{\code{row_sizes}}{A character vector of valid css sizes for the height of each 26 | row in your grid as given by the main layout definition. If a single value 27 | is passed, it will be repeated for all columns. If sizes are provided both 28 | here and in the main layout then these sizes will be the ones used.} 29 | \item{\code{col_sizes}}{Same as \code{row_sizes}, but for column widths} 30 | \item{\code{gap_size}}{Valid css sizing for gap to be left between each element in your 31 | grid. Like \code{row_sizes} and \code{col_sizes}, this will win-out over a gap size 32 | provided in the main layout table.} 33 | \item{\code{container_height}}{Valid css unit determining how tall the containing 34 | element should be for this layout. Defaults to \verb{100\%}. Special value of 35 | \code{"viewport"} for full-page height maps to the CSS value of \verb{100vh} if any 36 | relative units (e.g. \code{fr} or \code{auto}) are included in row sizes and \code{auto} 37 | otherwise. Values such as \code{"auto"} will let the page grow to as large as it needs to be to fit all 38 | content. This should most likely be avoided when using row heights in 39 | relative units.} 40 | \item{\code{alternate_layouts}}{A list of layouts to be used for different viewport 41 | widths. This is enables your app to adapt to different screensizes such as a 42 | phone or an ultra-wide monitor. Each entry in this list must contain a 43 | \code{layout}: or valid layout declaration (see \emph{Declaring your layout} section), and 44 | \code{width_bounds}: or a list with at least one of a \code{min} and \code{max} value for 45 | when your page appears. See \code{\link[=add_alternate_layout]{add_alternate_layout()}} for more details. If no 46 | alternate layouts are given a single-column layout will be automatically 47 | applied for mobile screens (viewports less than 600px wide). Set to \code{NULL} 48 | to avoid this.} 49 | }} 50 | } 51 | \value{ 52 | An object of class "grid_layout", which stores the layout as a 53 | matrix. This can be passed to other functions such as \code{layout_to_css()}. 54 | } 55 | \description{ 56 | This is just a wrapper around \link{new_gridlayout} that is more explicit about 57 | its input format. Also adds the ability to absorb errors for use when parsing 58 | free text that may or may not represent a layout table. 59 | } 60 | \examples{ 61 | md_to_gridlayout( 62 | layout_table = " 63 | | |120px |1fr |1fr | 64 | |:-----|:-------|:------|:------| 65 | |100px |header |header |header | 66 | |1fr |sidebar |plot_a |plot_c | 67 | |1fr |sidebar |plot_b |plot_b |" 68 | ) 69 | 70 | 71 | # Can specify gap size in upper left cell 72 | md_to_gridlayout( 73 | layout_table = " 74 | |25px |120px |1fr | 75 | |:-----|:------|:------| 76 | |100px |header |header | 77 | |1fr |plot |table | 78 | |1fr |footer |footer |" 79 | ) 80 | 81 | # Don't need to follow full md table with 82 | # header row if not desired 83 | md_to_gridlayout( 84 | layout_table = " 85 | |25px |120px |1fr | 86 | |100px |header |header | 87 | |1fr |plot |table | 88 | |1fr |footer |footer |" 89 | ) 90 | 91 | # Can omit sizing as well if desired 92 | md_to_gridlayout( 93 | layout_table = " 94 | |header |header | 95 | |plot |table | 96 | |footer |footer |" 97 | ) 98 | 99 | } 100 | \seealso{ 101 | \link{new_gridlayout} 102 | } 103 | -------------------------------------------------------------------------------- /man/pipe.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils-pipe.R 3 | \name{\%>\%} 4 | \alias{\%>\%} 5 | \title{Pipe operator} 6 | \usage{ 7 | lhs \%>\% rhs 8 | } 9 | \arguments{ 10 | \item{lhs}{A value or the magrittr placeholder.} 11 | 12 | \item{rhs}{A function call using the magrittr semantics.} 13 | } 14 | \value{ 15 | The result of calling \code{rhs(lhs)}. 16 | } 17 | \description{ 18 | See \code{magrittr::\link[magrittr:pipe]{\%>\%}} for details. 19 | } 20 | \keyword{internal} 21 | -------------------------------------------------------------------------------- /man/to_css.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/to_css.R 3 | \name{to_css} 4 | \alias{to_css} 5 | \title{Generate dynamic css for a given \code{gridlayout}} 6 | \usage{ 7 | to_css(layout, container_key) 8 | } 9 | \arguments{ 10 | \item{layout}{Object of class \code{"gridlayout"}.} 11 | 12 | \item{container_key}{The unique key used to identify the container to be 13 | targeted for the layout. If left blank it will default to applying grid 14 | styling to the whole app (aka the \code{body} element) for whole page grids. If 15 | plain character string is given, then it is assumed to be a 16 | \code{gridlayout-key} and the targeting is done using an attribute query for 17 | \code{data-gridlayout-key}. If container contains css selector characters such as 18 | a dot, the selector will not be transformed into an id automatically. E.g. 19 | \code{container = ".main-content"}.} 20 | } 21 | \value{ 22 | Character string of css used to setup grid layout and place elements 23 | (referenced by id) into correct locations 24 | } 25 | \description{ 26 | Generate dynamic css for a given \code{gridlayout} 27 | } 28 | \examples{ 29 | 30 | grid_obj <- md_to_gridlayout( 31 | layout_table = " 32 | | |120px |1fr |1fr | 33 | |:-----|:-------|:------|:------| 34 | |100px |header |header |header | 35 | |1fr |sidebar |plot_a |plot_c | 36 | |1fr |sidebar |plot_b |plot_b |" 37 | ) 38 | 39 | cat(to_css(grid_obj)) 40 | } 41 | -------------------------------------------------------------------------------- /man/to_matrix.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/to_md.R 3 | \name{to_matrix} 4 | \alias{to_matrix} 5 | \title{Convert \code{gridlayout} to matrix format} 6 | \usage{ 7 | to_matrix(layout) 8 | } 9 | \arguments{ 10 | \item{layout}{A gridlayout object (as created by \code{new_gridlayout()})} 11 | } 12 | \value{ 13 | A matrix of the layout with each grid cell and its item membership 14 | corresponding to a cell in the matrix 15 | } 16 | \description{ 17 | Convert \code{gridlayout} to matrix format 18 | } 19 | \examples{ 20 | 21 | layout <- md_to_gridlayout(" 22 | |10px |120px |1fr |1fr | 23 | |100px |header |header |header | 24 | |1fr |sidebar |plot_a |plot_c | 25 | |1fr |sidebar |plot_b |plot_b |") 26 | 27 | to_matrix(layout) 28 | 29 | } 30 | -------------------------------------------------------------------------------- /man/to_md.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/to_md.R 3 | \name{to_md} 4 | \alias{to_md} 5 | \title{Convert \code{gridlayout} to markdown table} 6 | \usage{ 7 | to_md(layout, include_gap_size = TRUE) 8 | } 9 | \arguments{ 10 | \item{layout}{Object of class \code{"gridlayout"}.} 11 | 12 | \item{include_gap_size}{Should the gap size for the layout be added in 13 | upper-left of table?} 14 | } 15 | \value{ 16 | Markdown table that defines the grid layout. This can be used with 17 | the function \code{grid_layout_from_md()} to build grid layouts. 18 | } 19 | \description{ 20 | Convert \code{gridlayout} to markdown table 21 | } 22 | \examples{ 23 | 24 | my_layout <- md_to_gridlayout(" 25 | | | | | | 26 | |:-----|:-------|:------|:------| 27 | |10px |120px |1fr |1fr | 28 | |100px |header |header |header | 29 | |1fr |sidebar |plot_a |plot_c | 30 | |1fr |sidebar |plot_b |plot_b |") 31 | cat(to_md(my_layout)) 32 | 33 | } 34 | -------------------------------------------------------------------------------- /man/use_gridlayout_rmd.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/use_gridlayout_rmd.R 3 | \name{use_gridlayout_rmd} 4 | \alias{use_gridlayout_rmd} 5 | \title{Enable \code{gridlayout} usage in RMarkdown documents} 6 | \usage{ 7 | use_gridlayout_rmd(container_query = ".main-container", selector_prefix = "#") 8 | } 9 | \arguments{ 10 | \item{container_query}{The CSS selector used to access the main grid 11 | container. This is typically left at the default of \code{'.main-container'} as 12 | that's the parent of each section in the standard rmd template.} 13 | 14 | \item{selector_prefix}{CSS prefix used to target grid elements. This will 15 | change if you're integrating grid with a system that you don't want to use 16 | ids (the \code{"#"} prefix) with because they are not available or are used for 17 | other reasons.} 18 | } 19 | \description{ 20 | Adds required hooks to RMarkdown to process \code{gridlayout} chunks and style 21 | document accordingly. Layout will be generated from a chunk identified with 22 | the syntax \verb{```\{gridlayout\} ```} 23 | } 24 | \details{ 25 | See \code{vignette("using_with_rmd", package = "gridlayout")} for a more in-depth 26 | overview. 27 | } 28 | -------------------------------------------------------------------------------- /man/use_gridlayout_shiny.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/use_gridlayout_shiny.R 3 | \name{use_gridlayout_shiny} 4 | \alias{use_gridlayout_shiny} 5 | \title{Convert layout to css for Shiny} 6 | \usage{ 7 | use_gridlayout_shiny(layout, ...) 8 | } 9 | \arguments{ 10 | \item{layout}{Either a markdown table representation (see 11 | \code{\link{md_to_gridlayout}}) or a \code{gridlayout} object defining the 12 | desired layout for your Shiny app.} 13 | 14 | \item{...}{ 15 | Arguments passed on to \code{\link[=to_css]{to_css}} 16 | \describe{ 17 | \item{\code{container_key}}{The unique key used to identify the container to be 18 | targeted for the layout. If left blank it will default to applying grid 19 | styling to the whole app (aka the \code{body} element) for whole page grids. If 20 | plain character string is given, then it is assumed to be a 21 | \code{gridlayout-key} and the targeting is done using an attribute query for 22 | \code{data-gridlayout-key}. If container contains css selector characters such as 23 | a dot, the selector will not be transformed into an id automatically. E.g. 24 | \code{container = ".main-content"}.} 25 | }} 26 | } 27 | \value{ 28 | Character string of css used to setup grid layout and place elements 29 | (referenced by id) into correct locations 30 | } 31 | \description{ 32 | This simply wraps the output of \code{to_css()} in a \code{style} tag and escapes HTML 33 | characters to simplify using in Shiny. Most of the time you'll want to use 34 | \code{\link{grid_page}} or \code{\link{grid_container}} instead of manually 35 | adding this css though. 36 | } 37 | \examples{ 38 | # Only run these examples in interactive R sessions 39 | if (interactive()) { 40 | library(shiny) 41 | my_layout <- " 42 | | | | | 43 | |------|--------|---------| 44 | |2rem |200px |1fr | 45 | |100px |header |header | 46 | |500px |sidebar |distPlot |" 47 | 48 | # The classic Geyser app with grid layout 49 | shinyApp( 50 | ui = fluidPage( 51 | use_gridlayout_shiny(my_layout, "#app-container"), 52 | div( 53 | id = "app-container", 54 | div( 55 | style = "grid-area: header;", 56 | h2(id = "app-title", "Old Faithful Geyser Data") 57 | ), 58 | div( 59 | style = "grid-area: sidebar;", 60 | sliderInput("bins", "Number of bins:", 61 | min = 1, max = 50, value = 30 62 | ) 63 | ), 64 | div( 65 | style = "grid-area:distPlot", 66 | plotOutput("distPlot", height = "100\%") 67 | ) 68 | ) 69 | ), 70 | server = function(input, output) { 71 | output$distPlot <- renderPlot({ 72 | x <- faithful[, 2] 73 | bins <- seq(min(x), max(x), length.out = input$bins + 1) 74 | hist(x, breaks = bins, col = "darkgray", border = "white") 75 | }) 76 | } 77 | ) 78 | 79 | } 80 | } 81 | \seealso{ 82 | \code{\link{to_css}}, \code{\link{grid_page}}, \code{\link{grid_container}} 83 | } 84 | \keyword{internal} 85 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(gridlayout) 3 | 4 | test_check("gridlayout") 5 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/grid_page.md: -------------------------------------------------------------------------------- 1 | # Works when it should 2 | 3 | Code 4 | grid_page(layout = "\n | | |\n |------|--------|\n |2rem |200px |\n |1fr |header |\n |1fr |plot |\n |1fr |footer |", 5 | grid_card("header", shiny::h2(id = "header", "This is my header content")), 6 | grid_card("footer", shiny::sliderInput("bins", "Number of bins:", min = 1, 7 | max = 50, value = 30)), grid_card("plot", shiny::plotOutput("myPlot"))) 8 | Output 9 |
    10 |
    11 |
    12 |
    13 |
    14 | 15 |
    16 | 17 |
    18 |
    19 |
    20 |
    21 | 22 | 23 |
    24 |
    25 | 26 |
    27 |
    28 |
    29 |
    30 |
    31 | 32 |
    33 |
    34 |
    35 |
    36 | 37 | # Warns about mismatches between layout and passed elements 38 | 39 | Code 40 | err_msg$message 41 | Output 42 | [1] "Grid areas \"footer\" are present in the children of grid container but not specified in the layout:\n" 43 | 44 | # Warns about both at the same time to help people debug easier 45 | 46 | Code 47 | err_msg$message 48 | Output 49 | [1] "Grid areas \"footer\" are specified in the layout but not present in the children:\n If the grid-area css property for these areas is being provided in a custom way, set `check_for_mismatches = FALSE` to avoid this error message.\nGrid areas \"footer2\" are present in the children of grid container but not specified in the layout:\n" 50 | 51 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/new_gridlayout.md: -------------------------------------------------------------------------------- 1 | # Handles an explicit missing row provided with dots 2 | 3 | Code 4 | new_gridlayout(layout_def = "\n | header | header |\n | plota | plotb |\n | . | . |", 5 | row_sizes = c("200px", "1fr", "2fr")) 6 | Output 7 | gridlayout of 3 elements: 8 | 1fr 1fr 9 | 200px header header 10 | 1fr plota plotb 11 | 2fr . . 12 | Gap of 12px. Total height of viewport. 13 | 14 | Alternate layouts: 15 | 16 | - Width < 500px 17 | 1fr 18 | 85px header 19 | 350px plota 20 | 350px plotb 21 | Gap of 12px. Total height of auto. 22 | 23 | --- 24 | 25 | Code 26 | new_gridlayout(layout_def = "\n | header | header | . |\n | plota | plotb | . |", 27 | col_sizes = c("200px", "1fr", "2fr")) 28 | Output 29 | gridlayout of 3 elements: 30 | 200px 1fr 2fr 31 | 1fr header header . 32 | 1fr plota plotb . 33 | Gap of 12px. Total height of viewport. 34 | 35 | Alternate layouts: 36 | 37 | - Width < 500px 38 | 1fr 39 | 85px header 40 | 350px plota 41 | 350px plotb 42 | Gap of 12px. Total height of auto. 43 | 44 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/to_css.md: -------------------------------------------------------------------------------- 1 | # Works with default body target 2 | 3 | Code 4 | cat(to_css(grid_obj)) 5 | Output 6 | 7 | body { 8 | display:grid; 9 | grid-template-rows:100px 1fr 1fr; 10 | grid-template-columns:120px 1fr 1fr; 11 | grid-template-areas: 12 | "header header header" 13 | "sidebar plot_a plot_c" 14 | "sidebar plot_b plot_b"; 15 | --grid-gap:12px; 16 | grid-gap:var(--grid-gap); 17 | padding:var(--grid-gap); 18 | height:100%; 19 | } 20 | 21 | 22 | 23 | @media (max-width:500px) { 24 | body { 25 | display:grid; 26 | grid-template-rows:85px 350px 350px 350px 350px; 27 | grid-template-columns:1fr; 28 | grid-template-areas: 29 | "header " 30 | "sidebar" 31 | "plot_a " 32 | "plot_b " 33 | "plot_c "; 34 | --grid-gap:12px; 35 | grid-gap:var(--grid-gap); 36 | padding:var(--grid-gap); 37 | height:auto; 38 | } 39 | } 40 | 41 | 42 | # Panel collapsibility rules are added as needed 43 | 44 | Code 45 | cat(to_css(grid_obj)) 46 | Output 47 | 48 | body { 49 | display:grid; 50 | grid-template-rows:80px auto 400px; 51 | grid-template-columns:1fr; 52 | grid-template-areas: 53 | "header " 54 | "sidebar" 55 | "plot "; 56 | --grid-gap:12px; 57 | grid-gap:var(--grid-gap); 58 | padding:var(--grid-gap); 59 | height:100vh; 60 | } 61 | body > div[data-gridlayout-area="sidebar"] { 62 | --collapsible-visibility:block; 63 | --collapsed-content-size:0; 64 | --collapsed-panel-height:min-content; 65 | --collapsed-panel-overflow:hidden; 66 | } 67 | 68 | 69 | @media (max-width:500px) { 70 | body { 71 | display:grid; 72 | grid-template-rows:85px 350px 350px; 73 | grid-template-columns:1fr; 74 | grid-template-areas: 75 | "header " 76 | "sidebar" 77 | "plot "; 78 | --grid-gap:12px; 79 | grid-gap:var(--grid-gap); 80 | padding:var(--grid-gap); 81 | height:auto; 82 | } 83 | } 84 | 85 | 86 | -------------------------------------------------------------------------------- /tests/testthat/test-alternate_layouts.R: -------------------------------------------------------------------------------- 1 | 2 | test_that("Alternate layouts can be added for different sized screens", { 3 | 4 | main_layout <- new_gridlayout( 5 | layout_def = " 6 | | header | header | 7 | | plota | plotb |", 8 | col_sizes = c("1fr", "2fr"), 9 | row_sizes = c("100px", "1fr"), 10 | gap_size = "2rem" 11 | ) 12 | 13 | mobile_layout <- new_gridlayout( 14 | layout_def = " 15 | |header| 16 | |plota | 17 | |plotb |", 18 | col_sizes = c("1fr"), 19 | row_sizes = c("100px", "400px", "500px"), 20 | gap_size = "2rem" 21 | ) 22 | 23 | main_w_alternate <- add_alternate_layout( 24 | layout = main_layout, 25 | alternate_layout = mobile_layout, 26 | width_bounds = c(max = 400) 27 | ) 28 | 29 | expect_equal( 30 | mobile_layout$layout, 31 | get_info(main_w_alternate, "alternates")[[1]]$layout, 32 | ignore_attr = TRUE 33 | ) 34 | 35 | mobile_wo_plota <- new_gridlayout( 36 | layout_def = " 37 | |header| 38 | |plota |", 39 | row_sizes = c("100px", "500px") 40 | ) 41 | 42 | expect_error( 43 | add_alternate_layout( 44 | layout = main_layout, 45 | alternate_layout = mobile_wo_plota, 46 | width_bounds = c(max = 400) 47 | ), 48 | regexp = "Layouts have mismatched elements: plotb", 49 | fixed = TRUE 50 | ) 51 | 52 | big_screen_layout <- new_gridlayout( 53 | layout_def = "|header|plota|plotb|", 54 | col_sizes = c("200px", "1fr", "1fr"), 55 | row_sizes = c("1fr") 56 | ) 57 | 58 | expect_error( 59 | add_alternate_layout( 60 | main_w_alternate, 61 | alternate_layout = big_screen_layout, 62 | width_bounds = c(min = 350) 63 | ), 64 | regexp = "New alternate interval overlaps with previous interval", 65 | fixed = TRUE 66 | ) 67 | }) 68 | 69 | test_that("Alternate layouts can be added with alternate layouts argument or chained with add_alternate_layout()", { 70 | main_layout <- " 71 | | | | | 72 | |------|--------|-------| 73 | |2rem |200px |1fr | 74 | |80px |header |header | 75 | |1fr |sidebar |plot |" 76 | 77 | mobile_layout <- " 78 | |----- |--------| 79 | |2rem |1fr | 80 | |80px |header | 81 | |auto |sidebar | 82 | |400px |plot |" 83 | 84 | big_screen_layout <- " 85 | |-----|-------|--------|-----| 86 | |2rem |250px | 250px |1fr | 87 | |1fr |header |sidebar |plot |" 88 | 89 | alternate_layouts_argument <- new_gridlayout( 90 | main_layout, 91 | alternate_layouts = list( 92 | list( 93 | layout = mobile_layout, 94 | width_bounds = c(max = 600), 95 | container_height = "auto" 96 | ), 97 | list( 98 | layout = big_screen_layout, 99 | width_bounds = c(min = 1200) 100 | ) 101 | ) 102 | ) 103 | 104 | chained_alternates <- new_gridlayout(main_layout) 105 | 106 | chained_alternates <- add_alternate_layout( 107 | chained_alternates, 108 | mobile_layout, 109 | width_bounds = c(max = 600), 110 | container_height = "auto" 111 | ) 112 | 113 | chained_alternates <- add_alternate_layout( 114 | chained_alternates, 115 | big_screen_layout, 116 | width_bounds = c(min = 1200) 117 | ) 118 | 119 | expect_identical( 120 | alternate_layouts_argument, 121 | chained_alternates 122 | ) 123 | }) 124 | 125 | test_that("A single alternate layout does not need to be double nested", { 126 | main_layout <- " 127 | | | | | 128 | |------|--------|-------| 129 | |2rem |200px |1fr | 130 | |80px |header |header | 131 | |1fr |sidebar |plot |" 132 | 133 | mobile_layout <- " 134 | |----- |--------| 135 | |2rem |1fr | 136 | |80px |header | 137 | |auto |sidebar | 138 | |400px |plot |" 139 | 140 | 141 | expect_identical( 142 | new_gridlayout( 143 | main_layout, 144 | alternate_layouts = list( 145 | layout = mobile_layout, 146 | width_bounds = c(min = 600), 147 | container_height = "auto" 148 | ) 149 | ), 150 | new_gridlayout( 151 | main_layout, 152 | alternate_layouts = list( 153 | list( 154 | layout = mobile_layout, 155 | width_bounds = c(min = 600), 156 | container_height = "auto" 157 | ) 158 | ) 159 | ) 160 | ) 161 | }) 162 | 163 | -------------------------------------------------------------------------------- /tests/testthat/test-demo-apps.R: -------------------------------------------------------------------------------- 1 | # Don't run these tests on the CRAN build servers 2 | skip_on_cran() 3 | 4 | library(shinytest2) 5 | library(shiny) 6 | 7 | for (demo_app in demo_apps) { 8 | path_to_app <- system.file(package = "gridlayout", paste0("demo_apps/", demo_app)) 9 | test_app(path_to_app, check_setup = FALSE) 10 | } 11 | -------------------------------------------------------------------------------- /tests/testthat/test-grid_container.R: -------------------------------------------------------------------------------- 1 | make_grid_container <- function(...){ 2 | htmltools::renderTags( 3 | grid_container( 4 | ..., 5 | id = "my_grid", 6 | grid_card_text("header", "This is my header content"), 7 | grid_card( 8 | "plot", 9 | shiny::plotOutput("myPlot") 10 | ) 11 | ) 12 | ) 13 | } 14 | 15 | 16 | md_table <- make_grid_container( 17 | layout = " 18 | |2rem |200px | 19 | |100px|header | 20 | |1fr |plot |" 21 | ) 22 | 23 | sized_array_table <- make_grid_container( 24 | layout = c( 25 | "2rem 200px", 26 | "100px header", 27 | "1fr plot" 28 | ) 29 | ) 30 | 31 | array_table_with_size_args <- make_grid_container( 32 | layout = c( 33 | "header", 34 | "plot" 35 | ), 36 | row_sizes = c("100px", "1fr"), 37 | col_sizes = c("200px"), 38 | gap_size = "2rem" 39 | ) 40 | 41 | array_table_with_gap_args <- make_grid_container( 42 | layout = c( 43 | " 200px", 44 | "100px header", 45 | "1fr plot" 46 | ), 47 | gap_size = "2rem" 48 | ) 49 | 50 | 51 | 52 | test_that("Different methods of defining the same layout return the same thing", { 53 | 54 | expect_equal( 55 | md_table, 56 | sized_array_table 57 | ) 58 | 59 | expect_equal( 60 | sized_array_table, 61 | array_table_with_size_args 62 | ) 63 | 64 | expect_equal( 65 | array_table_with_size_args, 66 | array_table_with_gap_args 67 | ) 68 | }) 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /tests/testthat/test-grid_page.R: -------------------------------------------------------------------------------- 1 | test_that("Works when it should", { 2 | 3 | expect_snapshot( 4 | grid_page( 5 | layout = " 6 | | | | 7 | |------|--------| 8 | |2rem |200px | 9 | |1fr |header | 10 | |1fr |plot | 11 | |1fr |footer |", 12 | grid_card( 13 | "header", 14 | shiny::h2(id = "header", "This is my header content") 15 | ), 16 | grid_card( 17 | "footer", 18 | shiny::sliderInput("bins","Number of bins:", min = 1, max = 50, value = 30) 19 | ), 20 | grid_card( 21 | "plot", 22 | shiny::plotOutput("myPlot") 23 | ) 24 | ) 25 | ) 26 | }) 27 | 28 | test_that("Warns about mismatches between layout and passed elements", { 29 | 30 | layout_wo_footer <- " 31 | | | | 32 | |------|--------| 33 | |2rem |200px | 34 | |1fr |header | 35 | |1fr |plot |" 36 | 37 | err_msg <- expect_error( 38 | grid_container( 39 | layout = layout_wo_footer, 40 | grid_card( 41 | "header", 42 | shiny::h2(id = "header", "This is my header content") 43 | ), 44 | grid_card( 45 | "footer", 46 | shiny::sliderInput("bins","Number of bins:", min = 1, max = 50, value = 30) 47 | ), 48 | grid_card( 49 | "plot", 50 | shiny::plotOutput("myPlot") 51 | ) 52 | ) 53 | ) 54 | 55 | expect_snapshot(err_msg$message) 56 | }) 57 | 58 | test_that("Warns about both at the same time to help people debug easier", { 59 | 60 | err_msg <- expect_error( 61 | grid_container( 62 | layout = " 63 | | | | 64 | |------|--------| 65 | |2rem |200px | 66 | |1fr |header | 67 | |1fr |plot | 68 | |1fr |footer |", 69 | grid_card( 70 | "header", 71 | shiny::h2(id = "header", "This is my header content") 72 | ), 73 | grid_card( 74 | "footer2", 75 | shiny::sliderInput("bins","Number of bins:", min = 1, max = 50, value = 30) 76 | ), 77 | grid_card( 78 | "plot", 79 | shiny::plotOutput("myPlot") 80 | ) 81 | ) 82 | ) 83 | 84 | expect_snapshot(err_msg$message) 85 | 86 | }) 87 | 88 | -------------------------------------------------------------------------------- /tests/testthat/test-markdown-layout-parsing.R: -------------------------------------------------------------------------------- 1 | 2 | test_that("Basic table works", { 3 | my_layout <- new_gridlayout(" 4 | | |120px |1fr |1fr | 5 | |------|--------|-------|-------| 6 | |100px |header |header |header | 7 | |1fr |sidebar |plot_a |plot_c | 8 | |1fr |sidebar |plot_b |plot_b |" 9 | ) 10 | 11 | expect_s3_class(my_layout, "gridlayout") 12 | 13 | expect_equal(get_info(my_layout, "col_sizes"), 14 | c("120px", "1fr", "1fr")) 15 | 16 | expect_equal(get_info(my_layout, "row_sizes"), 17 | c("100px", "1fr", "1fr")) 18 | 19 | expect_equal(get_info(my_layout, "gap_size"), 20 | default_gap_size) 21 | }) 22 | 23 | test_that("Can put gap size in upper left", { 24 | my_layout <- new_gridlayout(" 25 | | 2rem |120px |1fr |1fr | 26 | |------|--------|-------|-------| 27 | |100px |header |header |header | 28 | |1fr |sidebar |plot_a |plot_c | 29 | |1fr |sidebar |plot_b |plot_b |" 30 | ) 31 | 32 | expect_equal(get_info(my_layout, "gap_size"), 33 | "2rem") 34 | }) 35 | 36 | test_that("Doesn't matter if col sizes are given in table header or simply first row", { 37 | 38 | expect_equal( 39 | new_gridlayout(" 40 | | 2rem |120px |1fr |1fr | 41 | |------|--------|-------|-------| 42 | |100px |header |header |header | 43 | |1fr |sidebar |plot_a |plot_c | 44 | |1fr |sidebar |plot_b |plot_b |" 45 | ), 46 | new_gridlayout(" 47 | | | | | | 48 | |------|--------|-------|-------| 49 | | 2rem |120px |1fr |1fr | 50 | |100px |header |header |header | 51 | |1fr |sidebar |plot_a |plot_c | 52 | |1fr |sidebar |plot_b |plot_b |" 53 | )) 54 | }) 55 | 56 | test_that("No sizes will give you constant sizes rows and cols", { 57 | my_layout <- new_gridlayout(" 58 | | | | | 59 | |:-------|:------|:------| 60 | |header |header |header | 61 | |sidebar |plot_a |plot_c | 62 | |sidebar |plot_b |plot_b |" 63 | ) 64 | 65 | expect_equal(get_info(my_layout, "col_sizes"), 66 | c("1fr", "1fr", "1fr")) 67 | 68 | expect_equal(get_info(my_layout, "row_sizes"), 69 | c("1fr", "1fr", "1fr")) 70 | }) 71 | 72 | test_that("Nonsense will give a usefull error message", { 73 | expect_error( 74 | new_gridlayout("## THis was an accidentally 75 | selected chunk of text 76 | that is not a table at all" 77 | )) 78 | }) 79 | 80 | 81 | test_that("Markdown parsing -- All sizes provided", { 82 | expect_snapshot( 83 | new_gridlayout(" 84 | |2rem |200px |1fr | 85 | |80px |header |header | 86 | |1fr |sidebar |plot |" 87 | ) 88 | ) 89 | }) 90 | 91 | test_that("Markdown parsing -- Only row sizes", { 92 | expect_snapshot( 93 | new_gridlayout(" 94 | |80px |header |header | 95 | |1fr |sidebar |plot |" 96 | ) 97 | ) 98 | }) 99 | 100 | 101 | test_that("Markdown parsing -- Only col sizes", { 102 | expect_snapshot( 103 | new_gridlayout(" 104 | |200px |1fr | 105 | |header |header | 106 | |sidebar |plot |" 107 | ) 108 | ) 109 | }) 110 | 111 | test_that("Markdown parsing -- Gap and row sizes", { 112 | expect_snapshot( 113 | new_gridlayout(" 114 | |2rem | | | 115 | |80px |header |header | 116 | |1fr |sidebar |plot |" 117 | ) 118 | ) 119 | }) 120 | 121 | test_that("Markdown parsing -- Only gap size", { 122 | expect_snapshot( 123 | new_gridlayout(" 124 | |3rem | | | 125 | | |header |header | 126 | | |sidebar |plot |" 127 | ) 128 | ) 129 | }) 130 | 131 | test_that("Markdown parsing -- Single column", { 132 | 133 | expect_snapshot( 134 | new_gridlayout(" 135 | |----- |--------| 136 | |2rem |1fr | 137 | |80px |header | 138 | |auto |sidebar | 139 | |400px |plot |" 140 | ) 141 | ) 142 | 143 | }) 144 | 145 | test_that("Markdown parsing -- Single row", { 146 | 147 | expect_snapshot( 148 | new_gridlayout(" 149 | |2rem |auto | 200px | 1fr | 150 | |80px |header |sidebar |sidebar |" 151 | ) 152 | ) 153 | 154 | }) 155 | 156 | 157 | test_that("Markdown parsing -- No sizes", { 158 | expect_snapshot( 159 | new_gridlayout(" 160 | |header |header | 161 | |sidebar |plot |" 162 | ) 163 | ) 164 | }) 165 | -------------------------------------------------------------------------------- /tests/testthat/test-md_table_to_matrix.R: -------------------------------------------------------------------------------- 1 | test_that("all sizes", { 2 | expect_equal( 3 | md_table_to_matrix( " 4 | |2rem | 300px |1fr | 5 | |80px |header |header | 6 | |1fr |sidebar |plot |"), 7 | matrix( 8 | c( 9 | "2rem", "300px", "1fr", 10 | "80px", "header", "header", 11 | "1fr", "sidebar", "plot" 12 | ), 13 | nrow=3, 14 | byrow = TRUE 15 | ) 16 | ) 17 | }) 18 | test_that("gap-size and row-sizes", { 19 | expect_equal( 20 | md_table_to_matrix( " 21 | |2rem | | | 22 | |80px |header |header | 23 | |1fr |sidebar |plot |"), 24 | matrix( 25 | c( 26 | "2rem", "", "", 27 | "80px", "header", "header", 28 | "1fr", "sidebar", "plot" 29 | ), 30 | nrow=3, 31 | byrow = TRUE 32 | ) 33 | ) 34 | }) 35 | 36 | test_that("only row-sizes", { 37 | expect_equal( 38 | md_table_to_matrix( " 39 | |80px |header |header | 40 | |1fr |sidebar |plot |"), 41 | matrix( 42 | c( 43 | "80px", "header", "header", 44 | "1fr", "sidebar", "plot" 45 | ), 46 | nrow=2, 47 | byrow = TRUE 48 | ) 49 | ) 50 | }) 51 | 52 | test_that("only col-sizes", { 53 | expect_equal( 54 | md_table_to_matrix( " 55 | | 1rem | 1fr | 56 | |header |header | 57 | |sidebar |plot |"), 58 | matrix( 59 | c( 60 | "1rem", "1fr", 61 | "header", "header", 62 | "sidebar", "plot" 63 | ), 64 | nrow=3, 65 | byrow = TRUE 66 | ) 67 | ) 68 | }) 69 | 70 | 71 | test_that("only gap-size", { 72 | expect_equal( 73 | md_table_to_matrix( " 74 | |2rem | | | 75 | | |header |header | 76 | | |sidebar |plot |"), 77 | matrix( 78 | c( 79 | "2rem", "", "", 80 | "", "header", "header", 81 | "", "sidebar", "plot" 82 | ), 83 | nrow=3, 84 | byrow = TRUE 85 | ) 86 | ) 87 | }) 88 | 89 | test_that("no sizes", { 90 | expect_equal( 91 | md_table_to_matrix( " 92 | |header |header | 93 | |sidebar |plot |"), 94 | matrix( 95 | c( 96 | "header", "header", 97 | "sidebar", "plot" 98 | ), 99 | nrow=2, 100 | byrow = TRUE 101 | ) 102 | ) 103 | }) 104 | -------------------------------------------------------------------------------- /tests/testthat/test-nested-grids.R: -------------------------------------------------------------------------------- 1 | 2 | test_that("Can handle recursive nesting", { 3 | 4 | my_layout <- " 5 | | | | | | 6 | |-----|-----|-------|-------| 7 | |10px |1fr |5fr |1fr | 8 | |1fr |top |top |right | 9 | |5fr |left |nested |right | 10 | |1fr |left |bottom |bottom |" 11 | 12 | depth <- 2 13 | 14 | emoji_panel <- function(area, emoji){ 15 | grid_card( 16 | area = area, 17 | htmltools::div( 18 | style = htmltools::css( 19 | width = "100%", height = "100%", 20 | display = "grid", 21 | "font-size" = "2rem", 22 | "place-content" = "center"), 23 | emoji 24 | ) 25 | ) 26 | } 27 | 28 | make_nested_panels <- function(level = 1) { 29 | grid_nested( 30 | area = "nested", 31 | layout = my_layout, 32 | id = paste0("level", level), 33 | emoji_panel("top", "↓"), 34 | emoji_panel("bottom", "↑"), 35 | emoji_panel("left", "→"), 36 | emoji_panel("right", "←"), 37 | if(level < depth) make_nested_panels(level + 1) else emoji_panel("nested", "🐢") 38 | ) 39 | } 40 | 41 | make_nested_panels() 42 | 43 | expect_snapshot(make_nested_panels()) 44 | }) 45 | -------------------------------------------------------------------------------- /tests/testthat/test-parse_layout_matrix.R: -------------------------------------------------------------------------------- 1 | test_that("Fully defined sizes", { 2 | 3 | parsed <- parse_layout_matrix( 4 | matrix( 5 | c( 6 | "11px", "1fr", "300px", 7 | "100px", "header", "header", 8 | "1fr", "plota", "plotb", 9 | "2rem", "footer", "footer" 10 | ), 11 | nrow = 4, 12 | byrow = TRUE 13 | ) 14 | ) 15 | 16 | expect_equal( 17 | parsed$column_sizes, 18 | c("1fr", "300px") 19 | ) 20 | expect_equal( 21 | parsed$row_sizes, 22 | c("100px", "1fr", "2rem") 23 | ) 24 | expect_equal( 25 | parsed$gap_size, 26 | "11px" 27 | ) 28 | }) 29 | 30 | 31 | test_that("Missing gap size", { 32 | 33 | parsed <- parse_layout_matrix( 34 | matrix( 35 | c( 36 | "", "1fr", "300px", 37 | "100px", "header", "header", 38 | "1fr", "plota", "plotb", 39 | "2rem", "footer", "footer" 40 | ), 41 | nrow = 4, 42 | byrow = TRUE 43 | ) 44 | ) 45 | 46 | expect_equal( 47 | parsed$column_sizes, 48 | c("1fr", "300px") 49 | ) 50 | expect_equal( 51 | parsed$row_sizes, 52 | c("100px", "1fr", "2rem") 53 | ) 54 | expect_equal( 55 | parsed$gap_size, 56 | DEFAULT_SIZE_CHAR 57 | ) 58 | }) 59 | 60 | test_that("No column sizes", { 61 | parsed <- parse_layout_matrix( 62 | matrix( 63 | c( 64 | "100px", "header", "header", 65 | "1fr", "plota", "plotb", 66 | "2rem", "footer", "footer" 67 | ), 68 | nrow = 3, 69 | byrow = TRUE 70 | ) 71 | ) 72 | 73 | expect_equal( 74 | parsed$column_sizes, 75 | c(DEFAULT_SIZE_CHAR, DEFAULT_SIZE_CHAR) 76 | ) 77 | expect_equal( 78 | parsed$row_sizes, 79 | c("100px", "1fr", "2rem") 80 | ) 81 | expect_equal( 82 | parsed$gap_size, 83 | DEFAULT_SIZE_CHAR 84 | ) 85 | }) 86 | 87 | test_that("No row sizes", { 88 | parsed <- parse_layout_matrix( 89 | matrix( 90 | c( 91 | "1fr", "2fr", 92 | "header", "header", 93 | "plota", "plotb", 94 | "footer", "footer" 95 | ), 96 | nrow = 4, 97 | byrow = TRUE 98 | ) 99 | ) 100 | 101 | expect_equal( 102 | parsed$column_sizes, 103 | c("1fr", "2fr") 104 | ) 105 | expect_equal( 106 | parsed$row_sizes, 107 | c(DEFAULT_SIZE_CHAR, DEFAULT_SIZE_CHAR, DEFAULT_SIZE_CHAR) 108 | ) 109 | expect_equal( 110 | parsed$gap_size, 111 | DEFAULT_SIZE_CHAR 112 | ) 113 | }) 114 | 115 | 116 | 117 | test_that("Single column - no-sizes", { 118 | parsed <- parse_layout_matrix( 119 | matrix( 120 | c( 121 | "plota", 122 | "plotb" 123 | ), 124 | nrow = 2, 125 | byrow = TRUE 126 | ) 127 | ) 128 | 129 | expect_equal( 130 | parsed$column_sizes, 131 | c(DEFAULT_SIZE_CHAR) 132 | ) 133 | expect_equal( 134 | parsed$row_sizes, 135 | c(DEFAULT_SIZE_CHAR, DEFAULT_SIZE_CHAR) 136 | ) 137 | expect_equal( 138 | parsed$gap_size, 139 | DEFAULT_SIZE_CHAR 140 | ) 141 | }) 142 | 143 | test_that("Single column - all-sizes", { 144 | 145 | parsed <- parse_layout_matrix( 146 | matrix( 147 | c("1rem", "430px", 148 | "240px","plota", 149 | "1fr", "plotb"), 150 | nrow = 3, 151 | byrow = TRUE 152 | ) 153 | ) 154 | 155 | expect_equal( 156 | parsed$column_sizes, 157 | c("430px") 158 | ) 159 | expect_equal( 160 | parsed$row_sizes, 161 | c("240px", "1fr") 162 | ) 163 | expect_equal( 164 | parsed$gap_size, 165 | "1rem" 166 | ) 167 | }) 168 | 169 | 170 | test_that("Single row - no-sizes", { 171 | parsed <- parse_layout_matrix( 172 | matrix( 173 | c( 174 | "plota","plotb" 175 | ), 176 | nrow = 1, 177 | byrow = TRUE 178 | ) 179 | ) 180 | 181 | expect_equal( 182 | parsed$column_sizes, 183 | c(DEFAULT_SIZE_CHAR, DEFAULT_SIZE_CHAR) 184 | ) 185 | expect_equal( 186 | parsed$row_sizes, 187 | c(DEFAULT_SIZE_CHAR) 188 | ) 189 | expect_equal( 190 | parsed$gap_size, 191 | DEFAULT_SIZE_CHAR 192 | ) 193 | }) 194 | 195 | test_that("Partially defined sizes", { 196 | 197 | parsed <- parse_layout_matrix( 198 | matrix( 199 | c( 200 | "", "1fr", "", 201 | "100px", "header", "header", 202 | "", "plota", "plotb", 203 | "2rem", "footer", "footer" 204 | ), 205 | nrow = 4, 206 | byrow = TRUE 207 | ) 208 | ) 209 | 210 | expect_equal( 211 | parsed$column_sizes, 212 | c("1fr", DEFAULT_SIZE_CHAR) 213 | ) 214 | expect_equal( 215 | parsed$row_sizes, 216 | c("100px", DEFAULT_SIZE_CHAR, "2rem") 217 | ) 218 | expect_equal( 219 | parsed$gap_size, 220 | DEFAULT_SIZE_CHAR 221 | ) 222 | }) 223 | 224 | 225 | -------------------------------------------------------------------------------- /tests/testthat/test-to_css.R: -------------------------------------------------------------------------------- 1 | test_that("Works with default body target", { 2 | grid_obj <- new_gridlayout( 3 | " 4 | | |120px |1fr |1fr | 5 | |:-----|:-------|:------|:------| 6 | |100px |header |header |header | 7 | |1fr |sidebar |plot_a |plot_c | 8 | |1fr |sidebar |plot_b |plot_b |" 9 | ) 10 | 11 | expect_snapshot(cat(to_css(grid_obj))) 12 | }) 13 | 14 | test_that("Can change body target", { 15 | grid_obj <- new_gridlayout(" 16 | | |120px |1fr |1fr | 17 | |:-----|:-------|:------|:------| 18 | |100px |header |header |header | 19 | |1fr |sidebar |plot_a |plot_c | 20 | |1fr |sidebar |plot_b |plot_b |" 21 | ) 22 | 23 | app_css <- to_css(grid_obj, "app_container") 24 | 25 | expect_true( 26 | str_detect(app_css, "data-gridlayout-key=\"app_container\"", fixed = TRUE) 27 | ) 28 | }) 29 | 30 | 31 | 32 | 33 | test_that("Height setting can be customized", { 34 | grid_obj <- new_gridlayout(" 35 | | |120px |1fr |1fr | 36 | |:-----|:-------|:------|:------| 37 | |100px |header |header |header | 38 | |1fr |sidebar |plot_a |plot_c | 39 | |1fr |sidebar |plot_b |plot_b |", 40 | container_height = "viewport" 41 | ) 42 | 43 | expect_true( 44 | str_detect( 45 | to_css(grid_obj), 46 | "height:100vh", 47 | fixed = TRUE 48 | ) 49 | ) 50 | 51 | 52 | expect_true( 53 | str_detect( 54 | to_css(new_gridlayout(grid_obj,container_height = "800px")), 55 | "height:800px", 56 | fixed = TRUE 57 | ) 58 | ) 59 | 60 | }) 61 | 62 | 63 | test_that("Panel collapsibility rules are added as needed", { 64 | 65 | grid_obj <- new_gridlayout( " 66 | | |1fr | 67 | |80px |header | 68 | |auto |sidebar | 69 | |400px |plot |", 70 | container_height = "viewport" 71 | ) 72 | 73 | expect_snapshot(cat(to_css(grid_obj))) 74 | }) 75 | -------------------------------------------------------------------------------- /tests/testthat/test-to_md.R: -------------------------------------------------------------------------------- 1 | test_that("Can reacreate input table", { 2 | start_table <- 3 | "| 10px | 120px | 1fr | 1fr | 4 | |-------|---------|--------|--------| 5 | | 100px | header | header | header | 6 | | 1fr | sidebar | plot_a | plot_c | 7 | | 1fr | sidebar | plot_b | plot_b |" 8 | 9 | my_layout <- new_gridlayout(start_table) 10 | expect_equal( 11 | to_md(my_layout), 12 | start_table 13 | ) 14 | }) 15 | 16 | test_that("Gap size can be omitted if desired", { 17 | start_table <- 18 | "| 10px | 120px | 1fr | 1fr | 19 | |-------|---------|--------|--------| 20 | | 100px | header | header | header | 21 | | 1fr | sidebar | plot_a | plot_c | 22 | | 1fr | sidebar | plot_b | plot_b |" 23 | 24 | my_layout <- new_gridlayout(start_table) 25 | expect_equal( 26 | to_md(my_layout, include_gap_size = FALSE), 27 | "| | 120px | 1fr | 1fr | 28 | |-------|---------|--------|--------| 29 | | 100px | header | header | header | 30 | | 1fr | sidebar | plot_a | plot_c | 31 | | 1fr | sidebar | plot_b | plot_b |" 32 | ) 33 | }) 34 | -------------------------------------------------------------------------------- /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.R 3 | -------------------------------------------------------------------------------- /vignettes/alternate-layouts_resizing.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/vignettes/alternate-layouts_resizing.gif -------------------------------------------------------------------------------- /vignettes/alternate-layouts_w1000_h1000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/vignettes/alternate-layouts_w1000_h1000.png -------------------------------------------------------------------------------- /vignettes/alternate-layouts_w2000_h1000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/vignettes/alternate-layouts_w2000_h1000.png -------------------------------------------------------------------------------- /vignettes/alternate-layouts_w500_h1000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/vignettes/alternate-layouts_w500_h1000.png -------------------------------------------------------------------------------- /vignettes/defining-a-layout.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Defining a layout" 3 | output: rmarkdown::html_vignette 4 | vignette: > 5 | %\VignetteIndexEntry{Defining a layout} 6 | %\VignetteEngine{knitr::rmarkdown} 7 | %\VignetteEncoding{UTF-8} 8 | --- 9 | 10 | ```{r, include = FALSE} 11 | knitr::opts_chunk$set( 12 | collapse = TRUE, 13 | comment = "#>" 14 | ) 15 | ``` 16 | 17 | ```{r setup} 18 | library(gridlayout) 19 | 20 | # Print the layout without showing mobile layouts 21 | show_layout <- function(layout) { 22 | print(layout, show_alternates = FALSE) 23 | } 24 | ``` 25 | 26 | There are multiple ways to define a gridlayout. The following all describe the exact same layout. An app with a 100 pixel tall header spanning the width of the page, a 120 pixel wide sidebar on the left, and three plots in the remaining with `plot_a` being the smallest, `plot_b` being twice as wide as `plot_a`, and `plot_c` sitting below both `plot_a` and `plot_c` spanning the width of both. 27 | 28 | 29 | ## Where to pass your layout to 30 | 31 | All examples are passed to `new_gridlayout()` which generates a `gridlayout` object directly. However, typically these layouts are passed directly into the `gridlayout` container functions `new_gridlayout()`, `grid_page()`, `grid_container()`, or `grid_nested()`. 32 | 33 | 34 | ### A note sizing 35 | 36 | For the first two definition techniques we use inline sizes, however, for both layouts the sizes can be omitted for even sizes rows/columns, or passed as arrays to the`row_sizes` and `col_sizes` arguments in the grid layout container function. Additionally, the size of the gap between elements in your layout can be passed using either the upper-left "cell" of the table layout or the `gap_size` argument. 37 | 38 | 39 | ## The table array 40 | 41 | The most common way to define your layout is using an character vector where each element represents a row in your layout. Each column is delineated by one or more spaces between the names of the defined regions. This allows you to line up your layout visually. Sizes can be added for the rows and columns by placing row sizes at the left of the respective row and column sizes at the top of the respective column. 42 | 43 | 44 | ```{r md_to_gridlayout} 45 | library(gridlayout) 46 | 47 | table_array <- new_gridlayout(c( 48 | " 120px 1fr 2fr ", 49 | "100px header header header", 50 | "1fr sidebar plot_a plot_b", 51 | "1fr sidebar plot_c plot_c" 52 | )) 53 | 54 | show_layout(table_array) 55 | ``` 56 | 57 | ## The single character table 58 | 59 | Similar to the table array definition, a markdown table enclosed in a single string can be used as well. This is the method used to declare a layout in an RMarkdown file but since multi-line strings can be hard to keep formatted properly, the table-array definition is usually prefered for shiny-app usage. 60 | 61 | ```{r} 62 | md_table <- new_gridlayout(" 63 | | |120px |1fr |2fr | 64 | |------|--------|-------|-------| 65 | |100px |header |header |header | 66 | |1fr |sidebar |plot_a |plot_b | 67 | |1fr |sidebar |plot_c |plot_c |" 68 | ) 69 | 70 | show_layout(md_table) 71 | ``` 72 | 73 | 74 | ## Elements list 75 | 76 | The last method is using a list to define the give positions of the items. This method is much more verbose than the previous two but is easier to generate using code. This can be useful in layouts with a large number of rows and columns where writing out the table would be cumbersome. 77 | 78 | ```{r} 79 | # Assemble list of elements along with their positions 80 | layout_elements <- list( 81 | list( 82 | id = "header", start_row = 1, end_row = 1, 83 | start_col = 1, end_col = 3 84 | ), 85 | list( 86 | id = "sidebar", start_row = 2, end_row = 3, 87 | start_col = 1, end_col = 1 88 | ), 89 | list( 90 | id = "plot_a", start_row = 2, end_row = 2, 91 | start_col = 2, end_col = 2 92 | ), 93 | list( 94 | id = "plot_b", start_row = 2, end_row = 2, 95 | start_col = 3, end_col = 3 96 | ), 97 | list( 98 | id = "plot_c", start_row = 3, end_row = 3, 99 | start_col = 2, end_col = 3 100 | ) 101 | ) 102 | 103 | elements_list <- new_gridlayout( 104 | layout_elements, 105 | col_sizes = c("120px", "1fr", "2fr"), 106 | row_sizes = c("100px", "1fr", "1fr") 107 | ) 108 | 109 | show_layout(elements_list) 110 | ``` 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /vignettes/layout-examples_focal_chart_side.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/vignettes/layout-examples_focal_chart_side.png -------------------------------------------------------------------------------- /vignettes/layout-examples_focal_chart_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/vignettes/layout-examples_focal_chart_top.png -------------------------------------------------------------------------------- /vignettes/layout-examples_four_panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/vignettes/layout-examples_four_panel.png -------------------------------------------------------------------------------- /vignettes/layout-examples_scrolling_stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/vignettes/layout-examples_scrolling_stack.png -------------------------------------------------------------------------------- /vignettes/layout-examples_stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/vignettes/layout-examples_stack.png -------------------------------------------------------------------------------- /vignettes/use_gridlayout_rmd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/vignettes/use_gridlayout_rmd.png -------------------------------------------------------------------------------- /vignettes/use_gridlayout_rmd_base.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/vignettes/use_gridlayout_rmd_base.png -------------------------------------------------------------------------------- /vignettes/use_gridlayout_rmd_extra_child_styles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/vignettes/use_gridlayout_rmd_extra_child_styles.png -------------------------------------------------------------------------------- /vignettes/use_gridlayout_rmd_only_grid_panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/gridlayout/dc7d8912ae27e5393561dcc2b9bd637f3653ca09/vignettes/use_gridlayout_rmd_only_grid_panel.png -------------------------------------------------------------------------------- /vignettes/using_with_rmd.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Using with Rmds" 3 | output: rmarkdown::html_vignette 4 | vignette: > 5 | %\VignetteIndexEntry{Using with Rmds} 6 | %\VignetteEngine{knitr::rmarkdown} 7 | %\VignetteEncoding{UTF-8} 8 | --- 9 | 10 | ```{r, include = FALSE} 11 | knitr::opts_chunk$set( 12 | collapse = TRUE, 13 | comment = "", 14 | out.width = "100%", 15 | eval = FALSE 16 | ) 17 | 18 | rmd_loc <- "demo_apps/rmarkdown_demo/grid_markdown.Rmd" 19 | ``` 20 | 21 | ```{r, eval = FALSE, echo = FALSE} 22 | # Generate screenshots for this vignette 23 | source(here::here("inst/demo_apps/setupScreenshots.R")) 24 | 25 | 26 | screenshot_demo_app( 27 | app_path = here::here('inst/demo_apps/', rmd_loc), 28 | screenshot_name = "use_gridlayout_rmd.png", 29 | screenshot_root = "vignettes", 30 | vwidth = 1100 31 | ) 32 | 33 | ``` 34 | 35 | 36 | ```{r} 37 | library(gridlayout) 38 | ``` 39 | 40 | 41 | By placing the function `use_gridlayout_rmd` in the `setup` chunk of an Rmd you can define a gridlayout table using the a code chunk with the "language" set to `gridlayout`. Any markdown style table (see `md_to_gridlayout()`) will be used to setup a grid-layout for your page. Just make sure your sections are given headers corresponding to the respective grid elements (either using their name or by using the `#My header text {#custom_id}` syntax after the header.) 42 | 43 | 44 | The following shows how you can customize the appearance of the elements of your layout using "card styles" and custom css. 45 | 46 | 47 | # The markdown code 48 | 49 | The screenshot below is based on the following `.Rmd`. 50 | 51 | 52 | ```{r, echo=FALSE, eval=TRUE} 53 | rmd_content <- readLines( 54 | system.file(rmd_loc, package = "gridlayout") 55 | ) 56 | 57 | cat(paste(rmd_content, collapse = "\n")) 58 | ``` 59 | 60 | 61 | ## Result 62 | 63 | 64 | 65 | ```{r, echo = FALSE, message=FALSE, eval=TRUE} 66 | knitr::include_graphics("use_gridlayout_rmd.png") 67 | ``` 68 | 69 | 70 | --------------------------------------------------------------------------------