sexta-feira, 10 de janeiro de 2020

Responsividade com Flexbox

Existem muitos textos que falam sobre FLEXBOX na internet, assim não vou montar um manual a respeito deste assunto, mas sim focar naquilo que considero mais chato de ser resolvido: a largura dos itens na linha. Sem o domínio desse recurso, o resultado da interface não será convincente.

No Genexus este tema é muito relevante, pois, existem algumas propriedades que poderão ser  definidas no momento da criação da interface, e outras manualmente.

Vamos falar um pouco sobre a questão da largura dos elementos na interface, utilizando um simples arquivo HTML poderemos estudar com maior profundidade esse assunto antes de se aventurar a mexer na interface Genexus.

Largura dos itens

O default para se determinar a largura dos itens na linha em um display:flex, é determinado pelo conteúdo desses itens. A largura do texto, por exemplo, determina a largura do item. Se o conteúdo é menor que a largura, uma área com espaço vazio fica aparecendo, como na imagem abaixo.


Normalmente o que se busca fazer é que todo espaço da linha seja ocupado pelos itens, distribuídos com a mesma largura, ou que pelo menos a largura possa ser controlada.

Um dos recursos disponíveis no flexbox é determinar que os itens cresçam automaticamente, caso exista espaço. Para isso poderemos utilizar uma propriedade flex-grow:1 para determinar que os elementos possam crescer para o limite máximo disponível da linha. E se todos estiverem com 1 a dimensão será dividida de forma igual. O uso de flex-grow:0 fará com que o elemento fique em segundo plano na determinação da largura, ficando com o 'resto' do espaço disponível (normalmente o mínimo necessário para apresentar seu conteúdo).


O flex-grow não vai ajudar caso exista algum item maior (em termos de conteúdo) que os demais, pois este vai crescer mais que os outros. Se for um texto muito largo, praticamente vai consumir a largura inteira disponível, deixando os demais itens com o minimo possível, o suficiente apenas para manter visível seu conteúdo.


Desta forma é necessário mais alguns detalhes para se controlar a largura. Para se produzir isso, foi apenas necessário programar divs e classes css, conforme o exemplo a seguir.

<style>
.linha {display:flex; border:1px solid red; padding:2px;}
.item1 {flex-grow:1;border:1px solid red;}
.item2 {flex-grow:1;border:1px solid red;}
.item3 {flex-grow:1;border:1px solid red;}
</style>
<div class="linha">
 <div class="item1">Mussum Ipsum, ...</div>
 <div class="item2">2</div>
 <div class="item3">3</div>
</div>

Dominando a largura

Vimos que é necessário ter um pouco de controle sobre as larguras, e foi-se o tempo em que se podia determinar larguras fixas para os itens. Em tempos de responsividade a principal preocupação deve ser a de que a interface tenha uma apresentação boa em qualquer largura de dispositivo, e valores fixos em pixels não permitem isso.

Nesse exemplo anterior, é importante estabelecer um limite de crescimento para o item com muito texto, deixando espaço para os demais itens da linha. Para se dominar a largura dos itens é necessário utilizar max-widthmin-width ou flex-basis, e com esses estabelecer os limites máximos (max-width) e mínimos de cada item.

Desta forma, caso se deseje que o item que possui mais texto cresça até o limite máximo de 20% da largura da janela, bastará incluir o max-width:20% na classe do mesmo, e seguindo as definições em nossa classe item1 do exemplo anterior, poderíamos fazer: .item1{flex-grow:1; max-width:20%; border: 1px solid blue;}


O espaço que sobra será igualitariamente distribuído para o item 2 e 3.

E assim, se no item 3 espera-se que possua um conteúdo com no minimo 200px de largura, bastará incluir esta definição na classe correspondente. Por exemplo: .item3{flex-grow:1;  min-width:200px; border: 1px solid blue;}



Apesar de não ser recomendado o uso de pixels, isso será possível e não trará nenhum problema para a interface, desde que se planeje o tema responsividade com a determinação das dimensões para o caso do dispositivo possuir uma largura menor. Sempre que fixar algo em pixel, será necessário avaliar o resultado em dispositivos menores.

Uma propriedade flex-basis é disponibilizada no flexbox, que ao receber uma definição de tamanho em pixel ou % terá o significado de dimensão inicial, ou width.

Antes de encerrar essa parte é importante compreender que nos navegadores atuais temos várias unidades de medidas que poderão ser utilizadas para a determinação da largura., tais como: em, rem, vh, vw, vmin, vmax, ex, ch, px, %. E ainda será possível realizar cálculos das dimensões diretamente na propriedade com o width: calc(100%-20px), por exemplo. (https://developer.mozilla.org/pt-BR/docs/Web/CSS/calc). De todas essas unidades a única que não é boa no contexto atual é o pixel.

O script utilizado neste tópico, é apresentado a seguir.

<style>
.linha {display:flex; border:1px solid red; padding:2px;}
.item1 {flex-grow:1;max-width:20%; border:1px solid red;}
.item2 {flex-grow:1; border:1px solid red;}
.item3 {flex-grow:1;min-width:200px; border:1px solid blue;}
</style>
<div class="linha">
 <div class="item1">Mussum Ipsum, ...</div>
 <div class="item2">2</div>
 <div class="item3">3</div>
</div>

Responsividade

Finalmente dominada a questão das larguras, agora é necessário avançar sobre os diferentes dispositivos em que a interface será apresentada. O que significa avaliar a interface atual sob diversas dimensões distintas. Os navegadores atuais possuem um recurso de Inspecionar que permite reduzir as larguras das interfaces para dispositivos (android, iphone, ipad, ...), que facilita a compreensão da visualização final. Claro essa avaliação será apenas sob o ponto de vista da prototipação da interface, vai faltar uma avaliação final diretamente no dispositivo.

O recurso que precisaremos recorrer para construir interfaces responsivas é o media query, utilizado para selecionar certa característica de uma classe, assim que certa dimensão é alcançada. Para controlar a responsividade será necessário determinar as dimensões desejadas para cada um dos itens caso o dispositivo reduza ou amplie a largura.

Um exemplo poderia ser determinar largura de width:100% para os três itens da linha, fazendo com que os mesmos ocupem a largura toda da interface, isso quando o dispositivo atingir uma dimensão em pixel de 768px. Nesse contexto estamos dizendo que para smartphones os itens devem ser apresentados com 100% de largura.

Para programar essa definição será necessário programar, dentro da área script do exemplo:

@media max-width:768px) {
 .item1 {flex-grow:1;width:100%;border:1px solid red;}
 .item2 {flex-grow:1;width:100%;border:1px solid red;}
 .item3 {flex-grow:1;width:100%;border:1px solid red;}
}

As classes podem ser completamente redefinidas por meio desse recurso, estabelecendo fontes, cores, bordas diferentes para cada situação. Inclusive ocultar algum item na apresentação.

E para causar o efeito de reposicionamento em uma nova linha, será necessário incluir mais uma propriedade flex-wrap:wrap, indicando que em caso de não haver espaço suficiente para item na linha, será necessário definir o próximo item em uma nova linha. Quebra de linha, em outras palavras.


O código para produzir esse cenário responsivo é apresentado abaixo.

<style>
.linha {display:flex;flex-wrap:wrap;border:1px solid red; padding:2px;}
.item1 {flex-grow:1; border:1px solid red;}
.item2 {flex-grow:1; border:1px solid red;}
.item3 {flex-grow:1; border:1px solid red;}
@media(max-width: 768px) {
 .item1 {flex-grow:1;flex-basis:100%; border:1px solid red;}
 .item2 {flex-grow:1;flex-basis:100%; border:1px solid red;}
 .item3 {flex-grow:1;flex-basis:100%; border:1px solid red;}
}
</style>

Genexus

Tudo que vimos anteriormente poderá ser reproduzido no Genexus, sendo algumas coisas possíveis nas propriedades das tabelas, e outras que deverão ser programadas externamente em um arquivo CSS.

O elemento utilizado para produzir uma interface flexbox é a tabela (table), podendo ser a própria MainTable.



Será necessário converter a tabela com o botão direito do mouse e Convert to Flex.  Outra forma seria a programação hardcore em HTML + CSS inclusa em um textblock do tipo HTML, por exemplo. A tabela convertida receberá a class="Flex"[data-gx-flex]ao item, que definem o display:flex.

<div id="MAINTABLE" class="Flex" [data-gx-flex]></div>

As definições no carmine.css dessas classes são as seguintes:

[data-gx-flex] {
 display: flex;
}

.Flex {
    border-style: none;
    border-width: 0px;
}

Primeiramente será necessário criar o arquivo minhasclasses.css com as definições que desejamos para a nossa interface, inclusive com a programação do media query.  Este arquivo deverá estar na pasta web, juntamente com os demais arquivos do Genexus. Abaixo o conteúdo.

.linha{display:flex; flex-wrap:wrap;}
.item1{flex-grow:1;}
.item2{flex-grow:1;}
.item3{flex-grow:1;}
@media(max-width: 768px) {
 .item1{flex-grow:1;flex-basis:100%;}
 .item2{flex-grow:1;flex-basis:100%;}
 .item3{flex-grow:1;flex-basis:100%;}
}

Para incluir o arquivo no Webpanel, deverá ser programada a operação form.HeaderRawHTML no evento Start. Isso incluirá a chamada ao arquivo na área head do HTML.

Event Start
 form.HeaderRawHTML = '<link rel="stylesheet" type="text/css" href="minhasclasses.css" />'
Endevent

Se for necessário incluir mais coisas, basta colocar o conteúdo na mesma definição, ou ainda utilizar uma operação acumulativa: form.HeaderRawHTML +=  

Em seguida cada controle incluído na tabela será um item no layout flex, como por exemplo, inserindo três textblocks na sequencia. Para cada um deles será necessário determinar o conteúdo, em caption, e também a classe externa que definimos no arquivo minhasclasses.css.


Veja que a classe do textblock é associada a propriedade Cell Class, isso ocorre porque o Genexus vai incluir o item dentro de uma <div> específica, item (1) da imagem abaixo, e o próprio elemento textblock será definido em um <span>, item (2)


Não esqueça de definir a classe linha para o MainTable.


Optamos nesse exemplo em realizar externamente todas as definições de classe, mas se você acha mais interessante incluir tudo no Carmine.css, o efeito será o mesmo.

O resultado será um layout flexbox responsivo.


Que sofrerá alteração visual quando a interface for reduzida.




Genexus vai permitir que as definições sejam realizadas nas propriedades dos itens e tabelas, incluindo os elementos wrap, direction, justify content, entre outros. A opção por realizá-las externamente é que assim será possível alterar a visualização pela edição do CSS, sem a necessidade de se recompilar a interface. Acho melhor.

É claro que tem muita coisa a ser vista ainda, inclusive a troca de direção dos itens, mas isso é assunto para outro momento. Se você dominar a largura da interface já é um bom caminho percorrido. Sofri bastante para entender que a coisa era muito simples, espero que você sofra um pouco menos, e por falar nisso Feliz 2020!.

Nenhum comentário: