UI Library – Proyecto con Webpack – Parte #2

Segunda y última parte de este proyecto, donde estamos construyendo una librería UI de componentes re-utilizables tipo Bootstrap. Si acabas de aterrizar aquí, te recomiendo que veas la parte #1 antes. En esa parte hemos iniciado el proyecto y creado tooltips dropdowns. En esta parte crearemos el resto de nuestros componentes. ¡Vamos allá! 🤗

Creando tabs

El siguiente componente será un conjunto de pestañas (en inglés, tabs), es decir, una caja de texto con una especie de menú que sólo muestra un contenido según si hacemos click en una opción u otra. Algo así:

  • 1ª tab
  • 2ª tab
  • 3ª tab

Contenido de la primera pestaña

Por defecto siempre habrá una tab que esté seleccionada. En nuestro caso será la primera.

Empezaremos creando el esqueleto HTML de las tabs en el index.html. Lo escribimos a continuación de los dropdowns, pero dentro del div container.🧐

<!-- tabs -->
    <h3>More Minion information</h3>
    <div class="tabs">
      <ul>
        <li class="trigger active" data-target="#about">About Minions</li>
        <li class="trigger" data-target="#weapons">Minion Weapons</li>
        <li class="trigger" data-target="#delivery">Delivery policy</li>
      </ul>
      <div id="about" class="content active">
        <h4>About</h4>
        <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Ratione accusamus distinctio iste excepturi minus
        </p>
      </div>
      <div id="weapons" class="content">
        <h4>Weapons</h4>
        <p>Lorem ipsumctetur adipisicing dolor sit amet conse elit. Ratione atio iste excepturi dolor sit amet
          conseminus</p>
      </div>
      <div id="delivery" class="content">
        <h4>Delivery</h4>
        <p>Lorem ipst consectetur adipisicingum dolor sit ameone accusamusinctio iste distri minus exceptu</p>
      </div>
    </div>

La forma de indicar que una tab (y su correspondiente contenido) está seleccionada es mediante la clase activeque posteriormente configuraremos, y que por ahora hemos escrito en el HTML de manera estática.

Si guardas ya deberías ver el contenido en tu navegador. Vamos a añadirle la lógica de programación. 👩‍💻

1. Creamos un nuevo archivo JS a la altura de tooltip.js, llamado tabs.js.

2. Dentro creamos una clase JS llamada TabsSu constructor espera recibir un container, que será el <div> que abarca todo nuestro HTML de las tabs, es decir:

    <div class="tabs">

3. Creamos otra propiedad llamada tabs, que serán cada uno de los <li>. A ellos podemos acceder utilizando querySelectorAll. 

🧐 Ojo con no liarnos entre la propiedad tabs de la clase de JS y la clase de CSS tabs. Son cosas distintas. 

Con el constructor terminado, ya podemos crear un método llamado init()como para los otros componentes, que se encargará de inicializar el componente. Aquí añadiremos un event listener a cada uno de los <li>, porque queremos que algo suceda al hacer click sobre ellos. 

class Tabs {
    constructor(container) {
        this.container = container;
        this.tabs = container.querySelectorAll('.trigger');
    }
    init() {
        
    }
}

Para ello, iteramos sobre las tabs usando un loop forEachDentro de la callback function es donde añadimos un event listener a cada variable local (tab). 

4. Vinculamos el event listener a un click event y utilizamos el event object en la callback function, lo que nos permitirá acceder a cierta información sobre el click event. 

Cuando hacemos click sobre una pestañaqueremos alternar (en inglés, toggle) la clase active de todas las pestañasEs decir, añadir la clase active a las tabs que no la tengan y quitarla de las tabs que la tengan. Lo mismo es aplicable al <div> cuyo contenido esté vinculado (por medio de un id) a una tab. Por ejemplo, esta tab: 

<li class="trigger" data-target="#weapons">Minion Weapons</li>

está vinculada a este contenido:

      <div id="weapons" class="content">
        <h4>Weapons</h4>
        <p>Lorem ipsumctetur adipisicing dolor sit amet conse elit. Ratione atio iste excepturi dolor sit amet
          conseminus</p>
      </div>

No vamos a hacer todo eso en una callback function, sino que externalizaremos el código a unas funciones separadas y luego las invocaremos dentro de la callback function. De momento creamos únicamente los nombres de dichos métodos.

    init() {
        this.tabs.forEach(tab => {
            tab.addEventListener('click', (e) => {
                this.toggleTabs(e);
                this.toggleContent(e);
            });
        });
    }

5. Creamos la funcionalidad del método toggleTabs() debajo del init(), porque no deja de ser un método normal de la clase Tabs

  • le pasamos el event object porque es lo que espera como parámetro.
  • eliminamos la clase active de cualquier tab que la tenga. Para hacer esto, iteramos otra vez sobre la propiedad tabs.
  • añadimos la clase active a la tab sobre la que el usuario haya hecho click. Para eso utilizamos la propiedad target sobre el event object. 
    toggleTabs(e) {
        // remove class active from tab who has it
        this.tabs.forEach(tab => tab.classList.remove('active'));
        // add class active to clicked tab
        e.target.classList.add('active');
    }

6. Creamos la funcionalidad del método toggleContent() a continuación del toggleTabs().

  • le pasamos el event object.
  • eliminamos la clase active sobre los <div class="content"> que la tengan. Para eso utilizamos un loop forEach.
  • añadimos la clase active al <div class="content"> que esté vinculado a la tab sobre la que hayamos hecho click. Recordemos que ese vínculo está creado a través de un idPara averiguar sobre qué tab hemos hecho click, obtenemos el valor del atributo data-target y lo guardamos en una variable llamada selector, por ejemplo.

Con ese selector ya podemos seleccionar en el DOM un contenido y vincularlos entre sí, después de crear una variable para almacenar ese contentRecordemos que ese selector podrá tomar el valor de uno de los atributos data-target ("#about", "#weapons" "#delivery").

    toggleContent(e) {
        // remove active class present in content 
        this.container.querySelectorAll('.content').forEach(item => {
            item.classList.remove('active');
        });
        // add active class to content linked to tab clicked
        const selector = e.target.getAttribute('data-target');
        const content = this.container.querySelector(selector);
        content.classList.add('active');
    }

7. Exportamos la clase Tabs como default al final del archivo.

export { Tabs as default };

8. Vamos al index.js e importamos el módulo Tabs (recuerda que a una clase JS importada se la llama "módulo") al principio del archivo.

import Tabs from './ui/tabs';

9. Creamos una nueva instance de la clase Tabs al final del archivo index.js, que espera un container, así que le pasamos el <div class="tabs">. Finalmente, inicializamos la clase con el método init().

// create tabs
const tabs = new Tabs(document.querySelector('.tabs'));
tabs.init();

Aplicados estos cambios, si ahora guardas y vas a la consola de tu navegador, verás que la clase active se alterna en el título y en el contenido según hagamos click en un título o en otro. ¡Estupendo 👏! Sólo nos queda pasarle una capa de chapa y pintura, donde además complementaremos la funcionalidad.

10. Creamos un archivo CSS a la altura de tooltip.css, llamado tabs.css.

.tabs > ul {
    padding: 0;
}

.tabs .trigger {
    list-style-type: none;
    background: aliceblue;
    margin: 4px;
    border-radius: 6px;
    display: inline-block;
    padding: 10px 20px;
    cursor: pointer;
}

.tabs .trigger.active {
    background: rgb(187, 220, 250);
}

.tabs .content {
    background: aliceblue;
    padding: 10px 20px;
    border-radius: 6px;
    display: none;
}

.tabs .content.active {
    display: block;
}

11. Importamos tabs.css al principio del tabs.js.

¡Nuestras pestañas están listas! 😃

Creado una snackbar

Una snackbar es una notificación que aparece en la parte superior de la pantalla (si aparece en la parte inferior se le llama toast) después de que el usuario haya hecho alguna acción, como hacer click en un botón. Tras la acción, desaparece al cabo de unos segundos, Dicho esto, ¡construyamos la nuestra! 💪

1. En el index.html, creamos el botón que activará la snackbar, debajo del código de las tabs pero dentro del container. 

    <button class="snackbar-trigger">Click me!</button>

2. Añadimos un inline style en el index.html para darle un poco de estilo al botón, debajo de la configuración de la clase container.

    button {
      display: flex;
      justify-content: center;
      width: 200px;
      background: coral;
      padding: 10px;
      margin: 20px auto;
      border: none;
      border-radius: 5px;
      color: white;
      cursor: pointer;
    }

3. Creamos un archivo llamado snackbar.js en la carpeta ui, donde escribimos la última clase de este proyecto, la clase Snackbar. El constructor no va a recibir ningún parámetro, porque en nuestro proyecto todavía no existe el código HTML que crearía una snackbar. Es dentro del constructor donde la crearemos. Para eso utilizaremos el método createElement y le pasaremos un div.

class Snackbar {
    constructor() {
        this.snackbar = document.createElement('div');
    }
}

4. Creamos el método encargado de inicializar el componente, al que llamamos init(), como siempre. En el método utilizamos la propiedad snackbar para añadirle una clase de CSS con el mismo nombre, que posteriormente configuraremos.

5. Adjuntamos (en inglés, appendla propiedad snackbar al elemento <body>.

6. Exportamos la clase Snackbar como default. 

    init() {
        this.snackbar.classList.add('snackbar');
        document.querySelector('body').appendChild(this.snackbar);
    }
}

export { Snackbar as default };

7. Importamos el módulo en el index.js, al principio del archivo, como siempre.

import Snackbar from './ui/snackbar';

8. Inicializamos el módulo al final del archivo.

// create snackbar
const snackbar = new Snackbar();
snackbar.init();

Si guardas y vas a la pestaña Elements de tu consola, podrás ver que la snackbar existe, aunque no tenga ningún contenido. 👻

9. Volvemos al snackbar.js y configuramos un método llamado show()encargado de mostrar un mensaje en la snackbar en la parte de arriba de la pantalla. Por tanto, el método espera un parámetro (un message), que lo mostrará usando la propiedad textContent.

10. A la propiedad snackbar le añadimos una clase de CSS llamada activeque posteriormente configuraremos, y que será la encargada de mostrar la snackbar si el usuario ha hecho click sobre el botón.

Para hacerla desaparecer a los pocos segundos, utilizamos un setTimeout().

    show(message) {
        this.snackbar.textContent = message;
        this.snackbar.classList.add('active');
        setTimeout(() => {
            this.snackbar.classList.remove('active');
        }, 4000);
    }

11. En el index.js, llamamos al método show() cuando el usuario haga click en el botón "Click me!". Para eso utilizaremos un event listener vinculado a un evento click unido al botón. Así que primero debemos crear una referencia al botón.

En la callback function es donde utilizamos la instance de la clase Snackbar para llamar al método show(), que espera un mensaje, así que le pasamos uno.

const button = document.querySelector('.snackbar-trigger');
button.addEventListener('click', () => {
    snackbar.show('You clicked me!!😁');
});

Si guardas y vas a tu navegador, verás que el mensaje aparece abajo del todo, aunque sin ningún estilo todavía. Además, se queda en la página en lugar de desaparecer. Es normal, ya que no hemos configurado la UI, sólo la lógica 😌. Si observas tu consola, verás que el elemento

<div class="snackbar active">You clicked me!!😁</div>

es creado al hacer click en el botón, cuya clase active desaparece a los 4 segundos, ¡lo que significa que funciona! ✌

12. Creamos un archivo a la altura de dropdown.css, llamado snackbar.cssdonde le daremos finalmente estilo a nuestra snackbar.

.snackbar {
    width: 200px;
    padding: 20px;
    position: fixed;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    border-radius: 5px;
    box-shadow: 2px 6px 6px rgb(116, 116, 116);
    background: coral;
    color: white;
    text-align: center;
    margin-top: -100px;
    transition: all 0.2s;
}

.snackbar.active {
    margin-top: 0;
}

 En el CSS jugamos con los margins para ocultar y mostrar el mensaje. 😉

13. Importamos el archivo snackbar.css en el snackbar.js, al principio.

import './styles/snackbar.css';

Consideraciones finales

¡Y ya está! Nuestra snackbar está lista, ¡¡y nuestro proyecto terminado 👏!! Si ahora quisiéramos tener nuestra app lista para subir a un servidor, tendríamos que cancelar el npm run serve y utilizar el comando npm run build.

Esto cambiaría el entorno, de development mode production mode, dándonos un único archivo en la carpeta distel bundle.js, con todos los archivos de nuestro proyecto, fusionados y convertidos en código compatible con todos los navegadores, gracias a la magia de Babel y Webpack. 🧞‍♀️🧞‍♂️

THE END!

¡Y con esto terminamos esta UI library hecha con la ayuda de Webpack! Espero que hayas aprendido algo nuevo 😊.  Si te queda alguna duda, ¡nos vemos en los comentarios!

Sobre la autora de este post

Soy Rocío, una abogada reconvertida en programadora. Soy una apasionada de aprender cosas nuevas y ferviente defensora de que la única manera de ser feliz es alcanzando un equilibrio entre lo que te encanta hacer y lo que te saque de pobre. Mi historia completa, aquí. 

Otros artículos que pueden interesarte

Cómo aprendí a programar cuando estaba «programada» para ser de letras
[tcb-script src="https://player.vimeo.com/api/player.js"][/tcb-script]A nadie le gusta su trabajo. Eso es lo que me decía a mí misma cuando conseguí mi primer[...]
Días del 160 al 203 – ¡Primer objetivo conseguido!
“A veces podemos pasarnos años sin vivir en absoluto, y de pronto toda nuestra vida se concentra en un solo[...]
Claves para entender Angular. Qué es y cómo se utiliza
Angular es un framework creado por Google que nos permite construir Single Page Applications (SPA, por sus siglas en inglés).Frameworks¿Pero qué es[...]
Si crees que este post puede serle útil a alguien, ¡compártelo!:

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Esta web utiliza cookies para asegurar que se da la mejor experiencia al usuario. Si continúas utilizando este sitio se asume que estás de acuerdo. más información

Los ajustes de cookies en esta web están configurados para «permitir las cookies» y ofrecerte la mejor experiencia de navegación posible. Si sigues usando esta web sin cambiar tus ajustes de cookies o haces clic en «Aceptar», estarás dando tu consentimiento a esto.

Cerrar