Skip to content

Commit

Permalink
start rework dropdown
Browse files Browse the repository at this point in the history
  • Loading branch information
DivadNojnarg committed Nov 12, 2023
1 parent d8f3fa9 commit 85f7dc3
Show file tree
Hide file tree
Showing 9 changed files with 239 additions and 88 deletions.
3 changes: 2 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ RoxygenNote: 7.2.3
Imports:
htmltools,
shiny,
shiny.react
shiny.react,
jsonlite
Suggests:
testthat (>= 3.0.0),
shinytest2,
Expand Down
1 change: 0 additions & 1 deletion NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export(createReactShinyInput)
export(date_input)
export(divider)
export(dropdow_menu)
export(dropdown)
export(dropdown_item)
export(dropdown_section)
export(dropdown_trigger)
Expand Down
73 changes: 47 additions & 26 deletions R/inputs.R
Original file line number Diff line number Diff line change
Expand Up @@ -128,26 +128,32 @@ checkbox_input <- input("Checkbox", FALSE)
update_checkbox_input <- shiny.react::updateReactInput

#' @keywords internal
create_group_input <- function(inputId, ..., choices, selected, type = c("Checkbox", "Radio")) {
create_group_input <- function(
inputId,
...,
choices,
selected,
type = c("CheckboxGroup", "RadioGroup", "DropdownMenu")
) {

type <- match.arg(type)
module <- paste0(type, "Group")

process_val <- switch(
type,
"Checkbox" = as.list,
"Radio" = I
"CheckboxGroup" = as.list,
"RadioGroup" = I,
"DropdownMenu" = as.list
)

tagList(
# This seems a bit hacky but this can't be called from the main JS script
# because we only need it when the radio is invoked ...
tags$script(sprintf("jsmodule['@/ReactR']['%s']()", module)),
tags$script(sprintf("jsmodule['@/ReactR']['%s']()", type)),
createReactShinyInput(
inputId = inputId,
class = tolower(module),
class = tolower(type),
default = process_val(selected),
configuration = list(children = as.list(choices), ...),
configuration = listRenderTags(list(children = as.list(choices), ...)),
container = htmltools::tags$div
)
)
Expand All @@ -169,7 +175,7 @@ radio_input <- function(inputId, ..., choices, selected = choices[1]) {
...,
choices = choices,
selected = selected,
type = "Radio"
type = "RadioGroup"
)
}

Expand All @@ -180,15 +186,15 @@ update_group_input <- function(
...,
choices = NULL,
selected = NULL,
type = c("Checkbox", "Radio")
type = c("CheckboxGroup", "RadioGroup")
) {

type <- match.arg(type)

message <- list()
if (type == "Checkbox") selected <- as.list(selected)
if (type == "CheckboxGroup") selected <- as.list(selected)
message$value <- selected
configuration <- c(children = as.list(choices), list(...))
configuration <- listRenderTags(c(children = as.list(choices), list(...)))
if (length(configuration) > 0) {
message$configuration <- configuration
}
Expand All @@ -212,7 +218,7 @@ update_radio_input <- function(
...,
choices = choices,
selected = selected,
type = "Radio"
type = "RadioGroup"
)
}

Expand All @@ -225,7 +231,7 @@ checkboxgroup_input <- function(inputId, ..., choices, selected = NULL) {
...,
choices = choices,
selected = selected,
type = "Checkbox"
type = "CheckboxGroup"
)
}

Expand All @@ -245,7 +251,7 @@ update_checkboxgroup_input <- function(
...,
choices = choices,
selected = selected,
type = "Checkbox"
type = "CheckboxGroup"
)
}

Expand All @@ -263,28 +269,43 @@ accordion_item <- component("AccordionItem")
#' @export
update_accordion <- shiny.react::updateReactInput

#' @rdname dropdown
#' @inherit component params return
#' @export
dropdown <- component("Dropdown")

#' @rdname dropdown
#' @inherit shinyInput params return
#' @export
dropdow_menu <- input("DropdownMenu")

#' @rdname dropdown
#' @export
dropdown_trigger <- component("DropdownTrigger")
dropdow_menu <- function(inputId, ..., choices = NULL, selected = NULL) {
create_group_input(
inputId,
...,
choices = choices,
selected = selected,
type = "DropdownMenu"
)
}

#' @rdname dropdown
#' @export
dropdown_item <- component("DropdownItem")
dropdown_item <- function(...) {
list(..., dropdownItem = TRUE)
}

#' @rdname dropdown
#' @note Container for related \link{dropdown_item}.
#' @export
dropdown_section <- component("DropdownSection")
dropdown_section <- function(...) {
tmp <- list(...)
props <- list()
children <- list()
for (i in seq_along(tmp)) {
if (inherits(tmp[[i]], "list")) {
children <- append(children, tmp[[i]])
} else {
l <- tmp[[i]]
names(l) <- names(tmp)[[i]]
props <- append(props, l)
}
}
list(props = props, children = children, dropdownSection = TRUE)
}

#' @rdname dropdown
#' @export
Expand Down
17 changes: 17 additions & 0 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,23 @@ createReactShinyInput <- function (
)
}

# Converts any shiny tag into character so that toJSON does not cry
listRenderTags <- function(l) {
lapply(
X = l,
function(x) {
if (inherits(x, c("shiny.tag", "shiny.tag.list"))) {
as.character(x)
} else if (inherits(x, "list")) {
# Recursive part
listRenderTags(x)
} else {
x
}
}
)
}

#' Indicates whether testthat is running
#'
#' @return Boolean.
Expand Down
88 changes: 65 additions & 23 deletions inst/examples/dropdown/app.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,79 @@ library(shiny)
library(shinyNextUI)
library(shiny.react)

items <- lapply(1:8, function(i) {
dropdown_item(
key = i,
description = sprintf("Description %s", i),
sprintf("Item %s", i)
items <- list(
# Dropdown section
dropdown_section(
showDivider = TRUE,
title = "Section 1",
# Dropdown Items
list(
dropdown_item(
title = "Item 1",
shortcut = "⌘N",
color = "danger",
description = "Item description",
startContent = icon("clock")
),
dropdown_item(
title = "Item 2",
shortcut = "⌘N",
color = "success",
description = "Item description",
startContent = icon("home")
)
)
),
dropdown_section(
showDivider = FALSE,
title = "Section 2",
# Dropdown Items
list(
dropdown_item(
title = "Item 3",
color = "warning",
description = "Item description"
),
dropdown_item(
title = "Item 4"
)
)
)
})
)

# You can also skip section
#items <- list(
# dropdown_item(
# title = "Item 1",
# shortcut = "⌘N",
# color = "danger",
# description = "Item description"#,
# #startContent = icon("clock")
# ),
# dropdown_item(
# title = "Item 2",
# shortcut = "⌘N",
# color = "success",
# description = "Item description"#,
# #startContent = icon("home")
# )
#)

color <- "success"

ui <- nextui_page(
debug_react = TRUE,
div(
class = "flex gap-2 my-2",
dropdown(
dropdown_trigger(
button(
"Trigger",
color = color
)
),
dropdow_menu(
inputId = "dropdown",
color = color,
disabledKeys = JS("['3', '4']"),
selectionMode = "multiple",
items,
dropdown_section(
dropdown_item(key = 9, "Item 9")
)
)
dropdow_menu(
inputId = "dropdown",
label = "Dropdown menu",
color = color,
selected = "new",
variant = "bordered",
disabledKeys = c(3, 4),
selectionMode = "multiple",
choices = items
)
),
verbatimTextOutput("dropdown_val")
Expand Down
4 changes: 2 additions & 2 deletions inst/nextui-2.0.0/nextui.js

Large diffs are not rendered by default.

80 changes: 80 additions & 0 deletions js/src/custom.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,83 @@ export const RadioGroup = () => {
export const CheckboxGroup = () => {
ReactR.reactShinyInput('.checkboxgroup', 'CheckboxGroup', GroupBuilder("Checkbox"));
}

const DropdownBuilder = () => {
return(
({ configuration, value, setValue }) => {
let Dropdown = NextUI.Dropdown;
let DropdownTrigger = NextUI.DropdownTrigger;
let Button = NextUI.Button;
let DropdownMenu = NextUI.DropdownMenu;
let DropdownItem = NextUI.DropdownItem;
let DropdownSection = NextUI.DropdownSection;

let choices = configuration.children;
let menu = choices.map(
(choice) => {
let props = choice.props;
if (choice.dropdownSection) {
let items = choice.children.map(
(child) => <DropdownItem
key={child.title}
{...child}
>
</DropdownItem>
)

return(
<DropdownSection
{...props}
>
{items}
</DropdownSection>
)
} else {
return(
<DropdownItem
key={choice.title}
{...choice}
>
</DropdownItem>
)
}
})

return(
<Dropdown
classNames={{
base: "before:bg-default-200", // change arrow background
content: "py-1 px-1 border border-default-200 bg-gradient-to-br from-white to-default-200 dark:from-default-50 dark:to-black",
}}
>
<DropdownTrigger>
<Button
variant={configuration.variant}
>
{configuration.label}
</Button>
</DropdownTrigger>
<DropdownMenu
{...configuration}
selectedKeys={value}
onSelectionChange={
(keys) => {
let vals = [];
keys.forEach(key => {
vals.push(key);
});
setValue(vals)
}
}
>
{menu}
</DropdownMenu>
</Dropdown>
);
}
)
}

export const DropdownMenu = () => {
ReactR.reactShinyInput('.dropdownmenu', 'DropdownMenu', DropdownBuilder());
}
Loading

0 comments on commit 85f7dc3

Please sign in to comment.