Shiny dynamic layout: Automatically wrap fixed width elements to the next row - r

I'm aware of fluidPage() and fixedPage() layouts for shiny. In my case though it would be nice to have another behaviour for elements (plots/input fields/shinydashboard boxes).
Elements should have a fixed width (and height) and move automatically to the next row if the display width changes.
Legend:
[...] <- Element
| <- Right browser window border
Examples:
1. Big screen case
[...] [..] [.....] [...] [...] |
2. Small screen case
[...] [..] [.....] [...] |
[...] |
3. Even smaller screen case
[...] [..] |
[.....] |
[...] [...] |
Is a layout like this possible with shiny/shinydashboard?

Thanks to #SimonLarsen I was able to find a solution. Shiny offers flowLayout() which supports this kind of layout. Unfortunately shinydashboard boxes can't be used within this framework, because they expect width values within the bootstrap grid framework. You would have to change the implementation of shinydashbaord::box() to work with pixel width values and that would cause all sorts of other problems down the line.
I've opted for the following solution:
shiny::fluidRow(
shinydashboard::box(
width = 12,
shiny::div(
style = "overflow-x: scroll",
shiny::flowLayout(
cellArgs = list(
style = "
min-width: 300px;
width: auto;
height: auto;
border: 1px solid darkgray;
padding: 10px;
margin: 10px;
"),
plotly::plotlyOutput(
width = "500px",
ns("plot1")
),
plotly::plotlyOutput(
width = "500px",
ns("plot1")
),
plotly::plotlyOutput(
width = "1045px",
ns("plot2")
)
)
)
)
)
I build my own boxes with fixed height and for each plot/content element an individually defined width.

Related

Shinydashboard Tabbox Height

I'm trying to create a tabBox that spans the entire mainPanel. I'm able to get the width to span the entire screen but I'm unable to get the height to do the same. I do not wish to use absolute values in pixels (or some other unit) since I expect the app to be used across different screens.
I played with the example and an example of the modified tabBox is as below
fluidRow(
tabBox(
title = "First tabBox",
# The id lets us use input$tabset1 on the server to find the current tab
id = "tabset1", height = "450px",
tabPanel("Tab1", "First tab content"),
tabPanel("Tab2", "Tab content 2"),
width = 12
),
height = '20%',
width = 12
)
You can use the vh css unit that is defined as 1% of viewport height and then basically follow the example in this answer where you set the relative height in the css:
fluidRow(
tabBox(
tags$head(
tags$style(HTML(" #tabBox { height:90vh !important; } "))
),
id="tabBox",
title = "tabBox",
width = 12
)
You can of course also put this in an external css file, especially if you are going to do more than one of these css tricks. With 100% goes slightly over the bottom edge because of the header. Around 90% seems to work fine.

display input text as span, width trouble

I'm trying to make an input text look like exactly like a span. I want my user to be able to type text without noticing he's in a field.
I've almost succeeded, the last problem I've got is about the width of the input.
The input text is wider than the text it's replacing.
Here's the jsfiddle. (You may click on "Texte" to type yours).
Here's the interesting css part :
input {
background-color: transparent;
padding: 0px;
border: initial;
font: inherit;
margin: 0px;
}
Here is the idea try fixed width for the inputs
Demo : http://jsfiddle.net/nyitsol/eotd8p4n/17/
also you may try use the same width of the span for the input
$('span.width').width(); // returns a width value
using this get the width of the span and put it into the input box using JavaScript.
EDIT:
try this, if you want to grow the input based on text limit then you have to increase the size as well.
http://jsfiddle.net/nyitsol/eotd8p4n/25/
var input = $('<input onkeypress=\"this.style.width = ((this.value.length + 1) * 8) + \'px\';\" type=text>');

Floating with css percentages, full-width browsing

Original question:
Okay, this is plain and simple css. But there's a bug on my site and i can't get it out !
I making a site with this design: http://cl.ly/image/1j231y2x3w07
The site i fully responsive and makes use of the css-aspect-ratio-technique:
HTML:
<div class="post small">Post that is small</div>
<div class="post big">Post that is big</div>
<div class="post tall">Post that is tall</div>
<div class="post wide">Post that is wide</div>
CSS/LESS:
.post {position: relative;}
.post:after {display: block; content: '';}
.post.small {width: calc(1/4 * 100%);}
.post.small:after {padding-top: 70%;}
.post.big {width: calc(1/2 * 100%);}
.post.big:after {padding-top: 70%;}
.post.tall {width: calc(1/4 * 100%);}
.post.tall:after {padding-top: 140%;}
.post.wide {width: calc(1/2 * 100%);}
.post.wide:after {padding-top: 35%;}
You get the gist of it.
I also make use of the excellent plugin Packery.js to keep track of floats and position. I mostly use is as a resize and animation helper.
But being fully responsive gives me a problem. For example when the browser window is 1304px wide, i get some odd height values(ex. 477.4px) due to the technique mentioned above. Because of the odd numbers i am not able to keep my grid, let alone my design.
And if the windows width is not divisible by 4 i get overlapping or 1 pixel white lines.
I've been working on this for quite some time now, and need some fresh eyes.
So, if anybody got inputs to a solution i would be very happy. Thanks :-)
The solution:
A big thanks to #ScottS – great advice !
function frontpageResize() {
// container ... duh.
var container = $('#frontpage-wrapper');
// body width.
var bWidth = window.innerWidth;
// get the pixels missing for making bWidth divisible by 20.
var divisibleBy = bWidth % 20;
// setting an alternative width.
var altWidth = Math.ceil(bWidth - divisibleBy + 20);
// what's missing?
var leftover = altWidth - bWidth;
// if body width is divisible by 20 all is peaches?
if(divisibleBy === 0) {
container.width(bWidth);
} else {
// else set the alternative width as body width and set margin-left.
container.width(altWidth).css({ "margin-left" : -leftover / 2 + "px"});
}
// relayout Packery.js
container.packery();
}
The Only Way You Will Eliminate All Rounding Issues
To get the quartering division of width's without issues, as you noted, you need a total width that is divisible by 4. To get the height divisions with the percentages you are using you need a total width that is divisible by 20 (35% of 20 = 7, a prime number; there is no other values between 1-19 that can be multiplied by 35% without a fraction being generated).
So that means you need your widths to not shoot for 100% window width, but rather the width value that is the next smallest division by 20 (and then perhaps center your display, so that the 0 to 9.5px left over become side margins through an margin: 0 auto rule).
So your theoretical calculation you need for your width value needs to use a modulus function, something like this:
#width - mod(#width, 20) //in LESS
or
width - (width%20) //in javascript
As you can see, though, the point is you would need a dynamic action that CSS does not have available natively to make it pixel perfect.

Left align Google chart in a page using bootstrap 3?

In my page, I have the below divs
<div><h2>Test</h2></div>
<div id="chart_div" style="width: 1200px; height: 600px;"></div>
I don't have any style, other than the default provided by bootstrap 3.
But the google chart is rendered with so much empty space at left (see the screenshot)
is there a way to fix this?
Fix Update :
As per davidkonrad suggestion, I added the below option in my chart
chartArea : { left: 30, top:30 }
Now it works
It is not caused by bootstrap - can easily reproduce the behaviour in a "fresh" bootstrap 3. It is caused by the enormous width of the container.
When chartArea is not defined, then chartArea.left, top, width and height is per default set to auto, which means the chart tries to center itself inside the container, both vertically and horizontally. You can observe that yourself by setting a border around the container. Set chartArea.left to force the chart-position where you want it, for example :
var options = {
chartArea : { left: 80 }
};
or
var options = {
chartArea : { left: "10%" }
};

Twitter Bootstrap - Giving thumbnail caption a minimum number of lines

I have a carousel in Bootstrap that displays 4 columns of thumbnails. Here's the carousel in question. If you move to the third page, you can see that the container increases in height in order to accommodate the contents of the thumbnail captions. I've been trying many things such as setting bottom margins, min heights, etc. to get the position of the "View Details" button constant across the entire carousel.
My question is what is the best way to approach this issue? I was thinking somehow making the thumbnail caption height a minimum of 4 or so lines, but I tried that(probably the wrong way) to no avail.
When I add
.caption h4 {
min-height: 2.2em; /* 2 lines as line-height is 1.1 */
}
I get all "View details" at the same level. However, that obviously doesn't treat the problem of captions being even higher. It only works if no caption is higher in fact. (But it IS ok, if you know for sure nothing is going to be higher than your multiple.)
So, instead I apply this little bit of CSS to put a limit from the other side.
.caption h4 {
max-height: 4.4em; /* 4 lines as line-height is 1.1 */
height: 4.4em; /* making it a multiple allows usage of overflow */
overflow: hidden; /* without cutting a line in the middle */
}
If you want to set a max-height equal to the height of the highest of captions dynamically, than you would have to use a little bit of JS:
(function(d) {
var captions = d.querySelectorAll('.caption h4'),
height = 0;
for(var i = 0; i < captions.length; i++) {
height = Math.max(height, captions[i].offsetHeight); // or clientHeight depends on you box model
}
var style = d.createElement('style');
style.type = 'text/css';
style.innerHTML = '.caption h4 { max-height: '+ height +'px; height: '+ height +'px; }'; // they don't need overflow as none of them can overflow;
d.getElementsByTagName('head')[0].appendChild(style);
})(document);
You add this script at the end of body, so that the DOM is already loaded (or somehow trigger it onload).
Important: this snippet is not supported by older browsers because of the querySelectorAll.
And that does the trick when I run it on your site.

Resources