Navegación con teclado para Easy UI Datagrid

Hace poco me vi en la necesidad de usar el componente Datagrid de la extensión Easy UI para JQuery y, aunque sus características cubrían perfectamente mis requisitos me hacía falta poder navegar por las celdas mediante el ratón, no implementado en el componente. Los problemas que necesitaba resolver eran:

  • Easy UI Datagrid marca (highlight) la fila entera sobre la que seleccionamos una celda, no la celda en si.
  • También marca la fila entera sobre la que situamos el ratón (hover).
  • Dispone de un método para obtener el índice de la fila seleccionada, pero no lo hay para el índice de la columna.


Para evitar que aparezcan en diferente color la fila seleccionada y la fila donde situamos el ratón simplemente cambiamos el estilo de las clases CSS usadas para definirlas. Dentro de la carpeta del estilo que estemos usando localizamos dos ficheros: easyui.css y datagrid.css, en los cuales localizamos las clases:

.datagrid-row-selected
.datagrid-row-over

y las redefinimos. En mi caso necesitaba que no fueran visibles así que:
.datagrid-row-selected {
     background: transparent; 
     color: #ffffff;
}

.datagrid-row-over {
     background: transparent;
     color: #ffffff;
     cursor: default;
}
Con ésto hemos solucionado los dos primeros puntos. Para solucionar el tercero necesitamos declarar una variable en nuestro script para contener el índice a la columna seleccionada en cada momento. Con ello podremos seguir la pista al cursor para marcar visualmente las celdas seleccionadas y también podremos obtener el nombre de la columna (field). Para empezar definimos una clase CSS para nuestras celdas seleccionadas, algo simple:
.cell-selected {
     background-color: green;
     color: #000000;
}

Ahora necesitamos saber cómo localizar la celda que nos interesa dentro del árbol DOM generado por Datagrid. Un vistazo con el inspector del navegador nos permite observar la estructura de la página: los datos se organizan dentro de tablas donde cada fila (TR) se identifica con la cadena datagrid-row-2-3-  (no se si estos dígitos corresponden a la versión) seguida del índice de la fila a la que corresponde. Dentro de ella habrá por cada dato una etiqueta TD FIELD=nombre del campo y dentro de ésta un DIV para contener el dato en si. Sabiendo ésto podemos acceder a dicho DIV para modificar sus atributos con el siguiente código:
var elem = $('#' + 'datagrid-row-r3-2-' + index).children('td[field="' + field + '"]').children('div');
$(elem).addClass('cell-selected');

Con esta información ya podemos implementar una pequeña rutina de teclado para navegar por la tabla. Con el ejemplo siguiente además de usar las teclas arriba, abajo, derecha e izquierda podemos usar también tabulación, enter (para iniciar edición) o escape (para cancelar edición):

  var gridId; // El selector de nuestro Datagrid
  var column = 0; // Inicializamos a la primera columna
        var editIndex = undefined; // Contiene indice de la fila en edición

  function bindKeys()
  {
         // 'Bindeamos' el evento keydown al panel que contiene la tabla de datos
   $(gridId).datagrid('getPanel').panel('panel').attr('tabindex',1).bind('keydown',function(e)
   {
    var selected = $(gridId).datagrid('getSelected');

    var index = 0; // Indice de la fila seleccionada
    var ed = null; // Editor activo (si lo hay)
    if (selected)
    {
     index = $(gridId).datagrid('getRowIndex', selected);
     ed = $(gridId).datagrid('getEditors', $(gridId).datagrid('getRowIndex', selected));
    }
    else
     index = $(gridId).datagrid('getRows').length - 1;
    if (index > -1)
    {     
                 // Cada código de desplazamiento comprueba que no se esté editando una celda (editIndex) y los límites
     switch(e.keyCode)
     {
      case 38: // arriba
       if (editIndex == undefined)
        highlightCella((index - 1 > 0) ? index - 1 : 0);
       break;
      case 39: // derecha
       if (editIndex == undefined)
       {      
        column = (column < $(gridId).datagrid('getColumnFields').length)) ? column + 1 : column;
        highlightCella(index);
       }
       break;  
      case 37: // izquierda
       if (editIndex == undefined)
       {      
        column = (column > 1) ? column - 1 : column;
        highlightCella(index);
       }
       break;       
      case 40: // abajo
       if (editIndex == undefined)
        highlightCella((index + 1 < $(gridId).datagrid('getRows').length) ? index + 1 : index);
       break;
      case 13: // enter, editamos, gracias a 'column' obtenemos el nombre del campo
       onDblClickCell(index, $(gridId).datagrid('getColumnFields')[column]);
       break;
      case 27: // ESC
       if (ed.length > 0)
       {
        endEditing(true);
        highlightCella(index);
       }
       break; 
      case 9: // TAB
       if (ed.length > 0)
       {
        endEditing(true);
        column = (column < $(gridId).datagrid('getColumnFields').length)) ? column + 1 : column;
        highlightCella(index);
       }
       break;               
                     
     }
    }
    if (e.keyCode == 9)
     e.preventDefault();  // Si hemos pulsado TAB evitamos el proceso de la tecla para no sacar el foco del Datagrid
   });   
  }

  // Se añade/elimina la clase cell-selected a cada celda según estés seleccionada o no
  function highlightCella(index, field)
  {
   if (cellaAnt != null) // Si hay una celda anterior seleccionada la deseleccionamos
    $(cellaAnt).removeClass('cell-selected');
   if (typeof field === 'undefined') // Si recibimos el field vacío lo leemos a partir de column
    field = $(gridId).datagrid('getColumnFields')[column];   
   var elem = $('#' + 'datagrid-row-r3-2-' + index).children('td[field="' + field + '"]').children('div');
   $(elem).addClass('cell-selected'); // Añadimos la clase a la celda seleccionamos
   $(gridId).datagrid('selectRow', index); // Actualizamos el índice de fila de Datagrid
   cellaAnt = elem; // Y guardamos la celda por si nos desplazamos
  }
  
        // Finalizar edición
        // El parámetro escap sirve para indicar si entramos aquí por presionar ENTER o TAB
        // Si se ha pulsado ENTER y el campo es validado se acepta normalmente
        // sino se cancela la edición (else)
        function endEditing(escap){
            if (editIndex == undefined){return true}
            if ($(gridId).datagrid('validateRow', editIndex)) // Si se valida la edición aceptamos y salimos ok
   {
                $(gridId).datagrid('endEdit', editIndex);
                editIndex = undefined;
                return true;
            }
   else
   { // Sino no aceptamos la edición (caso de las select list vacías)
    if (typeof escap !== 'undefined')
    { // Si hemos llegado aquí es que se ha presionado ENTER o TAB
                 // Sin este test el editor no se cerraría
      $(gridId).datagrid('cancelEdit', editIndex);
                 editIndex = undefined;
    }
                return false;
            }
        }
  
        // Función para actualizar la columna
  function buscaColumna(field)
  {
   $.each($(gridId).datagrid('getColumnFields'), function(idx, val) {
    if (val == field)
    {
     column = idx;
     return false;
    }
   });
  }
  
        // Función para iniciar la edición de una celda
        // Llamamos a buscaColumna para mantener actualizado el índice de columnas
        function onDblClickCell(index, field){
            if (endEditing()){
    buscaColumna(field);
                $(gridId).datagrid('selectRow', index)
                        .datagrid('editCell', {index:index,field:field});
                editIndex = index;
            }
     } 
  
        // Función para actualizar índices de fila al hacer click en una celda
  function onClickCell(index, field)
  {
   if (editIndex == undefined)
   {
   $(gridId).datagrid('selectRow', index);
   highlightCella(index, field);
   }
  }
Las opciones de nuestro Datagrid deben contener las propiedades:
    onDblClickCell: onDblClickCell,
    onClickCell: onClickCell
y para inicializar ejecutamos:
   gridId = selector del Datagrid
   bindKeys();
   $(gridId).datagrid('selectRow', 0);
   highlightCella(0, $(gridId).datagrid('getColumnFields')[column]);
   $(gridId).datagrid('getPanel').panel('panel').focus();

No hay comentarios:

Publicar un comentario