在DT::datatable中使用多种选择颜色?

huangapple go评论75阅读模式
英文:

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的初步答案之后,我想稍微修改我的问题:

我最喜欢的逻辑是:

  1. 我们有m种唯一颜色。之前已选择了n < m行。
  2. n+1行被选择后,会以第n+1种颜色着色,并保持此颜色直到取消选择。
  3. 当当前选择了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 &lt;- 
   DT::renderDataTable({DT::datatable(..., selection = &quot;multiple&quot;)})

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:

  1. We have m unique colors. n &lt; m previous rows have been selected.
  2. The n+1th row gets selected and is then coloured in the n+1th color and keeps this color until deselected.
  3. When m rows are currently selected, no more selections are possible. Alternatively, I would also be interested in: When the m+1th 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 the m+2th 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 = &quot;multiple&quot;, selected = 1:3, target = &quot;row&quot;)) 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 &lt;- &quot;
#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;
}
&quot;

ui &lt;- fluidPage(
  tags$head(
    tags$style(
      HTML(css)
    )
  ),
  br(), br(),
  DTOutput(&quot;dtable&quot;)
)

server &lt;- function(input, output, session) {
  
  output[[&quot;dtable&quot;]] &lt;- renderDT({
    datatable(iris, selection = &quot;multiple&quot;)
  })
  
}

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(&quot;mystyle.scss&quot;), output = &quot;mystyle.css&quot;)

Put the file mystyle.css in the www subfolder of the app, and then include it in the app as follows:

ui &lt;- fluidPage(
  tags$head(
    tags$link(
      href = &quot;mystyle.css&quot;, rel = &quot;stylesheet&quot;
    )
  ),
  ......

EDIT : jQuery solution

library(shiny)
library(DT)

js &lt;- &#39;
var colors = [&quot;red&quot;, &quot;green&quot;, &quot;blue&quot;, &quot;yellow&quot;, &quot;purple&quot;];
table.on(&quot;select&quot;, function(e, dt, type, indexes) {
  var count = table.rows({selected: true}).count();
  for(var i = 0; i &lt; count; i++) {
    $(&quot;#dtable tbody tr.selected&quot;).eq(i).find(&quot;td&quot;).css(
      &quot;box-shadow&quot;, &quot;inset 0 0 0 9999px &quot; + colors[i]
    );
  }
}).on(&quot;deselect&quot;, function(e, dt, type, indexes) {
  for(var i = 0; i &lt; indexes.length; i++) {
    $(&quot;#dtable tbody tr&quot;).eq(indexes[i]).find(&quot;td&quot;).css(
      &quot;box-shadow&quot;, &quot;&quot;
    );
  }
  var count = table.rows({selected: true}).count();
  for(var i = 0; i &lt; count; i++) {
    $(&quot;#dtable tbody tr.selected&quot;).eq(i).find(&quot;td&quot;).css(
      &quot;box-shadow&quot;, &quot;inset 0 0 0 9999px &quot; + colors[i]
    );
  }
});
&#39;

ui &lt;- fluidPage(
  br(), br(),
  DTOutput(&quot;dtable&quot;)
)

server &lt;- function(input, output, session) {
  
  output[[&quot;dtable&quot;]] &lt;- renderDT({
    datatable(
      iris, 
      extensions = &quot;Select&quot;,
      selection = &quot;none&quot;, 
      callback = JS(js),
      options = list(
        &quot;select&quot; = &quot;multi&quot; 
      )
    )
  }, server = FALSE)
  
}

EDIT: correction of previous edit

Here is the correct JS code:

js &lt;- &#39;
var colors = [&quot;red&quot;, &quot;green&quot;, &quot;blue&quot;, &quot;yellow&quot;, &quot;purple&quot;];
var stack = [];
table.on(&quot;select&quot;, function(e, dt, type, indexes) {
  stack.push(indexes[0]);
  for(var i = 0; i &lt; stack.length; i++) {
    $(&quot;#dtable tbody tr&quot;).eq(stack[i]).find(&quot;td&quot;).css(
      &quot;box-shadow&quot;, &quot;inset 0 0 0 9999px &quot; + colors[i]
    );
  }
}).on(&quot;deselect&quot;, function(e, dt, type, indexes) {
  var i0 = stack.indexOf(indexes[0]);
  $(&quot;#dtable tbody tr&quot;).eq(stack[i0]).find(&quot;td&quot;).css(
    &quot;box-shadow&quot;, &quot;&quot;
  );
  stack.splice(i0, 1);
  for(var i = 0; i &lt; stack.length; i++) {
    $(&quot;#dtable tbody tr&quot;).eq(stack[i]).find(&quot;td&quot;).css(
      &quot;box-shadow&quot;, &quot;inset 0 0 0 9999px &quot; + colors[i]
    );
  }
});
&#39;

在DT::datatable中使用多种选择颜色?


EDIT: without the 'Select' extension

js &lt;- &#39;
var colors = [&quot;red&quot;, &quot;green&quot;, &quot;blue&quot;, &quot;yellow&quot;, &quot;purple&quot;];
var stack = [];
table.on(&quot;click&quot;, &quot;tr&quot;, function() {
  var $rows = $(&quot;#dtable tbody tr&quot;); // SIMONSIMON I moved this line
  var $row = $(this);
  var idx = $row.index();
  if($row.hasClass(&quot;selected&quot;)) {
    stack.push(idx);
    for(var i = 0; i &lt; stack.length; i++) {
      $rows.eq(stack[i]).find(&quot;td&quot;).css(
        &quot;box-shadow&quot;, &quot;inset 0 0 0 9999px &quot; + colors[i]
      );
    }
  } else {
    var i0 = stack.indexOf(idx);
    $rows.eq(stack[i0]).find(&quot;td&quot;).css(
      &quot;box-shadow&quot;, &quot;&quot;
    );
    stack.splice(i0, 1);
    for(var i = 0; i &lt; stack.length; i++) {
      $rows.eq(stack[i]).find(&quot;td&quot;).css(
        &quot;box-shadow&quot;, &quot;inset 0 0 0 9999px &quot; + colors[i]
      );
    }
  }
});
&#39;

......

  output[[&quot;dtable&quot;]] &lt;- renderDT({
    datatable(
      iris, 
      selection = &quot;multiple&quot;, 
      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 &lt;- suppressWarnings(cbind(as.data.table(iris),
currently_selected = numeric(),
selected_as_nr     = numeric(),
overwrite          = numeric()))
ui &lt;- fluidPage(
dataTableOutput(&quot;table&quot;),
)
server &lt;- function(input, output, session) {
iris[, currently_selected := 0]
iris[, selected_as_nr     := NA]
iris[, overwrite          := 0]
output$table &lt;- DT::renderDataTable({
DT::datatable(iris,
selection = list(mode = &quot;multiple&quot;,
selected = 1,
target = &quot;row&quot;)) %&gt;%
formatStyle(&quot;Sepal.Length&quot;,
&quot;selected_as_nr&quot;,
backgroundColor = styleEqual(0:4, c(&quot;green&quot;,
&quot;red&quot;,
&quot;black&quot;,
&quot;blue&quot;,
&quot;yellow&quot;)))
})
proxy &lt;- dataTableProxy(outputId = &quot;table&quot;)
observeEvent(input$table_rows_selected, {
new_selected_row &lt;- 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 &lt;- suppressWarnings(iris[, min(setdiff(0:4, unique(selected_as_nr)))])
if (new_selection_nr != &quot;Inf&quot;) {
iris[, overwrite := 0]
iris[new_selected_row, selected_as_nr := new_selection_nr] 
} else {
overwrite &lt;- 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)

huangapple
  • 本文由 发表于 2023年2月14日 03:26:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/75440401.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定