I use rpivottable on several (rmarkdown) web pages.
I have seen an example here of saving/restoring table configuration to/from cookie.
Since I am not good in javascript, I would like to ask if it is possible to programmatically add two buttons in the rmd page, on top of the table control, allowing the users to save/load their preferred table configurations (either to cookie, or to a local file, if possible). Could you provide sample code to achieve that?
Thanks.
This one took a while. I used local storage. I've got a lot of styling here, but it's unnecessary. I used the output of flexdashboard, since that tends to cause me the most problems with JS.
<style>
body { /*push content away from far right and left edges*/
margin-right: 2%;
margin-left: 2%;
}
.rpivotTable {
overflow:auto;
resize: both;
box-shadow: 0 22px 70px 4px rgba(0,0,0,0.56);
-moz-box-shadow: 0 22px 70px 4px rgba(0,0,0,0.56);
-webkit-box-shadow: 0 22px 70px 4px rgba(0,0,0,0.56);
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
border: 1px solid white;
padding: 5px;
margin: 5px 20px 50px 5px;
}
.btn {
vertical-align: middle;
-moz-box-shadow: 0px 10px 14px -7px #000000;
-webkit-box-shadow: 0px 10px 14px -7px #000000;
box-shadow: 0px 10px 14px -7px #000000;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
border: .5px solid black;
display: inline-block;
font-size: 1.3em;
padding: .3em 0px;
width: 18em;
text-decoration: none; /*no underline!!*/
cursor: pointer;
}
.btn:active { /*simulate movement*/
position: relative;
top: 1px;
}
</style>
I've used the content that I've found in other questions.
## R Markdown
<div style="margin-right:5%;">
`r stringi::stri_rand_lipsum(10)`
</div>
```{r cars}
library(rpivotTable)
data(mtcars)
names(mtcars)[10] <- "George.Dontas"
```
Here is the **first** Div.
## Including Plots
Do you want to save or restore the previously saved pivot tables' configuration?
<a id='saveBtn' class='btn' style="background-color:#003b70;color:white;">Save Current Configuration</a>
<a id='restoBtn' class='btn' style="background-color:#b21e29;color:white;">Restore Previous Configuration</a>
```{r pressure, echo=FALSE, fig.show="hold"}
rpivotTable(mtcars,rows="George.Dontas", cols=c("cyl","carb"),width="100%", height="400px")
```
```{r morePressure, echo=FALSE, fig.show="hold"}
rpivotTable(mtcars,rows="George.Dontas", cols=c("cyl","carb"),width="100%", height="400px")
```
This should be a different aspect of the report.
```{r evenMorePressure, echo=FALSE, fig.show="hold"}
rpivotTable(mtcars,rows="George.Dontas", cols=c("cyl","carb"),width="100%", height="400px")
```
Here is the JS/JQuery...it's a bit ugly and a rather unseemly hodgepodge of the two (JS/JQuery).
```{r listenOrElse,results="as-is",engine="js"}
// save current state of the tables to my browser
setTimeout(function(){ //add the events first
document.querySelector('a#saveBtn').addEventListener('click', savoring);
document.querySelector('a#restoBtn').addEventListener('click', giveItBack);
function savoring() { // function to save
el = document.querySelectorAll('.rpivotTable');
for(i=0; i < el.length; i++){
elId = el[i].getAttribute("id");
stringy = $('#' + elId).data("pivotUIOptions"); // collect rows/columns filters
delete stringy['aggregators']; // remove the arbitrary
delete stringy['renderers'];
stringy2 = JSON.stringify(stringy); // make it one key:value
window.localStorage.setItem('table' + i, stringy2); // store it!
}
};
function giveItBack() { // function to regurgitate
el = document.querySelectorAll('.rpivotTable');
console.log("working on the giver");
ods = [...el[0].ownerDocument.scripts]; // make it an array
for(j=0; j < el.length; j++){
elId = el[j].getAttribute("id");
where = ods.filter(function(ods){ // filter scripts for table data
return ods.dataset['for'] === elId;
})[0].innerHTML;
where2 = JSON.parse(where).x.data; // WOOO HOO! I figured it out!!
where3 = HTMLWidgets.dataframeToD3(where2); // finally sheesh!!
gimme = window.localStorage.getItem('table' + j); // get storage
$('#' + elId).pivotUI(where3, JSON.parse(gimme), true, "en"); // put it back!
}
}
},100);
```
Update
Thanks for pointing out some opportunities for improvement, #George Dontas. This update changes how the configuration is saved. I'm sure there are still ways to improve it, though.
This update adds the file or webpage name as part of the key-value pair used to store the information. Now, both the name of the webpage/script and table number need to match for the tables to update. Additionally, this will alert the user when a configuration cannot be restored. This alert would occur if there is nothing saved and if there is no file name and table matching configuration saved.
Updates to Saving the Configuration
There is one new line and one modified line of code in savoring().
New:
path = window.location.pathname.split("/").pop().split(".").slice()[0]; //f name
Modified:
window.localStorage.setItem(path + '_table' + i, stringy2); // store it
The entire function with changes:
function savoring() { // function to save
el = document.querySelectorAll('.rpivotTable');
path = window.location.pathname.split("/").pop().split(".").slice()[0];
for(i=0; i < el.length; i++){
elId = el[i].getAttribute("id");
stringy = $('#' + elId).data("pivotUIOptions"); // collect filters
delete stringy['aggregators']; // remove the arbitrary
delete stringy['renderers'];
stringy2 = JSON.stringify(stringy); // make it one key:value
window.localStorage.setItem(path + '_table' + i, stringy2); // store it
}
};
Updates to Restoring the Configuration
There are few new lines in this function. The name has to be collected, as in the savoring() changes. Additionally, this function now has an alert for the user.
I started out with the basic system alert, but it wasn't up to snuff for my tastes, so I also developed a custom alert box. I've included both here.
Basic Alert and Updated Configuration Retrieval
The only thing that changes from my original answer to making a basic alert are the following lines of code within the giveItBack() function:
path = window.location.pathname.split("/").pop().split(".").slice()[0]; //f name
and
if(window.localStorage.getItem(path + '_table' + j) === null) {
jj = j + 1;
alert("WARNING: There is no saved pivot table configuration for " + path + "'s table " + jj + ".");
continue; // don't update, go to next table (if more than 1)
}
Here is the complete giveItBack() function (note that notice(msg) and msg are here, but commented out):
function giveItBack() { // function to regurgitate
el = document.querySelectorAll('.rpivotTable');
console.log("working on the giver");
ods = [...el[0].ownerDocument.scripts]; // make it an array
path = window.location.pathname.split("/").pop().split(".").slice()[0]; //name
for(j=0; j < el.length; j++){
elId = el[j].getAttribute("id");
where = ods.filter(function(ods){ // filter scripts data
return ods.dataset['for'] === elId;
})[0].innerHTML;
where2 = JSON.parse(where).x.data; // WOOO HOO! I figured it out!!
where3 = HTMLWidgets.dataframeToD3(where2); // finally formatted
// is there a saved configuration that matches this file and table?
if(window.localStorage.getItem(path + '_table' + j) === null) {
jj = j + 1;
//this is for the standard alert box
alert("WARNING: There is no saved pivot table configuration for " + path + "'s table " + jj + ".");
//msg = "<b>WARNING</b><br><br>There is no saved pivot table configuration for<br>" + path + "."
//notice(msg); //this is for the custom alert box
continue; // go to next loop
}
gimme = window.localStorage.getItem(path + '_table' + j); // get storage
$('#' + elId).pivotUI(where3, JSON.parse(gimme), true, "en"); // put it back!
}
};
Custom Alert and Updated Configuration Retrieval
If you choose to use a more custom approach to the alert message, there is a lot more (luckily, it should be copy and paste). You will use the giveItBack function from the updates for the basic alert, but comment out or delete alert(... and uncomment msg and notice().
For the CSS in my original answer, update the styles for .btn to .btn, #noted and .btn:active to btn:active, #noted:active.
This is the remaining CSS for the custom alert. You can add this CSS to the other style tags or keep them separated.
<style>
#notice-wrapper {
width: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 1000000;
background: transparent;
display: none;
transition: opacity 1s ease-in;
}
#notice-box {
-moz-box-shadow: 0px 10px 14px -7px #000000;
-webkit-box-shadow: 0px 10px 14px -7px #000000;
box-shadow: 0px 10px 14px -7px #000000;
border-radius: 4px;
border: .5px solid black;
width = 300px;
background: #003b70;
color: white;
min-height: 200px;
position: absolute;
top: 50%;
left: 50%;
margin: -100px 0 0 -150px;
}
#notHead {
text-align: center;
font-size: 1.3em;
padding: 4px;
margin: 2.5em;
font-family: Verdana, sans-serif;
}
#noted {
background: #b21e29;
margin: .5em;
width: 120px;
font-family: Verdana, sans-serif;
}
</style>
The JS for the custom alert box is next. I placed this function within the setTimeout(function(){ with savoring() and giveItBack().
function notice(msg) {
function cr() {
if(document.querySelector('#notice-wrapper') === null) {
wrapper = document.createElement('div');
wrapper.id = 'notice-wrapper';
html = "<div id='notice-box'><h2 id='notHead'></h2><div id='noticeBtns'>";
html += "<button id='noted'>OK</button></div></div>";
wrapper.innerHTML = html;
document.body.appendChild(wrapper);
}
insta = document.querySelector('#notice-wrapper');
placer(insta);
return(insta);
}
function placer(insta) {
wrapper = insta;
winHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientheight;
wrapper.style.height = winHeight + "px";
}
function showy(el) {
el.style.display = "block";
el.style.opacity = 1;
}
function goAway(el) {
el.style.opacity = 0;
setTimeout(function(){
el.style.display = "none";
}, 1000);
}
function takeAction(msg) {
insta = cr();
insta.querySelector('#notHead').innerHTML = msg;
showy(insta);
insta.querySelector('#noted').addEventListener('click', function() {
goAway(insta);
}, false);
}
takeAction(msg);
}
Of course, with this custom option, you have the opportunity to style it as you see fit. Style control isn't an option with the system alert messaging system.
Don't mark up this answer—it's not another answer—it's an update. (And written for anyone who might read it; not just who asked...)
This updated method of saving and restoring table configuration is based on the modified version of the package rpivotTable (in Github: fraupflaume/rpivotTable).
I've kept this answer self-contained so that you would not have to look at the other answer to assemble the code.
This works for one to many rpivotTable widgets in the same RMarkdown rendered webpage.
Dynamic Sizing: This script includes dynamic resizing, limited by the available width of the webpage (it will not overlap other content, and it will not make the webpage wider). For dynamic sizing to work—
If you use the output html_document, you have to unset the max-width of the class main-container. This setting creates massive margins, which is anti-conducive to this widget.
You need to style the class .rpivotTable with overflow: auto; you cannot assign resize to this class.
If you use the output flex_dashboard, you cannot leave the default setting vertical-scroll: fit (the opposite of dynamic sizing!)
You cannot set the default height and width of the widget at the widget level. Setting this makes the size utterly static.
It is perfectly acceptable to set the height and width of the table with static or dynamic sizes when creating the table (rpivotTable()).
What did I forget?
Cookie-ish Alert: The configuration save has two options for rendering an alert (alert that indicates there was no data to restore).
System Alert: If using the system alert there are two lines of Javascript in the cookie function. The rest of the Javascript and CSS related to the alert is for the custom alert option.
Custom Alert: I'm going to touch on this from two perspectives: the CSS and the JS.
CSS: The only required CSS is ensuring the pop-up alert ack button has the style cursor: pointer. I believe that styling beyond that is strictly preference. The CSS that is related to the alert includes the following classes and ids: #noted, #notice-wrapper, notice-box, and notHead.
JS: There are just a few lines of JS in the cookie-ish function, giveItBack. The notice function for the alert box.
Cookie-ish vs. subtotal: The fix for this issue (and what would have been an issue with tsv) required a few additional lines of code in the cookie-ish function, giveItBack. savoring did not change.
Cookie-ish Alignment: Currently, the configuration is identified by the name of the file or webpage and by iteration (when you have more than one table, the order in which they appear by index). Consider the impact of reordering tables or renaming the file/page.
Dynamic Sizing Chunk
This chunk can go anywhere in your script; assumed: echo=FALSE is default, or the output option hides echoes.
```{r spicy, engine="js", include=FALSE, results="asis"}
scrp = ["https://cdnjs.cloudflare.com/ajax/libs/css-element-queries/1.2.3/ElementQueries.min.js",
"https://cdnjs.cloudflare.com/ajax/libs/css-element-queries/1.2.3/ResizeSensor.js"];
setTimeout(function(){ // this function adds the URLs to the HTML <head>
for(i=0; i < scrp.length; i++) {
script = document.createElement('script');
script.src = scrp[i];
script.type = "text/javascript";
document.head.appendChild(script);
}
ElementQueries.listen(); // just listen!!
}, 200); //wait a sec!
```
The JS for the Cookie-ish and Alerts
There should be plenty of comments to decipher the basic idea of what this does.
```{r listenOrElse,results="asis",engine="js"}
// save current state of the tables to my browser
setTimeout(function(){
// add to buttons
document.querySelector('a#saveBtn').addEventListener('click', savoring);
document.querySelector('a#restoBtn').addEventListener('click', giveItBack);
function savoring() { // function to save
el = document.querySelectorAll('.rpivotTable');
path = window.location.pathname.split("/").pop().split(".").slice()[0]; //filename
for(i=0; i < el.length; i++){
elId = el[i].getAttribute("id");
stringy = $('#' + elId).data("pivotUIOptions"); // collect rows/col filters
delete stringy['aggregators']; // remove not-parse-friendly keys
delete stringy['renderers'];
stringy2 = JSON.stringify(stringy); // one key:value pair for storage
window.localStorage.setItem(path + '_table' + i, stringy2); // STORE it!
}
};
function giveItBack() { // function to regurgitate
el = document.querySelectorAll('.rpivotTable');
console.log("working on the giver");
ods = [...el[0].ownerDocument.scripts]; // make it an array
path = window.location.pathname.split("/").pop().split(".").slice()[0]; //filename
for(j=0; j < el.length; j++){
elId = el[j].getAttribute("id");
where = ods.filter(function(ods){ // filter scripts for data
return ods.dataset['for'] === elId;
})[0].innerHTML;
where2 = JSON.parse(where).x.data; // format data for pivotUI()
where3 = HTMLWidgets.dataframeToD3(where2); // ...still formatting
if(window.localStorage.getItem(path + '_table' + j) === null) { // alert
// basic system alert
//jj = j + 1;
//alert("WARNING: There is no saved pivot table configuration for " + path + "'s table " + jj + ".");
// custom alert
msg = "<b>WARNING</b><br><br>There is no saved pivot table configuration for<br>" + path + "."
notice(msg);
// Either Alert: from here on needed whether basic or custom alert
continue;
}
gimme = window.localStorage.getItem(path + '_table' + j); // get storage
gimmeMore = JSON.parse(gimme); // prepare for recall
if(where.includes('"subtotals":true')){ // is the option 'subtotals' used?
gimmeMore.renderers = $.pivotUtilities.subtotal_renderers;
gimmeMore.dataClass = $.pivotUtilities.SubtotalPivotData;
};
if(where.includes('"tsv":true')){ // is the option 'tsv' used?
gimmeMore.renderers = $.extend(gimmeMore.renderers, $.pivotUtilities.export_renderers);
};
$('#' + elId).pivotUI(where3, gimmeMore, true, "en"); // put it back!
}
};
function notice(msg) { // all of this is for the custom alert box
function cr() {
if(document.querySelector('#notice-wrapper') === null) { // if an alert doesn't exist
wrapper = document.createElement('div');
wrapper.id = 'notice-wrapper';
html = "<div id='notice-box'><h2 id='notHead'></h2><div id='noticeBtns'>";
html += "<button id='noted'>OK</button></div></div>";
wrapper.innerHTML = html;
document.body.appendChild(wrapper);
}
insta = document.querySelector('#notice-wrapper'); // a container for the alert box
placer(insta);
return(insta);
}
function placer(insta) { // make the size reasonable, based on viewing screen
wrapper = insta;
winHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientheight;
wrapper.style.height = winHeight + "px";
}
function showy(el) {
el.style.display = "block"; // keep the content confined; add a control
el.style.opacity = 1;
}
function goAway(el) { // use the control to hide the alert when ack
el.style.opacity = 0;
setTimeout(function(){
el.style.display = "none";
}, 1000);
}
function takeAction(msg) { // use all of the above: make alert and render it
insta = cr();
insta.querySelector('#notHead').innerHTML = msg;
showy(insta);
insta.querySelector('#noted').addEventListener('click', function() {
goAway(insta);
}, false);
}
takeAction(msg); // pop-up ENGAGED
}
},200); // give me a sec—my browser may be slow... or my widgets may be fast... or...
```
The CSS
This will not go into an R chunk. This is all of the CSS used in my example. I did not use an external style sheet, although that is a better practice.
This CSS controls the alert, the buttons used for saving and retrieving the configuration, along with body and margin styles. I have included the CSS to make the pivot tables pop out, but it is commented out (these are /* CSS commenters */). You can use/not use at your discretion.
<style>
body { /*push content away from far right and left edges*/
margin-right: 2%;
margin-left: 2%;
}
.main-container {
max-width: unset; // remove default from RMD
}
.rpivotTable {
overflow:auto; /*this is absolutely needed*/
/*resize: both; <- cannot have this*/
/*box-shadow: 0 22px 70px 4px rgba(0,0,0,0.56);
-moz-box-shadow: 0 22px 70px 4px rgba(0,0,0,0.56);
-webkit-box-shadow: 0 22px 70px 4px rgba(0,0,0,0.56);
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
border: 1px solid white;
padding: 5px;
margin: 5px 20px 50px 5px;
max-width: 1100px;*/
}
.btn, #noted {
vertical-align: middle;
-moz-box-shadow: 0px 10px 14px -7px #000000;
-webkit-box-shadow: 0px 10px 14px -7px #000000;
box-shadow: 0px 10px 14px -7px #000000;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
border: .5px solid black;
display: inline-block;
font-size: 1.3em;
padding: .3em 0px;
width: 18em;
text-decoration: none; /*no underline!!*/
cursor: pointer;
}
.btn:active, #noted:active { /*simulate movement*/
position: relative;
top: 1px;
}
#notice-wrapper {
width: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 1000000;
background: transparent;
display: none;
transition: opacity 1s ease-in;
}
#notice-box {
-moz-box-shadow: 0px 10px 14px -7px #000000;
-webkit-box-shadow: 0px 10px 14px -7px #000000;
box-shadow: 0px 10px 14px -7px #000000;
border-radius: 4px;
border: .5px solid black;
width = 300px;
background: #003b70;
color: white;
min-height: 200px;
position: absolute;
top: 50%;
left: 50%;
margin: -100px 0 0 -150px;
}
#notHead {
text-align: center;
font-size: 1.3em;
padding: 4px;
margin: 2.5em;
font-family: Verdana, sans-serif;
}
#noted {
background: #b21e29;
margin: .5em;
width: 120px;
font-family: Verdana, sans-serif;
}
</style>
R <- that too... Well, the rest of the RMD...
Here is the code for the RMD I use as an example. The only thing that I didn't include are my version notes (at 16 now...).
---
title: "rpivottable_test"
output: html_document
---
```{r setup,include=F}
knitr::opts_chunk$set(echo = FALSE)
```
```{r data,include=F}
# devtools::install_github("fraupflaume/rpivotTable")
library(rpivotTable)
data(mtcars)
names(mtcars)[10] <- "George.Dontas"
```
## Make it Interesting...or not
Do you want to save or restore the previously saved pivot tables' configuration?
<!--- cookie-ish's buttons --->
<a id='saveBtn' class='btn' style="background-color:#003b70;color:white;">Save Current Configuration</a>
<a id='restoBtn' class='btn' style="background-color:#b21e29;color:white;">Restore Previous Configuration</a>
```{r showMe, echo=FALSE, fig.show="hold"}
rpivotTable(mtcars,rows="George.Dontas", cols = c("cyl"), width = "90%", height = "40%",
rendererOptions = list(
c3 = list(legend = list(show = FALSE),
data = list(labels = TRUE),
options = list(responsive = TRUE,
maintainAspectRatio = FALSE),
size = list(width = "600",
height = "500")),
d3 = list(size = list(width = "500", height = "500"))))
```
`r stringi::stri_rand_lipsum(3)`
## How about Another Table?
Tell me things. Make sure I am not going to overlap later. You better be listening!
```{r morePressure, echo=FALSE, fig.show="hold"}
rp <- rpivotTable(mtcars, rows = c("mpg", "am"), cols = "cyl",
width = "90%", height = "40%", subtotals = T,
tsv = T,
rendererOptions = list(
c3 = list(legend = list(show = FALSE), # this works!!
data = list(labels = TRUE),
size = list(width = "600",
height = "500"),
options = list(responsive = TRUE,
maintainAspectRatio = FALSE))))
rp
```
This should be *anywhere* other than here.
```{r itsMine, echo=FALSE, fig.show="hold"}
df1 <- data.frame(where = LETTERS[1:3], what = c(3.6, 5.6, 1.1))
x = rpivotTable(df1, width="80%", height="40%",
aggregatorName = "Count",
vals = "Sum",
cols = "where",
rows = "what",
rendererOptions = list(c3 = list(legend = list(show = FALSE), # this works!!
data = list(labels = TRUE),
size = list(width = "500",
height = "500"),
options = list(responsive = TRUE,
maintainAspectRatio = FALSE))))
x
```
Put something here
I only controlled d3 size in the first table. The next image reflects the difference between control and uncontrolled Treemap sizing.
I zoomed out in my browser so I could see the limits of the second treemap:
While I tried to provide enough information that anyone who came across this Q&A could potentially use it, I anxiously await when the questioner finds a way to break it :)
I have two custom elements
<desktop-canvas id="desktop">
#shadow-root (open)
<desktop-window>
</desktop-window>
<desktop-canvas>
I'm trying to style <desktop-window> like so:
#desktop::shadow desktop-window {
background-color: red;
padding: 25px;
margin: 25px;
display: block;
}
But desktop-window dosen't receive the style. What am I doing wrong? The same syntax seems to be working in this codepen (not by me): https://codepen.io/matt-west/pen/FtmBL
As announced here...
Starting in Chrome 63, you cannot use the shadow-piercing selectors ::shadow and /deep/ to style content inside of a shadow root.
According to that page you are only affected if you use Shadow DOM v0 components. You either use the shady DOM polyfill, switch to Shadow DOM v1 components or place the styles inside the component and use :host:
var XProductProto = Object.create(HTMLElement.prototype);
XProductProto.createdCallback = function() {
var shadow = this.createShadowRoot();
var img = document.createElement('img');
img.alt = this.getAttribute('data-name');
img.src = this.getAttribute('data-img');
img.width = '150';
img.height = '150';
img.className = 'product-img';
shadow.appendChild(img);
img.addEventListener('click', function(e) {
window.location = this.getAttribute('data-url');
});
var link = document.createElement('a');
link.innerText = this.getAttribute('data-name');
link.href = this.getAttribute('data-url');
link.className = 'product-name';
shadow.appendChild(link);
var styleEl = document.createElement('style');
styleEl.innerHTML = `
:host .product-img {
cursor: pointer;
background: #FFF;
margin: 0.5em;
}
:host .product-name {
display: block;
text-align: center;
text-decoration: none;
color: #08C;
border-top: 1px solid #EEE;
font-weight: bold;
padding: 0.75em 0;
}`;
shadow.appendChild(styleEl);
};
var XProduct = document.registerElement('x-product', {
prototype: XProductProto
});
body {
background: #F7F7F7;
}
x-product {
display: inline-block;
float: left;
margin: 0.5em;
border-radius: 3px;
background: #FFF;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
font-family: Helvetica, arial, sans-serif;
-webkit-font-smoothing: antialiased;
}
<x-product data-name="Ruby" data-img="https://s3-us-west-2.amazonaws.com/s.cdpn.io/4621/ruby.png" data-url="http://example.com/1"></x-product>
<x-product data-name="JavaScript" data-img="https://s3-us-west-2.amazonaws.com/s.cdpn.io/4621/javascript.png" data-url="http://example.com/2"></x-product>
<x-product data-name="Python" data-img="https://s3-us-west-2.amazonaws.com/s.cdpn.io/4621/python.png" data-url="http://example.com/3"></x-product>
CSS Scoping Module Level 1 provides an answer to: Why is the shadow host so weird?:
The shadow host lives outside the shadow tree, and its markup is in control of the page author, not the component author.
It would not be very good if a component used a particular class name internally in a shadow tree stylesheet, and the page author using the component accidentally also used the the same class name and put it on the shadow host. Such a situation would result in accidental styling that is impossible for the component author to predict, and confusing for the page author to debug.
I would like to apply exactly the same styles to a styled input element and a styled select element.
Currently, I'm using string interpolation to do this:
const styles = `
background-color: white;
width: 100%;
border: 0 solid transparent;
border-radius: 10px;
margin-bottom: 20px;
padding-top: 5px;
padding-bottom: 5px;
font-size: 1.2rem;
`
const Select = styled.select`${styles}`
const Input = styled.input`${styles}`
Is there a better way of doing this which doesn't involve using a 'raw' string? The disadvantage of using the raw styles string is that Visual Studio Code doesn't syntax-highlight it:
You have few options here:
css helper function:
const styles = css`
background-color: white;
// ...
`;
const Select = styled.select`${styles}`;
const Input = styled.input`${styles}`;
"as" polymorphic prop (added in v4):
<Select as="input">
...
</Select>
withComponent method (candidate for deprecation):
const Select = styled.select`
background-color: white;
// ...
`;
const Input = Select.withComponent('input');
You can use the css tagged template literal:
import styled, { css } from "styled-components";
const styles = css`
background-color: white;
width: 100%;
`;
const Select = styled.select`${styles}`;
const Input = styled.input`${styles}`;
That should get properly syntax highlighted (haven't tested).
I am building an app in Shiny and would like to interface it with metafizzy's Flickity carousel. On a single page app flickity works well using the initializing code in the example below.
However, the app I am creating uses a navbar. It seems that when I initialize the carousel it works well on the first tab, but carousels on the second tab (or other) will not initialize. However, if you are on Tab 2 (for example) and resize the window, the carousel seems to initialize and all runs smoothly on that Tab, but this will turn off / hide the other carousels on the other tabs.
I am wondering if there is a way to change the script so that all carousels on all tabs will be initialized and available when the page loads? I am very new to javascript so apologies if this is a simple question.
Thank you
ui:
library(shiny)
shinyUI(tagList(
tags$head(
includeCSS("www/flickity-docs.css"),
includeCSS("www/styles.css"),
includeScript("www/flickity-docs.min.js")
),
navbarPage(' ',
position = ('fixed-top'),
collapsible = T,
# Panel 1 =====================================================
tabPanel('Page 1',
div(class = "main-carousel",
div(class = "carousel-cell",
div(class = "carousel-inner",
h2('This text on page 1.1 is appearing')
)
)
)
),
# Panel 2 =====================================================
tabPanel('Page 2',
div(class = "main-carousel",
div(class = "carousel-cell",
div(class = "carousel-inner",
h2('This text on page 2.1 is not appearing')
)
)
)
)
),
HTML(
"<script type='text/javascript'>
// selectorAll for multiple flickity
var elems = document.querySelectorAll('.main-carousel');
// loop to initialize
for ( var i=0, len = elems.length; i < len; i++ ) {
var elem = elems[i]
new Flickity( elem, {
// options
cellAlign: 'center',
wrapAround: true,
cellSelector: '.carousel-cell',
});
}
</script>"
)
)
)
server (blank for now):
server <- function(input, output, session) {
}
CSS:
.carousel-cell {
width: 100%;
height: 100px;
margin-right: 10px;
padding: 0%;
line-height:0px;
background-color: black;
display: block;
}
.carousel-cell:before {
content: normal;
}
.carousel-inner{
width: 100%;
height: 100%;
background-color: black;
color: white;
content: normal;
padding-right: 12.5%;
padding-left: 12.5%;
padding-top: 5%;
padding-bottom: 5%;
border-radius: 5px;
font-size:20px;
}
i am working in extjs4. i have view with ganttChart. I want to change color of splitter line which is between treecolumn and gantt. i am creating gantt as=
var gantt = Ext.create("Gnt.panel.Gantt", {
itemId :'project-list-gantt-chart',
cls : 'projectlistganttchart',
enableTaskDragDrop: false,
columnLines :true,
lockedGridConfig :{
split: false
},
viewPreset : 'customPreset',
columns : [{
xtype : 'treecolumn',
sortable: true,
dataIndex: 'Name',
}],
And i am including this gantt in my following form panel=
Ext.define('GanttChart' ,{
extend: 'Ext.form.Panel',
border: false,
})
By using panel's add method i am including it as-
afterrender : function(){
var me = this;
me.add(gantt);
}
So for changing color, i change css as-
.x-panel-default{
border-color: red;
padding: 0;
}
But its changing color of all panels within my project. So how to override this css so that it will change color of only gantt.
Use the ui cfg param for that:
// add this config line to your panel that host the gantt
ui: 'gantt'
and add this css
.x-panel-gantt{
border-color: red;
padding: 0;
}
Note that the ui is meant for exactly such things.
You might want to refer to this fiddle. Note that it may miss some css classes but I guess it should show you the trick. And please note that you will need to set the border in a correct way because based on the theme the border might be 0px. That is also done in the example. In addition here is the demo code:
Javascript
Ext.create('Ext.panel.Panel', {
title: 'test',
height: 200,
width: 200,
ui: 'custom',
renderTo: Ext.getBody()
})
CSS (just copied from developer tools)
.x-panel-body-custom {
color: black;
font-size: 13px;
font-size: normal;
}
.x-panel-custom{
border: 1px solid red;
padding: 0;
}
.x-panel-body-custom {
background: white;
border-color: #157fcc;
color: black;
font-size: 13px;
font-size: normal;
border-width: 1px;
border-style: solid;
}
.x-panel-header-custom {
background-image: none;
background-color: #157fcc;
}
.x-panel-header-custom-horizontal-noborder {
padding: 10px 10px 10px 10px;
}
.x-panel-header-custom-horizontal {
padding: 9px 9px 10px 9px;
}
You can use id of the component(panel) itself or any top level id of the component in which the panel is rendered like
#toplevelid .x-panel-default{
border-color: red;
padding: 0;
}
You should be able to get your gantt item and change the css class of it using something like:
var ganttItem = Ext.getCmp('your item id');
ganttItem.addCls('your css class for specifc color');
I haven't tested this but you can get the idea.
If you want to reset the css class for the item use .setCls() to set it up from scratch.