DT icon indicating copy to clipboard operation
DT copied to clipboard

the cell size changed when editing in DT

Open zhangjunjiang opened this issue 4 years ago • 6 comments

Hello I'm using shiny and DT to make a small app for my workmate. Here is the problem I meet.

I have a datatframe to show by DT, which contain some long str. These str should be editable. all the app work fine. But when I try to make some change in the str dataframe, I find something inconvenient. The origin cell was transformed into several line automatically. When I was editing, the "cell" or the "editable area" changed into only one line in a small "window". And I need to use "<" button to get to the star part of the string.

I doubt if there is any way to set the size of the "editable area", which could be as big as the origin cell size ?

Thanks a lot

https://community.rstudio.com/t/the-cell-size-changed-when-editing-in-dt/71675


By filing an issue to this repo, I promise that

  • [x] I have fully read the issue guide at https://yihui.name/issue/.

  • [x] I have provided the necessary information about my issue.

    • If I'm asking a question, I have already asked it on Stack Overflow or RStudio Community, waited for at least 24 hours, and included a link to my question there.
    • If I'm filing a bug report, I have included a minimal, self-contained, and reproducible example, and have also included xfun::session_info('DT'). I have upgraded all my packages to their latest versions (e.g., R, RStudio, and R packages), and also tried the development version: remotes::install_github('rstudio/DT').
    • If I have posted the same issue elsewhere, I have also mentioned it in this issue.
  • [x] I have learned the Github Markdown syntax, and formatted my issue correctly.

I understand that my issue may be closed if I don't fulfill my promises.

zhangjunjiang avatar Jul 03 '20 08:07 zhangjunjiang

That's normal, the editable area is a type=text input, which has no support for multiline. Try this, this opens a textarea input for the edit:

library(shiny)
library(DT)

callback <- c(
  "table.on('focus', 'input', function(){",
  "  $(this).on('blur', function(e){",
  "    e.stopImmediatePropagation();",
  "    return false;",
  "  })",
  "});",
  "$.contextMenu({",
  "  selector: '#dtable td input[type=text]',", 
  "  trigger: 'hover',",
  "  autoHide: true,",
  "  items: {",
  "    text: {",
  "      name: 'Edit:',", 
  "      type: 'textarea',", 
  "      value: ''", 
  "    },",
  "    sep1: '---------',",
  "    cancel: {",
  "      name: 'Cancel',",
  "      icon: function($element, key, item){",
  "        return 'context-menu-icon context-menu-icon-quit';",
  "      },",
  "      callback: function(itemKey, opts, e){",
  "        this.trigger('change');",
  "        $.contextMenu('destroy');",
  "      }",
  "    }",
  "  },",
  "  events: {",
  "    show: function(opts){",
  "      $.contextMenu.setInputValues(opts, {text: opts.$trigger.val()});",
  "    },",
  "    hide: function(opts){",
  "      var $this = this;",
  "      var data = $.contextMenu.getInputValues(opts, $this.data());",
  "      var $input = opts.$trigger;",
  "      $input.val(data.text);",
  "      $input.trigger('change');",
  "    }",
  "  }",
  "});" 
)

dat <- cbind(
  iris[1:2,],
  data.frame(
    X = c(
      "A very loooooooooooooooooooooooooooooooooooonnnnnnnnnggggggg string",
      "xxx"
    )
  )
)

ui <- fluidPage(
  tags$head(
    tags$link(
      rel = "stylesheet", 
      href = "https://cdnjs.cloudflare.com/ajax/libs/jquery-contextmenu/2.8.0/jquery.contextMenu.min.css"
    ),
    tags$script(
      src = "https://cdnjs.cloudflare.com/ajax/libs/jquery-contextmenu/2.8.0/jquery.contextMenu.min.js"
    )
  ),
  DTOutput("dtable")
)

server <- function(input, output){
  output[["dtable"]] <- renderDT({
    datatable(
      dat, editable = "cell", callback = JS(callback)
    )
  })
}

shinyApp(ui, server)

stla avatar Jul 05 '20 11:07 stla

@stla thank you so much for your suggestion I try your code and it works fine. but when i want get my datatable work as yours. something wrong happened. the "area didn't showed up. and here is my brief code

my_cb=c(.....what you show above.......)
ui <- pageWithSidebar(
 mainPanel(
    tags$head(
      tags$link(
        rel = "stylesheet", 
        href = "https://cdnjs.cloudflare.com/ajax/libs/jquery-contextmenu/2.8.0/jquery.contextMenu.min.css"
      ),
      tags$script(
        src = "https://cdnjs.cloudflare.com/ajax/libs/jquery-contextmenu/2.8.0/jquery.contextMenu.min.js"
      )
    ),
    DTOutput("comm_tmp"),
))
server <- function(input, output,session) {
     output$comm_tmp<-renderDT({
    
    datatable(x$comm,callback = JS(my_cb),editable = list(target = 'cell',disable = list(columns = c(1))),options = list(dom = 't'))                                                                                                                      
     })
   observeEvent(input$comm_tmp_cell_edit, {
    x$comm[input$comm_tmp_cell_edit$row,input$comm_tmp_cell_edit$col] <<- input$comm_tmp_cell_edit$value
  })
}

also here is another suestion: what if I want a little larger "area"? how could I set the "size"? thank you for your help

zhangjunjiang avatar Jul 07 '20 02:07 zhangjunjiang

Hello @zhangjunjiang

You have to set the id of your table in the selector option:

" selector: '#comm_tmp td input[type=text]',"

To change the size of the textarea, click on its bottom right corner and drag.

stla avatar Jul 07 '20 06:07 stla

Hello @zhangjunjiang

You have to set the id of your table in the selector option:

" selector: '#comm_tmp td input[type=text]',"

To change the size of the textarea, click on its bottom right corner and drag.

Thank you ,It works!!!!!

zhangjunjiang avatar Jul 08 '20 02:07 zhangjunjiang

@zhangjunjiang

I've fixed an error in my code and I've improved it. With this code you can select the columns for which you request a textarea editing. For example here I select columns 5 and 6:

      options = list(
        columnDefs = list(
          list(targets = c(5,6), className = "areaEdit")
        )
      )

To select all columns, set targets = "_all".

Moreover you don't need to set the table id in the JS code. In this way you can use the same callback for multiple tables.

library(shiny)
library(DT)

callback <- c(
  "table.on('focus', 'td.areaEdit input[type=text]', function(){",
  "  $(this).on('blur', function(e){",
  "    e.stopImmediatePropagation();",
  "    return false;",
  "  })",
  "});",
  "var id = $(table.table().node()).closest('.datatables').attr('id');",
  "$.contextMenu({",
  "  selector: '#' + id + ' td.areaEdit input[type=text]',",
  "  trigger: 'hover',",
  "  autoHide: true,",
  "  items: {",
  "    text: {",
  "      name: 'Edit:',",
  "      type: 'textarea',",
  "      value: ''",
  "    },",
  "    sep1: '---------',",
  "    cancel: {",
  "      name: 'Cancel',",
  "      icon: function($element, key, item){",
  "        return 'context-menu-icon context-menu-icon-quit';",
  "      },",
  "      callback: function(itemKey, opts, e){",
  "        this.trigger('change');",
  "      }",
  "    }",
  "  },",
  "  events: {",
  "    show: function(opts){",
  "      $.contextMenu.setInputValues(opts, {text: opts.$trigger.val()});",
  "    },",
  "    hide: function(opts){",
  "      var $this = this;",
  "      var data = $.contextMenu.getInputValues(opts, $this.data());",
  "      var $input = opts.$trigger;",
  "      $input.val(data.text);",
  "      $input.trigger('change');",
  "    }",
  "  }",
  "});"
)

dat <- cbind(
  iris[1:2,],
  data.frame(
    X = c(
      "A very loooooooooooooooooooooooooooooooooooonnnnnnnnnggggggg string",
      "xxx"
    )
  )
)

ui <- fluidPage(
  tags$head(
    tags$link(
      rel = "stylesheet",
      href = "https://cdnjs.cloudflare.com/ajax/libs/jquery-contextmenu/2.8.0/jquery.contextMenu.min.css"
    ),
    tags$script(
      src = "https://cdnjs.cloudflare.com/ajax/libs/jquery-contextmenu/2.8.0/jquery.contextMenu.min.js"
    )
  ),
  DTOutput("dtable")
)

server <- function(input, output){
  output[["dtable"]] <- renderDT({
    datatable(
      dat, editable = "cell", callback = JS(callback),
      options = list(
        columnDefs = list(
          list(targets = c(5,6), className = "areaEdit")
        )
      )
    )
  })
}

shinyApp(ui, server)

stla avatar Jul 11 '20 10:07 stla

@stla Thank you :)

zhangjunjiang avatar Jul 13 '20 01:07 zhangjunjiang