How to color table rows with 3 alternate color - row

I want a table to display with 3 alternate colors (1-black,2-red,3-white,4-black, 5-red,6-white ....) i tried with nth-child(even) and nth-child(odd)
but how to get alternate 3 row colors

Following to the w3c, you try this:
tr:nth-child(3n+1) {
background-color: black;
}
tr:nth-child(3n+2) {
background-color: red;
}
tr:nth-child(3n) {
background-color: white;
}

Related

Cell colouring in Shiny Rendertable when using shiny modules

I have been using the guidance here to colour the cells of my table in based on the number in the cell. However the whole table is currently displaying in the colour selected and not just the one cell.
This is what is currently outputting
I believe the issue with this is that my shiny app is built in modules.
This is the code in my DriversTable module:
# UI ----
topDriversTableUI <- function(id) {
tagList(
div(
style = "text-align: left; font-size: 120%",
h4(strong("Social risk")),
p("This section of the tool looks exclusively at the reasons why a neighbourhood is socially vulnerable.")
),
textOutput(NS(id, "lsoas_clicked_name")),
br(),
# dataTableOutput(NS(id, "drivers_table_domains")),
fluidRow(box(
tableOutput(NS(id, "top_drivers_table_domains")),
title = "Overarching reasons why the neighbourhood is socially vulnerable to flooding",
solidHeader = TRUE,
width = 11,
status = "primary",
collapsible = TRUE
)),
fluidRow(box(
tableOutput(NS(id, "top_drivers_table_variables")),
title = "Underlying reasons why the neighbourhood is socially vulnerable to flooding",
solidHeader = TRUE,
width = 11,
status = "primary",
collapsible = TRUE)
),
tags$head(tags$style("#top_drivers_table_variables td{
position:relative;
};
"))
)
}
# Server ----
topDriversTableServer <- function(id,
vuln_drivers,
lsoas_clicked,
selected_ltlas) {
# Checks to ensure the inputs are reactive (data not reactive)
stopifnot(is.reactive(lsoas_clicked))
moduleServer(id, function(input, output, session) {
observeEvent(
lsoas_clicked(),
{
top_drivers_data <- reactive({
vuln_drivers |>
dplyr::filter(lsoa11_name %in% lsoas_clicked()) |>
# explain the concept of quantiles in plain language
# variable_quantiles = 1 means in top 10% worst scoring neighborhoods nationally
mutate(quantiles_eng = case_when(
quantiles_eng <= 5 ~ '<div style="width: 100%; height: 100%; z-index: 0; background-color: red; position:absolute; top: 0; left: 0; padding:5px;">
<span>1</span></div>',
quantiles_eng > 5 ~ '<div style="width: 100%; height: 100%; z-index: 0; background-color: green; position:absolute; top: 0; left: 0; padding:5px;">
<span>1</span></div>')
) |>
select(
`Rank` = normalised_rank,
`Driver of flooding vulnerability` = domain_variable_name,
`Domain or variable` = domain_variable,
`Comparison of value nationally` = quantiles_eng
# `Values` = values
) |>
arrange(`Domain or variable`, Rank) |>
mutate(Rank = if_else(is.na(Rank), "-", as.character(Rank))) |>
mutate(`Comparison of value nationally` = if_else(is.na(`Comparison of value nationally`), "No data available", `Comparison of value nationally`))
})
output$top_drivers_table_domains <- renderTable({
top_drivers_data() |>
filter(`Domain or variable` == "domain") |>
select(-`Domain or variable`)
}, sanitize.text.function = function(x) x)
output$top_drivers_table_variables <- renderTable({
top_drivers_data() |>
filter(`Domain or variable` == "variable") |>
select(-`Domain or variable`)
}, sanitize.text.function = function(x) x)
output$lsoas_clicked_name <- renderText({
# Message to user if no LSOAs selected ----
# Blank since error message captured in 'top_drivers_table' object
validate(need(
length(lsoas_clicked()) > 0,
""
))
paste("Neighbourhood: ", lsoas_clicked())
})
},
ignoreNULL = FALSE # means event triggered when the input (i.e. lsoa_clicked()) is NULL. Needed to trigger the validate message
)
observeEvent(
selected_ltlas(),
{
lsoas_clicked(NULL)
}
)
})
}
I believe the issue is coming from this part of the code:
tags$head(tags$style("#top_drivers_table_variables td{
position:relative;
};
"))
I believe it is this part of the code because if I run this test code and commented out that part then a similar issue occurs:
test <- data.frame(test1 = c(1:3), test2 = c(4:6))
test[test$test2 == 5, "test2"] <- '<div style="width: 100%; height: 100%; z-index: 0; background-color: green; position:absolute; top: 0; left: 0; padding:5px;">
<span>1</span></div>'
test[test$test2 == 4, "test2"] <- '<div style="width: 100%; height: 100%; z-index: 0; background-color: red; position:absolute; top: 0; left: 0; padding:5px;">
<span>1</span></div>'
library(shiny)
ui <- shinyUI(fluidPage(
box(tableOutput("tt"), title = "title"),
# tags$head(tags$style("#tt td{
# position:relative;
# };
#
# "))
)
)
server <- shinyServer(function(input, output) {
output$tt <- renderTable({
test
}, sanitize.text.function = function(x) x)
})
shinyApp(ui = ui, server = server)
When using module, you must use ns() around your objects IDs in your module. This ns() is adding the module id to the object id.
Which mean your object #top_drivers_table_variables actually is #moduleid-top_drivers_table_variables in your HTML code when it is inside a module.
So to add some CSS to it, you need to add the module id to it.
I think doing something like this should solve the problem
tags$head(tags$style(paste0("#",NS(id, "top_drivers_table_variables"), "td{
position:relative;
};
")))
To make some tests on what is really the id of an object when using modules, and especially nested modules, you can launch your app, and on you browser do a right click on the object and click "Inspect'. Then the HTML and CSS code of the app will appear on a panel on your browser. You can then verify what is really the id of you object.

Blazor ChartJs: Expanding bar to the end of the canvas

Using the Blazor.ChartJs library, I have a series of horizontal stacked bar charts, the purpose of which is to show the percentage complete. The red portion is the yearly goal and the blue is actual number achieved so far:
The first chart could have a goal of $10,000,000, the second $3,000,000, the third $7,000,000, etc. But I would like each chart to expand to the end of the table cell in which it is contained because I don't care about the numbers, just the percentages:
As you can tell in the first chart, the numbers are stopping before the end of the cell. I am adding the charts to HTML table cells, one for each statistic I'm tracking:
<table>
<tbody>
<tr>
<td></td>
<td>% Complete</td>
</tr>
#for (int i = 0; i < typeOfProject.Count; i++)
{
<tr>
<td><Chart Config="_configs[i]" Height="8" /></td>
<td>#String.Format("{0:P0}", percentagesComplete[i])</td>
</tr>
}
</tbody>
</table>
I build the _configs collection using a method that returns a BarConfig options, and the configurations are identical for all charts. I have experimented with the Responsive and MaintainAspectRatio properties, but this does not seem to affect the results.
config = new BarConfig
{
Options = new BarOptions
{
MaintainAspectRatio = true,
Responsive = true,
Legend = new Legend
{
Display = false
},
Scales = new BarScales
{
XAxes = new List<CartesianAxis>
{
new BarLinearCartesianAxis
{
Stacked = true,
OffsetGridLines = false,
Offset = false,
Display = AxisDisplay.False
}
},
YAxes = new List<CartesianAxis>
{
new BarCategoryAxis
{
Stacked = true,
OffsetGridLines = false,
Offset = false,
Display = AxisDisplay.False,
BarThickness = 45
}
}
}
}
I have also add a div tag around the Canvas object in each cell as mentioned in this question, but that wasn't the solution to my problem.
How can I expand the bars to the end of the canvas in each cell?
I created my own code to do this because I could not get the chart width to consistently expand to the end of the table cell. The requirements changed slightly as well, so I needed to draw an empty rectangle representing 100% of the goal and then filling the rectangle with a percentage of how complete the goal was.
First, I have a JavaScript method that gets the width of the canvas, calculates how many pixels to draw based on that width and the "value" variable (which is a percentage), then uses the context to first draw a clear rectangle. It draws a filled rectangle representing the progress to the goal:
function buildCanvas(canvasId, value) {
var canvasWidth = document.getElementById(canvasId).offsetWidth;
var calculatedWidth = parseInt(canvasWidth * value);
var canvasToDraw = document.querySelector('#' + canvasId);
const ctx1 = canvasToDraw.getContext('2d');
ctx1.clearRect(0, 0, 300, 150);
ctx1.fillStyle = 'rgba(173, 116, 76, 1.0)';
ctx1.fillRect(0, 0, calculatedWidth, 150);
}
I define the chart that I want to draw:
<canvas id="chart" style="border: 1px solid #AD744C; height: 18px; width: 300px">
I then inject IJSRuntime and call the JavaScript method to draw the rectangle, the value being the goal divided by the value to get a percentage:
#inject IJSRuntime JsRuntime
...
JsRuntime.InvokeAsync<string>("buildCanvas", $"chart", $"{value}");

QPushButton background-color overlaps border

I am using QPushButton class and adding the object to a QGraphicsProxyWidget after setting the style sheet. However, when I set a border-radius, the background-color still overlaps the border. How do I get rid of this?
Example: (where levelOneEasyProxy is QGrpahicsProxyWidget)
QPushButton* levelOneEasyButton = new QPushButton();
levelOneEasyButton->setGeometry(QRect(sceneRect().width()*0.05, sceneRect().height()*0.2, 70, 50));
levelOneEasyButton->setText("1");
levelOneEasyButton->setStyleSheet("QPushButton {"
"background-color: rgb(92, 249, 158);"
"color: white;"
"font-size: 16px;"
"border-style: solid;"
"border-width: 2px;"
"border-radius: 10px;"
"}"
"QPushButton:pressed {"
"background-color: rgb(66, 191, 118);"
"}");
levelOneEasyProxy = addWidget(levelOneEasyButton);
levelOneEasyProxy->setZValue(10.0);
Current Result:
I discovered that using the code above will set the border-radius but the background needs to be set to translucent by using setAttribute on the QPushButton. This can be done with the following code:
qPushButtonObject->setAttribute(Qt::WA_TranslucentBackground);
This will get rid of the overlapping background-color.

Swift - Add attributed string to items in parenthesis

I'm trying to display text to a view from a set of definitions and in cases where that text/string has parenthesis, I would like to display that parenthesis element in a different way - let's say bolded vs non bolded.
"This string a"
"This string b (has parenthesis)" - parenthesis show in lower
weight font
Now I'm aware that the solution is found by combining regular expressions - \(\w*\) - with attributed strings, but I haven't been able to combine it meaningfully.
This is my function that prints the words
func setWord(_ index:Int) {
if (index < 0) { return }
let word:[String:AnyObject] = self.definitions[index]
if let wordLabelText = word[self.store.sourceLanguage.lowercased()] as? String {
self.wordLabel.attributedText = NSMutableAttributedString(string: wordLabelText, attributes: [NSKernAttributeName: 1.0])
}
print(word)
self.definitionView.word = word
}
What I've done so far is added some more definitions for the attributed strings output, but then I'm not sure how to continue:
func setWord(_ index:Int) {
if (index < 0) { return }
let font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.headline)
let fontSize = font.pointSize * 3
let plainFont = UIFont(name: "X-BoldItalic", size: fontSize)
let boldFont = UIFont(name: "X-Italic", size: fontSize)
let word:[String:AnyObject] = self.definitions[index]
if let wordLabelText = word[self.store.sourceLanguage.lowercased()] as? String {
self.wordLabel.attributedText = NSMutableAttributedString(string: wordLabelText, attributes: [NSKernAttributeName: 1.0, NSFontAttributeName: boldFont])
Here I'd need to loop through the words looking for the (elements), but I'm not sure how to do that and properly return the words. Am I on the right path? Thanks a bunch :)
A solution in Objective-C, with explained logic that should be easily translated in Swift.
NSDictionary *defaultAttributes = #{NSFontAttributeName: [UIFont boldSystemFontOfSize:15]};
NSDictionary *otherAttributes = #{NSFontAttributeName: [UIFont systemFontOfSize:12]};
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"\\(.*?\\)" options:0 error:nil];
NSString *initialString = #"This string a (has parenthesis), This string b (has parenthesis too), This string C hasn't.";
NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithString:initialString attributes:defaultAttributes];
NSLog(#"Attr: %#", attr);
NSArray *allMatches = [regex matchesInString:[attr string] options:0 range:NSMakeRange(0, [attr length])];
for (NSTextCheckingResult *aResult in allMatches)
{
[attr addAttributes:otherAttributes range:[aResult range]];
}
NSLog(#"Attr: %#", attr);
Logs:
$> Attr: This string a (has parenthesis), This string b (has parenthesis too), This string C hasn't.{
NSFont = "<UICTFont: 0x14663df0> font-family: \".SFUIText-Semibold\"; font-weight: bold; font-style: normal; font-size: 15.00pt";
}
$> Attr: This string a {
NSFont = "<UICTFont: 0x14663df0> font-family: \".SFUIText-Semibold\"; font-weight: bold; font-style: normal; font-size: 15.00pt";
}(has parenthesis){
NSFont = "<UICTFont: 0x14666260> font-family: \".SFUIText\"; font-weight: normal; font-style: normal; font-size: 12.00pt";
}, This string b {
NSFont = "<UICTFont: 0x14663df0> font-family: \".SFUIText-Semibold\"; font-weight: bold; font-style: normal; font-size: 15.00pt";
}(has parenthesis too){
NSFont = "<UICTFont: 0x14666260> font-family: \".SFUIText\"; font-weight: normal; font-style: normal; font-size: 12.00pt";
}, This string C hasn't.{
NSFont = "<UICTFont: 0x14663df0> font-family: \".SFUIText-Semibold\"; font-weight: bold; font-style: normal; font-size: 15.00pt";
}
What is the idea:
Create a NSMutableAttributedString with the "default" attributes (in our case bold font with "big size").
Then create a NSRegularExpression and find all occurrences.
You'll add the attributes (in our case small and normal font) at that occurrence range.
In our case, it works simply, because since you can only have one attribute per kind maximum at a specific range, the NSFontAttributeName attribute will be replaced for that range.
If you added more attributes, and want to remove them, you may need to not call addAttributes:range:, but replaceCharactersInRange:withAttributedString: instead:
NSAttributedString *replacement = [[NSAttributedString alloc] initWithString:[[attr string] substringWithRange:[aResult range]]
attributes:otherAttributes];
[attr replaceCharactersInRange:[aResult range] withAttributedString:replacement];
Edit: Swift 3 Version
Nota Bene: I'm clearly not a Swift Developer, this code seems to work, but clearly, I "write" Swift like I write Objective-C, and many things since I don't use them daily and didn't read the doc are wrongly done (like the conversion/cast/explicit type/class, the "as", the "!", the "?", etc.), but it could be a start for you.
If you are a Swift developer and spots issues, feel free to comment the post and suggest your modifications. If you're just here because you have the same issue, don't forget to read in the comment if there are more Swifty things to fix in my pseudo code.
let defaultAttributes:[String:Any] = [NSFontAttributeName:UIFont.boldSystemFont(ofSize:15)];
let otherAttributes:[String:Any] = [NSFontAttributeName:UIFont.systemFont(ofSize:12)];
do {
let regex = try NSRegularExpression.init(pattern: "\\(.*?\\)", options: [])
let initialString:String = "This string a (has parenthesis), This string b (has parenthesis too), This string C hasn't."
let attr = NSMutableAttributedString.init(string: initialString, attributes: defaultAttributes)
print("Attr\(attr)");
let allMatches:[NSTextCheckingResult] = regex.matches(in: attr.string, options:[], range: NSRange(location: 0, length: attr.string.characters.count))
for aResult in allMatches
{
let occurrence = (attr.string as NSString).substring(with: aResult.range)
let replacement = NSAttributedString.init(string: occurrence , attributes: otherAttributes)
attr.replaceCharacters(in: aResult.range, with: replacement)
//attr.addAttributes(otherAttributes, range: aResult.range)
}
print("Attr\(attr)");
} catch let regexError {
print(regexError)
}

Truncating paragraph text with a fixed hight and no bleed

See attached image. (http://i.imgur.com/SWlUllK.jpg)
I have three adjacent paragraphs which I would like to truncate before the "Read More" call-to-action.
The catch is that each of the articles has a fixed height so that my truncation buttons ("Read More") can line up on the same horizontal as you can see in the image.
The problem I'm facing here is that the length of the excerpt is dependant on the length of the heading text. If there is a three line heading then the length of the excerpt will be shorter due to the increased height that the header is taking up.
At the moment I'm using some jQuery to do character count and truncate after X words. It's occurred to me that that is not a viable solution.
I couldn't figure out a way to use CCS either because the button at the bottom is absolutely positioned so overflow:hidden ignores the height of the button and just flows behind it cutting it off at the end of the article.
The only thing that I've thought of is forcing the article to be a specific height and then moving the "Read More" button outside the article tag but that doesn't seem like good semantics to me.
Any thoughts on this?
Sass:
article {
border-right: 1px solid #e7e7e7;
height: 506px;
position: relative;
p {
font-size: emCalc(16px);
overflow: hidden;
margin-bottom: 0.6em;
max-height: 255px;
}
p + a {
position: absolute;
bottom: 0;
display: block;
background: $lightBlue;
padding: 0.8em;
color: #fff;
text-align: center;
}
}
I good way of doing it is having "overflow-y: scroll" on your article HTML element and then removing text until the ".scrollHeight == .offsetHeight" of the HTML element.
http://jsfiddle.net/qdxTj/
Here's the straight JavaScript.
var all = document.getElementsByTagName("div");
for(var i=0; i<all.length; i++) {
var article = all[i];
if(article.className && /(^|\s)article($|\s)/.test(article.className)) {
article.scrollTop = 1;
var cnt = 0;
while(article.scrollTop != 0 || article.scrollHeight != article.offsetHeight) {
cnt++; if(cnt > 50) break;
var ps = article.getElementsByTagName("p");
if(ps.length == 0)
break;
var p = ps[ps.length - 1];
var shorter = p.innerHTML;
var idx = shorter.lastIndexOf(" ");
shorter = idx >= 0 ? shorter.substring(0, idx) : "";
p.innerHTML = shorter;
if(p.innerHTML.length == 0)
article.removeChild(p);
article.scrollTop = 1;
}
article.style.overflowY = "hidden";
}
}