Closed Teolone88 closed 6 months ago
Hello,
It might be difficult to add Shiny input widget or sophisticated elements in the built-in pop-up, a solution can be to use Shiny's logic with insertUI() / removeUI()
and an absolutePanel()
, like that you can put whatever you want in the pop-up. Here's a full example:
# Calendar: custom popup --------------------------------------------------
library(shiny)
library(toastui)
## Schedules data
schedules <- cal_demo_data()
# add an id
schedules$id <- seq_len(nrow(schedules))
# add custom data in "raw" attribute
schedules$raw <- lapply(
X = seq_len(nrow(schedules)),
FUN = function(i) {
list(
status = sample(c("Completed", "In progress", "Closed"), 1)
)
}
)
## App
ui <- fluidPage(
fluidRow(
column(
width = 8, offset = 2,
tags$h2("Custom popup made with Shiny"),
calendarOutput(outputId = "cal"),
uiOutput("ui")
)
)
)
server <- function(input, output, session) {
output$cal <- renderCalendar({
calendar(view = "month", taskView = TRUE, useDetailPopup = FALSE) %>%
cal_props(cal_demo_props()) %>%
cal_schedules(schedules) %>%
cal_events(
clickSchedule = JS(
"function(event) {",
"Shiny.setInputValue('calendar_id_click', {id: event.schedule.id, x: event.event.clientX, y: event.event.clientY});",
"}"
)
)
})
observeEvent(input$calendar_id_click, {
removeUI(selector = "#custom_popup")
id <- as.numeric(input$calendar_id_click$id)
# Get the appropriate line clicked
sched <- schedules[schedules$id == id, ]
insertUI(
selector = "body",
ui = absolutePanel(
id = "custom_popup",
top = input$calendar_id_click$y,
left = input$calendar_id_click$x,
draggable = FALSE,
width = "300px",
tags$div(
style = "width: 250px; position: relative; background: #FFF; padding: 10px; box-shadow: 0px 0.2em 0.4em rgb(0, 0, 0, 0.8); border-radius: 5px;",
actionLink(
inputId = "close_calendar_panel",
label = NULL, icon = icon("close"),
style = "position: absolute; top: 5px; right: 5px;"
),
tags$br(),
tags$div(
style = "text-align: center;",
tags$p(
"Here you can put custom", tags$b("HTML"), "elements."
),
tags$p(
"You clicked on schedule", sched$id,
"starting from", sched$start,
"ending", sched$end
),
tags$b("Current status:"), tags$span(class = "label label-primary", sched$raw[[1]]$status),
radioButtons(
inputId = "status",
label = "New status:",
choices = c("Completed", "In progress", "Closed"),
selected = sched$raw[[1]]$status
)
)
)
)
)
})
observeEvent(input$close_calendar_panel, {
removeUI(selector = "#custom_popup")
})
rv <- reactiveValues(id = NULL, status = NULL)
observeEvent(input$status, {
rv$id <- input$calendar_id_click$id
rv$status <- input$status
})
output$ui <- renderUI({
tags$div(
"Schedule", tags$b(rv$id), "has been updated with status", tags$b(rv$status)
)
})
}
shinyApp(ui, server)
let me know if you have any questions about the above code
Victor
Really smooth!!! Thank you so much Victor. I was not aware of the absolutePanel
function.
You should commit this option as it makes sense to customize your pop up going forward.
Just came from the documentation to your Github repo to see whether one can select a date on the monthly calendar and get a menu to enter data for that specific date (or a date range starting from that date). From what I read here, this seems possible, correct? It would be amazing if you would include this as a feature!
@januz yes you can interactively add schedules in a calendar, but it will only work in Shiny since you need a backend to save data.
This work well when the schedule is already created. However, the event is not triggered when creating a new schedule by clicking on some other part of the calendar.
For example, "clickSchedule" is triggered when clicking on the schedules with blue background but not when defining a new period (see period defined on Wednesday in the above image).
I looked at "beforeCreateEvent" which seems to be the relevant event but I am not sure how to redefine it (I am JS/htmlwidget newbie).
Accessorily, let me state some reasons why I want to do that in case I should use a different approach:
1) I want to be able to control id of the newly created schedule (I already have unique ids defined in other part of my application). 2) I already have title, body and calendardId info in my app when creating a new schedule. I am using the calendar to define the start and end times when creating a schedule, and to visualize/modify time period of existing schedules. I could use a date/time picker but I like better the calendar visualization which shows empty time slots.
UPDATE Looking at your example I see that there are shiny events that I could use:
observeEvent(input$cal_add, {
new <- input$cal_add
new$id <-patient_id # new id
new$title <- "X,Y"
new$body <- paste0("patient id:", patient_id)
new$calendardId <- 2
cal_proxy_add("cal", new)
})
observeEvent(input$cal_update, {
cal_proxy_update("cal", input$cal_update)
})
observeEvent(input$cal_delete, {
cal_proxy_delete("cal", input$cal_delete)
})
I am still exploring it (are these events documented? why use cal_proxy_xxxx?)
Hello @gavril0 , Can you open a new issue with a reproducible example ? It's possible that the documentation is incomplete, it's a work in progress
I looked at "beforeCreateEvent" which seems to be the relevant event but I am not sure how to redefine it (I am JS/htmlwidget newbie).
@gavril0 did you figure this out? I would also like to create a custom pop-up for new events, but I don't see any examples of how to do this (calendar-popup-shiny.R just works for existing events... after applying the bugfix for v0.3).
UPDATE Looking at your example I see that there are shiny events that I could use:
@gavril0 at least for me, input$cal_update
is not created, which seems to be due to using a custom-popup. Did you find a way around this, such as "manually" creating the value
in cal_proxy_update(proxy, value)
?
@nick-youngblut I ended up not using pop-up. However, I have to go back to the issue because I need to update my code to the new version of calendar
Thanks @gavril0 for letting me know!
@pvictor any suggestions on how to create a custom pop-up for new events, and also for updating the toastui calendar with a custom pop-up (see above)?
Hello @nick-youngblut, can you provide an exemple to reproduce the issue ?
@pvictor I have 2 issues:
input$cal_update
object is not created, so I cannot simply use cal_proxy_update(proxy, input$cal_update)
as in this example.I need a custom pop-up for both creating new events and updating existing events, because I need to include 2 drop-downs in the pop-up that the user MUST select when creating an event. This is why I cannot simply use Google Calendar, since one cannot add mandatory drop-downs for creating events.
If you could point me to any documentation/examples, or provide some here, I'd appreciate it.
I'm using toastui v0.3.
For the first issue, you can try this example : https://github.com/dreamRs/toastui/blob/master/inst/examples/calendar-custom-create.R, note that you need last GitHub version where I added support for selectdatetime event.
For the second one,, you should be able to do something like this with your own infos about the schedule to update :
library(shiny)
library(toastui)
library(lubridate)
ui <- fluidPage(
tags$h2("Update schedule"),
actionButton("update", "Update"),
calendarOutput(outputId = "cal")
)
server <- function(input, output, session) {
output$cal <- renderCalendar({
calendar() %>%
cal_schedules(
id = "MY_CUSTOM_SCHEDULE_ID",
calendarId = "MY_CUSTOM_CALENDAR_ID",
title = "Title",
body = "Some description",
start = Sys.Date(),
end = Sys.Date(),
category = "allday"
)
})
observeEvent(input$update, {
date <- Sys.Date() + sample(-10:10, 1)
cal_proxy_update(
"cal",
list(
id = "MY_CUSTOM_SCHEDULE_ID",
calendarId = "MY_CUSTOM_CALENDAR_ID",
title = sample(ls(pos = "package:base"), 1),
start = date,
end = date
)
)
})
}
shinyApp(ui, server)
Thanks @pvictor for the detailed response!
I haven't tried the 1st issue solution yet, but the for 2nd, cal_proxy_update()
does nothing. I know that it is triggered, but the calendar event is not updated.
What is actually needed for the value
parameter in cal_proxy_update()
? When I run the ex-proxy-schedule.R example, and print input$my_calendar_update
after editing an event, I get:
$schedule
$schedule$id
[1] "shd_ec425d5b"
$schedule$title
[1] "test"
$schedule$location
[1] "test"
$schedule$start
[1] "2024-01-30T00:00:00-08:00"
$schedule$end
[1] "2024-01-30T00:00:00-08:00"
$schedule$isAllday
[1] FALSE
$schedule$category
[1] "time"
$schedule$calendarId
[1] "clnd_00c2cdbf"
$changes
$changes$title
[1] "test1"
Based on the cal_proxy_update code, it appears that I need to provide:
value$changes
& value$schedule
value$schedule$calendarId
value$schedule$id
value$changes
I've tried this:
$schedule$id
[1] "dxsr26fhl7xkk8m2cqfo121958"
$schedule$calendarId
[1] "c_189f0a7x8kle8genl785cu3rixqke@resource.calendar.google.com"
$changes
$changes$title
[1] "Nick Youngblut"
$changes$start
[1] "2024-01-30 09:55:00 PST"
$changes$end
[1] "2024-01-30 10:50:00 PST"
However, this input to cal_proxy_update
doesn't seem to do anything. Where does the $schedule$id
value come from?
UPDATE:
I see that I have to use input$calendar_id_click$id
as the ID. The start and end times now update. However, to render the custom pop-up for editing events, I'm using the data.frame of events used for rendering the calendar. This data.frame is not updated by cal_proxy_update
, so the pop-up info is not updated.
I need to get the updated event info after cal_proxy_update
is run, but I don't see how to pull the event info. The cal_*
functions seems to focus on adding data/params to the calendar and not retrieving (updated) info from the calendar.
I was hoping that input$my_calendar_schedules
(in my case input$cal_sechedules
) would provide the updated schedule information for all events (schedules), but it seems to only provide a list for the most recent event (schedule) that is on the calendar (e.g., the event for today and not the other events from previous days).
cal_proxy_update() does nothing
the example I provided doesn't work for you?
cal_proxy_update()
update an existing schedule using its id
and calendarId
to identify it, if you do not provide id
/ calendarId
when creating a schedule, they will be generated automatically.
input$<outputId>_update
is an event sent when using builtin popup, if you do not use buitlin popup this event isn't triggered.
I need to get the updated event info after cal_proxy_update
You already have that since you called cal_proxy_update()
with it, calendar here is just for display
You have to update yourself your starting data.frame with the infos you collect from your custom popup, the calendar cannot do your backend.
the example I provided doesn't work for you?
A challenge for me in creating a custom pop-up allowing for schedule editing was that I not only had to figure out what data structure to provide as the value
parameter to cal_proxy_update()
, but I also had to edit the reactive data.frame object that I was using to fill the values in the custom pop-up. Once I figured out the list
structure for the value
parameter of cal_proxy_update()
(thanks for your help!), the schedule in the calendar would update (e.g., the schedule start time), but when I clicked on the pop-up again, the values in the pop-up (e.g., time or title) were not changing. I had to also update the reactive data.frame object in addition to running cal_proxy_update()
so that when the custom pop-up was rendered, it used the updated values instead of the original schedule values.
The pop-up for schedule editing seems to be correctly working fully. Thanks for all of your help.
I was also able to create a custom pop-up for creating new events (after updating the package to the latest version on github). Will you be releasing a new version on CRAN soon, or are you waiting on some more updates first?
Thanks! 🙏
@pvictor in the calendar-custom-create.R example, you use showModal(modalDialog())
to show a model instead of a pop-up at the location on the calendar where the user clicked. One can use input$calendar_id_click$y
and input$calendar_id_click$x
for a custom pop-up that edits an existing schedule, but input$calendar_id_click$y
and input$calendar_id_click$x
do not seem to be available for a custom pop-up for creating a new event, as described in calendar-custom-create.R.
Is there a way to create a custom pop-up for creating an event at the location on the calendar (e.g., day) were the user clicks?
Is there a way to create a custom pop-up for creating an event at the location on the calendar (e.g., day) were the user clicks?
It's certainly possible, but I find it hard to see the added value compared to the complexity it would require to implement. The modal solution seems simple, robust and immediately available.
The modal solution seems simple, robust and immediately available.
After trying out the modal solution, I agree... especially given that one can obtain the date/time info from where the user clicks on the calendar to fill in date/time info in the modal.
@pvictor in the calendar-custom-create.R example, you use
showModal(modalDialog())
to show a model instead of a pop-up at the location on the calendar where the user clicked. One can useinput$calendar_id_click$y
andinput$calendar_id_click$x
for a custom pop-up that edits an existing schedule, butinput$calendar_id_click$y
andinput$calendar_id_click$x
do not seem to be available for a custom pop-up for creating a new event, as described in calendar-custom-create.R.Is there a way to create a custom pop-up for creating an event at the location on the calendar (e.g., day) were the user clicks?
How can I add namespaces to javascript in calendar-custom-create.R, if you know, thank you very much!
@pvictor after I add a new event, the event cannot be selected via my custom pop-up code for editing (no value for the new event in input$calendar_id_click$id
).
I'm using cal_proxy_add()
to add the new event, but it doesn't seem to create a new input$calendar_id_click$id
value for the event. However, if I reload my app (and thus re-render the calendar), my newly added event is listed in input$calendar_id_click$id
, so then it can be updated via my custom pop-up.
Any ideas on why input$calendar_id_click$id
is not updated after creating a new event via a custom modal (as we discussed above)?
Note: it appears that calendar-custom-create.R also does not update input$calendar_id_click$id
, given that if I add observeEvent(input$calendar_id_click, { print(input$calendar_id_click$id) })
to the app code, no click events occur when I click on events after creating them via the modal.
@nick-youngblut , @toxintoxin : can you please open new issues with some reproducible example ?
Hi,
Is there any way to customize the
calendar()
popup with different user inputs whenisReadOnly = FALSE
?Should I change the inputs from https://github.com/dreamRs/toastui/blob/master/inst/htmlwidgets/calendar.js under
cal.createSchedules([{}])
&Shiny.setInputValue({})
Please forgive my n000biness in Js.
Best, Teo