ShinySky -- A collection of Shiny UI components/widgets!!

4,532 views
Skip to first unread message

ZJ

unread,
Nov 27, 2013, 12:17:28 PM11/27/13
to shiny-...@googlegroups.com
Introducing ShinySky -- A collection of Shiny UI components/widgets that are not a part of standard Shiny.

Currently there is only styled buttons and alerts in ShinkySky, but the aim is to slowly add to it so that it becomes at "feature-parity" with Bootstrap, i.e. using standard R idiom, functions, one can completely define everything there is in Bootstrap.

The intention is also to collate together the various components being customised by the good people in this forum and condense it into this one package for all to use!

Get started with the example

install.packages("devtools") # if not already installed
devtools::install_github("ShinySky","AnalytixWare")
#require(shinysky)
shinysky::run.shinysky.example()

Welcomes contribution on Github



Stéphane Laurent

unread,
Nov 27, 2013, 12:47:20 PM11/27/13
to shiny-...@googlegroups.com
Wow ! Thank you very much !

Vincent Nijs

unread,
Nov 27, 2013, 2:42:04 PM11/27/13
to shiny-...@googlegroups.com
Great idea ZJ. I have a couple of bindings, several that you helped me with, that might be useful to others. For example, the drag-and-drop binding (link below). What would you need from me to add this?

ZJ

unread,
Nov 27, 2013, 9:00:57 PM11/27/13
to shiny-...@googlegroups.com
GitHub

I guess the most ideal way is if you can fork my Github repository write your new component in your branch of the code and then do a pull request.

If you are unfamiliar with GitHub then this is the great place to get started https://help.github.com/set-up-git-redirect

Another way to contribute is to simply add an enhancement request by pressing the "New Issue" button on https://github.com/AnalytixWare/ShinySky/issues. You can paste the link to the component in there so that it's implementation will be on the radar.

Build package

Once you have fork the repository I recommend using RStudio IDE for building the packages as the build feature is built into RStudio.


Simple Rules

The code in there are still quite simple so it shouldn't be too hard to learn from the code examples. But following these rules will be beneficial (http://rstudio.github.io/shiny/tutorial/#building-inputs)

Shiny input components should try to adhere to the following principles, if possible:

  • Designed to be used from HTML and R: Shiny user interfaces can either be written using R code (that generates HTML), or by writing the HTML directly. A well-designed Shiny input component will take both styles into account: offer an R function for creating the component, but also have thoughtfully designed and documented HTML markup.
  • Configurable using HTML attributes: Avoid requiring the user to make JavaScript calls to configure the component. Instead, it’s better to use HTML attributes. In your component’s JavaScript logic, you can easily access these values using jQuery (or simply by reading the DOM attribute directly).

Jin Rou New

unread,
Nov 27, 2013, 9:48:51 PM11/27/13
to shiny-...@googlegroups.com
Wow it's up! Very very nice! :)

ZJ

unread,
Nov 28, 2013, 12:02:39 PM11/28/13
to shiny-...@googlegroups.com

Updated with more options: ?shinkysky::actionButtion


ZJ

unread,
Nov 29, 2013, 10:37:50 AM11/29/13
to shiny-...@googlegroups.com

Updated ShinySky with eventsButtons! You can style them just as you can actionButons. But they will be invalidated upon certain events (these can be configured by the user). The default is double-click, so the button won't "respond" to clicks but only double-clicks. 
In the example ( I made the input source change upon mouseenter and double click. This is built on top of the eventInput Shiny binding which will open other possibilities in the future when the input component can become invalidated based on event types!

ZJ

unread,
Dec 12, 2013, 1:43:32 PM12/12/13
to shiny-...@googlegroups.com
Typeahead Text Input

Just updated ShinySky with the typeahead textinput. Hope you like it!



Install and Run Example
install.packages("devtools")#if not alrady installed
devtools::install_github("ShinySky","AnalytixWare")
require(shinysky)
shinysky::run.shinysky.example()

ZJ

unread,
Dec 27, 2013, 12:29:20 AM12/27/13
to shiny-...@googlegroups.com

Updated ShinySky with select2Input, at present it's arguments are completely aligned with selectInput.

Code
install.packages("devtools") # if not already installed
devtools::install_github("AnalytixWare/ShinySky")
#require(shinysky)
shinysky::run.shinysky.example()


The code was adapted from David P's post
https://groups.google.com/forum/#!searchin/shiny-discuss/select2/shiny-discuss/nPvSyyydfrI/ia7oet30ZiQJ

David Ardestani

unread,
Dec 27, 2013, 10:25:34 AM12/27/13
to shiny-...@googlegroups.com
I love this select2Input feature!  I find this useful for the user o have control over the order of the selections.  It would be nice if the user had the flexibility to swap the order of the selections, so in the example, they can easily swap the "a" and "c" selection (I believe similar to http://www.w3schools.com/html/html5_draganddrop.asp)

David Ardestani

unread,
Dec 27, 2013, 12:19:23 PM12/27/13
to shiny-...@googlegroups.com
Is there also going to be an updateSelect2Input function?

David Ardestani

unread,
Dec 27, 2013, 12:32:39 PM12/27/13
to shiny-...@googlegroups.com
Sorry for all these messages.  When I initialize my select2Input object with pre-selected values, the values get sorted in alphabetical order whereas they should remain in the order in which they were specified.  Also, once an i'th value has been selected by the user, he or she cannot modify that value unless they remove it.  This could cause problems for my order sensitive application.

ZJ

unread,
Dec 27, 2013, 12:59:27 PM12/27/13
to shiny-...@googlegroups.com
I see. You can put an issue here https://github.com/AnalytixWare/ShinySky/issues

I will see what can be done.

ZJ

unread,
Dec 27, 2013, 1:39:42 PM12/27/13
to shiny-...@googlegroups.com
check it again

Code
install.packages("devtools") # if not already installed
devtools::install_github("AnalytixWare/ShinySky")
#require(shinysky)
shinysky::run.shinysky.example()

David Ardestani

unread,
Dec 27, 2013, 1:50:30 PM12/27/13
to shiny-...@googlegroups.com
It has an error "ERROR: object 'multiple' not found" when I try running the example.

ZJ

unread,
Jan 2, 2014, 12:00:23 PM1/2/14
to shiny-...@googlegroups.com
ShinySky select2Input Demo
http://youtu.be/9T4F-j76Vf0

Also updated select2Input with updateSelect2Input

David Ardestani

unread,
Jan 2, 2014, 1:21:25 PM1/2/14
to shiny-...@googlegroups.com
The arrange feature is pretty neat.  Thanks so much!

Brent Crossman

unread,
Mar 4, 2014, 2:21:46 PM3/4/14
to shiny-...@googlegroups.com
Is there a way to use the select2input to select multiple items from a list (and re-arrange them) but not have it remove the item when selected. i.e. if choices=c("a","b","c"), I could select A, then B,  then A again, then C, then A?

ZJ

unread,
Mar 5, 2014, 12:59:24 AM3/5/14
to shiny-...@googlegroups.com
doesn't look possible. But if the select2 javascript library(http://ivaynberg.github.io/select2/) allows and the browsers allows then shinysky can implement it. Usually you would select a second element using Ctrl + click, but this doesn't seem to work when I tried. Maybe you could request the select2 author to implement this first.

Brent Crossman

unread,
Mar 6, 2014, 2:15:22 PM3/6/14
to shiny-...@googlegroups.com
Ok, thanks. I'll check in on that.  One more question, is it possible to apply a CSS tag that will change the width of the select2input

ZJ

unread,
Mar 26, 2014, 1:04:45 PM3/26/14
to shiny-...@googlegroups.com
Looks a number of forum memebers is requesting some sort of grid that can act as an input.

The excellent shinyTable is being fixed but in the meantime if your needs are simple you can try out the hotable binding in ShinySky, it is based on the handsontable js component which is the same that shinyTable uses. 




On Thursday, November 28, 2013 1:17:28 AM UTC+8, ZJ wrote:

James Ferguson

unread,
Apr 16, 2014, 3:46:20 AM4/16/14
to shiny-...@googlegroups.com
Hi ZJ

I think I have found a ShinySky bug (but am new to shiny so please forgive if in error)... 

I create a simple tree structure 
> nav = list(
+     "Branch" = list("leaf1","leaf2"),
+     "Topleaf"
+ )

Building a jstree.obj causes loss of the named ul item "Branch"
> atree <- jstree.obj(nav)
> print(atree)
<ul>
  <ul>
    <li>leaf1</li>
    <li>leaf2</li>
  </ul>
  <li>Topleaf</li>
</ul> 


As a result when rendered by 
> print(jstree("test",atree))
<script>$(function() {$('#test').jstree()})</script>
<div id="test" class="ss-jstree">
  <ul>
    <ul>
      <li>leaf1</li>
      <li>leaf2</li>
    </ul>
    <li>Topleaf</li>
  </ul>
</div> 

if this is rendered in shiny the tree is incomplete as shown here

I have attached minimal UI.R and Server.R files

I would really appreciate a fix of this and hope this helps isolate the problem which I assumes lies in the following where the recursive lapply call returns an unnamed UL obect,


but have no idea how you would fix 

#' jstree.obj
#' 
#' Creates a jstree object to be passed to jstree for creating a tree view
#' 
#' @param id The id used to refer to the jstree
#'   
#' @export
jstree.obj <- function(tree.list) {
   if(is.list(tree.list)) {
    res <- lapply(tree.list,jstree.obj)
    return(tags$ul(res)) # 
   } else {
    return(tags$li(tree.list))
   }
}

Many thanks for your efforts

ui.R
server.R

James Ferguson

unread,
Apr 16, 2014, 5:43:30 AM4/16/14
to shiny-...@googlegroups.com
I think I have managed to fix this replacing your 

jstree.obj <- function(tree.list)

with

tree <- function(x) {  
  handle <- function(ind, theList) { 
    name<-names(theList)[[ind]]
    if(!is.null(name)){
      a<- tags$li(list(name, tree(theList[[ind]])) )
    } else{
      a <- tags$li(theList[[ind]])
    }
    return(a)
  }
  if(is.list(x)) {
    ind <- seq(1:length(x))
    res<- lapply(ind,handle,x)
    return(tags$ul(res))
  } else {
    x
  }  
}


So this is result given serevr and UI below



Server.R
require(shiny)
require(shinysky)
nav = list(
  "Branch"=list("twig"=list("leafOnTwigA","leafOnTwigB" ),"leafOnBranch"),
  "Topleaf"
)

tree <- function(x) {  
  handle <- function(ind, theList) { 
    name<-names(theList)[[ind]]
    if(!is.null(name)){
      a<- tags$li(list(name, tree(theList[[ind]])) )
    } else{
      a <- tags$li(theList[[ind]])
    }
    return(a)
  }
  if(is.list(x)) {
    ind <- seq(1:length(x))
    res<- lapply(ind,handle,x)
    return(tags$ul(res))
  } else {
    x
  }  
}


shinyServer(function(input, output,session) {
  # jstree
  observe({
    showshinyalert(session, "alert_mytree",
                   paste0("You selected these items in the tree: ",
                          paste0(input$mytree,collapse=", ")))
  })
  
  output$mod <- renderUI(jstree("mytree",tree(nav)))
})

with UI.R


# This is the user-interface definition of a Shiny web application.
# You can find out more about building applications with Shiny here:
#
#

library(shiny)
library(shinysky)

shinyUI(fluidPage(
  # Application title
  titlePanel("ShinySky Problem"),
  # Sidebar with a slider input for number of bins
  sidebarLayout(
    sidebarPanel(
      div(class="well container-fluid", 
          div(class = "row-fluid", h4("Select")),
          div(class="row-fluid", 
              div(class="span2",
                  htmlOutput("mod")
              )
          )
      )
    ),
    
    # Show a plot of the generated distribution
    mainPanel(
      div(class="span10", shinyalert("alert_mytree"))
    )
  )
))



ZJ

unread,
Apr 16, 2014, 11:01:33 AM4/16/14
to shiny-...@googlegroups.com
Beautiful!! Thanks James I've copied your code and put it directly back into ShinySky. Except I have renamed tree to jstree.obj as other packages might already have a tree function.

James Ferguson

unread,
Apr 16, 2014, 4:06:24 PM4/16/14
to shiny-...@googlegroups.com
Hi ZJ

Thanks -  its my first ever  open source contribution - (so I'm glad you liked it :)

 - I am not sure how one submits patches officially (should I have modified a clone of your code) and where would I submit / push it to ? 

I am sure there are some guidelines somewhere - Any pointers gratefully received.

I am thinking I may need features to  eg return the path from root to the selected branch, show siblings, children etc and these are generically useful 
as would a means to display a label and Id field separately  (like selectInput)

 so if/ when I get the chance - would you be interested in these ?

Nicholas Crookston

unread,
Apr 16, 2014, 5:50:17 PM4/16/14
to shiny-...@googlegroups.com
James and ZJ:

First, thanks to both of you for your contributions. 

Please, consider the following:

th2 = HTML('<ul>
  <li id="aa">a</li>
  <li class="jstree-open"  id="tb">b<ul>
      <li>b1</li>
      <li>b2</li>
    </ul>
  </li>
  <li>c</li>
</ul>')

When th2 is placed in a jstree, the node "b" is automatically "opened" due to the class=jstree-open designation. In addition, this node has an id and I think id's will be needed. 

This suggests that the "names" of list elements shouldn't be used to generate tags. What I'm thinking is that the list members need to be character vectors with attached attributes. The "label" would be the string itself. A list inside the list would be understood to be a set of nodes attached to the preceding list (see below for an alternative). The (optional) attributes could then be used by the html-builder functions (tags...)  to allow for customization. I'm thinking of having some keyword-driven attributes, like "open=TRUE" signaling the class="jstree-open" be added, "selected=TRUE", showing that this node be preselected, id="<string>" would add the ID, maybe css="<string>" and name="<string>" will also be needed. These will dependent on the requirements of the jstree API.  Using the idea above, I would see that the list would be formed something like this:
s1 = "a"
attributes(s1)=list(id="aa")
s2 = "b"
attributes(s2)=list(opened=TRUE)
s3 = "b1"
attributes(s3)=list(selected=TRUE,id="s3id")
s4 = "b2"
attributes(s2)=list(id="b2id")
s5 = "c"

tls = list(s1,s2,list(s3,s4),s5)

The tree would look this when created:

|- a
|- b
| |- b1     <<--selected
| |- b2
|- c

Another reasonable approach would have:
tls = list(s1,list(s2,list(s3,s4)),s5)  .... what would this mean, that s2 is a child of s1 with children s3 and s4, or 
is s2 at the same level of s1 with children s3 and s4. 

Comments?

Nick

ZJ

unread,
Apr 16, 2014, 10:16:07 PM4/16/14
to shiny-...@googlegroups.com
Hi James,

Thank you for contribution. Please check https://help.github.com/articles/fork-a-repo

Also I recommend downloanding the github client https://windows.github.com/ or https://mac.github.com/

To contribute to shinysky you can follow these rough steps

  1. you make your own fork of the repository and 
  2. then edit your code 
  3. when you have finished editing the code you can do a pull-request to request a merge of your code into the main base. 
  4. We can then discuss the code and whether any modification is required. 
  5. Merge once we all agree it's a nice change
It takes some effort to learn Github but I found it to be tremendously useful!

Alternatively you can make your own repository with just the jstree component and maintain it. ShinySky is a collection of odd bits at the moment, and I can't really commit to maintain the treeview as I am not using it in my products atm.

Thanks

Regards

Aurelien

unread,
Apr 18, 2014, 4:58:18 AM4/18/14
to shiny-...@googlegroups.com
Hello,

sorry if my english isn't correct, i'mnot native, i'm a french frog eater. First, i really would like to thank you for your amazing package ZJ and thank all the Shiny team for the Shiny package.

But i have a trouble, i've searched a lot on the web but i can't find the solution : / .

When i use hotable, i would like to make people being able to modify the table and then download it. I'ven't any trouble to modify but when i want to download, sometimes the input$hotable is empty sometimes it's good.

Here a part of my code, makeTable3 is the "reactive" i use to generate my table, i use input$goButton for user to say to the app "it's good you can generate the download file". 

  output$hotable1 <- renderHotable({
    table <- isolate({ makeTable3()})
    return(table)
  })
  
  output$hotabledl <- downloadHandler(
    filename = function() {paste('MPA_', Sys.Date(), '.csv', sep='')},
    content = function(file) {if (input$goButton != 0) write.table(hot.to.df(input$hotable1), file, sep=";",dec = ",", row.names =F) else return()})

Thank to read this message ;) 
  

James Ferguson

unread,
Apr 19, 2014, 2:38:55 AM4/19/14
to shiny-...@googlegroups.com
Hi Aurielen

I had a similar probelm which this fixed

# Create the hotable somehow - here a csv
output$vactable <- renderHotable( {    
    inFile <- input$file1
    if (is.null(inFile))
      return(NULL)
    read.csv(inFile$datapath, header=input$header, sep=input$sep, quote=input$quote)
  }, readOnly = FALSE ) 

# reactive component that keeps dataframe up to date 
vdata <- reactive({ hot.to.df(input$vactable)})

#  A comoenent that attcahes to a downloadUI component
  output$downloadVData <- downloadHandler(
      filename = function() {
        paste('filePrefix', Sys.Date(), '.csv', sep='')
      },
      content = function(con) {
        write.csv(vdata(),file= con)
      }
    )

# I dont know why having the reactive version  of the table outside the download handler works (but docs suggest you can do it this way and it works for me

Good Luck !

Aurelien

unread,
Apr 22, 2014, 4:16:18 AM4/22/14
to shiny-...@googlegroups.com
Hey,

James thanks for your help, it partally works because the user have to double click on a cell of the table to generate the input, otherwise the input is null : / . 

So if i put my hotable in read only then people can't double click on it and so download always stay empty : / . I would like to be sure my download is always ready : S .

Thank to read this message ; )

ZJ

unread,
Apr 22, 2014, 1:04:14 PM4/22/14
to shiny-...@googlegroups.com
Hi Aurelien,

I fixed the issue. Just install the latest ShinySky package. Now the hotable would not return null on input even if it has not been updated. It was my oversight.

Btw Singapore has tasty frog porridge have you tried?

zhao...@gmail.com

unread,
Apr 22, 2014, 11:44:11 PM4/22/14
to shiny-...@googlegroups.com
I love it!

在 2013年11月28日星期四UTC+8上午1时17分28秒,ZJ写道:

zhao...@gmail.com

unread,
Apr 22, 2014, 11:44:36 PM4/22/14
to shiny-...@googlegroups.com

Aurelien Tylski

unread,
Apr 23, 2014, 4:47:17 AM4/23/14
to shiny-...@googlegroups.com
Thanks a lot, now it works just like i wanted. You did a really good job thank a lot.

No i don't know the frog porridge, i google it and it seems really yummy, i'll try to look for all ingredients and try to cook some, thanks again.

Have a great day ;)

Aurelien Tylski

unread,
Jul 30, 2014, 5:10:11 AM7/30/14
to shiny-...@googlegroups.com
Hello,

i'm back on ShinySky and i'm trying to make an update of a Hotable when user click on a button (input$utilisateur_apercu_Button), i try with a reactive fonction but none works =$ . Do someone have an idea on what i did wrong ? =$ Thank a lot (and sorry for my english i still be a french frog eater :( who didn't find frog porridge ;( héhé)

My server.R looks like this :

apercuDB <-reactive({

do stuff to get DF

})

  output$apercu <- renderHotable({
    if (input$utilisateur_apercu_Button == FALSE)
      return()
    table <- isolate({ apercuDB()})
    return(table)
  }) 

David Ardestani

unread,
Aug 4, 2014, 12:27:08 AM8/4/14
to shiny-...@googlegroups.com
Is shinysky still supported, or is it now incorporated into base shiny?  I am going back to using R, but in this package I really thought the select2Input feature is very useful because it allows order specific selection.

Roger Day

unread,
Aug 16, 2014, 11:31:17 AM8/16/14
to shiny-...@googlegroups.com
I've worked a lot with shinysky's jstree, super-useful for my project ClinicalTrialDesignExperimenter on github.
I modified jstree.obj() to add class, level and id tags into the tree nodes,
and modified ss-jstree.js to return this information upon selecting one or more nodes.
In ClinicalTrialDesignExperimenter, you can see how to use the node levels and classes
to do css decorating, and how to make a conditionalPanel element conditional on the selected node's tree depth.
Lots of other jstree-related tricks are in that package,
for example how to start with the tree fully open,
how to respond to node selections both from JS and from R,
and how to map selected tree nodes back to objects that generated them. 

I learned a lot studying ZJ's work on shinysky jstree.

Here is the modified jstree.obj ( in a shinysky pull request pending from May 31).

jstree.obj = 
  function (x, addLevelClass=TRUE, addLevelType=TRUE, addIndex=TRUE, level=0, index="0") 
  {
    handle <- function(ind, theList, level) {
      name <- names(theList)[[ind]]
      if (!is.null(name)) {
        level=level+1
        index = paste0(index, "_", ind)
        a <- tags$li(list(name, 
                          myjstree.obj(theList[[ind]], 
                                       addLevelClass=addLevelClass,
                                       addLevelType=addLevelType,
                                       addIndex=addIndex,
                                       level=level,
                                       index=index
                          )))
      }
      else {
        a <- tags$li(theList[[ind]])
      }
      ### This line is added to shinysky:::jstree.obj
      if(addLevelClass)
        a <- tagAppendAttributes(a, class=paste0("treeclass_", level))
      if(addLevelType) {
        a <- tagAppendAttributes(a, type=paste0("level_", level))
        a <- tagAppendAttributes(a, rel=paste0("level_", level))
      }
      if(addIndex)
        a <- tagAppendAttributes(a, index=index)
      return(a)
    }
    if (is.list(x)) {
      ind <- seq(along=x)
      res <- lapply(ind, handle, x, level=level)
      return(tags$ul(res))
    }
    else {
      x
    }
  }


Here is the modified ss-jstree.js:

//input binding for jstree

//alert("In my ss-jstree.js");
var ss_jstree = new Shiny.InputBinding();

$.extend(ss_jstree, {
  find: function(scope) {
    return $(scope).find(".ss-jstree");
  },
  getValue: function(el) {
var tree = $(el).jstree();
var leaves = tree.get_selected(); // an array of node id's, like "j1_5".
var i, j, r = [];
var mynode;
var pathlength;
var mynode_data = [];
var mynode_li_attr;

// R-parse-able output string;
    for (i = 0, j = leaves.length; i < j; i++) {
   mynode = tree.get_node(leaves[i]);
    mynode_li_attr = mynode.li_attr;
    mynode_li_attr["id"] = mynode.id;
    mynode_li_attr["text"] = mynode.text;
    r.push([mynode_li_attr]);
    }
    return r;
  },

  setValue: function(el, value) {},
  subscribe: function(el, callback) {
    $(el).on("changed.jstree", function(e) {
      callback();
    });
  },
  unsubscribe: function(el) {
    $(el).off(".ss_jstree");
  }
});
Shiny.inputBindings.register(ss_jstree);

Reply all
Reply to author
Forward
0 new messages