-
Notifications
You must be signed in to change notification settings - Fork 64
feat: Add input_submit_textarea() input element
#1204
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
693b87c
34cf8ee
75d73fb
3f98b68
4f2ab2b
924d98d
1d69765
27137d2
dcf5a10
3da38ac
7d88ff7
8afa5db
8ee990f
981dd96
b6294cd
cc43168
d7036a1
57a41f8
aa5cb10
2a6995f
168c332
9bfa28a
9cd0726
f1c06ea
53eb8f8
4f4cc18
ddf78a2
141e27f
bf27042
4c889ea
da98f13
e4443ff
7155c53
bac1e68
0351b64
2511d40
cfe0a04
6bdf0d2
7529734
ef1e0f7
c46e452
18c9476
b93ea6a
cbec5c3
106f5c3
1e8a61a
86eec98
b47b227
703d6ee
6cedfb5
2dc98ef
e455d82
23242a2
61af9f9
5810cba
c66c6b6
8544f5f
a21777c
cdf4d8d
331aaba
ff02c93
e5a41e4
e9ef2eb
674eb85
138b83d
1640259
0013d15
d1d1c0d
b7358fe
7ccf8e8
bff5c54
71bca10
012ac22
f9692f8
29375f3
74479dc
e3080a7
e2553ea
104b788
a340934
f02252a
693cf19
f663f60
23cb0e5
c35e47a
96ab310
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,225 @@ | ||
| #' Suspend input changes until a button is clicked | ||
| #' | ||
| #' Suspend changes to a particular set of input controls until a submit button | ||
| #' is clicked. This is particularly useful for allowing the user to review their | ||
| #' input(s) before sending them to the server for a potentially expensive | ||
| #' operation. Note that, by default, all inputs that are children of the | ||
| #' button's parent are deferred until the button is clicked. This can be changed | ||
| #' by setting the `scope` argument to a CSS selector that matches the container | ||
| #' of inputs you wish to suspend. | ||
| #' | ||
| #' @param id The input ID. | ||
| #' @param label A label to place on the button. | ||
| #' @param ... Arguments passed along to [input_task_button()]. | ||
| #' @param scope The scope of the submit button. Can be one of the following: | ||
| #' - `NULL`: Inputs that are children of the button's parent are | ||
| #' deferred until the button is clicked. | ||
| #' - A CSS selector: Only inputs that are within the element matching the | ||
| #' selector are deferred until the button is clicked. | ||
| #' | ||
| #' @seealso [input_submit_textarea()], [input_task_button()] | ||
| #' @export | ||
| input_submit_button <- function(id, label, ..., scope = NULL) { | ||
| if (!is_installed("shiny", "1.10.0.9001")) { | ||
| rlang::abort("shiny v1.10.0.9001 or higher is required") | ||
| } | ||
|
|
||
| btn <- input_task_button(id, label, ...) | ||
|
|
||
| # Change type from "button" to "submit" | ||
| btn$attribs$type <- "submit" | ||
|
|
||
| tagAppendAttributes( | ||
| btn, | ||
| class = "bslib-submit-button", | ||
| `data-submit-scope` = scope | ||
| ) | ||
| } | ||
|
|
||
|
|
||
| # TODO: maybe update_task_button() should gain label/icon arguments | ||
| # and then we can just call that here? Or just tell people to use | ||
| # update_task_button() directly? | ||
|
|
||
| ## @param id The input ID. | ||
| ## @param ... Currently ignored. | ||
| ## @param label The label of the button. | ||
| ## @param icon An optional icon to display next to the label while the button | ||
| ## is in ready state. See [fontawesome::fa_i()]. | ||
| ## @param session The `session` object; using the default is recommended. | ||
| ## @rdname input_submit_button | ||
| ## @export | ||
| #update_submit_button <- function( | ||
| # id, | ||
| # ..., | ||
| # label = NULL, | ||
| # icon = NULL, | ||
| # session = get_current_session() | ||
| #) { | ||
| # | ||
| #} | ||
|
|
||
| #' Create a textarea input control with explicit submission | ||
| #' | ||
| #' Creates a textarea input where users can enter multi-line text and submit | ||
| #' their input using a dedicated button or keyboard shortcut. This control is | ||
| #' ideal when you want to capture finalized input, rather than reacting to every | ||
| #' keystroke, making it useful for chat boxes, comments, or other scenarios | ||
| #' where users may compose and review their text before submitting. | ||
| #' | ||
| #' @param id The input ID. | ||
| #' @param placeholder A character string giving the user a hint as to what can | ||
| #' be entered into the control. | ||
| #' @param value The initial input text. Note that, unlike [textAreaInput()], | ||
| #' this won't set a server-side value until the value is submitted. | ||
| #' @param button A [tags] element to use for the submit button. It's recommended | ||
cpsievert marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| #' that this be a [input_task_button()] since it will automatically provide a | ||
| #' busy indicator (and disable) until the next flush occurs. Note also that if | ||
| #' the submit button launches a [ExtendedTask], this button can also be bound | ||
cpsievert marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| #' to the task ([bind_task_button()]) and/or manually updated for more | ||
| #' accurate progress reporting ([update_task_button()]). | ||
| #' @param submit_key A character string indicating what keyboard event should | ||
| #' trigger the submit button. The default is `enter`, which will submit the | ||
| #' input when the user presses the Enter/Return key. The `enter+modifier` | ||
| #' option will submit the input when the user presses the Enter key while | ||
| #' holding down Ctrl/Cmd. | ||
| #' | ||
| #' @return A textarea input control that can be added to a UI definition. | ||
| #' | ||
| #' @seealso [input_submit_button()], [input_task_button()] | ||
| #' | ||
| #' @examplesIf rlang::is_interactive() | ||
| #' | ||
| #' ui <- page_fluid( | ||
| #' input_submit_textarea("text", "Enter some input..."), | ||
| #' verbatimTextOutput("value") | ||
| #' ) | ||
| #' server <- function(input, output) { | ||
| #' output$value <- renderText({ | ||
| #' req(input$text) | ||
| #' Sys.sleep(2) | ||
| #' paste("You entered:", input$text) | ||
| #' }) | ||
| #' } | ||
| #' shinyApp(ui, server) | ||
| #' | ||
| #' @section Server value: | ||
| #' A character string of the text input. The default value is `""` even if | ||
| #' `value` is provided. The value will only be set/updated when the user submits | ||
| #' the input by pressing the Enter key or clicking the submit button. | ||
cpsievert marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| #' | ||
| #' @export | ||
| input_submit_textarea <- function( | ||
gadenbuie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| id, | ||
| placeholder, | ||
| value = "", | ||
| ..., | ||
| button = NULL, | ||
| label = NULL, | ||
| width = "min(600px, 100%)", | ||
| submit_key = c("enter", "enter+modifier") | ||
cpsievert marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
gadenbuie marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ) { | ||
| if (!is_installed("shiny", "1.10.0.9001")) { | ||
| rlang::abort("shiny v1.10.0.9001 or higher is required") | ||
| } | ||
|
|
||
| rlang::check_dots_empty() | ||
|
||
|
|
||
| value <- shiny::restoreInput(id = id, default = value) | ||
| if (length(value) != 1 || !is.character(value)) { | ||
| stop("`value` must be a character string", call. = FALSE) | ||
| } | ||
|
|
||
| submit_key <- rlang::arg_match(submit_key) | ||
| needs_modifier <- isTRUE(submit_key == "enter+modifier") | ||
|
|
||
| if (is.null(button)) { | ||
| if (needs_modifier) { | ||
| btn_label <- "Submit ⌘ ⏎" | ||
| btn_title <- "Press ⌘ + Enter to Submit" | ||
| } else { | ||
| btn_label <- "Submit ⏎" | ||
| btn_title <- "Press Enter to Submit" | ||
| } | ||
|
|
||
| button <- input_task_button( | ||
cpsievert marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| id = paste0(id, "_submit"), | ||
| class = "btn-sm", | ||
| label = btn_label, | ||
| title = btn_title, | ||
| `aria-label` = btn_title | ||
| ) | ||
| } | ||
|
|
||
| if (!is_button_tag(button)) { | ||
| stop("`button` must be a `tags$button()`", call. = FALSE) | ||
| } | ||
|
|
||
| div( | ||
| class = "shiny-input-container bslib-mb-spacing", | ||
| style = css( | ||
| # TODO: validateCssUnit() needs to handle more complex CSS | ||
| width = if (is.numeric(width)) paste0(width, "px") else width, | ||
| ), | ||
| shiny_input_label(id, label), | ||
| div( | ||
| class = "bslib-input-textsubmit", | ||
| tags$textarea( | ||
| id = id, | ||
| class = "textarea-autoresize form-control", | ||
| style = css(width = if (!is.null(width)) "100%"), | ||
| placeholder = placeholder, | ||
| `data-needs-modifier` = if (needs_modifier) "", | ||
| rows = 1, | ||
| value | ||
| ), | ||
| button | ||
| ) | ||
| ) | ||
| } | ||
|
|
||
| is_button_tag <- function(x) { | ||
| if (!inherits(x, "shiny.tag")) { | ||
| return(FALSE) | ||
| } | ||
|
|
||
| isTRUE(x$name == "button") || | ||
| isTRUE(x$attribs$type == "button") | ||
| } | ||
|
|
||
| #' @param value The value to set the user input to. | ||
| #' @param placeholder The placeholder text for the user input. | ||
| #' @param submit Whether to automatically submit the text for the user. Requires `value`. | ||
| #' @param focus Whether to move focus to the input element. Requires `value`. | ||
| #' | ||
| #' @rdname input_submit_textarea | ||
| #' @export | ||
| update_submit_textarea <- function( | ||
| id, | ||
| ..., | ||
| value = NULL, | ||
| placeholder = NULL, | ||
| label = NULL, | ||
| submit = FALSE, | ||
| focus = FALSE, | ||
| session = get_current_session() | ||
| ) { | ||
| rlang::check_dots_empty() | ||
|
|
||
| if (is.null(value) && (submit || focus)) { | ||
| stop( | ||
| "An input `value` must be provided when `submit` or `focus` are `TRUE`.", | ||
| call. = FALSE | ||
| ) | ||
| } | ||
|
|
||
| message <- dropNulls(list( | ||
| value = value, | ||
| placeholder = placeholder, | ||
| label = label, | ||
| submit = submit, | ||
| focus = focus | ||
| )) | ||
|
|
||
| session$sendInputMessage(id, message) | ||
| } | ||
Large diffs are not rendered by default.

Uh oh!
There was an error while loading. Please reload this page.