Tabelas de grade CSS flexíveis


Exibir uma lista de leads (contatos frios)

Desde que já começamos, posso finalmente falar sobre um projeto secreto no qual trabalho nos últimos dois anos. Um dos recursos interessantes do Teamwork CRM é uma exibição de lista.

Este é um componente poderoso que ocorre sete vezes no aplicativo. De fato, uma mesa com esteróides. Eu sabia muito, mas não quero aborrecer você. Vou me concentrar em como implementamos essa flexibilidade com apenas algumas linhas de CSS (Grid). Ou seja, como apresentamos tabelas de dados pesadas, como suportamos o redimensionamento de colunas e muito mais.

Primeiro, o contexto precisa ser explicado, começando com as tarefas de finalidade e design dessas tabelas. Se isso não for do seu interesse, não hesite em prosseguir imediatamente para a implementação técnica .

Antes de tudo, nossa solução permite que os clientes visualizem rapidamente listas, por exemplo, leads ou contatos, e observem detalhes importantes. Não se parece com uma planilha do Excel. É melhor lidarmos com o layout dos dados, se houver muitos deles.

Absolutamente tudo funciona em um design flexível e adaptável. Começamos com a opção mais estreita e personalizamos o layout com base no conteúdo, design e casos de uso (não temos pontos de interrupção orientados a dispositivos).

Na largura mínima da tela, as colunas são empilhadas verticalmente, ocupando toda a largura da tela.


Como é a lista em uma tela estreita

Mesas flexíveis não são fáceis de fazer. Você pode escolher entre vários modelos existentes . Pense nas informações que os usuários estão procurando e escolha sabiamente.

Uma vez que temos pixels suficientes na tela, passamos para um layout mais típico, para que as colunas ... bem, se tornem colunas. Depois, não há grandes alterações no layout, mas ainda queremos exibir as colunas o mais conveniente possível para o cliente.

Suponha que tenhamos muitas colunas (posteriormente veremos como o usuário pode personalizá-las). Primeiro, a tabela deve preencher pelo menos a largura da tela. Em segundo lugar, a largura das colunas deve ser determinada pelo seu conteúdo e pelo tipo de valores. Por exemplo, texto curto / longo, data, número, URL, etc. As colunas com uma data devem ocupar menos espaço do que as colunas de texto longo.

As colunas devem ter uma largura mínima. O resultado geralmente é uma tabela que rola na vertical e na horizontal.


Como o layout depende da largura da janela. Desculpe pelo GIF estremecido, darei alguns exemplos interativos mais tarde.

Para começar, ao máximo usamos o CSS regular da velha escola para a tabela. Em seguida, aprimoramos com CSS Grid. Em seguida, mostrarei como os usuários podem redimensionar as colunas usando as ferramentas Grid, o que foi muito mais inconveniente com o CSS comum.

Bem, mostre já CSS Grid


Não sou especialista em CSS Grid, mas gosto disso. Essa é uma ferramenta extremamente poderosa e simples que implementa layouts complexos com código mínimo. Aqui vou pular a introdução à tecnologia. Você pode ler The New CSS Layouts de Rachel Andrew ou The Complete Grid Guide . Quando você terminar de pensar sobre onde essa ferramenta esteve toda a sua vida, volte para mim.

Primeiro, aplique display:grid em <table> para transformar a tabela em uma grade. Isso não quebrará nada: se o navegador não suportar Grid, ele usará display:table . Os elementos filho <thead> e <tbody> tornam-se elementos de malha. Não precisamos pensar em <thead> , <tbody> ou mesmo em <tr> . Queremos colocar nossos <th> e <td> nessa grade aplicando display:grid a cada uma delas (ou seja, grades dentro das grades), mas isso não é o ideal. Cada grade <tr> será independente das outras e isso não é bom (você verá mais tarde que o mesmo problema com o Flexbox).

A solução alternativa é usar display:contents em <thead> , <tbody> e <tr> . Isso basicamente os remove do layout da grade, empurrando os elementos filhos para a frente ( <th> e <td> ).

Em seguida, usamos a regra mágica grid-template-columns para controlar os elementos da grade. Sim, apenas uma linha de CSS. Por exemplo, se tivermos uma coluna de data e uma coluna de URL, será algo parecido com isto:

 grid-template-columns: minmax(150px, 1.33fr) minmax(150px, 2.33fr); 

Usamos o mesmo tamanho mínimo para todas as colunas, mas o valor máximo ( fr ) é determinado pelo tipo de dados da coluna. Tentei max-content auto e max-content , mas criamos uma opção melhor. Aqui está um exemplo simplificado: uma tabela interativa com código . Tente redimensionar a janela.

Alterando as larguras das colunas com Grade


Além disso, em nossas tabelas você pode trocar, alterar a largura e ocultar as colunas. O último é importante porque muitas colunas com diferentes tipos de dados são suportadas: essas são as propriedades do próprio elemento (por exemplo, leads), as propriedades de elementos relacionados (por exemplo, uma empresa associada a um lead) e campos personalizados.

Por exemplo, um usuário pode criar um campo personalizado (data) para contatos chamado "Data de nascimento", que será rastreado no sistema para cada contato.

Como ao criar um campo personalizado, o tipo "Data" é selecionado, o sistema processará esse campo levando em consideração esse tipo. Primeiro, explicarei como a largura muda.

  1. Quando o usuário move o cursor sobre o cabeçalho da coluna, um marcador de redimensionamento é exibido à direita. Ouvimos o evento mousedown no controle deslizante de redimensionamento.
  2. Quando o usuário clica no controle deslizante, vinculamos mais alguns métodos para ouvir os eventos de mousedown e mousedown (para a window ). Nesta fase, também adicionamos algumas aulas para decoração.
  3. Quando o usuário move o mouse, calculamos a nova largura da coluna levando em consideração a posição do cursor, a posição de rolagem da tabela e o mínimo definido. Em seguida, redefinimos a regra de grid-template-columns para <table> (através do atributo style ), desta vez substituindo o valor máximo ( fr ) por um pixel. Por exemplo, grid-template-columns: minmax(150px, 1.33fr) 296px; . Isso é feito usando requestAnimationFrame para fornecer a animação mais suave possível.
  4. Quando o mouseup chega, cancelamos os ouvintes do evento e excluímos as classes.

Experimente este exemplo simplificado .

Notavelmente, basta atualizar apenas um elemento no DOM, e não todas as células.

Sempre desenvolvemos uma interface do usuário com base na entrada por toque, mas, neste caso, é normal não suportá-la. Esta é uma ação muito precisa. Mesmo se eu quisesse redimensionar a coluna na tela sensível ao toque, provavelmente esperaria outra interação, por exemplo, através de um gesto multitoque.

Colunas de largura fixa


Você deve ter notado que ficou em silêncio sobre algo. De fato, a largura não apenas de uma coluna, mas de todas as colunas muda. Talvez você nem tenha notado isso, porque é assim que deve funcionar.

Inicialmente, achei que os usuários gostariam: quando esticam ou espremem colunas, outros se ajustam também. Se as colunas preencherem bem a largura da tela e você restringir uma delas, as outras poderão se expandir se houver conteúdo que não caiba em uma linha. Tente neste exemplo .

Mas, após um pequeno teste do usuário, descobriu-se que para as pessoas esse é um comportamento inesperado. O usuário sente uma certa perda de controle quando suas ações causam efeitos colaterais imprevisíveis.

Não devemos fazer suposições com base em quais colunas interagiram e quais não. Ao redimensionar uma coluna, o usuário já pode tomar uma decisão implícita de que a largura do resto é perfeita.

Portanto, se você abrir o aplicativo pela primeira vez, as colunas serão dispostas da maneira ideal. Se você alterar o tamanho da tela, as colunas também serão alteradas de acordo com o mesmo princípio. Assim que você toca no controle deslizante para redimensionar qualquer coluna, a largura de todas as colunas visíveis é fixa.


Antes, durante e depois de alterar a largura da coluna. Mais uma vez, desculpe o GIF se contorcer um pouco

Cada vez que uma coluna é redimensionada ou corrigida, criamos um registro localStorage independente que corresponde ao identificador da coluna com um valor de pixel para preservar as preferências do usuário.

Não me lembro exatamente por que decidimos definir um valor fixo em pixels, não uma opção adaptável. Talvez por simplicidade. Ou porque, na ausência de suporte para Grid e display:contents há uma reversão para uma abordagem mais arcaica para ajustar a largura das colunas.

Provavelmente, a opção adaptativa, em qualquer caso, não corresponderá às intenções do usuário. Não podemos assumir que o mais importante para ele é diminuir todas as colunas para que todas permaneçam na tela. Se uma pessoa alterou a largura da coluna, ele deseja ver uma certa quantidade de conteúdo nessa coluna. Se tivermos um bloco adaptável e ele se estreitar em uma janela menor, ignoraremos a escolha da pessoa. Ele precisará alterar a largura da coluna novamente para ver o mesmo conteúdo. É improvável que o usuário pense: "Hmm, quero que essa coluna ocupe 20% da janela, mesmo que eu a mude". No entanto, eu me aprofundo profundamente na situação limítrofe: na verdade, os usuários raramente redimensionam janelas.

Movendo e excluindo colunas



Interface para personalizar colunas exibidas

Imagine que um usuário alterou um conjunto de colunas por meio dessa interface. Se nenhuma das colunas selecionadas tiver sido alterada anteriormente, elas serão exibidas usando os valores padrão grid-template-column , dependendo do tipo de dados. Por exemplo, minmax(150px, 3.33fr) .

Se a largura de uma coluna for fixada em localStorage, fixamos a largura de todas as colunas selecionadas e também armazenamos esses valores em localStorage.

Com o tempo, mais e mais colunas mantêm uma largura fixa. Para os usuários, a única maneira de retornar ao design responsivo é redefinir as colunas.

Também armazenamos uma matriz de identificadores de coluna em localStorage, separados das entradas de largura.

"Por que você não usou {{libraryName}}?"


Com a biblioteca JavaScript, a solução será pesada, instável, não fornecerá interatividade e pode até não suportar <table>. Eu também não queria escrever algo assim. Pensei: "Deve haver uma maneira melhor".

"Por que você não usou o Flexbox?"


Cada linha será avaliada / exibida independentemente uma da outra. A coluna pode não estar alinhada com a coluna acima devido ao volume diferente do conteúdo.

Eu poderia mudar para <div> 's para colunas com células agrupadas verticalmente dentro. Mas não queria fazer isso. Eu queria usar <table>. Além disso, podemos facilmente encontrar outros problemas: por exemplo, incompatibilidade de células na altura entre colunas.

"Por que você não usou o <colgroup> ?"


De fato, <colgroup> é um elemento antigo conveniente. Após definir as colunas usando <col>, os estilos aplicados a um serão efetivamente aplicados a todas as células nessa coluna.

Mas isso acabou por ser uma solução muito limitada. Tentamos, mas rapidamente recusamos. Tão rápido que não consigo lembrar exatamente quais eram os problemas. Estou quase certo de que era impossível alcançar o nível desejado de adaptabilidade e não funcionava com o Flexbox e o Grid.

"Por que você não usou o layout da tabela: fixo?"


Eu poderia aplicar a regra de table-layout: fixed da table-layout: fixed na <table> e definir a largura da coluna como uma porcentagem. Mas olhando os exemplos e jogando com essa regra, tive a impressão de que ela só funciona em mesas com 100% de largura. Além disso, o redimensionamento de uma coluna altera o tamanho das outras colunas para atingir uma largura total de 100%.

"Mas você pode conviver com tabelas simples!"


Sim, as tabelas prontas para uso são capazes de muitas coisas inteligentes, mas não podem suportar efetivamente tudo o que eu queria implementar. Não concorda? Tudo bem, mago, me ensine.

Não exagere na exibição: conteúdo


O valor display: contents pode salvar o layout da tabela. Use-o somente quando realmente precisar. Alguns navegadores tiveram, ou pelo menos tiveram problemas com acessibilidade e leitores de tela.

Encontramos uma display: contents estranha display: contents bug de display: contents com arrastar e soltar nativo no Firefox.

Felizmente, em breve será lançada uma função de sub-grade que permitirá que os elementos filhos se integrem corretamente às grades. Em nosso aplicativo, queremos apenas simplificar a marcação, mas as sub-redes abrirão as portas para orgias de grade multidimensionais selvagens. Consulte “Por que exibir: o conteúdo não é um Layout de Grade CSS” .

Eu acho que esqueci algo


Parece que ainda havia um problema com excesso de texto ao redimensionar colunas, mas não me lembro exatamente.

Para salvar os cabeçalhos da tabela ao rolar para baixo, usamos position: sticky . Esse é um ótimo aprimoramento e se degrada muito bem em navegadores mais antigos. No entanto, para usuários do IE11, temos JavaScript de backup. Na verdade, eu não recomendaria a position: sticky devido a dificuldades com a rolagem horizontal.

Eu nem mencionei algumas das funções de nossas exibições de lista. Por exemplo, os usuários podem aplicar, salvar e trocar filtros personalizados (por exemplo, mostrar leads acima de US $ 500 com clientes em potencial na Europa). Nesses filtros, você pode memorizar um conjunto de colunas para sempre exibir colunas específicas para um fluxo de trabalho específico.

Em breve, implementaremos a edição em massa na exibição de lista e exportaremos visualizações personalizadas para CSV.

De qualquer forma, obrigado pela leitura.

Source: https://habr.com/ru/post/pt452922/


All Articles