shiny 4 small textInput boxes side-by-side - r

I've got a shiny server version 0.4.0 and I want to have 4 small textInput boxes to look like this:
x-min x-max y-min y-max
[...] [...] [...] [...]
They now look like this:
x-min
[...................]
x-max
[...................]
y-min
[...................]
y-max
[...................]
With this code:
textInput(inputId="xlimitsmin", label="x-min", value = 0.0),
textInput(inputId="xlimitsmax", label="x-max", value = 0.5),
textInput(inputId="ylimitsmin", label="y-min", value = 0.5),
textInput(inputId="ylimitsmax", label="y-max", value = 1.0),
Any ideas how to achieve this?
EDITED: I've successfully changed things like this elsewhere in the code:
<style type="text/css">select#yaxis4 { height: 280px; width: 500px; }</style>
[... which links to this later on in the page...]
<label class="control-label" for="yaxis4">Y-Axis</label>
<select id="yaxis4" multiple="multiple">
And this is what it looks like for the ones that don't work:
<style type="text/css">select#xlimitsmax { display: inline-block; max-width: 50px; }</style>
[... which links to...]
<label>x-max</label>
<input id="xlimitsmax" type="text" value="0.5"/>
EDITED:
Here is a self contained example ui.R that doesn't work:
library(shiny)
shinyUI(
pageWithSidebar(
# application title
headerPanel("test01"),
sidebarPanel(
tags$head(
tags$style(type="text/css", "select { max-width: 360px; }"),
tags$style(type="text/css", ".span4 { max-width: 360px; }"),
tags$style(type="text/css", ".well { max-width: 360px; }")
),
wellPanel(
p(strong("Side Panel:"))
)
),
mainPanel(
textInput(inputId="xlimitsmin", label="x-min", value = 0.0),
tags$head(tags$style(type="text/css", "select#xlimitsmin { max-width: 50px }")),
textInput(inputId="xlimitsmax", label="x-max", value = 0.5),
tags$head(tags$style(type="text/css", "select#xlimitsmax { display: inline-block; max-width: 50px; }"))
)
))
Resulting page:

to paraphrase (and to simplify to the case of 2 inputs), your problem is that:
runApp(list(
ui = bootstrapPage(
textInput(inputId="xlimitsmin", label="x-min", value = 0.0),
textInput(inputId="xlimitsmax", label="x-max", value = 0.5)
),
server = function(input, output) {}
))
shows
But you want side-by-side small inputs, like so:
The short answer
textInputRow<-function (inputId, label, value = "")
{
div(style="display:inline-block",
tags$label(label, `for` = inputId),
tags$input(id = inputId, type = "text", value = value,class="input-small"))
}
runApp(list(
ui = bootstrapPage(
textInputRow(inputId="xlimitsmin", label="x-min", value = 0.0),
textInputRow(inputId="xlimitsmax", label="x-max", value = 0.5)
),
server = function(input, output) {}
))
gives:
The long answer
Side-by-side inputs
Let's do side-by-side first:
Currently textInput generates two separate tags - the label, and the input, each of which is configured by CSS as display:block, which means it's a rectangle that will break to the left side of the container. We need to wrap each textInput's field in new container (div) and tell that container that the container that follows it (the next textInput) is allowed to be on the same horizontal row on the page, using CSS's display:inline-block.
So we add a div with a style around each textInput:
runApp(list(
ui = bootstrapPage(
div(style="display:inline-block",textInput(inputId="xlimitsmin", label="x-min", value = 0.0)),
div(style="display:inline-block",textInput(inputId="xlimitsmax", label="x-max", value = 0.5))
),
server = function(input, output) {}
))
Small inputs
Now let's deal with small. There are a few ways to do small,
make the font smaller,
make the input box have fewer characters in it.
tell css or (here) bootstrap to draw a smaller box
Since bootstrap.js is really in control of layout when we use shiny, only 3 will reliably work, so let's use that.
Input sizes are documented in Bootstrap 2.3.2's CSS Forms documentation, under 'Control Sizing'. It includes a variety of sizes from mini, small, medium, large, xlarge, and xxlarge, and the default is probably medium. Let's try small, instead.
To set the size, we need to change the class of the input tag generated by textInput.
Now textInput is just a convenience function around the more powerful tags functions such as tags$label and tags$input. We can build a more powerful version of textInput that allows us to configure the elements, specifically the class of the input node:
textInput2<-function (inputId, label, value = "",...)
{
tagList(tags$label(label, `for` = inputId), tags$input(id = inputId,
type = "text", value = value,...))
}
runApp(list(
ui = bootstrapPage(
div(style="display:inline-block",textInput2(inputId="xlimitsmin", label="x-min", value = 0.0, class="input-small")),
div(style="display:inline-block",textInput2(inputId="xlimitsmax", label="x-max", value = 0.5, class="input-small"))
),
server = function(input, output) {}
))
And we are done - but we can roll some of this functionality up by having textInput3 generate the div tag. It could also set the class by itself, but I'll leave that for you to write.
Wrapping it up
textInput3<-function (inputId, label, value = "",...)
{
div(style="display:inline-block",
tags$label(label, `for` = inputId),
tags$input(id = inputId, type = "text", value = value,...))
}
runApp(list(
ui = bootstrapPage(
textInput3(inputId="xlimitsmin", label="x-min", value = 0.0, class="input-small"),
textInput3(inputId="xlimitsmax", label="x-max", value = 0.5, class="input-small")
),
server = function(input, output) {}
))
For interest's sake, here's the version using class input-mini:

Using Shiny (>= 0.11), you can accomplish this by putting the input calls within a splitLayout(). This will split the fluid row, box, etc. into the necessary columns required to show your input fields side-by-side.
The example below would give you three text inputs in a box, that will appear side-by-side in the fluidRow.
fluidRow(
box(width = 12, title = "A Box in a Fluid Row I want to Split",
splitLayout(
textInput("inputA", "The first input"),
textInput("inputB", "The second input"),
textInput("inputC", "The third input")
)
)
)

Maybe this solution wasn't around in 2013 but if you want to do this without writing HTML or CSS you can just use the column function within a fluidRow like so:
fluidRow(
column(3,
selectInput('pcat', 'Primary Category', c("ALL", "Some"))),
column(3,
selectInput('smodel', 'Statistical Model', c("NONE", "LINEAR REGRESSION", "LOWESS")))
)
And it will place things side by side.
EDIT: Now there is another very easy way to do this using the splitLayout() function. See Nadir Sidi's answer for more details.

I deleted the old answer - here is one that works:
ui.r:
library(shiny)
shinyUI(
pageWithSidebar(
# application title
headerPanel("test01"),
sidebarPanel(
tags$head(
tags$style(type="text/css", "select { max-width: 360px; }"),
tags$style(type="text/css", ".span4 { max-width: 360px; }"),
tags$style(type="text/css", ".well { max-width: 360px; }")
),
wellPanel(
p(strong("Side Panel:"))
)
),
mainPanel(
div(id="XXmin",textInput(inputId="xlimitsmin", label="x-min", value = 0.0)),
tags$head(tags$style(type="text/css", "#XXmin {display: inline-block}")),
tags$head(tags$style(type="text/css", "#xlimitsmin {max-width: 50px}")),
div(id="XXmax",textInput(inputId="xlimitsmax", label="x-max", value = 0.5)),
tags$head(tags$style(type="text/css", "#XXmax {display: inline-block}"),
tags$head(tags$style(type="text/css", "#xlimitsmax {max-width: 50px}"))
))
))
Here are the changes I made:
1) I eliminated the select from select#xlimitsmax and select#xlimitsmin in your .css statements
2) I put your two controls each in their own div() and gave them the names XXmin and XXmax. I then added .css statements to make them inline-block.
If you have a bunch of these you might want to use a class statement - such as:
div(class="MyClass",textInput(inputId="xlimitsmin", label="x-min", value = 0.0)),
tags$head(tags$style(type="text/css", ".MyClass {display: inline-block}")),
tags$head(tags$style(type="text/css", "#xlimitsmin {max-width: 50px}")),
then you can tag each of the control div()'s as class="MyClass" and use only one .css statement.
Edited to add: Thanks for posting the example code - that made it much easier.
2nd Edit: Just to clarify. The point of putting the textInput commands inside a div() is to unite the input box and its label into a single object so that styles (in this case the display style) can be applied. If you don't do this, the label and the box act as two separate entities and it is harder to manipulate them in cases like this.

As an alternative to putting verbose style declarations in a class, it seems you can easily extend the shiny tags functions to your liking. This particular one would be convenient to have around by default.
(this is with shiny shiny_0.14.1 ). Thought I was going to need to write a closure but this seems to work.
inline = function (x) {
tags$div(style="display:inline-block;", x)
}
inline(textInput(inputId="xlimitsmin", label="x-min", value = 0.0)),
inline(textInput(inputId="xlimitsmax", label="x-max", value = 0.5)),
inline(textInput(inputId="ylimitsmin", label="y-min", value = 0.5)),
inline(textInput(inputId="ylimitsmax", label="y-max", value = 1.0)),

I wasn't happy with splitLayout() because it introduces scroll bars when space is limited.
I found that, at least for input widgets like buttons or text boxes, a quite easy solution with better responsive behaviour is using flex-box: (see this great guide: https://css-tricks.com/snippets/css/a-guide-to-flexbox/)
div(
style = "display: flex; flex-wrap: wrap;",
div(
style = "flex: 1;",
textInput("inputA", "The first input")
),
div(
style = "flex: 1;",
textInput("inputB", "The second input")
),
div(
style = "flex: 1;",
textInput("inputC", "The third input")
)
)
It is possible to adjust relative widths. Corresponding to splitLayout(cellWidths = c("25%", "75%"), ...):
div(
style = "display: flex; flex-wrap: wrap;",
div(
style = "flex: 1;",
textInput("inputA", "The first input")
),
div(
style = "flex: 3;", # second item 3 times as wide as first one
textInput("inputB", "The second input")
)
)

If you want the inputs in mainPanel you can use the following:
div(class="row-fluid",
div(class="span1",textInput("xlimitsmin", label = "x-min", value = 0.0)),
div(class="span1",textInput("xlimitsmax", label = "x-max", value = 0.5)),
div(class="span1",textInput("ylimitsmin", label = "y-min", value = 0.5)),
div(class="span1",textInput("ylimitsmax", label = "y-max", value = 1.0))
)
Add:
#xlimitsmin, #xlimitsmax, #ylimitsmin, #ylimitsmax {
max-width: 25px;
}
in a css file (e.g., style.css in the www/ directory) in your app and source it from ui.R with:
includeCSS('www/style.R')
I am not sure why you need a textInput rather than a numericInput since the input you seem to be looking for is numeric. If you choose numericInput you can simply replace textInput with numericInput in the above. If you want the inputs in the sidebarPanel you could use the code below. The same css file mentioned above would be needed.
div(class="row-fluid",
div(class="span3",numericInput("xlimitsmin", label = "x-min", value = 0.0)),
div(class="span3",numericInput("xlimitsmax", label = "x-max", value = 0.5)),
div(class="span3",numericInput("ylimitsmin", label = "y-min", value = 0.5)),
div(class="span3",numericInput("ylimitsmax", label = "y-max", value = 1.0))
)

Sgrubsmyon's approach was almost perfect for me however I ran into a new problem with the flex-box approach in that there was no padding between the inputs. Apparently this has something to do with "display: flex" being a wrapper for "flex-grow 1" which uses up all available space. I dove into the rabbit hole and couldnt get this to work but learnt about a similar approach which uses "CSS - Grid" and is even easier (relevant SO question where I learnt this from):
div(
style = "display: grid;
grid-template-columns: 20% repeat(3, 20%); ## same as repeat(4, 20%)
grid-gap: 10px;",
textInput("inputA", "The first input"),
textInput("inputB", "The second input"),
textInput("inputC", "The third input"),
textInput("inputD", "The fourth input")
)
A similar great guide exists for the CSS - Grid approach located here where you can learn about all the different arguments and customisability that you can use. Note that I have never touched CSS until 2 hours before writing this answer so any corrections are welcome =)

Related

Weird behavior of selectizeInput

In the Shiny App below, I am facing a very strange behavior, where selectInput box slides downwards when I type something in this box. Also, the text inside selectInput box moves towards the right while I type in this box. I have spent a lot of time to find out the reason for this problem but could not figure it out. Can someone point out the mistake I am doing causing this strange behavior?
library(shiny)
library(shinydashboard)
library(highcharter)
siderbar <- dashboardSidebar(
sidebarMenu(
selectizeInput(inputId = "select_by", label = "Select by:", choices = NULL, multiple = FALSE, options = NULL)
)
)
body <- dashboardBody(
fluidRow(
tabBox(
side = "right",
selected = "Tab1",
tabPanel("Tab1", "Tab content 1", highchartOutput("tabset1Selected"))
)
),
)
shinyApp(
ui = dashboardPage(
dashboardHeader(title = "tabBoxes"),
siderbar,
body
),
server = function(input, output, session) {
selectedVal <- reactiveValues()
updateSelectizeInput(session, "select_by", choices = c(as.character(1:10000)), selected = 2, server = TRUE)
output$tabset1Selected <- renderHighchart({
selectedVal <- input$select_by
print(highcharts_demo())
})
}
)
We were on the right track. It has something to do with selectize.js updating the items from the server. You can verify that by setting the loadThrottle option to 5000. This option determines how long the widget waits "before requesting options from the server" (see the manual). Now you have to wait exactly 5 seconds and then the select widget flickers.
The issue seems to be caused by a CSS conflict. selectize.js adds a CSS class to the widget. If you remove that feature, the flicker goes away.
selectizeInput(inputId = "select_by", label = "Select by:",
choices = NULL, multiple = FALSE,
options = list(loadThrottle=200, loadingClass=""))
loadingClass sets a specific CSS class (default: 'loading') while loading data from the server. Purpose: to change how the widget looks and communicate to users that an update is in progress.
loadThrottle does not need to be set. It's default is 300. You can set it to any value that suits your needs.
Details
highcharter defines it's own CSS class names loading with these specs:
.loading {
margin-top: 10em;
text-align: center;
color: gray;
}
That is the reason for the CSS conflict. The widget gets a top margin and it's content moved to the center, because the browser does not distinguish the source of the class. It only sees some CSS that fits and uses it. This image shows where you need to look:

How to better position Next/Back button in shiny glide, in order to eliminate large white space?

The Shinyglide package is just what I need, using a carousel for grouped radio buttons giving the user many choices for data parsing.
However, the "Next" (and "Back") button occupies a large white space. I'd like to shift the button in line with the glide row (see image at bottom). Does anyone know how to do this? Is there a CSS trick? Reading through the Glide manual, the only choices are "top" and "bottom".
If moving the Next/Back button isn't possible, a secondary option is to insert (a somewhat superfluous) line of text but in line with the Next/Back buttons, to at least cover up the annoyingly large white space.
The actual panel this is for has much more information presented than in this example, so I'm trying to make the page as clean as possible.
Please see image at bottom that better explains what I'm trying to do.
Reproducible example:
library(dplyr)
library(DT)
library(shiny)
library(shinyglide)
ui <-
fluidPage(
fluidRow(div(style = "margin-top:15px"),
strong("Input choices shown in row below, click ´Next´ to see more choices:"),
column(12, glide(
height = "25",
controls_position = "top",
screen(
div(style = "margin-top:10px"),
wellPanel(
radioButtons(inputId = 'group1',
label = NULL,
choiceNames = c('By period','By MOA'),
choiceValues = c('Period','MOA'),
selected = 'Period',
inline = TRUE
),
style = "padding-top: 12px; padding-bottom: 0px;"
)
),
screen(
div(style = "margin-top:10px"),
wellPanel(
radioButtons(inputId = 'group2',
label = NULL,
choiceNames = c('Exclude CT','Include CT'),
choiceValues = c('Exclude','Include'),
selected = 'Exclude',
inline = TRUE
),
style = "padding-top: 12px; padding-bottom: 0px;"
)
)
)
)
),
DTOutput("plants")
)
server <- function(input, output, session) {
output$plants <- renderDT({iris %>% datatable(rownames = FALSE)})
}
shinyApp(ui, server)
You could use a custom control element with custom_controls, and then have it hover over the displayed screen on the top right with a container set to absolute positioning. Setting a limited width for the container will ensure that the back button won't fly too far out.
Something along these lines:
glide(custom_controls = div(class = "glide-controls", glideControls()), ...)
# Somewhere in the UI
tags$style(
".glide-controls { position: absolute; top: 18px; right: 15px; width: 160px; }"
)
Just make sure to also set controls_position = "bottom" so that the controls hover over the screen content, rather than under it.
A minimal example app:
library(shiny)
library(shinyglide)
ui <- fixedPage(
h3("Simple shinyglide app"),
tags$style(
".glide-controls { position: absolute; top: 18px; right: 15px; width: 160px; }"
),
glide(
custom_controls = div(class = "glide-controls", glideControls()),
screen(wellPanel(p("First screen."))),
screen(wellPanel(p("Second screen.")))
)
)
server <- function(input, output, session) {}
shinyApp(ui, server)

css styling for select and numeric Input

I am using R shiny and i am trying to set the same heigth for selectInput and numericInput in my ui.
I found this line:
tags$style(type="text/css", ".selectize-input {line-height: 20px;}"),
which changes the heigth for all my selectInputs.
Is there any similar line to numericInput?
Thanks
Well, it does not have a specific class such as selectInput. But you can still get the same result using the more general class form-control. We need to use height instead of line-height though, which will work fine for selectInput anyway.
ui <- fluidPage(
tags$style(type = "text/css", ".form-control.shiny-bound-input,
.selectize-input {height: 70px;}"),
selectInput(inputId = "another_id",
label = "bar",
choices = c(1, 2)),
numericInput(inputId = "some_id",
label = "foo",
value = 1)
)
server <- function(input, output, session) {}
shinyApp(ui, server)
EDIT:
In case you want to apply some CSS to a specific input, you need to use its id, so the style does not apply to all elements sharing the same class. In the example above, if you only want to change the height of the numericInput whose id is some_id, you would use #some_id to select this element, leading to the following code:
tags$style(type = "text/css",
"#some_id.form-control.shiny-bound-input {height: 70px;}")

R Shiny multiple Inputs in a row [duplicate]

I've got a shiny server version 0.4.0 and I want to have 4 small textInput boxes to look like this:
x-min x-max y-min y-max
[...] [...] [...] [...]
They now look like this:
x-min
[...................]
x-max
[...................]
y-min
[...................]
y-max
[...................]
With this code:
textInput(inputId="xlimitsmin", label="x-min", value = 0.0),
textInput(inputId="xlimitsmax", label="x-max", value = 0.5),
textInput(inputId="ylimitsmin", label="y-min", value = 0.5),
textInput(inputId="ylimitsmax", label="y-max", value = 1.0),
Any ideas how to achieve this?
EDITED: I've successfully changed things like this elsewhere in the code:
<style type="text/css">select#yaxis4 { height: 280px; width: 500px; }</style>
[... which links to this later on in the page...]
<label class="control-label" for="yaxis4">Y-Axis</label>
<select id="yaxis4" multiple="multiple">
And this is what it looks like for the ones that don't work:
<style type="text/css">select#xlimitsmax { display: inline-block; max-width: 50px; }</style>
[... which links to...]
<label>x-max</label>
<input id="xlimitsmax" type="text" value="0.5"/>
EDITED:
Here is a self contained example ui.R that doesn't work:
library(shiny)
shinyUI(
pageWithSidebar(
# application title
headerPanel("test01"),
sidebarPanel(
tags$head(
tags$style(type="text/css", "select { max-width: 360px; }"),
tags$style(type="text/css", ".span4 { max-width: 360px; }"),
tags$style(type="text/css", ".well { max-width: 360px; }")
),
wellPanel(
p(strong("Side Panel:"))
)
),
mainPanel(
textInput(inputId="xlimitsmin", label="x-min", value = 0.0),
tags$head(tags$style(type="text/css", "select#xlimitsmin { max-width: 50px }")),
textInput(inputId="xlimitsmax", label="x-max", value = 0.5),
tags$head(tags$style(type="text/css", "select#xlimitsmax { display: inline-block; max-width: 50px; }"))
)
))
Resulting page:
to paraphrase (and to simplify to the case of 2 inputs), your problem is that:
runApp(list(
ui = bootstrapPage(
textInput(inputId="xlimitsmin", label="x-min", value = 0.0),
textInput(inputId="xlimitsmax", label="x-max", value = 0.5)
),
server = function(input, output) {}
))
shows
But you want side-by-side small inputs, like so:
The short answer
textInputRow<-function (inputId, label, value = "")
{
div(style="display:inline-block",
tags$label(label, `for` = inputId),
tags$input(id = inputId, type = "text", value = value,class="input-small"))
}
runApp(list(
ui = bootstrapPage(
textInputRow(inputId="xlimitsmin", label="x-min", value = 0.0),
textInputRow(inputId="xlimitsmax", label="x-max", value = 0.5)
),
server = function(input, output) {}
))
gives:
The long answer
Side-by-side inputs
Let's do side-by-side first:
Currently textInput generates two separate tags - the label, and the input, each of which is configured by CSS as display:block, which means it's a rectangle that will break to the left side of the container. We need to wrap each textInput's field in new container (div) and tell that container that the container that follows it (the next textInput) is allowed to be on the same horizontal row on the page, using CSS's display:inline-block.
So we add a div with a style around each textInput:
runApp(list(
ui = bootstrapPage(
div(style="display:inline-block",textInput(inputId="xlimitsmin", label="x-min", value = 0.0)),
div(style="display:inline-block",textInput(inputId="xlimitsmax", label="x-max", value = 0.5))
),
server = function(input, output) {}
))
Small inputs
Now let's deal with small. There are a few ways to do small,
make the font smaller,
make the input box have fewer characters in it.
tell css or (here) bootstrap to draw a smaller box
Since bootstrap.js is really in control of layout when we use shiny, only 3 will reliably work, so let's use that.
Input sizes are documented in Bootstrap 2.3.2's CSS Forms documentation, under 'Control Sizing'. It includes a variety of sizes from mini, small, medium, large, xlarge, and xxlarge, and the default is probably medium. Let's try small, instead.
To set the size, we need to change the class of the input tag generated by textInput.
Now textInput is just a convenience function around the more powerful tags functions such as tags$label and tags$input. We can build a more powerful version of textInput that allows us to configure the elements, specifically the class of the input node:
textInput2<-function (inputId, label, value = "",...)
{
tagList(tags$label(label, `for` = inputId), tags$input(id = inputId,
type = "text", value = value,...))
}
runApp(list(
ui = bootstrapPage(
div(style="display:inline-block",textInput2(inputId="xlimitsmin", label="x-min", value = 0.0, class="input-small")),
div(style="display:inline-block",textInput2(inputId="xlimitsmax", label="x-max", value = 0.5, class="input-small"))
),
server = function(input, output) {}
))
And we are done - but we can roll some of this functionality up by having textInput3 generate the div tag. It could also set the class by itself, but I'll leave that for you to write.
Wrapping it up
textInput3<-function (inputId, label, value = "",...)
{
div(style="display:inline-block",
tags$label(label, `for` = inputId),
tags$input(id = inputId, type = "text", value = value,...))
}
runApp(list(
ui = bootstrapPage(
textInput3(inputId="xlimitsmin", label="x-min", value = 0.0, class="input-small"),
textInput3(inputId="xlimitsmax", label="x-max", value = 0.5, class="input-small")
),
server = function(input, output) {}
))
For interest's sake, here's the version using class input-mini:
Using Shiny (>= 0.11), you can accomplish this by putting the input calls within a splitLayout(). This will split the fluid row, box, etc. into the necessary columns required to show your input fields side-by-side.
The example below would give you three text inputs in a box, that will appear side-by-side in the fluidRow.
fluidRow(
box(width = 12, title = "A Box in a Fluid Row I want to Split",
splitLayout(
textInput("inputA", "The first input"),
textInput("inputB", "The second input"),
textInput("inputC", "The third input")
)
)
)
Maybe this solution wasn't around in 2013 but if you want to do this without writing HTML or CSS you can just use the column function within a fluidRow like so:
fluidRow(
column(3,
selectInput('pcat', 'Primary Category', c("ALL", "Some"))),
column(3,
selectInput('smodel', 'Statistical Model', c("NONE", "LINEAR REGRESSION", "LOWESS")))
)
And it will place things side by side.
EDIT: Now there is another very easy way to do this using the splitLayout() function. See Nadir Sidi's answer for more details.
I deleted the old answer - here is one that works:
ui.r:
library(shiny)
shinyUI(
pageWithSidebar(
# application title
headerPanel("test01"),
sidebarPanel(
tags$head(
tags$style(type="text/css", "select { max-width: 360px; }"),
tags$style(type="text/css", ".span4 { max-width: 360px; }"),
tags$style(type="text/css", ".well { max-width: 360px; }")
),
wellPanel(
p(strong("Side Panel:"))
)
),
mainPanel(
div(id="XXmin",textInput(inputId="xlimitsmin", label="x-min", value = 0.0)),
tags$head(tags$style(type="text/css", "#XXmin {display: inline-block}")),
tags$head(tags$style(type="text/css", "#xlimitsmin {max-width: 50px}")),
div(id="XXmax",textInput(inputId="xlimitsmax", label="x-max", value = 0.5)),
tags$head(tags$style(type="text/css", "#XXmax {display: inline-block}"),
tags$head(tags$style(type="text/css", "#xlimitsmax {max-width: 50px}"))
))
))
Here are the changes I made:
1) I eliminated the select from select#xlimitsmax and select#xlimitsmin in your .css statements
2) I put your two controls each in their own div() and gave them the names XXmin and XXmax. I then added .css statements to make them inline-block.
If you have a bunch of these you might want to use a class statement - such as:
div(class="MyClass",textInput(inputId="xlimitsmin", label="x-min", value = 0.0)),
tags$head(tags$style(type="text/css", ".MyClass {display: inline-block}")),
tags$head(tags$style(type="text/css", "#xlimitsmin {max-width: 50px}")),
then you can tag each of the control div()'s as class="MyClass" and use only one .css statement.
Edited to add: Thanks for posting the example code - that made it much easier.
2nd Edit: Just to clarify. The point of putting the textInput commands inside a div() is to unite the input box and its label into a single object so that styles (in this case the display style) can be applied. If you don't do this, the label and the box act as two separate entities and it is harder to manipulate them in cases like this.
As an alternative to putting verbose style declarations in a class, it seems you can easily extend the shiny tags functions to your liking. This particular one would be convenient to have around by default.
(this is with shiny shiny_0.14.1 ). Thought I was going to need to write a closure but this seems to work.
inline = function (x) {
tags$div(style="display:inline-block;", x)
}
inline(textInput(inputId="xlimitsmin", label="x-min", value = 0.0)),
inline(textInput(inputId="xlimitsmax", label="x-max", value = 0.5)),
inline(textInput(inputId="ylimitsmin", label="y-min", value = 0.5)),
inline(textInput(inputId="ylimitsmax", label="y-max", value = 1.0)),
I wasn't happy with splitLayout() because it introduces scroll bars when space is limited.
I found that, at least for input widgets like buttons or text boxes, a quite easy solution with better responsive behaviour is using flex-box: (see this great guide: https://css-tricks.com/snippets/css/a-guide-to-flexbox/)
div(
style = "display: flex; flex-wrap: wrap;",
div(
style = "flex: 1;",
textInput("inputA", "The first input")
),
div(
style = "flex: 1;",
textInput("inputB", "The second input")
),
div(
style = "flex: 1;",
textInput("inputC", "The third input")
)
)
It is possible to adjust relative widths. Corresponding to splitLayout(cellWidths = c("25%", "75%"), ...):
div(
style = "display: flex; flex-wrap: wrap;",
div(
style = "flex: 1;",
textInput("inputA", "The first input")
),
div(
style = "flex: 3;", # second item 3 times as wide as first one
textInput("inputB", "The second input")
)
)
If you want the inputs in mainPanel you can use the following:
div(class="row-fluid",
div(class="span1",textInput("xlimitsmin", label = "x-min", value = 0.0)),
div(class="span1",textInput("xlimitsmax", label = "x-max", value = 0.5)),
div(class="span1",textInput("ylimitsmin", label = "y-min", value = 0.5)),
div(class="span1",textInput("ylimitsmax", label = "y-max", value = 1.0))
)
Add:
#xlimitsmin, #xlimitsmax, #ylimitsmin, #ylimitsmax {
max-width: 25px;
}
in a css file (e.g., style.css in the www/ directory) in your app and source it from ui.R with:
includeCSS('www/style.R')
I am not sure why you need a textInput rather than a numericInput since the input you seem to be looking for is numeric. If you choose numericInput you can simply replace textInput with numericInput in the above. If you want the inputs in the sidebarPanel you could use the code below. The same css file mentioned above would be needed.
div(class="row-fluid",
div(class="span3",numericInput("xlimitsmin", label = "x-min", value = 0.0)),
div(class="span3",numericInput("xlimitsmax", label = "x-max", value = 0.5)),
div(class="span3",numericInput("ylimitsmin", label = "y-min", value = 0.5)),
div(class="span3",numericInput("ylimitsmax", label = "y-max", value = 1.0))
)
Sgrubsmyon's approach was almost perfect for me however I ran into a new problem with the flex-box approach in that there was no padding between the inputs. Apparently this has something to do with "display: flex" being a wrapper for "flex-grow 1" which uses up all available space. I dove into the rabbit hole and couldnt get this to work but learnt about a similar approach which uses "CSS - Grid" and is even easier (relevant SO question where I learnt this from):
div(
style = "display: grid;
grid-template-columns: 20% repeat(3, 20%); ## same as repeat(4, 20%)
grid-gap: 10px;",
textInput("inputA", "The first input"),
textInput("inputB", "The second input"),
textInput("inputC", "The third input"),
textInput("inputD", "The fourth input")
)
A similar great guide exists for the CSS - Grid approach located here where you can learn about all the different arguments and customisability that you can use. Note that I have never touched CSS until 2 hours before writing this answer so any corrections are welcome =)

R Shiny - resize numericInput box

I have a very basic Shiny app.
ui.R:
library(shiny)
shinyUI(fluidPage(
titlePanel("Average Run Length Simulation"),
fluidRow(
tabsetPanel(
tabPanel("Shewhart v. Shewhart",
fluidRow(
column(4,"Rule"),
column(2,"Group 1"),
column(2,"Group 2")
),
fluidRow(
column(4,"1 point more than k sigma away from mean"),
column(2,
checkboxInput("svsg1r1",label=" ",value=F),
numericInput("svsg1k1",label=" ",value=3,min=1,step=1)
),
column(2,
checkboxInput("svsg2r1",label=" ",value=F),
numericInput("svsg2k1",label=" ",value=3,min=1,step=1)
)
)
)
)
)
))
The server.r file is the basic one created by Rstudio in a new project.
What I really want is a tabular layout of widget elements, but, I don't think I'll get that without a lot of work and this isn't worth it. So, instead, I want to reduce the width of the numericInput() boxes akin the the size attribute of an <input> element in an HTML form.
How would I do this in shiny? Is there a standard way to apply css/html specifics to particular elements?
This works, though it's global css and I'd rather have element specific. I added this inside the fluidPage() element in ui.r and it resized both boxes.
tags$head(
tags$style(HTML("
input[type=\"number\"] {
width: 100px;
}
"))
)
For a numericInput box specific resizing, you can use the CSS id selector. In the example above, the following should work to just resize the first box.
tags$head(
tags$style(HTML("
#svsg1k1 {
width: 100px;
}
")))

Resources