Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
^.*\.Rproj$
^\.Rproj\.user$
^.*\.git$
^\.github$
^codecov\.yml$
^LICENSE\.md$
^README\.Rmd$
README.md
52 changes: 52 additions & 0 deletions .github/workflows/R-CMD-check.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]

name: R-CMD-check

permissions: read-all

jobs:
R-CMD-check:
runs-on: ${{ matrix.config.os }}

name: ${{ matrix.config.os }} (${{ matrix.config.r }})

strategy:
fail-fast: false
matrix:
config:
- {os: macos-latest, r: 'release'}
- {os: windows-latest, r: 'release'}
- {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'}
- {os: ubuntu-latest, r: 'release'}
- {os: ubuntu-latest, r: 'oldrel-1'}

env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
R_KEEP_PKG_SOURCE: yes

steps:
- uses: actions/checkout@v4

- uses: r-lib/actions/setup-pandoc@v2

- uses: r-lib/actions/setup-r@v2
with:
r-version: ${{ matrix.config.r }}
http-user-agent: ${{ matrix.config.http-user-agent }}
use-public-rspm: true

- uses: r-lib/actions/setup-r-dependencies@v2
with:
extra-packages: any::rcmdcheck
needs: check

- uses: r-lib/actions/check-r-package@v2
with:
upload-snapshots: true
build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")'
47 changes: 47 additions & 0 deletions .github/workflows/test-coverage.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]

name: test-coverage

permissions: read-all

jobs:
test-coverage:
runs-on: ubuntu-latest
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}

steps:
- uses: actions/checkout@v4

- uses: r-lib/actions/setup-r@v2
with:
use-public-rspm: true

- uses: r-lib/actions/setup-r-dependencies@v2
with:
extra-packages: any::covr, any::xml2
needs: coverage

- name: Test coverage
run: |
cov <- covr::package_coverage(
quiet = FALSE,
clean = FALSE,
install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package")
)
covr::to_cobertura(cov)
shell: Rscript {0}

- uses: codecov/codecov-action@v4
with:
fail_ci_if_error: ${{ github.event_name != 'pull_request' && true || false }}
file: ./cobertura.xml
plugin: noop
disable_search: true
token: ${{ secrets.CODECOV_TOKEN }}
19 changes: 15 additions & 4 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,21 @@ Authors@R: c(
person("Kevin", "Louie", role = c("ctb", "cph"), comment = "Materialize CSS library")
)
Maintainer: Eric Anderson <eric.ray.anderson@gmail.com>
Description: Allows shiny developers to incorporate UI elements based on Google's Material design. See <https://material.io/guidelines/> for more information.
Description: Allows shiny developers to incorporate UI elements based on Google's
Material design. See <https://m3.material.io/> for more information about
Material Design.
URL: https://ericrayanderson.github.io/shinymaterial/
BugReports: https://github.com/ericrayanderson/shinymaterial/issues
License: GPL-3 | file LICENSE
Imports: shiny (>= 0.7.0), jsonlite, sass
Imports:
shiny (>= 1.7.0),
jsonlite,
sass,
rlang (>= 1.0.0),
cli
Suggests:
testthat (>= 3.0.0)
Config/testthat/edition: 3
Encoding: UTF-8
LazyData: true
RoxygenNote: 7.1.0
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.3.2
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
importFrom("utils", "capture.output")
importFrom("cli", "cli_abort", "cli_alert_info")
importFrom("rlang", "abort")
export(open_material_modal)
export(close_material_modal)
export(material_button)
Expand Down
24 changes: 0 additions & 24 deletions R/dir-recursion.R

This file was deleted.

26 changes: 17 additions & 9 deletions R/shiny-material-page.R
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,11 @@ material_page <- function(..., title = "", nav_bar_fixed = FALSE, nav_bar_color
}

if(include_fonts){

dir_recursion("www/fonts/roboto")

if (!dir.exists("www/fonts/roboto")) {
cli::cli_alert_info("Creating directory: {.path www/fonts/roboto}")
dir.create("www/fonts/roboto", recursive = TRUE)
}

font_files <- list.files(
system.file(paste0("materialize/", materialize_version, "/fonts/roboto"),
Expand All @@ -123,7 +126,7 @@ material_page <- function(..., title = "", nav_bar_fixed = FALSE, nav_bar_color


if (!dir.exists("www/icons/materialicons/")) {
message("[shinymaterial] Creating directory: www/icons/materialicons/")
cli::cli_alert_info("Creating directory: {.path www/icons/materialicons/}")
dir.create("www/icons/materialicons/", recursive = TRUE)
}

Expand Down Expand Up @@ -214,11 +217,16 @@ material_page <- function(..., title = "", nav_bar_fixed = FALSE, nav_bar_color
package = "shinymaterial")
),
shiny::tags$script("
Shiny.addCustomMessageHandler('shinymaterialJS',
function(code) {
//console.log(code.split('\\\\').join('').trim());
eval(code.split('\\\\').join('').trim());
});
")
Shiny.addCustomMessageHandler('shinymaterialJS', function(code) {
// Use Function constructor instead of eval for improved security
// Function() doesn't have access to local scope, reducing attack surface
try {
var cleanCode = code.split('\\\\').join('').trim();
new Function(cleanCode)();
} catch (e) {
console.error('shinymaterial: Error executing code:', e.message);
}
});
")
)
}
24 changes: 15 additions & 9 deletions R/update-shiny-material-dropdown.R
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,27 @@
#' }
update_material_dropdown <- function(session, input_id, value = NULL, choices = NULL){
if(is.null(value)) {
message("ERROR: Must include 'value' with update_material_dropdown")
return(NULL)
cli::cli_abort(
"Must include {.arg value} with {.fn update_material_dropdown}",
class = "shinymaterial_error_missing_value"
)
}


if(!is.null(choices)){

if ( is.null(names(choices)) ){
names(choices) <- choices
}


if(!(value %in% choices)) {
message("ERROR: value '", value, "' not found in choices")
return(NULL)
cli::cli_abort(
c("Value not found in choices.",
"x" = "Value {.val {value}} is not in the provided choices.",
"i" = "Available choices: {.val {choices}}"),
class = "shinymaterial_error_invalid_value"
)
}

choices_value_js_code <- paste0("$('#", input_id, "').empty(); $('#", input_id, "')")
Expand Down
57 changes: 33 additions & 24 deletions inst/js/shiny-material-button.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,37 @@
$(document).ready(function () {
$(".shiny-material-button").on("click", function () {
var el = $(this);
var curVal = parseInt(el.val());
el.val(curVal + 1);
el.trigger("change");
});
/**
* Material Design Button Input Binding for Shiny
* @description Handles button click events and value updates
*/
'use strict';

var shinyMaterialButton = new Shiny.InputBinding();
$.extend(shinyMaterialButton, {
find: function (scope) {
return $(scope).find(".shiny-material-button");
},
getValue: function (el) {
return parseInt($(el).val());
},
subscribe: function (el, callback) {
$(el).on("change.shiny-material-button", function (e) {
callback();
});
},
unsubscribe: function (el) {
$(el).off(".shiny-material-button");
}
document.addEventListener('DOMContentLoaded', () => {
// Handle button click events
document.querySelectorAll('.shiny-material-button').forEach((button) => {
button.addEventListener('click', function() {
const currentValue = parseInt(this.value, 10) || 0;
this.value = currentValue + 1;
this.dispatchEvent(new Event('change', { bubbles: true }));
});
});

// Define the Shiny input binding using ES6 class syntax
class ShinyMaterialButton extends Shiny.InputBinding {
find(scope) {
return $(scope).find('.shiny-material-button');
}

getValue(el) {
return parseInt(el.value, 10) || 0;
}

subscribe(el, callback) {
$(el).on('change.shiny-material-button', () => callback());
}

unsubscribe(el) {
$(el).off('.shiny-material-button');
}
}

Shiny.inputBindings.register(shinyMaterialButton);
Shiny.inputBindings.register(new ShinyMaterialButton());
});
49 changes: 27 additions & 22 deletions inst/js/shiny-material-checkbox.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
$(document).ready(function () {

var shinyMaterialCheckbox = new Shiny.InputBinding();
$.extend(shinyMaterialCheckbox, {
find: function (scope) {
return $(scope).find(".shiny-material-checkbox");
},
getValue: function (el) {
return $(el).val();
},
subscribe: function (el, callback) {
$(el).on("change.shiny-material-checkbox", function (e) {
callback();
});
},
unsubscribe: function (el) {
$(el).off(".shiny-material-checkbox");
}
});

Shiny.inputBindings.register(shinyMaterialCheckbox);
});
/**
* Material Design Checkbox Input Binding for Shiny
* @description Handles checkbox state changes
*/
'use strict';

document.addEventListener('DOMContentLoaded', () => {
class ShinyMaterialCheckbox extends Shiny.InputBinding {
find(scope) {
return $(scope).find('.shiny-material-checkbox');
}

getValue(el) {
return $(el).val();
}

subscribe(el, callback) {
$(el).on('change.shiny-material-checkbox', () => callback());
}

unsubscribe(el) {
$(el).off('.shiny-material-checkbox');
}
}

Shiny.inputBindings.register(new ShinyMaterialCheckbox());
});
Loading