Tag: estilo

  • Sistemas de Design: Padronização de Sistemas

    Sistemas de Design: Padronização de Sistemas

    Design é projeto. E, como todo projeto, ele deve ser documentado, possuindo descrições, informações de uso e instruções em geral. É exatamente isso que são Sistemas de Design.

    Sistemas de Design são documentações, online ou físicas, que descrevem a padronização e uso de elementos de um projeto, com o objetivo de que ele possa ser usado em diferentes aplicações, mantendo uma identidade compartilhada. Essa identidade pode ser referenciada a um projeto, linha, segmento ou empresa. São usados elementos como formas, grades, tipografia, assets, iconografia, recomendações de uso, dentre outros. Algo muito semelhante ao que o design gráfico já está acostumado ao criar um Manual de Identidade Visual.

    Um exemplo comum, do dia a dia dos usuários de computador, é o Microsoft Office. Trata-se de um conjunto de aplicativos que, por serem da mesma linha, precisam seguir a mesma identidade. Para que os desenvolvedores possam trabalhar com esse padrão, um sistema de design certamente foi desenvolvido para que a equipe de desenvolvimento possa trabalhar de forma coerente.

    O Sistema de Design garante ao usuário uma fluidez no uso das aplicações e reconhecimento das propriedades do software. Facilitando, dessa forma, não só a identificação, como ajudando o usuário a pressupor determinados recursos ou localizações de ferramentas.

    Experiência Compartilhada

    Grandes empresas de tecnologia desenvolvem seus próprios sistemas de design para que desenvolvedores terceiros possa criar experiências mais coesas e confortáveis para o usuário. Seguir esses sistemas pode ser requerimento ou uma simples recomendação, dependendo da loja e sistema operacional que precisar aprovar.

    Aplicativos, mobile e desktop, são os mais comuns a receberem influencias direta dos sistemas de design de sistemas operacionais que, recomenda-se, devem ser mesclados aos sistemas da própria empresa. Para esses casos temos como exemplo:

    Como dito anteriormente, seguir um sistema de design de sistema operacional é fundamental para manter uma experiência fluida para o usuário. Por isso que, por exemplo, é possível sentir diferenças entre uma mesma aplicação desenvolvida para Windows, Android e dispositivos Apple, porque cada desenvolvedora, apesar de manter sua própria identidade, adapta-se para o ambiente em que o usuário estará.

    Screenshot do WhatsApp vs iOS
    O Whatsapp adapta quase que completamente sua UI de acordo com o sistema de design de cada Sistema Operacional
    Captura de tela do Visual Studio no Windows 10 x MacOSX
    Visual Studio no Windows 10 vs Visual Studio no MacOSX

    Não necessariamente o designer ou desenvolvedor são obrigados a seguir um determinado sistema de design de terceiros. Às vezes, a melhor opção é justamente criar o seu próprio sistema ou criar uma barreira para ter uma identidade única para sua aplicação.

    Algumas empresas, como a Google, estimulam para que os designers utilizem seus sistemas também em sites ou aplicativos web. Para isso, dão descrições específicas de usabilidade e ferramentas que possam agilizar o desenvolvimento dessas aplicações.

    Frameworks e Bibliotecas

    Para facilitar o desenvolvimento, muitas empresas oferecem frameworks e bibliotecas, tanto web quanto para desktop e mobile, afim de estimular o desenvolvedor a seguir aquele determinado padrão.

    É interessante para as grandes desenvolvedoras que outras empresas sigam seus padrões de design, pois, dessa forma, ditam tendências e criam assimilações dos usuários com suas próprias plataformas, principalmente quando essas outras empresas utilizam ferramentas e APIs das grandes.

    A exemplo de frameworks e bibliotecas temos:

    Porém, nem sempre essas opções suprem a demanda, pois não são compatíveis com outras bibliotecas e tecnologias. Por isso, muitos grupos de desenvolvedores trabalham para criar frameworks e bibliotecas abertas que seguem os padrões estipulados pelos sistemas de design. Como por exemplo:

    Utilizar Sistemas de Design, além de facilitar o processo de desenvolvimento, ajuda a manter o usuário mais confortável no ambiente em que ele está acostumado. Criar ou adaptar o seu próprio sistema também ajuda a criar uma identidade única para seu conjunto de aplicativos e serviços.

    Que outros sistemas de design e frameworks vocês podem recomendar? Deixem nos comentários.

  • Como adicionar e remover dinamicamente campos HTML em um form? (Javascript puro)

    Como adicionar e remover dinamicamente campos HTML em um form? (Javascript puro)

    O Javascript nos permite criar conteúdos dinâmicos e podemos usar isso para adicionar remover e adicionar elementos de acordo com as opções do usuário. Dados como informações sobre dependentes, links de mídias sociais, e-mails adicionais, etc. são curtos e não fazem sentido criarmos um formulário separado apenas para estes. Por isso, é interessante incluirmos a opção de adicionar diretamente esses campos em um subformulário dinâmico.

    Obs. Se quiser ir direto para o código final, procure o link do JsFiddle no final do post.

    HTML

    Para iniciarmos, basta criar um container no HTML onde você quer que os elementos sejam exibidos, além do botão simples de adição e um botão de captura de dados, para que, desta forma, possamos enviar o JSON resultante para o back-end.

    A organização dos containers é sempre muito importante quando trabalhamos com Javascript e nos dedicamos ao uso correto da web semântica.

    <div class="dependentes">
      <button id="btnAdicionarDependentes">
      📝Adicionar Dependentes
      </button>
      <div class="container" id="dependentesContainer">
      </div>
      <button class="green" id="btnCapturarDados">
      ✅ Capturar Dados
      </button>
    </div>
    <pre id="containerDados">  
    </pre>

    Javascript

    Como de praxe, usaremos o Javascript puro para realizar essa tarefa, dessa forma você poderá usar em qualquer lugar o que aprender aqui.

    Para poder capturar e devolver os dados, usaremos um objeto JSON, dessa forma fica fácil remontar, tanto no back-end, quando no front-end, os dados nas formatações e/ou elementos que precisamos.

    Vamos criar, como exemplo, o JSON abaixo, e vamos declará-lo em uma variável global chamada dependentes, seguindo a ideia de um cadastro de lista de dependentes, então temos:

    var dependentes = [{
      identificador: 13,
      nome: "Joana da Silva",
      idade: 12,
    }];

    Agora vamos nos focar nas funções. A primeira coisa que vamos fazer é criar um método que pegue os dados do JSON e o converta para elementos HTML renderizados na tela. Dessa forma, usaremos um laço para ler o JSON e aplicamos seus dados em uma template string e o adicionamos no container específico:

    function carregarDependentes() {
      let dependentes_container = document.querySelector("#dependentesContainer");
      dependentes_container.innerHTML = "";
      dependentes.forEach((el) =&gt; {
        let identificador = el.identificador;
        let nome = el.nome;
        let idade = el.idade;
        let dependente_container = `<div class="dependente" data-id="${identificador}">
        								<input class="nome" placeholder="Digite o nome" type="text" value="${nome}">
                                        <input class="idade" placeholder="Digite a idade" type="number" value="${idade}">
                                        <div class="action">
                                            <a href="#" class="salvar">salvar 💾</a>
                                            <a href="#" class="remover">❌</a>
    									</div>
                                    </div>`;
        dependentes_container.innerHTML += dependente_container;
      });
    }

    Agora vem o segredo que facilita o processo e o deixa mais organizado. Ao invés de remover e adicionar os elementos na tela, iremos nos focar em remover e adicionar do JSON e, em seguida, regenerar os elementos a partir desse objeto. Ficando, assim, com um código mais limpo. Outro motivo pelo qual usamos a regeneração dos elementos é para evitar criarmos IDs únicos temporários. Ao regenerar, podemos usar os índices do próprio array como identificador.

    Para adicionar um novo item, basta incluirmos um dado vazio no JSON, porém, seguindo nosso modelo, e mandamos gerar novamente os elementos:

    function adicionarDependentes() {
      dependentes.push({ identificador: "", nome: "", idade: "" });
      carregarDependentes();
    }

    Para remover, similar a criação de um novo, vamos usar um laço, porém para adicionar o evento aos botões de excluir. Usaremos então o método splice para remover o array. Depois, obviamente, vamos regenerar os elementos a partir da função carregarDependentes():

    function removerDependentes() {
      document
        .querySelectorAll("#dependentesContainer .remover")
        .forEach((el, i) =&gt; {
          el.addEventListener("click", () =&gt; {
            dependentes.splice(i, 1); // O splice vai remover um item do array no JSON
            carregarDependentes(); // E chamamos o método para regenerar os elementos
          });
        });
    }

    Uma vez que adicionamos uma nova linha em branco precisamos salvar seu preenchimento e aí iremos usar a mesma lógica de remoção, mas usaremos o splice para substituir e não para remover um dado do JSON. Porém, adicionaremos uma pequena validação para evitar entrar dados incompletos:

    unction salvarDependentes() {
      document
        .querySelectorAll("#dependentesContainer .salvar")
        .forEach((el, i) =&gt; {
          el.addEventListener("click", () =&gt; {
            let identificador = el.parentElement.parentElement.getAttribute(
              "data-id"
            );
            let nome = el.parentElement.parentElement.querySelector(".nome").value;
            let idade = el.parentElement.parentElement.querySelector(".idade")
              .value;
    
            if (!nome.length || !idade.length) { // Verifica se nome e idade foram preenchidos
              alert("Nome e idade precisam ser preenchidos para salvar.");
              return false;
            }
            dependentes.splice(i, 1, {
              identificador: identificador,
              nome: nome,
              idade: idade,
            }); // Substitui o dado no JSON
            carregarDependentes(); //  E chamamos o método para regenerar os elementos
          });
        });
    }

    Um outro método que precisamos adicionar é uma forma de bloquear para que um usuário consiga clicar em outros elementos ao redor, sem antes finalizar a edição do item. Para isso, iremos fazer um laço que adiciona uma classe de CSS, que vamos chamar de disabled, em todos os elementos, exceto o que está sendo editado. Essa classe possui um point-events: 0 e um opacity: 0.5. Para demonstrar que está desativado, você pode ainda adicionar outros efeitos, como filtros de baixo contraste ou escala de cinza.

    function travarOutros(element) {
      if (element == false) { // Passar false como parâmetro para que todos os elementos fiquem habilitados novamente, ao invés de apenas o elemento que queremos liberar
        document
          .querySelectorAll(".dependentes button, .dependentes .container &gt; div")
          .forEach((el) =&gt; {
            el.classList.remove("disabled");
          });
        document.querySelector("#containerDados").innerHTML = "";
        return false;
      }
      document
        .querySelectorAll(".dependentes button, .dependentes .container &gt; div")
        .forEach((el) =&gt; {
          if (el != element) {  // Verifica se o elemento no laço é o que está sendo editado
            el.classList.add("disabled");
          }
        });
    }

    Agora, antes de continuarmos, vamos revisitar as funções acima para chamar, quando necessário, uma função dentro da outra (leia os comentários no código para entender), ficando assim:

    function carregarDependentes() {
      let dependentes_container = document.querySelector("#dependentesContainer");
      dependentes_container.innerHTML = "";
      dependentes.forEach((el) =&gt; {
        let identificador = el.identificador;
        let nome = el.nome;
        let idade = el.idade;
        let dependente_container = `<div class="dependente" data-id="${identificador}">
        															<input class="nome" placeholder="Digite o nome" type="text" value="${nome}">
                                      <input class="idade" placeholder="Digite a idade" type="number" value="${idade}">
                                      <div class="action">
                                      	<a href="#" class="salvar">salvar 💾</a>
                                        <a href="#" class="remover">❌</a>
    																	</div>
    															  </div>`;
        dependentes_container.innerHTML += dependente_container;
      });
      salvarDependentes(); // Adicionamos o método aqui para que o laço seja aplicado nos novos items adicionados
      removerDependentes(); // Adicionamos o método aqui para que o laço seja aplicado nos novos items adicionados
      travarOutros(false); // Adicionamos para destravar tudo
    }
    
    function removerDependentes() {
      document
        .querySelectorAll("#dependentesContainer .remover")
        .forEach((el, i) =&gt; {
          el.addEventListener("click", () =&gt; {
            dependentes.splice(i, 1);
            carregarDependentes(); // Regenerar os elementos HTML após remover do JSON
          });
        });
    }
    
    function adicionarDependentes() {
      dependentes.push({ identificador: "", nome: "", idade: "" });
      carregarDependentes(); // Regenerar os elementos HTML após remover do JSON
      travarOutros(
        document.querySelector("#dependentesContainer &gt; div:last-child")
      ); // Desabilitar todos os outros elementos, exceto o que acabou de ser adicionado
    }
    
    function salvarDependentes() {
      document
        .querySelectorAll("#dependentesContainer .salvar")
        .forEach((el, i) =&gt; {
          el.addEventListener("click", () =&gt; {
            let identificador = el.parentElement.parentElement.getAttribute(
              "data-id"
            );
            let nome = el.parentElement.parentElement.querySelector(".nome").value;
            let idade = el.parentElement.parentElement.querySelector(".idade")
              .value;
    
            if (!nome.length || !idade.length) {
              alert("Nome e idade precisam ser preenchidos para salvar.");
              return false;
            }
            dependentes.splice(i, 1, {
              identificador: identificador,
              nome: nome,
              idade: idade,
            });
            carregarDependentes(); // Regenerar os elementos HTML após remover do JSON
            travarOutros(false); // Liberar todos os elementos novamente
          });
        });
    }
    
    function travarOutros(element) {
      if (element == false) {
        document
          .querySelectorAll(".dependentes button, .dependentes .container &gt; div")
          .forEach((el) =&gt; {
            el.classList.remove("disabled");
          });
        document.querySelector("#containerDados").innerHTML = "";
        return false;
      }
      document
        .querySelectorAll(".dependentes button, .dependentes .container &gt; div")
        .forEach((el) =&gt; {
          if (el != element) {
            el.classList.add("disabled");
          }
        });
    }

    Agora, tudo o que precisamos fazer é incluir os comandos de inicialização, onde aplicamos o método de adição ao evento de clique do botão e carregamos os dados iniciais do JSON:

    //init
    document.querySelector("#btnAdicionarDependentes").addEventListener("click", adicionarDependentes);
    carregarDependentes();

    Por fim, vamos criar uma função para o botão de capturar dados apenas para extrair e mostrar os dados em JSON. Você pode, eventualmente, usar esses dados e enviar via POST, por AJAX ou via campo oculto, e pegar no back-end para guardar ou processar os dados (como com um json_decoder, do PHP):

    //capturarDados
    document.querySelector("#btnCapturarDados").addEventListener("click", ()=&gt;{
    	document.querySelector("#containerDados").innerHTML = JSON.stringify(dependentes, undefined, 4);
    });

    CSS

    O único CSS que precisaremos usar é para aplicar a classe disabled. Se por acaso você está usando algum framework CSS, recomendo que você use o relativo a esta classe deste. Consulte a documentação, onde geralmente está relacionado aos helpers:

    .disabled{
      pointer-events: none;
      opacity: .5;
    }

    Se você quer usar o CSS mais elaborado que usei aqui, veja abaixo o link do JsFiddle.

    Finalizando

    Criar formulários dinâmicos auxilia a usabilidade à medida que permite que o usuário adicione dados de forma mais rápida e com respostas visuais imediatas. O uso aqui do Javascript puro visa a facilidade para que você possa implementar em quaisquer projetos, incluindo os com Typescript. Trabalhe um pouco no código para adequá-lo à sua necessidade. E, como sempre, você poderá puxar o código e testar direto do JsFiddle.

    Aproveite e entre para nosso grupo de discussão no Telegram.