inst/examples/app_complete.R

library(jsonlite)
library(dplyr)
library(tibble)
library(lubridate)

# ── Dados ─────────────────────────────────────────────────────────────────────
json_txt <- '{
  "data": [
    {"name":"Tiger Nixon","position":"System Architect","salary":"320800","start_date":"2011-04-25","office":"Edinburgh","extn":"5421"},
    {"name":"Garrett Winters","position":"Accountant","salary":"170750","start_date":"2011-07-25","office":"Tokyo","extn":"8422"},
    {"name":"Ashton Cox","position":"Junior Technical Author","salary":"86000","start_date":"2009-01-12","office":"San Francisco","extn":"1562"},
    {"name":"Cedric Kelly","position":"Senior JavaScript Developer","salary":"433060","start_date":"2012-03-29","office":"Edinburgh","extn":"6224"},
    {"name":"Airi Satou","position":"Accountant","salary":"162700","start_date":"2008-11-28","office":"Tokyo","extn":"5407"},
    {"name":"Brielle Williamson","position":"Integration Specialist","salary":"372000","start_date":"2012-12-02","office":"New York","extn":"4804"},
    {"name":"Herrod Chandler","position":"Sales Assistant","salary":"137500","start_date":"2012-08-06","office":"San Francisco","extn":"9608"},
    {"name":"Rhona Davidson","position":"Integration Specialist","salary":"327900","start_date":"2010-10-14","office":"Tokyo","extn":"6200"},
    {"name":"Colleen Hurst","position":"JavaScript Developer","salary":"205500","start_date":"2009-09-15","office":"San Francisco","extn":"2360"},
    {"name":"Sonya Frost","position":"Software Engineer","salary":"103600","start_date":"2008-12-13","office":"Edinburgh","extn":"1667"},
    {"name":"Jena Gaines","position":"Office Manager","salary":"90560","start_date":"2008-12-19","office":"London","extn":"3814"},
    {"name":"Quinn Flynn","position":"Support Lead","salary":"342000","start_date":"2013-03-03","office":"Edinburgh","extn":"9497"},
    {"name":"Charde Marshall","position":"Regional Director","salary":"470600","start_date":"2008-10-16","office":"San Francisco","extn":"6741"},
    {"name":"Haley Kennedy","position":"Senior Marketing Designer","salary":"313500","start_date":"2012-12-18","office":"London","extn":"3597"},
    {"name":"Tatyana Fitzpatrick","position":"Regional Director","salary":"385750","start_date":"2010-03-17","office":"London","extn":"1965"},
    {"name":"Michael Silva","position":"Marketing Designer","salary":"198500","start_date":"2012-11-27","office":"London","extn":"1581"},
    {"name":"Paul Byrd","position":"Chief Financial Officer (CFO)","salary":"725000","start_date":"2010-06-09","office":"New York","extn":"3059"},
    {"name":"Gloria Little","position":"Systems Administrator","salary":"237500","start_date":"2009-04-10","office":"New York","extn":"1721"},
    {"name":"Bradley Greer","position":"Software Engineer","salary":"132000","start_date":"2012-10-13","office":"London","extn":"2558"},
    {"name":"Dai Rios","position":"Personnel Lead","salary":"217500","start_date":"2012-09-26","office":"Edinburgh","extn":"2290"},
    {"name":"Jenette Caldwell","position":"Development Lead","salary":"345000","start_date":"2011-09-03","office":"New York","extn":"1937"},
    {"name":"Yuri Berry","position":"Chief Marketing Officer (CMO)","salary":"675000","start_date":"2009-06-25","office":"New York","extn":"6154"},
    {"name":"Caesar Vance","position":"Pre-Sales Support","salary":"106450","start_date":"2011-12-12","office":"New York","extn":"8330"},
    {"name":"Doris Wilder","position":"Sales Assistant","salary":"85600","start_date":"2010-09-20","office":"Sydney","extn":"3023"},
    {"name":"Angelica Ramos","position":"Chief Executive Officer (CEO)","salary":"1200000","start_date":"2009-10-09","office":"London","extn":"5797"},
    {"name":"Gavin Joyce","position":"Developer","salary":"92575","start_date":"2010-12-22","office":"Edinburgh","extn":"8822"},
    {"name":"Jennifer Chang","position":"Regional Director","salary":"357650","start_date":"2010-11-14","office":"Argentina","extn":"9239"},
    {"name":"Brenden Wagner","position":"Software Engineer","salary":"206850","start_date":"2011-06-07","office":"San Francisco","extn":"1314"},
    {"name":"Fiona Green","position":"Chief Operating Officer (COO)","salary":"850000","start_date":"2010-03-11","office":"San Francisco","extn":"2947"},
    {"name":"Shou Itou","position":"Regional Marketing","salary":"163000","start_date":"2011-08-14","office":"Tokyo","extn":"8899"},
    {"name":"Michelle House","position":"Integration Specialist","salary":"95400","start_date":"2011-06-02","office":"Sydney","extn":"2769"},
    {"name":"Suki Burks","position":"Developer","salary":"114500","start_date":"2009-10-22","office":"London","extn":"6832"},
    {"name":"Prescott Bartlett","position":"Technical Author","salary":"145000","start_date":"2011-05-07","office":"London","extn":"3606"},
    {"name":"Gavin Cortez","position":"Team Leader","salary":"235500","start_date":"2008-10-26","office":"San Francisco","extn":"2860"},
    {"name":"Martena Mccray","position":"Post-Sales support","salary":"324050","start_date":"2011-03-09","office":"Edinburgh","extn":"8240"},
    {"name":"Unity Butler","position":"Marketing Designer","salary":"85675","start_date":"2009-12-09","office":"San Francisco","extn":"5384"},
    {"name":"Howard Hatfield","position":"Office Manager","salary":"164500","start_date":"2008-12-16","office":"San Francisco","extn":"7031"},
    {"name":"Hope Fuentes","position":"Secretary","salary":"109850","start_date":"2010-02-12","office":"San Francisco","extn":"6318"},
    {"name":"Vivian Harrell","position":"Financial Controller","salary":"452500","start_date":"2009-02-14","office":"San Francisco","extn":"9422"},
    {"name":"Timothy Mooney","position":"Office Manager","salary":"136200","start_date":"2008-12-11","office":"London","extn":"7580"},
    {"name":"Jackson Bradshaw","position":"Director","salary":"645750","start_date":"2008-09-26","office":"New York","extn":"1042"},
    {"name":"Olivia Liang","position":"Support Engineer","salary":"234500","start_date":"2011-02-03","office":"Argentina","extn":"2120"},
    {"name":"Bruno Nash","position":"Software Engineer","salary":"163500","start_date":"2011-05-03","office":"London","extn":"6222"},
    {"name":"Sakura Yamamoto","position":"Support Engineer","salary":"139575","start_date":"2009-08-19","office":"Tokyo","extn":"9383"},
    {"name":"Thor Walton","position":"Developer","salary":"98540","start_date":"2013-08-11","office":"New York","extn":"8327"},
    {"name":"Finn Camacho","position":"Support Engineer","salary":"87500","start_date":"2009-07-07","office":"San Francisco","extn":"2927"},
    {"name":"Serge Baldwin","position":"Data Coordinator","salary":"138575","start_date":"2012-04-09","office":"Argentina","extn":"8352"},
    {"name":"Zenaida Frank","position":"Software Engineer","salary":"125250","start_date":"2010-01-04","office":"New York","extn":"7439"},
    {"name":"Zorita Serrano","position":"Software Engineer","salary":"115000","start_date":"2012-06-01","office":"San Francisco","extn":"4389"},
    {"name":"Jennifer Acosta","position":"Junior JavaScript Developer","salary":"75650","start_date":"2013-02-01","office":"Edinburgh","extn":"3431"},
    {"name":"Cara Stevens","position":"Sales Assistant","salary":"145600","start_date":"2011-12-06","office":"New York","extn":"3990"},
    {"name":"Hermione Butler","position":"Regional Director","salary":"356250","start_date":"2011-03-21","office":"London","extn":"1016"},
    {"name":"Lael Greer","position":"Systems Administrator","salary":"103500","start_date":"2009-02-27","office":"London","extn":"6733"},
    {"name":"Jonas Alexander","position":"Developer","salary":"86500","start_date":"2010-07-14","office":"San Francisco","extn":"8196"},
    {"name":"Shad Decker","position":"Regional Director","salary":"183000","start_date":"2008-11-13","office":"Edinburgh","extn":"6373"},
    {"name":"Michael Bruce","position":"JavaScript Developer","salary":"183000","start_date":"2011-06-27","office":"Argentina","extn":"5384"},
    {"name":"Donna Snider","position":"Customer Support","salary":"112000","start_date":"2011-01-25","office":"New York","extn":"4226"}
  ]
}'

df <- fromJSON(json_txt, flatten = TRUE)$data %>%
  as_tibble() %>%
  mutate(
    salary     = as.numeric(salary),
    extn       = as.integer(extn),
    start_date = ymd(start_date)
  )

# ── App ───────────────────────────────────────────────────────────────────────
library(shiny)
library(bslib)
library(DT2)
library(htmlwidgets)

ui <- page_sidebar(
  theme = bs_theme(version = 5, bootswatch = "spacelab"),
  title = "DT2 — Exemplo Completo",
  sidebar = sidebar(
    h5("Ajustes de layout"),
    p("Demonstra: ColumnControl, botões agrupados com spacer, ",
      "renderers JS customizados, bandeiras e paginação traduzida.")
  ),
  # CSS das bandeiras
  tags$head(
    tags$link(
      rel = "stylesheet", type = "text/css",
      href = "https://cdn.jsdelivr.net/gh/lafeber/world-flags-sprite/stylesheets/flags32-both.css"
    ),
    tags$style(HTML("
      .f32 .flag { display:inline-block; width:32px; height:32px;
                    vertical-align:middle; margin-right:6px; }
      table.dataTable tbody td { vertical-align: middle; }
    "))
  ),
  card(
    card_header("DT2"),
    card_body(dt2_output("example", height = "auto"))
  ),
  card(
    card_header("Estado recebido do cliente (debug)"),
    card_body(verbatimTextOutput("state"))
  )
)

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

  output$example <- render_dt2({

    # ── Renderers JS (assinatura: data, type, row, meta) ──────────────────────

    position_js <- JS("
      function(data, type, row, meta) {
        if (type === 'display') {
          var link = 'https://datatables.net';
          if (data && data[0] < 'H')      link = 'https://cloudtables.com';
          else if (data && data[0] < 'S')  link = 'https://editor.datatables.net';
          return '<a href=\"' + link + '\" target=\"_blank\" rel=\"noopener\">' + data + '</a>';
        }
        return data;
      }
    ")

    office_js <- JS("
      function(data, type, row, meta) {
        if (type === 'display') {
          var country = '';
          switch (data) {
            case 'Argentina':     country = 'ar'; break;
            case 'Edinburgh':     country = '_Scotland'; break;
            case 'London':        country = '_England'; break;
            case 'New York':
            case 'San Francisco': country = 'us'; break;
            case 'Sydney':        country = 'au'; break;
            case 'Tokyo':         country = 'jp'; break;
          }
          return '<span class=\"flag ' + country + '\"></span> ' + data;
        }
        return data;
      }
    ")

    extn_js <- JS("
      function(data, type, row, meta) {
        return (type === 'display')
          ? '<progress value=\"' + data + '\" max=\"9999\"></progress>'
          : data;
      }
    ")

    salary_js <- JS("
      (function() {
        var nfmt = DataTable.render.number('.', ',', 2, 'R$ ');
        return function(data, type, row, meta) {
          var number = nfmt.display(data);
          if (type === 'display') {
            var color = (data < 250000) ? 'red' : (data < 500000 ? 'orange' : 'green');
            return '<span style=\"color:' + color + '\">' + number + '</span>';
          }
          return number;
        };
      })()
    ")

    # ── Options ───────────────────────────────────────────────────────────────

    opts <- list(
      pageLength   = 10,
      lengthChange = TRUE,
      lengthMenu   = c(10, 25, 50, -1),

      # Layout v2: posiciona elementos livremente
      layout = list(
        topStart = "pageLength",
        topEnd   = list(
          buttons = list(
            list(extend = "copyHtml5", text = "Copiar"),
            list(extend = "csvHtml5"),
            list(extend = "excelHtml5", title = "Dados exportados"),
            list(extend = "pdfHtml5",   title = "Dados exportados"),
            list(extend = "print", text = "Imprimir",
                 title = "Dados exportados", messageTop = ""),
            # Spacer visual separando export de colunas
            list(extend = "spacer", style = "bar"),
            list(extend = "colvis", text = "Colunas")
          ),
          search = list(placeholder = "")
        ),
        bottomEnd = list(paging = list(
          firstLast    = TRUE,
          previousNext = TRUE,
          numbers      = FALSE
        ))
      ),

      # ColumnControl: menus dropdown no header
      columnControl = list(
        target  = 0,
        content = list(
          "order",
          "searchDropdown",
          list(
            list(extend = "orderAsc",        text = "Ordem crescente"),
            list(extend = "orderDesc",       text = "Ordem decrescente"),
            "spacer",
            list(extend = "colVisDropdown",  text = "Selecionar colunas")
          )
        )
      ),

      # Recomendado quando ColumnControl gerencia ordering
      ordering = list(indicators = FALSE, handler = FALSE),

      # Mapeamento colunas (DT2 aceita vetor de nomes)
      columns = names(df),

      # Renderers por coluna (targets = índice 0-based)
      columnDefs = list(
        list(targets = which(names(df) == "position") - 1L,
             render  = position_js),
        list(targets = which(names(df) == "office") - 1L,
             className = "f32",
             render    = office_js),
        list(targets = which(names(df) == "start_date") - 1L,
             className = "dt-body-center dt-left"),
        list(targets = which(names(df) == "extn") - 1L,
             className = "dt-left",
             render    = extn_js),
        list(targets = which(names(df) == "salary") - 1L,
             className = "dt-body-right dt-left",
             render    = salary_js)
      ),

      # Tradução pt-BR
      language = list(
        lengthMenu   = "Mostrar&nbsp; _MENU_",
        lengthLabels = list(
          `10` = "10", `25` = "25", `50` = "50", `-1` = "Todas"
        ),
        search       = "Buscar",
        paginate     = list(
          first = "&laquo;", previous = "\u2039",
          `next` = "\u203A", last = "&raquo;"
        ),
        info          = "Mostrando _START_ a _END_ de _TOTAL_ registros",
        infoEmpty     = "Mostrando 0 a 0 de 0 registros",
        infoFiltered  = "(filtrado de _MAX_ registros no total)",
        zeroRecords   = "Nenhum registro encontrado",
        emptyTable    = "Nenhum dado dispon\u00edvel",
        loadingRecords = "Carregando dados...",
        decimal       = ",",
        thousands     = ".",
        infoThousands = ".",
        buttons = list(
          copyTitle   = "Copiado para a \u00e1rea de transfer\u00eancia",
          copyKeys    = paste0(
            "Pressione <i>Ctrl</i> ou <i>\u2318</i> + <i>C</i> para copiar ",
            "os dados da tabela.<br><br>Para cancelar, clique nesta mensagem ",
            "ou pressione Esc."
          ),
          copySuccess = list(`_` = "%d linhas copiadas", `1` = "1 linha copiada")
        ),
        columnControl = list(
          buttons       = list(searchClear = "Limpar pesquisa"),
          colVis        = "Visibilidade da coluna",
          colVisDropdown = "Visibilidade da coluna",
          dropdown      = "Mostrar mais...",
          list          = list(
            all = "Todos", empty = "Vazio", none = "Nenhum",
            search = "Pesquisar..."
          ),
          orderAddAsc   = "Adicionar \u00e0 ordem crescente",
          orderAddDesc  = "Adicionar \u00e0 ordem decrescente",
          orderAsc      = "Ordem crescente",
          orderClear    = "Remover ordena\u00e7\u00e3o",
          orderDesc     = "Ordem decrescente",
          orderRemove   = "Remover ordena\u00e7\u00e3o",
          reorder       = "Reordenar",
          reorderLeft   = "Mover para a esquerda",
          reorderRight  = "Mover para a direita",
          search = list(
            datetime = list(
              empty = "Vazio", equal = "Igual a", greater = "Posterior a",
              less = "Anterior a", notEmpty = "N\u00e3o est\u00e1 vazio",
              notEqual = "Diferente de"
            ),
            number = list(
              empty = "Vazio", equal = "Igual a", greater = "Maior que",
              greaterOrEqual = "Maior ou igual a", less = "Menor que",
              lessOrEqual = "Menor ou igual a",
              notEmpty = "N\u00e3o est\u00e1 vazio", notEqual = "Diferente de"
            ),
            text = list(
              contains = "Cont\u00e9m", empty = "Vazio", ends = "Termina em",
              equal = "Igual a", notContains = "N\u00e3o cont\u00e9m",
              notEmpty = "N\u00e3o est\u00e1 vazio", notEqual = "Diferente de",
              starts = "Come\u00e7a por"
            )
          ),
          searchClear     = "Limpar pesquisa",
          searchDropdown  = "Pesquisar"
        )
      )
    )

    # ── Cria o widget ─────────────────────────────────────────────────────────
    # Styling via parâmetros diretos do dt2() (API v2)
    dt2(
      data       = df,
      compact    = TRUE,
      striped    = TRUE,
      hover      = TRUE,
      font_scale = 0.85,
      responsive = FALSE,   # responsive pode conflitar com renderers customizados
      options    = opts
    )
  })

  # ── Debug: mostra estado da tabela ──────────────────────────────────────────
  output$state <- renderPrint({
    s <- input$example_state
    if (is.null(s)) return("(interaja com a tabela)")
    cat("Reason:", s$reason, "\n")
    if (!is.null(s$page))
      cat("Page:",  s$page$page + 1, "of", s$page$pages, "\n")
    if (!is.null(s$search) && nzchar(s$search))
      cat("Search:", s$search, "\n")
    if (!is.null(s$order))
      cat("Order:", paste(s$order, collapse = ", "), "\n")
  })
}

shinyApp(ui, server)

Try the DT2 package in your browser

Any scripts or data that you put into this service are public.

DT2 documentation built on June 14, 2026, 9:06 a.m.