Use TAB to edit next cell on DT table
Asked Answered
A

1

6

Is there a trick to edit next cell after pressing TAB in an editable DT ?
It would help to edit faster a whole row.

Below an basic example of an editable DT.

library(shiny)
library(DT)

ui <- fluidPage(
    DT::dataTableOutput('tbl1'),
    verbatimTextOutput("print")
)

server <- function(input, output, session) {

    data <- reactiveValues(x = iris[1:10,1:4])

    output$tbl1 <- DT::renderDataTable({
        DT::datatable(data = isolate(data$x), editable = TRUE, rownames = FALSE)
    })

    proxy_tbl1 <- dataTableProxy("tbl1")

    observeEvent(input$tbl1_cell_edit, {
        info = input$tbl1_cell_edit
        i = info$row
        j = info$col + 1
        v = info$value
        data$x[i, j] <- DT::coerceValue(v, data$x[i, j])
        replaceData(proxy_tbl1, data$x, resetPaging = FALSE, rownames = FALSE)
    })

    output$print <- renderPrint({
        print(data$x)
    })
}

shinyApp(ui, server)
Atmosphere answered 27/2, 2019 at 14:5 Comment(2)
Sounds like a good idea. You may file a feature request to github.com/rstudio/DT/issues. Thanks!Jasik
Don't you think this would be related to PR #509 ?Atmosphere
C
12

This is probably doable with the Editor extension but it is not free.

Here is something close, using the KeyTable extension.

library(shiny)
library(DT)

ui <- fluidPage(
  DTOutput("table")
)

js <- c(
  "table.on('key', function(e, datatable, key, cell, originalEvent){",
  "  var targetName = originalEvent.target.localName;",
  "  if(key == 13){",
  "    if(targetName == 'body'){",
  "      $(cell.node()).trigger('dblclick.dt');",
  "    }else if(targetName == 'input'){",
  "      $(originalEvent.target).trigger('blur');",
  "    }",
  "  }",
  "})"
)

server <- function(input, output, session){
  output$table <- renderDT({
    datatable(
      iris,
      selection = "none",
      editable = TRUE, 
      callback = JS(js),
      extensions = "KeyTable",
      options = list(
        keys = TRUE
      )
    )
  })
}

shinyApp(ui, server)

1) Select a cell:

enter image description here

2) Press Enter to edit the cell:

enter image description here

3) Press Enter when the edit is done, and press Tab to go the next cell:

enter image description here

4) Press Enter to edit the cell:

enter image description here

etc...

This is not so nice as a spreadsheet editor, but this allows to edit the cells with the keyboard only.

You can also use the arrows to navigate between cells.


EDIT

Here is a better solution. Replace js with

js <- c(
  "table.on('key', function(e, datatable, key, cell, originalEvent){",
  "  var targetName = originalEvent.target.localName;",
  "  if(key == 13 && targetName == 'body'){",
  "    $(cell.node()).trigger('dblclick.dt');",
  "  }",
  "});",
  "table.on('keydown', function(e){",
  "  if(e.target.localName == 'input' && [9,13,37,38,39,40].indexOf(e.keyCode) > -1){",
  "    $(e.target).trigger('blur');",
  "  }",
  "});"
)

Now when you edit a cell you can:

  • press Enter to validate the edit and stay at the same position;

  • or press Tab or an arrow key to validate the edit and navigate, and there's no need to press Enter to validate the edit.


EDIT 2

With the code below:

  • navigate in the table, press Enter to edit;

  • press Enter to validate the edit and stay at the same position;

  • if you are editing a cell, then pressing Tab or an arrow key will trigger the edit of the new cell.

.

js <- c(
  "table.on('key', function(e, datatable, key, cell, originalEvent){",
  "  var targetName = originalEvent.target.localName;",
  "  if(key == 13 && targetName == 'body'){",
  "    $(cell.node()).trigger('dblclick.dt');",
  "  }",
  "});",
  "table.on('keydown', function(e){",
  "  if(e.target.localName == 'input' && [9,13,37,38,39,40].indexOf(e.keyCode) > -1){",
  "    $(e.target).trigger('blur');",
  "  }",
  "});",
  "table.on('key-focus', function(e, datatable, cell, originalEvent){",
  "  var targetName = originalEvent.target.localName;",
  "  var type = originalEvent.type;",
  "  if(type == 'keydown' && targetName == 'input'){",
  "    if([9,37,38,39,40].indexOf(originalEvent.keyCode) > -1){",
  "      $(cell.node()).trigger('dblclick.dt');",
  "    }",
  "  }",
  "});"
)
Culhert answered 27/2, 2019 at 16:43 Comment(3)
Thanks a lot, Very good solution ! The main thing I was looking for was not to use the mouse.Atmosphere
@Atmosphere I'm glad to help. See my edit, I have added a better solution. No need to press Enter with this solution: the edit is validated whenever you press a navigation key (Tab or an arrow).Polysyndeton
Thanks again @Stéphane, even better indeed. Do you think it would be possible to send a 'enter' or 'dblclick' event after arrow or tab is pressed ? I tried to add this to your code unsuccessfully (I almost don't know at all about js)Atmosphere

© 2022 - 2024 — McMap. All rights reserved.