英文:
Use several selection colors in DT::datatable?
问题
我已经设置了:
output$tableId <-
DT::renderDataTable({DT::datatable(..., selection = "multiple")})
我希望选择的背景颜色根据选择是第一个、第二个等而有所变化。
可以使用length(input$tableId_rows_selected)
(在这里描述)来实现这个行为吗?我猜想可以与创建一些修改以下CSS的代码结合使用:
.table.dataTable tbody td.active, .table.dataTable tbody tr.active td {
background-color: #007bff;
color: white;
}
我对CSS、HTML和JavaScript了解甚少,所以这些对我来说很困难。
编辑 2022-02-14:
在@StéphaneLaurent的初步答案之后,我想稍微修改我的问题:
我最喜欢的逻辑是:
- 我们有
m
种唯一颜色。之前已选择了n < m
行。 - 第
n+1
行被选择后,会以第n+1
种颜色着色,并保持此颜色直到取消选择。 - 当当前选择了
m
行时,不再可以选择更多行。或者,我也对以下情况感兴趣:当选择第m+1
行时,它将以第一种颜色着色,并且占据此颜色的其他行将被取消选择。现在选择了m+1
行,当选择第m+2
行时,它将以第二种颜色着色,并且占据此颜色的其他行将被取消选择。以此类推。
此外:
- 由于我的应用将通过网站上的iframe运行,并且应用的底层数据将有1000万到1亿条观察结果,我认为使用
DT::renderDataTable({...}, server = TRUE)
的解决方案可能比较好。 - 我还希望解决方案能够与
DT::datatable(..., selection = list(mode = "multiple", selected = 1:3, target = "row"))
选项一起使用。
我将尝试使用@StéphaneLaurent的答案,并结合@YihuiXie在这里中示例的工具(链接的页面中的应用程序)来实现上述要求。
英文:
I have set:
output$tableId <-
DT::renderDataTable({DT::datatable(..., selection = "multiple")})
And I would like the background color of the selection to vary depending on of it's the first selection, or the second, etc.
It should be possible to use length(input$tableId_rows_selected)
(which is described here) to obtain this behaviour? I'm guessing in combination with creating some CSS modifying this:
.table.dataTable tbody td.active, .table.dataTable tbody tr.active td {
background-color: #007bff;
color: white;
}
I know very little CSS, HTML, and JavaScript, so I find these things difficult.
EDIT 2022-02-14:
After @StéphaneLaurent initial answer below, I want to change my question slightly:
The logic I would prefer the most is:
- We have
m
unique colors.n < m
previous rows have been selected. - The
n+1
th row gets selected and is then coloured in then+1
th color and keeps this color until deselected. - When
m
rows are currently selected, no more selections are possible. Alternatively, I would also be interested in: When them+1
th row gets selected, it gets coloured in the first color, and other row occupying this color gets deselected.m+1
rows are now chosen, and when them+2
th row gets selected, it gets coloured in the second color, and the other row occupying this color gets deselected. And so on.
Moreover:
- Since my app will be running through an iframe on a website, and the underlying data of the app will have 10 to 100 million observations, I think a solution where
DT::renderDataTable({...}, server = TRUE)
would be good. - I also want the solution to work with the
DT::datatable(..., selection = list(mode = "multiple", selected = 1:3, target = "row"))
option.
I will try to implement the above using @StéphaneLaurent answer, combined with the tools @YihuiXie illustrate here (app from page linked earlier).
答案1
得分: 1
以下是翻译好的部分:
如果您使用以下的CSS,那么当您选择时,第一行将变为红色,第二行将变为绿色,第三行将变为蓝色:
#dtable tbody tr.selected:nth-of-type(1) td {
box-shadow: inset 0 0 0 9999px red;
}
#dtable tbody tr.selected:nth-of-type(2) td {
box-shadow: inset 0 0 0 9999px green;
}
#dtable tbody tr.selected:nth-of-type(3) td {
box-shadow: inset 0 0 0 9999px blue;
}
这里的 dtable
是ID。
以下是如何使用 sass 包生成具有随机颜色的100行CSS的方法。将以下 scss 文件保存为 mystyle.scss:
$s-min: 20;
$s-max: 70;
$l-min: 30;
$l-max: 90;
@for $i from 1 through 100 {
#dtable tbody tr.selected:nth-of-type(#{$i}) td {
box-shadow: inset 0 0 0 9999px hsl(random(360),$s-min+random($s-max+-$s-min),$l-min+random($l-max+-$l-min));
}
}
现在将其编译为CSS文件:
library(sass)
sass(sass_file("mystyle.scss"), output = "mystyle.css")
将文件 mystyle.css 放入应用程序的 www 子文件夹中,然后在应用程序中包含它,如下所示:
ui <- fluidPage(
tags$head(
tags$link(
href = "mystyle.css", rel = "stylesheet"
)
),
......
编辑:jQuery 解决方案已添加,您可以查看上面的代码进行参考。
英文:
If you use the following CSS, then the first row will be red if you select it, the second will be green, the third will be blue:
#dtable tbody tr.selected:nth-of-type(1) td {
box-shadow: inset 0 0 0 9999px red;
}
#dtable tbody tr.selected:nth-of-type(2) td {
box-shadow: inset 0 0 0 9999px green;
}
#dtable tbody tr.selected:nth-of-type(3) td {
box-shadow: inset 0 0 0 9999px blue;
}
Here dtable
is the id.
library(shiny)
library(DT)
css <- "
#dtable tbody tr.selected:nth-of-type(1) td {
box-shadow: inset 0 0 0 9999px red;
}
#dtable tbody tr.selected:nth-of-type(2) td {
box-shadow: inset 0 0 0 9999px green;
}
#dtable tbody tr.selected:nth-of-type(3) td {
box-shadow: inset 0 0 0 9999px blue;
}
"
ui <- fluidPage(
tags$head(
tags$style(
HTML(css)
)
),
br(), br(),
DTOutput("dtable")
)
server <- function(input, output, session) {
output[["dtable"]] <- renderDT({
datatable(iris, selection = "multiple")
})
}
shinyApp(ui, server)
Here is how to generate the CSS for 100 rows with a random color, using the sass package. Save the following scss file, say mystyle.scss:
$s-min: 20;
$s-max: 70;
$l-min: 30;
$l-max: 90;
@for $i from 1 through 100 {
#dtable tbody tr.selected:nth-of-type(#{$i}) td {
box-shadow: inset 0 0 0 9999px hsl(random(360),$s-min+random($s-max+-$s-min),$l-min+random($l-max+-$l-min));
}
}
Now compile it to a CSS file:
library(sass)
sass(sass_file("mystyle.scss"), output = "mystyle.css")
Put the file mystyle.css in the www subfolder of the app, and then include it in the app as follows:
ui <- fluidPage(
tags$head(
tags$link(
href = "mystyle.css", rel = "stylesheet"
)
),
......
EDIT : jQuery solution
library(shiny)
library(DT)
js <- '
var colors = ["red", "green", "blue", "yellow", "purple"];
table.on("select", function(e, dt, type, indexes) {
var count = table.rows({selected: true}).count();
for(var i = 0; i < count; i++) {
$("#dtable tbody tr.selected").eq(i).find("td").css(
"box-shadow", "inset 0 0 0 9999px " + colors[i]
);
}
}).on("deselect", function(e, dt, type, indexes) {
for(var i = 0; i < indexes.length; i++) {
$("#dtable tbody tr").eq(indexes[i]).find("td").css(
"box-shadow", ""
);
}
var count = table.rows({selected: true}).count();
for(var i = 0; i < count; i++) {
$("#dtable tbody tr.selected").eq(i).find("td").css(
"box-shadow", "inset 0 0 0 9999px " + colors[i]
);
}
});
'
ui <- fluidPage(
br(), br(),
DTOutput("dtable")
)
server <- function(input, output, session) {
output[["dtable"]] <- renderDT({
datatable(
iris,
extensions = "Select",
selection = "none",
callback = JS(js),
options = list(
"select" = "multi"
)
)
}, server = FALSE)
}
EDIT: correction of previous edit
Here is the correct JS code:
js <- '
var colors = ["red", "green", "blue", "yellow", "purple"];
var stack = [];
table.on("select", function(e, dt, type, indexes) {
stack.push(indexes[0]);
for(var i = 0; i < stack.length; i++) {
$("#dtable tbody tr").eq(stack[i]).find("td").css(
"box-shadow", "inset 0 0 0 9999px " + colors[i]
);
}
}).on("deselect", function(e, dt, type, indexes) {
var i0 = stack.indexOf(indexes[0]);
$("#dtable tbody tr").eq(stack[i0]).find("td").css(
"box-shadow", ""
);
stack.splice(i0, 1);
for(var i = 0; i < stack.length; i++) {
$("#dtable tbody tr").eq(stack[i]).find("td").css(
"box-shadow", "inset 0 0 0 9999px " + colors[i]
);
}
});
'
EDIT: without the 'Select' extension
js <- '
var colors = ["red", "green", "blue", "yellow", "purple"];
var stack = [];
table.on("click", "tr", function() {
var $rows = $("#dtable tbody tr"); // SIMONSIMON I moved this line
var $row = $(this);
var idx = $row.index();
if($row.hasClass("selected")) {
stack.push(idx);
for(var i = 0; i < stack.length; i++) {
$rows.eq(stack[i]).find("td").css(
"box-shadow", "inset 0 0 0 9999px " + colors[i]
);
}
} else {
var i0 = stack.indexOf(idx);
$rows.eq(stack[i0]).find("td").css(
"box-shadow", ""
);
stack.splice(i0, 1);
for(var i = 0; i < stack.length; i++) {
$rows.eq(stack[i]).find("td").css(
"box-shadow", "inset 0 0 0 9999px " + colors[i]
);
}
}
});
'
......
output[["dtable"]] <- renderDT({
datatable(
iris,
selection = "multiple",
callback = JS(js)
)
}, server = TRUE)
答案2
得分: 0
虽然从技术上讲,这并不是我最初请求的方式(即动态更改选择颜色),但从这个实现中得到的结果在视觉上与最终用户看到的相似。
该解决方案的思路是跟踪行选择计数器,通过代理更新表格,并应用条件格式。
library(shiny)
library(data.table)
library(DT)
data(iris)
iris <- suppressWarnings(cbind(as.data.table(iris),
currently_selected = numeric(),
selected_as_nr = numeric(),
overwrite = numeric()))
ui <- fluidPage(
dataTableOutput("table"),
)
server <- function(input, output, session) {
iris[, currently_selected := 0]
iris[, selected_as_nr := NA]
iris[, overwrite := 0]
output$table <- DT::renderDataTable({
DT::datatable(iris,
selection = list(mode = "multiple",
selected = 1,
target = "row")) %>%
formatStyle("Sepal.Length",
"selected_as_nr",
backgroundColor = styleEqual(0:4, c("green",
"red",
"black",
"blue",
"yellow")))
})
proxy <- dataTableProxy(outputId = "table")
observeEvent(input$table_rows_selected, {
new_selected_row <- input$table_rows_selected[1]
iris[new_selected_row, currently_selected := (currently_selected + 1) %% 2]
if (iris[new_selected_row, currently_selected] == 1) {
new_selection_nr <- suppressWarnings(iris[, min(setdiff(0:4, unique(selected_as_nr)))])
if (new_selection_nr != "Inf") {
iris[, overwrite := 0]
iris[new_selected_row, selected_as_nr := new_selection_nr]
} else {
overwrite <- iris[, unique(overwrite)]
iris[selected_as_nr == overwrite, currently_selected := 0]
iris[selected_as_nr == overwrite, selected_as_nr := NA]
iris[new_selected_row, selected_as_nr := overwrite]
iris[, overwrite := (overwrite + 1) %% 5]
}
} else {
iris[new_selected_row, selected_as_nr := NA]
}
DT::replaceData(proxy, iris)
})
}
shinyApp(ui, server)
英文:
While technically this doesn't do what I originally requested (i.e. changing the selection colouring dynamically), the results from this implementation appear visually similar to the end-user.
The idea behind the solution is to keep track of the row selection counter, update the table via a proxy, and apply conditional formatting.
library(shiny)
library(data.table)
library(DT)
data(iris)
iris <- suppressWarnings(cbind(as.data.table(iris),
currently_selected = numeric(),
selected_as_nr = numeric(),
overwrite = numeric()))
ui <- fluidPage(
dataTableOutput("table"),
)
server <- function(input, output, session) {
iris[, currently_selected := 0]
iris[, selected_as_nr := NA]
iris[, overwrite := 0]
output$table <- DT::renderDataTable({
DT::datatable(iris,
selection = list(mode = "multiple",
selected = 1,
target = "row")) %>%
formatStyle("Sepal.Length",
"selected_as_nr",
backgroundColor = styleEqual(0:4, c("green",
"red",
"black",
"blue",
"yellow")))
})
proxy <- dataTableProxy(outputId = "table")
observeEvent(input$table_rows_selected, {
new_selected_row <- input$table_rows_selected[1]
iris[new_selected_row, currently_selected := (currently_selected + 1) %% 2]
if (iris[new_selected_row, currently_selected] == 1) {
new_selection_nr <- suppressWarnings(iris[, min(setdiff(0:4, unique(selected_as_nr)))])
if (new_selection_nr != "Inf") {
iris[, overwrite := 0]
iris[new_selected_row, selected_as_nr := new_selection_nr]
} else {
overwrite <- iris[, unique(overwrite)]
iris[selected_as_nr == overwrite, currently_selected := 0]
iris[selected_as_nr == overwrite, selected_as_nr := NA]
iris[new_selected_row, selected_as_nr := overwrite]
iris[, overwrite := (overwrite + 1) %% 5]
}
} else {
iris[new_selected_row, selected_as_nr := NA]
}
DT::replaceData(proxy, iris)
})
}
shinyApp(ui, server)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论