The timevis
package is an htmlwidget that lets you create timeline visualizations. This package includes a very basic implementation of the timevis
package and is used as an educational tool for teaching advanced htmlwidgets tips.
>> Click here to read the full tutorial <<
The code in this package is used as the starting point for the tutorial above. Every tip in the tutorial will walk you through adding a useful feature to the package, and you can see the final result of each step as a separate branch in this repo.
The master
branch of this repo has the most basic htmlwidget possible, and there are 12 other branches, corresponding to the 12 steps in the tutorial. Each branch has the complete code from its corresponding step, so that you can see it in a real working app to be convinced that what I wrote in the tutorial really does work.
Since each tip in the tutorial results in a more and more feature-heavy widget, I will give you an example code usage for every step. Don't forget to include library(shiny)
to make all the sample codes work.
Using the code in the master branch, the following simple app should give you a minimal timeline widget.
data <- list(list(content = "today", start = Sys.Date()),
list(content = "tomorrow", start = Sys.Date() + 1))
shinyApp(
ui = timevisOutput("timeline"),
server = function(input, output) {
output$timeline <- renderTimevis(
timevis(data)
)
}
)
See the code changes for tip 1
Tip 1 was a very small change so the previous code will still work the same way.
See the code changes for tip 2
The previous code will still work, but now you should be able to see zoom in and zoom out buttons (though we didn't make those buttons functional).
See the code changes for tip 3
We added dataframeToD3()
function, so now our data frame can be simplified, but the app code remains the same.
data <- data.frame(content = c("today", "tomorrow"),
start = c(Sys.Date(), Sys.Date() + 1))
shinyApp(
ui = timevisOutput("timeline"),
server = function(input, output) {
output$timeline <- renderTimevis(
timevis(data)
)
}
)
See the code changes for tip 4
In this tip, we made sure the widget only renders the latest data, rather than all the previous data as well. Run the following shiny app now, and try running it prior to tip 4 to see the difference.
shinyApp(
ui = fluidPage(
textInput("text", "Text", "Hello"),
timevisOutput("timeline")
),
server = function(input, output) {
output$timeline <- renderTimevis({
data <- data.frame(content = input$text, start = Sys.Date())
timevis(data)
})
}
)
See the code changes for tip 5
Run the same code as before, and you'll see that the initialization message runs only once.
See the code changes for tip 6
If you run the same Shiny app code as before, you'll get a message telling you that you're inside a Shiny app. If you simply run timevis()
in the console, the widget will render, but you will not be told you're in Shiny.
See the code changes for tip 7a
You can now select events in the timeline (by clicking on them) and the event ID (which just looks like random text) will be passed back to R.
data <- data.frame(content = c("today", "tomorrow"),
start = c(Sys.Date(), Sys.Date() + 1))
shinyApp(
ui = fluidPage(
timevisOutput("timeline"),
textOutput("selected")
),
server = function(input, output) {
output$timeline <- renderTimevis({
timevis(data)
})
output$selected <- renderText({
input$timeline_selected
})
}
)
See the code changes for tip 7b
The data from the timeline can now be passed back into R whenever it changes (you can double click anywhere to create a new item, or click on an item to delete it).
data <- data.frame(content = c("today", "tomorrow"),
start = c(Sys.Date(), Sys.Date() + 1))
shinyApp(
ui = fluidPage(
timevisOutput("timeline"),
tableOutput("data")
),
server = function(input, output) {
output$timeline <- renderTimevis({
timevis(data)
})
output$data <- renderTable({
input$timeline_data
})
}
)
See the code changes for tip 8a
We just implemented a simple API function setWindow()
, which we can call in a Shiny app.
shinyApp(
ui = fluidPage(
timevisOutput("timeline"), br(),
actionButton("btn", "Set window between yesterday and tomorrow")
),
server = function(input, output) {
output$timeline <- renderTimevis({
timevis()
})
observeEvent(input$btn, {
setWindow("timeline", start = Sys.Date() - 1, end = Sys.Date() + 1)
})
}
)
See the code changes for tip 8b
Nothing user-facing has changed, we just abstracted a lot of the API code. The previous app code should still work.
See the code changes for tip 8c
We can now chain API functions with %>%
to make it easy to call multiple functions consecutively.
shinyApp(
ui = fluidPage(
timevisOutput("timeline"), br(),
actionButton("btn", "Add a blue line at midnight last night and set window between yesterday and tomorrow")
),
server = function(input, output) {
output$timeline <- renderTimevis({
timevis()
})
observeEvent(input$btn, {
setWindow("timeline", start = Sys.Date() - 1, end = Sys.Date() + 1) %>%
addCustomTime(Sys.Date())
})
}
)
See the code changes for tip 8d
You can now call API functions directly on an htmlwidget rather than using an ID, and they are also chain-able. This means that API functions can even be called on timeline widgets that are rendered outside of Shiny.
timevis() %>%
setWindow(start = Sys.Date() - 1, end = Sys.Date() + 1) %>%
addCustomTime(Sys.Date())
After implementing all these tips, the app is a lot more useful. Hopefully you can now translate this knowledge into your own htmlwidget!