Category: Inspiración

  • Cómo construir un Multiselect Dropdown en 4 Frameworks modernos

    Cómo construir un Multiselect Dropdown en 4 Frameworks modernos

    Vamos a construir un componente multiselect desde cero usando React y Tailwind CSS, que nos permitirá seleccionar múltiples opciones desde un dropdown y mostrar en consola las opciones seleccionadas. Empezaremos con React ya que es lo que más hemos visto hasta ahora.

    Paso 1: Crear la estructura base del componente

    Primero vamos a crear el layout base del componente usando sólo HTML y clases de Tailwind. Esto nos permite enfocarnos primero en el diseño sin preocuparnos por la lógica todavía.

    Creamos un archivo: MultiSelectDropdown.tsx Y pegamos esta estructura:

    export default function MultiSelectDropdown() {
      return (
        <div className="w-60 m-auto">
          <button
            type="button"
            className="text-white justify-between w-full bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 inline-flex items-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
          >
            Dropdown button
            <svg
              className="w-2.5 h-2.5 ml-3"
              viewBox="0 0 10 6"
              fill="none"
              xmlns="http://www.w3.org/2000/svg"
            >
              <path
                d="M1 1L5 5L9 1"
                stroke="currentColor"
                strokeWidth="2"
                strokeLinecap="round"
                strokeLinejoin="round"
              />
            </svg>
          </button>
    
          {/* Dropdown visible por ahora */}
          <div className="z-10 w-full mt-2 block bg-white divide-y divide-gray-100 rounded-lg shadow-sm dark:bg-gray-700">
            <ul className="p-3 space-y-3 text-sm text-gray-700 dark:text-gray-200">
              <li>
                <label className="flex items-center gap-2">
                  <input type="checkbox" />
                  React
                </label>
              </li>
              <li>
                <label className="flex items-center gap-2">
                  <input type="checkbox" />
                  Vue
                </label>
              </li>
            </ul>
          </div>
        </div>
      );
    }
    

    ¿Qué hicimos aquí?

    • Creamos un contenedor de ancho fijo centrado.

    • Agregamos un botón con estilos de Tailwind (bg-blue-700, rounded-lg, etc.).

    • Insertamos una lista de opciones de ejemplo.

    • Todo el diseño es estático todavía, sin funcionalidad.

    Paso 2: Manejar la apertura y cierre del dropdown

    Ahora vamos a hacer que el dropdown se oculte o muestre cuando hacemos clic en el botón.

    import { useState } from "react";
    
    export default function MultiSelectDropdown() {
      const [isOpen, setIsOpen] = useState(false);
    
      return (
        <div className="w-60 m-auto">
          <button
            type="button"
            onClick={() => setIsOpen((prev) => !prev)}
            className="text-white justify-between w-full bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 inline-flex items-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
          >
            Dropdown button
            <svg
              className="w-2.5 h-2.5 ml-3"
              viewBox="0 0 10 6"
              fill="none"
              xmlns="http://www.w3.org/2000/svg"
            >
              <path
                d="M1 1L5 5L9 1"
                stroke="currentColor"
                strokeWidth="2"
                strokeLinecap="round"
                strokeLinejoin="round"
              />
            </svg>
          </button>
    
          {isOpen && (
            <div className="z-10 w-full mt-2 block bg-white divide-y divide-gray-100 rounded-lg shadow-sm dark:bg-gray-700">
              <ul className="p-3 space-y-3 text-sm text-gray-700 dark:text-gray-200">
                <li>
                  <label className="flex items-center gap-2">
                    <input type="checkbox" />
                    React
                  </label>
                </li>
                <li>
                  <label className="flex items-center gap-2">
                    <input type="checkbox" />
                    Vue
                  </label>
                </li>
              </ul>
            </div>
          )}
        </div>
      );
    }
    

    ¿Qué aprendemos aquí?

    • React usa useState para manejar el estado de apertura del menú.

    • Con {isOpen && (...)} mostramos u ocultamos el dropdown de forma condicional.

    • Esto ya nos permite ver el menú al hacer clic, y ocultarlo al hacer clic de nuevo.

    Paso 3: Crear opciones dinámicas

    Ahora definimos una lista de opciones para renderizarlas dinámicamente. Así podemos cambiar fácilmente las opciones más adelante sin repetir código (Por el momento dentro del componente pero se puede fácilmente pasar como prop).

    const options = [
      { label: "React", value: "react" },
      { label: "Vue", value: "vue" },
      { label: "Svelte", value: "svelte" },
      { label: "Solid", value: "solid" },
      { label: "Angular", value: "angular" },
    ];
    

    Y en lugar de escribir <li> manualmente, iteramos con .map():

    <ul className="p-3 space-y-3 text-sm text-gray-700 dark:text-gray-200">
      {options.map((option) => (
        <li key={option.value}>
          <label className="flex items-center gap-2">
            <input type="checkbox" />
            {option.label}
          </label>
        </li>
      ))}
    </ul>
    

    Paso 4: Guardar y manejar las opciones seleccionadas

    Ahora implementamos la lógica para guardar las selecciones. Creamos una nueva variable de estado llamada selected que guardará un array con los valores seleccionados.

    Además, queremos que cada vez que se seleccione o deseleccione algo, se haga un console.log() del array actualizado. Para evitar logs duplicados, lo haremos con useEffect.

    import { useState, useEffect } from "react";
    
    export default function MultiSelectDropdown() {
      const [isOpen, setIsOpen] = useState(false);
      const [selected, setSelected] = useState<string[]>([]);
    
      // Este efecto imprime en consola cada vez que cambia la selección
      useEffect(() => {
        console.log("Seleccionadas:", selected);
      }, [selected]);
    
      const handleChange = (value: string) => {
        setSelected((prev) =>
          prev.includes(value)
            ? prev.filter((v) => v !== value)
            : [...prev, value]
        );
      };
    
      const options = [
        { label: "React", value: "react" },
        { label: "Vue", value: "vue" },
        { label: "Svelte", value: "svelte" },
        { label: "Solid", value: "solid" },
        { label: "Angular", value: "angular" },
      ];
    
      return (
        <div className="w-60 m-auto">
          <button
            onClick={() => setIsOpen((prev) => !prev)}
            type="button"
            className="text-white justify-between w-full bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 inline-flex items-center"
          >
            Dropdown button
            <svg className="w-2.5 h-2.5 ml-3" /* ... */ />
          </button>
    
          {isOpen && (
            <div className="z-10 w-full mt-2 block bg-white rounded-lg shadow-sm">
              <ul className="p-3 space-y-3 text-sm text-gray-700">
                {options.map((option) => (
                  <li key={option.value}>
                    <label className="flex items-center gap-2">
                      <input
                        type="checkbox"
                        checked={selected.includes(option.value)}
                        onChange={() => handleChange(option.value)}
                      />
                      {option.label}
                    </label>
                  </li>
                ))}
              </ul>
            </div>
          )}
        </div>
      );
    }
    

    Resultado

    Ya tenemos un componente 100% funcional en React:

    • Visualmente estilizado con Tailwind.

    • Dropdown desplegable al clic.

    • Permite seleccionar y deseleccionar múltiples opciones.

    • Imprime en consola las opciones seleccionadas sin duplicados.

     

    Multiselect Dropdown en Vue 3 + Tailwind + TypeScript

    Aquí replicamos el mismo componente de React, pero usando Vue 3, Composition API, TypeScript y Tailwind. Lo haremos desde cero, en pasos simples y comentados.

    • Mostrará un botón para desplegar un menú con opciones.

    • Permitirá seleccionar múltiples opciones (checkboxes).

    • Al seleccionar/deseleccionar, actualizará un array reactivo.

    • Imprimirá en consola las opciones seleccionadas (sin duplicados).

    • Usará Tailwind CSS para estilos.

     

    Paso 1: Crear la estructura del componente

    Creamos el archivo:

    src/components/MultiSelectDropdown.vue
    

    Y comenzamos con la estructura mínima:

    <template>
      <div class="w-60 m-auto">
        <button
          type="button"
          class="text-white justify-between w-full bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 inline-flex items-center"
        >
          Dropdown button
          <svg
            class="w-2.5 h-2.5 ml-3"
            viewBox="0 0 10 6"
            fill="none"
            xmlns="http://www.w3.org/2000/svg"
          >
            <path
              d="M1 1L5 5L9 1"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            />
          </svg>
        </button>
    
        <div class="z-10 w-full mt-2 block bg-white divide-y divide-gray-100 rounded-lg shadow-sm">
          <ul class="p-3 space-y-3 text-sm text-gray-700">
            <li>
              <label class="flex items-center gap-2">
                <input type="checkbox" />
                React
              </label>
            </li>
            <li>
              <label class="flex items-center gap-2">
                <input type="checkbox" />
                Vue
              </label>
            </li>
          </ul>
        </div>
      </div>
    </template>
    
    <script setup lang="ts">
    // Lógica vendrá después
    </script>
    

    ¿Qué hicimos aquí?

    • Creamos la estructura visual: un botón y un dropdown con dos opciones fijas.

    • Usamos Tailwind para el diseño (botón azul, checkbox alineado).

    • No hay interactividad aún: ni apertura/cierre, ni selección.

    Este paso sirve para tener el esqueleto visual listo antes de agregar la lógica.

    Paso 2: Mostrar/Ocultar el dropdown con ref()

    Queremos que el dropdown solo se muestre cuando hagamos clic en el botón. Para eso:

    • Creamos una variable reactiva llamada isOpen

    • Usamos v-if="isOpen" para mostrar el menú

    • Alternamos el valor con un método toggleDropdown()

    Actualizamos el componente así:

    <template>
      <div class="w-60 m-auto">
        <!-- Botón que abre/cierra el dropdown -->
        <button
          type="button"
          @click="toggleDropdown"
          class="text-white justify-between w-full bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 inline-flex items-center"
        >
          Dropdown button
          <svg
            class="w-2.5 h-2.5 ml-3"
            viewBox="0 0 10 6"
            fill="none"
            xmlns="http://www.w3.org/2000/svg"
          >
            <path
              d="M1 1L5 5L9 1"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            />
          </svg>
        </button>
    
        <!-- Mostramos el dropdown solo si isOpen es true -->
        <div
          v-if="isOpen"
          class="z-10 w-full mt-2 block bg-white divide-y divide-gray-100 rounded-lg shadow-sm"
        >
          <ul class="p-3 space-y-3 text-sm text-gray-700">
            <li>
              <label class="flex items-center gap-2">
                <input type="checkbox" />
                React
              </label>
            </li>
            <li>
              <label class="flex items-center gap-2">
                <input type="checkbox" />
                Vue
              </label>
            </li>
          </ul>
        </div>
      </div>
    </template>
    
    <script setup lang="ts">
    import { ref } from 'vue'
    
    const isOpen = ref(false)
    
    const toggleDropdown = () => {
      isOpen.value = !isOpen.value
    }
    </script>
    

    ¿Qué aprendimos aquí?

    • ref(false) crea una variable reactiva en Vue.

    • Usamos v-if="isOpen" para mostrar el menú condicionalmente.

    • Al hacer clic en el botón, toggleDropdown() invierte su valor (true / false).

    Ya tenemos la interacción básica lista. ¡Se ve como un dropdown real!

    Paso 3: Mostrar opciones desde un array con v-for

    Hasta ahora, las opciones están “quemadas” en el HTML. Vamos a moverlas a un array para que el componente sea dinámico y reusable.

    1. Creamos un array de opciones en el <script setup>

    Usamos TypeScript para tipar las opciones:

    interface Option {
      label: string
      value: string
    }
    
    const options: Option[] = [
      { label: 'React', value: 'react' },
      { label: 'Vue', value: 'vue' },
      { label: 'Svelte', value: 'svelte' },
      { label: 'Solid', value: 'solid' },
      { label: 'Angular', value: 'angular' }
    ]
    

    2. Reemplazamos los <li> fijos por un v-for

    Actualizamos el template:

    <ul class="p-3 space-y-3 text-sm text-gray-700">
      <li v-for="option in options" :key="option.value">
        <label class="flex items-center gap-2">
          <input type="checkbox" />
          {{ option.label }}
        </label>
      </li>
    </ul>
    

    ¿Qué logramos aquí?

    • El componente ahora usa un array tipado con Option[], lo que facilita reusarlo.

    • Con v-for, renderizamos tantas opciones como queramos sin duplicar código.

    • Vue se encarga de mantener la eficiencia del DOM con :key="option.value".

    Paso 4: Manejar selección múltiple (checkboxes)

    Ahora queremos que:

    • Cada checkbox represente una opción seleccionable.

    • Las opciones seleccionadas se guarden en un array selected.

    • Si ya está seleccionada, se deseleccione (toggle).

    • Se imprima en consola cada vez que el array cambie.

    1. Creamos el estado selected

    En el <script setup>:

    import { ref, watch } from 'vue'
    
    const selected = ref<string[]>([])
    

    2. Creamos una función para agregar o quitar selecciones

    const toggleSelection = (value: string) => {
      if (selected.value.includes(value)) {
        selected.value = selected.value.filter(v => v !== value)
      } else {
        selected.value.push(value)
      }
    }
    

    3. Escuchamos los cambios con watch (como useEffect en React)

    Usamos :checked y @change para enlazar con el estado:

    <input
      type="checkbox"
      :checked="selected.includes(option.value)"
      @change="toggleSelection(option.value)"
    />
    

    Resultado

    Ahora el componente:

    • Permite seleccionar múltiples opciones.

    • Guarda las seleccionadas en un array.

    • Hace console.log() al cambiar la selección.

     

    Multiselect Dropdown en Svelte + Tailwind + TypeScript

    Paso 1: Estructura HTML + Tailwind base

    Vamos a comenzar por construir solo la estructura visual estática del componente, sin lógica aún. El objetivo es ver cómo se verá el dropdown, el botón y las opciones con checkboxes, usando únicamente HTML y Tailwind CSS.

    Código inicial: MultiSelectDropdown.svelte

    <script lang="ts">
      // lógica vendrá después
    </script>
    
    <div class="w-60 m-auto">
      <!-- Botón para abrir el dropdown -->
      <button
        type="button"
        class="text-white justify-between w-full bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 inline-flex items-center"
      >
        Dropdown button
        <svg
          class="w-2.5 h-2.5 ml-3"
          viewBox="0 0 10 6"
          fill="none"
          xmlns="http://www.w3.org/2000/svg"
        >
          <path
            d="M1 1L5 5L9 1"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          />
        </svg>
      </button>
    
      <!-- Dropdown (visible todo el tiempo por ahora) -->
      <div
        class="z-10 w-full mt-2 block bg-white divide-y divide-gray-100 rounded-lg shadow-sm"
      >
        <ul class="p-3 space-y-3 text-sm text-gray-700">
          <li>
            <label class="flex items-center gap-2">
              <input type="checkbox" />
              React
            </label>
          </li>
          <li>
            <label class="flex items-center gap-2">
              <input type="checkbox" />
              Vue
            </label>
          </li>
        </ul>
      </div>
    </div>
    

    ¿Qué tenemos hasta ahora?

    • Un botón con diseño responsivo (w-full, bg-blue-700, hover, rounded-lg).

    • Un ícono de flecha hacia abajo (svg).

    • Un menú tipo dropdown con dos opciones de ejemplo (React, Vue) como <li>.

    • Cada opción es un <label> que contiene un checkbox y un texto.

    Paso 2: Mostrar/Ocultar el dropdown con una variable reactiva

    Ahora vamos a hacer que el dropdown se abra y cierre al hacer clic en el botón, usando reactividad en Svelte con let.

    ¿Cómo lo haremos?

    • Creamos una variable isOpen con let

    • Alternamos su valor al hacer clic en el botón

    • Mostramos el dropdown solo si isOpen === true usando {#if}

    Código actualizado

    <script lang="ts">
      let isOpen = false;
    
      function toggleDropdown() {
        isOpen = !isOpen;
      }
    </script>
    
    <div class="w-60 m-auto">
      <!-- Botón con evento on:click -->
      <button
        type="button"
        on:click={toggleDropdown}
        class="text-white justify-between w-full bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 inline-flex items-center"
      >
        Dropdown button
        <svg
          class="w-2.5 h-2.5 ml-3"
          viewBox="0 0 10 6"
          fill="none"
          xmlns="http://www.w3.org/2000/svg"
        >
          <path
            d="M1 1L5 5L9 1"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          />
        </svg>
      </button>
    
      <!-- Mostrar dropdown solo si isOpen es true -->
      {#if isOpen}
        <div
          class="z-10 w-full mt-2 block bg-white divide-y divide-gray-100 rounded-lg shadow-sm"
        >
          <ul class="p-3 space-y-3 text-sm text-gray-700">
            <li>
              <label class="flex items-center gap-2">
                <input type="checkbox" />
                React
              </label>
            </li>
            <li>
              <label class="flex items-center gap-2">
                <input type="checkbox" />
                Vue
              </label>
            </li>
          </ul>
        </div>
      {/if}
    </div>
    

    ¿Qué aprendimos aquí?

    • En Svelte, let crea variables reactivas automáticamente.

    • on:click={toggleDropdown} es la forma de escuchar eventos.

    • {#if ...} es como v-if en Vue o un render condicional en React ({isOpen && (...)}).

     

    Paso 3: Mostrar las opciones dinámicamente con {#each}

    Hasta ahora, las opciones (React, Vue) están escritas directamente en el HTML. Ahora vamos a moverlas a un array tipado para que el componente sea dinámico y escalable.

    1. Creamos el array de opciones en el <script>

    interface Option {
      label: string;
      value: string;
    }
    
    const options: Option[] = [
      { label: 'React', value: 'react' },
      { label: 'Vue', value: 'vue' },
      { label: 'Svelte', value: 'svelte' },
      { label: 'Solid', value: 'solid' },
      { label: 'Angular', value: 'angular' },
    ];
    

    Option es una interfaz que usamos para tipar las opciones. Esto es posible gracias a que activaste TypeScript

    2. Reemplazamos los <li> fijos por {#each}

    <ul class="p-3 space-y-3 text-sm text-gray-700">
      {#each options as option (option.value)}
        <li>
          <label class="flex items-center gap-2">
            <input type="checkbox" />
            {option.label}
          </label>
        </li>
      {/each}
    </ul>
    

    ¿Qué logramos?

    • Las opciones ya no están duplicadas manualmente.

    • Podemos fácilmente agregar, quitar o pasar las opciones como props después.

    • Cada opción se representa con un checkbox y su label.

    Paso 4: Manejar selección múltiple y mostrarla en consola

    Queremos que el componente:

    1. Permita seleccionar múltiples opciones (con checkboxes).

    2. Guarde las opciones seleccionadas en un array.

    3. Haga console.log() del array cada vez que cambie.

    ¿Cómo lo haremos?

    • Creamos un array reactivo selected: string[].

    • Usamos bind:checked para vincular cada checkbox con el estado.

    • O mejor: manejamos el on:change manualmente, como hicimos en Vue y React (para control total).

    • Imprimimos con $: console.log(...) que es como watch o useEffect.

    1. Creamos selected y toggleSelection()

    let selected: string[] = [];
    
    function toggleSelection(value: string) {
      if (selected.includes(value)) {
        selected = selected.filter((v) => v !== value);
      } else {
        selected = [...selected, value];
      }
    }
    
    // log reactivo
    $: console.log('Seleccionadas:', selected);
    

    2. Actualizamos los checkboxes

    En cada input, usamos:

    <input
      type="checkbox"
      checked={selected.includes(option.value)}
      on:change={() => toggleSelection(option.value)}
    />
    

    ¿Qué logramos?

    • El dropdown ya permite selección múltiple.

    • Los checkboxes reflejan el estado real.

    • Cada cambio en selected hace un console.log() sin duplicados.

    • Comportamiento igual al de React y Vue

    Multiselect Dropdown en Angular + Tailwind + TypeScript

    Paso 1: Estructura HTML + Tailwind base

    Primero, creamos solo la estructura visual del componente, sin lógica aún. Esto nos permite ver el diseño del botón y el menú desplegable.

    1. Crear el componente

    Desde la terminal, en el proyecto Angular:

    ng generate component components/multi-select-dropdown
    

    Esto creará 4 archivos dentro de src/app/components/multi-select-dropdown:

    • multi-select-dropdown.component.ts

    • multi-select-dropdown.component.html

    • multi-select-dropdown.component.css

    • multi-select-dropdown.component.spec.ts

     

    2. Usar el componente en app.component.html

    Edita el archivo src/app/app.component.html y agrega:

    <app-multi-select-dropdown></app-multi-select-dropdown>
    

    3. Estructura HTML base en multi-select-dropdown.component.html

    Pegamos la estructura visual básica con Tailwind:

    <div class="w-60 m-auto">
      <!-- Botón que abre el dropdown -->
      <button
        type="button"
        class="text-white justify-between w-full bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 inline-flex items-center"
      >
        Dropdown button
        <svg
          class="w-2.5 h-2.5 ml-3"
          viewBox="0 0 10 6"
          fill="none"
          xmlns="http://www.w3.org/2000/svg"
        >
          <path
            d="M1 1L5 5L9 1"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          />
        </svg>
      </button>
    
      <!-- Dropdown visible por ahora (sin lógica) -->
      <div
        class="z-10 w-full mt-2 block bg-white divide-y divide-gray-100 rounded-lg shadow-sm"
      >
        <ul class="p-3 space-y-3 text-sm text-gray-700">
          <li>
            <label class="flex items-center gap-2">
              <input type="checkbox" />
              React
            </label>
          </li>
          <li>
            <label class="flex items-center gap-2">
              <input type="checkbox" />
              Vue
            </label>
          </li>
        </ul>
      </div>
    </div>
    

    Resultado esperado

    Si recargas http://localhost:4200, deberías ver:

    • Un botón azul que dice “Dropdown button”

    • Un dropdown con dos opciones (React y Vue)

    • Estilo limpio con Tailwind

    Paso 2: Mostrar/Ocultar el dropdown con una variable en el componente

    Queremos que el dropdown:

    • Se muestre al hacer clic en el botón.

    • Se oculte al volver a hacer clic.

    ¿Cómo lo haremos?

    1. Creamos una propiedad booleana isOpen en el .ts.

    2. Hacemos toggle con un método toggleDropdown().

    3. En el HTML, usamos *ngIf="isOpen" para mostrar/ocultar el menú.

    1. Agregar lógica en el archivo .ts

    Edita multi-select-dropdown.component.ts:

    import { Component } from '@angular/core';
    
    @Component({
      selector: 'app-multi-select-dropdown',
      templateUrl: './multi-select-dropdown.component.html',
      styleUrls: ['./multi-select-dropdown.component.css'],
    })
    export class MultiSelectDropdownComponent {
      isOpen = false;
    
      toggleDropdown(): void {
        this.isOpen = !this.isOpen;
      }
    }
    

    2. Conectar el botón al método

    Edita multi-select-dropdown.component.html:

    <!-- Botón con (click) -->
    <button
      type="button"
      (click)="toggleDropdown()"
      class="text-white justify-between w-full bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 inline-flex items-center"
    >
      Dropdown button
      <svg
        class="w-2.5 h-2.5 ml-3"
        viewBox="0 0 10 6"
        fill="none"
        xmlns="http://www.w3.org/2000/svg"
      >
        <path
          d="M1 1L5 5L9 1"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        />
      </svg>
    </button>
    

    3. Mostrar el dropdown condicionalmente

    Reemplaza el div del dropdown así:

    <div
      *ngIf="isOpen"
      class="z-10 w-full mt-2 block bg-white divide-y divide-gray-100 rounded-lg shadow-sm"
    >
      <ul class="p-3 space-y-3 text-sm text-gray-700">
        <li>
          <label class="flex items-center gap-2">
            <input type="checkbox" />
            React
          </label>
        </li>
        <li>
          <label class="flex items-center gap-2">
            <input type="checkbox" />
            Vue
          </label>
        </li>
      </ul>
    </div>
    

    Resultado

    Ahora al hacer clic en el botón:

    • El menú aparece y desaparece.

    • El estado lo maneja isOpen con un simple toggle.

    Paso 3: Mostrar las opciones dinámicamente desde un array

    Hasta ahora, las opciones (React, Vue) están escritas directamente en el HTML. Vamos a moverlas a un array tipado en TypeScript y usar *ngFor para renderizarlas dinámicamente.

    1. Declarar la interfaz y el array de opciones

    Edita multi-select-dropdown.component.ts:

    interface Option {
      label: string;
      value: string;
    }
    
    export class MultiSelectDropdownComponent {
      isOpen = false;
    
      options: Option[] = [
        { label: 'React', value: 'react' },
        { label: 'Vue', value: 'vue' },
        { label: 'Svelte', value: 'svelte' },
        { label: 'Solid', value: 'solid' },
        { label: 'Angular', value: 'angular' }
      ];
    
      toggleDropdown(): void {
        this.isOpen = !this.isOpen;
      }
    }
    

    2. Usar *ngFor para renderizar el listado en el HTML

    En multi-select-dropdown.component.html, reemplaza los <li> fijos por:

    <ul class="p-3 space-y-3 text-sm text-gray-700">
      <li *ngFor="let option of options">
        <label class="flex items-center gap-2">
          <input type="checkbox" />
          {{ option.label }}
        </label>
      </li>
    </ul>
    

    ¿Qué logramos?

    • Las opciones ahora se gestionan desde el .ts, de forma escalable.

    • Podemos cambiar las opciones fácilmente o cargarlas desde un API más adelante.

    • El template está más limpio, sin duplicación.

    Paso 4: Manejar selección múltiple con checkboxes

    Queremos que:

    1. Cada vez que se seleccione o deseleccione una opción, actualicemos un array.

    2. El array contenga los value de las opciones seleccionadas.

    3. Usemos console.log() para mostrar el estado en tiempo real.

    ¿Cómo lo haremos?

    • Creamos selected: string[] = [] en el componente.

    • Usamos (change) en cada checkbox para manejar la selección.

    • Usamos [checked] para marcar el estado del checkbox.

    • Imprimimos en consola cada vez que cambia la lista.

    1. Agrega el estado y función en el .ts

    selected: string[] = [];
    
    toggleSelection(value: string): void {
      if (this.selected.includes(value)) {
        this.selected = this.selected.filter(v => v !== value);
      } else {
        this.selected.push(value);
      }
    
      console.log('Seleccionadas:', this.selected);
    }
    

    2. Actualiza el input en el HTML

    Modifica el input dentro del *ngFor así:

    <input
      type="checkbox"
      [checked]="selected.includes(option.value)"
      (change)="toggleSelection(option.value)"
    />
    

    Resultado

    Ahora tu componente:

    • Permite seleccionar múltiples opciones.

    • Guarda la selección actualizada en this.selected.

    • Muestra el array actualizado en consola cada vez que haces clic.

     

    Cierre del post:

    🎯 ¿Qué hicimos?

    Construimos el mismo componente multiselect dropdown visualmente consistente en:

    Framework Enfoque Lenguaje Estado JSX / Template Observaciones
    React Hooks (useState, useEffect) TypeScript useState JSX Familiar y flexible. Control explícito.
    Vue 3 Composition API + ref() + watch TypeScript ref, watch Plantilla declarativa Reactividad clara y directa. Fácil binding.
    Svelte Variables reactivas (let, $:) TypeScript Local vars + auto-reactividad Template HTML puro Sintaxis más limpia y mínima. Muy ergonómico.
    Angular Component Class + *ngIf + (change) TypeScript Clases + propiedades Template estructurado Verboso pero robusto. Ideal en equipos grandes.

    ¿Qué aprendimos?

    • Todos los frameworks modernos permiten construir componentes dinámicos y accesibles.

    • Aunque el resultado visual es el mismo, cada ecosistema tiene su propio “ritmo”:

      • React son ideales si te gusta el control imperativo y JSX.

      • Vue y Svelte son más declarativos y expresivos.

      • Angular prioriza estructura, reglas y escalabilidad.

     

    ¿Cuál fue más simple?

    • En cuanto a cantidad de código y claridad inmediata, Svelte y Vue 3 se sintieron más livianos.

    • React ofrecieron control fino y granularidad, ideales para lógica compleja.

    • Angular fue el más verboso, pero también el más estricto y mantenible para proyectos grandes.

    ¿Dónde usar este patrón?

    Un componente como este es útil en:

    • Formularios de filtro avanzados

    • Paneles de administración

    • Dashboards

    • Apps móviles con diseño adaptativo

    Conclusión

    Construir un componente funcional, accesible y visualmente uniforme en diferentes frameworks es una excelente forma de:

    • Aprender sus fortalezas y diferencias

    • Comparar enfoques reactivos

    • Evaluar la curva de entrada y el esfuerzo de mantenimiento

    Como desarrolladores, saber trabajar en más de un entorno nos hace más versátiles y estratégicos.

  • Los mejores UI kits gratuitos con Tailwind CSS

    Los mejores UI kits gratuitos con Tailwind CSS

    Aquí tienes un resumen rápido de las 10 mejores opciones:

    • Flowbite: Más de 400 componentes, compatible con Laravel y Vue.js
    • DaisyUI: Más de 50 componentes, sintaxis sencilla, más de 15,000 estrellas en GitHub
    • Preline UI: Más de 300 componentes, 160 páginas de inicio, soporte para varios frameworks
    • HyperUI: Más de 42 componentes enfocados en marketing, eCommerce y aplicaciones
    • TailGrids: Más de 300 componentes, compatible con React, Vue y Angular
    • Headless UI: Componentes accesibles sin estilos para control total
    • Tailblocks: Componentes prediseñados para ensamblado rápido de páginas
    • Meraki UI: 108 componentes, soporte para modo oscuro y RTL (idiomas de derecha a izquierda)
    • Mamba UI: Mezcla de componentes gratuitos y de pago, diseños modernos
    • Tailwind Starter Kit: Elementos HTML y componentes dinámicos para múltiples frameworks

    Comparación rápida:

     

    Librería Componentes Estrellas GitHub Soporte de Framework Personalización
    Flowbite 400+ 1,076 Alpine.js, Laravel, Vue.js Alta
    DaisyUI 56 28,494 Cualquier compatible con Tailwind Alta
    Preline UI 300+ 3,200 React, Next.js, Svelte, Remix Alta
    HyperUI 42+ 6,949 Cualquier compatible con Tailwind Moderada
    TailGrids 300+ N/D React, Vue, Angular Alta
    Headless UI Sin estilo 17,300 React, Vue Alta
    Tailblocks Variado N/D Cualquier compatible con Tailwind Limitada
    Meraki UI 108 N/D Cualquier compatible con Tailwind Alta
    Mamba UI 150+ 300 Angular, React, Svelte, Vue Moderada
    Tailwind Starter Kit 120+ 5,000 React, Vue, Angular Alta

    Elige basándote en tus necesidades:

    Cantidad de componentes, compatibilidad con frameworks y nivel de personalización.
    Para muchos componentes, prueba Flowbite o Preline UI.
    ¿Buscas gran comunidad? DaisyUI es la opción.
    ¿Necesitas accesibilidad? Explora Headless UI.

    ¿Cómo elegimos estas herramientas?

    Tomamos en cuenta:

    1. Gratuidad: 100% gratuitas.

    2. Cantidad de componentes: como Flowbite (400+) o TailGrids (300+).

    3. Compatibilidad con frameworks: Flowbite trabaja con Laravel, React y Vue.

    4. Facilidad de uso: DaisyUI destaca con su sintaxis simple.

    5. Personalización: Meraki UI ofrece flexibilidad con Flexbox y CSS Grid.

    6. Actualización: Todos funcionan con la última versión de Tailwind CSS.

    7. Apoyo comunitario: Preferimos librerías open-source activas.

    Los 10 mejores kits gratuitos de Tailwind CSS

    1. Flowbite

    • 400+ componentes y elementos interactivos.

    • Copiar y pegar directamente en tu proyecto.

    • Compatible con Laravel y Vue.js (para React usar Flowbite React).

    Ejemplo de botón Flowbite:

    <button type="button" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">Default</button>

    2. DaisyUI

    • Más de 50 componentes.

    • 15,000+ estrellas en GitHub.

    • Más de 2 millones de instalaciones en NPM.

    • Sintaxis limpia y temas personalizables.

    • Totalmente responsive.

    • Algunas críticas por colores predeterminados muy brillantes.

    3. Preline UI

    • Más de 300 componentes y 160 páginas de inicio.

    • Compatible con React, Next.js, Svelte, Remix, y más.

    • Incluye kit de Figma.

    4. HyperUI

    • Más de 42 componentes.

    • Enfocado en marketing, eCommerce y UIs de aplicaciones.

    • Ideal para prototipar rápidamente.

    5. TailGrids

    • 300+ componentes.

    • Compatible con React, Vue y Angular.

    • Documentación extensa con vistas en vivo.

    6. Headless UI

    • Componentes accesibles y sin estilos.

    • Diseñado para control total del estilo.

    • Integración perfecta con Tailwind CSS.

    7. Tailblocks

    • Componentes prediseñados para montar páginas rápido.

    • Diseño limpio y moderno.

    • Personalización limitada.

    8. Meraki UI

    • 108 componentes.

    • Soporta RTL y modo oscuro.

    • Uso de Flexbox y CSS Grid.

    9. Mamba UI

    • Más de 150 componentes (algunos gratuitos, otros pagos).

    • Código en HTML o JSX.

    • Diseños modernos y atractivos.

    10. Tailwind Starter Kit

    • Plantillas básicas de HTML.

    • Componentes dinámicos para React, Vue y Angular.

    • Código abierto y gratuito.

    Comparativa final

     

    Librería Componentes Estrellas GitHub Frameworks Soportados Personalización
    Flowbite 400+ 1,076 Alpine.js, Laravel, Vue.js Alta
    DaisyUI 56 28,494 Cualquiera compatible Alta
    Preline UI 300+ 3,200 React, Next.js, Svelte, Remix Alta
    HyperUI 42+ 6,949 Cualquiera compatible Moderada
    TailGrids 300+ N/D React, Vue, Angular Alta
    Headless UI Sin estilo 17,300 React, Vue Alta
    Tailblocks Variado N/D Cualquiera compatible Limitada
    Meraki UI 108 N/D Cualquiera compatible Alta
    Mamba UI 150+ 300 Angular, React, Svelte, Vue Moderada
    Tailwind Starter Kit 120+ 5,000 React, Vue, Angular Alta

    ¿Qué significa esto para ti?

    • ¿Necesitas muchos componentes? → Flowbite o Preline UI.

    • ¿Quieres apoyo de comunidad? → DaisyUI.

    • ¿Buscas accesibilidad en React/Vue? → Headless UI.

    • ¿Máxima personalización? → Casi todos ofrecen alta, excepto Tailblocks.

    Extras:

    • Meraki UI: Soporte RTL y modo oscuro.

    • Headless UI: Accesibilidad enfocada.

    • HyperUI: Especial para marketing y comercio electrónico.

    Cómo elegir:

    • ¿Cuántos componentes necesitas?

    • ¿Qué framework estás usando?

    • ¿Cuánto nivel de personalización deseas?

    • ¿Requieres características especiales (accesibilidad, RTL)?

    ✅ ¿Accesibilidad para React? → Headless UI
    ✅ ¿Muchos componentes pre-estilizados? → Flowbite o Preline UI

    Cierre

    Elegir el kit de UI adecuado en Tailwind CSS puede definir el éxito de tu proyecto. Considera:

    – Cantidad de componentes:

    • Flowbite: 400+

    • Tailwind Elements: 500+

    • TailGrids: 300+

    Para proyectos pequeños, DaisyUI o HyperUI son ideales.

    – Compatibilidad con frameworks:

    • Headless UI: React, Vue

    • Flowbite: Alpine.js, Laravel, Vue.js

    • TailGrids: React, Vue, Angular

    – Personalización:

    • Casi todas ofrecen alta personalización, excepto Tailblocks.

    – Características únicas:

    • Meraki UI: Idiomas RTL y modo oscuro

    • Headless UI: Accesibilidad

    • HyperUI: Componentes de marketing/eCommerce

  • ¡Configura tu terminal con OhMyZSH como los más capos!

    ¡Configura tu terminal con OhMyZSH como los más capos!

    ¿Cansado del terminal por defecto en macOS? Personalizarlo no solo mejora la estética, sino que también incrementa tu productividad.

    Z Shell (ZSH) es una poderosa shell de Unix basada en Bash (la shell predeterminada de macOS), pero con muchísimas mejoras.

    En esta guía, configuraremos iTerm2 junto con ZSH y algunos complementos esenciales para lograr un terminal espectacular.

    Temas que cubriremos:

    • Instalación de Homebrew

    • Instalación de iTerm2

    • Instalación de ZSH y Oh My ZSH

    • Configuración de temas, fuentes y plugins para un terminal potente

    Paso 1: Instalar Homebrew

    Homebrew es un gestor de paquetes para macOS que facilita la instalación de software.

    Primero, instala las herramientas de línea de comandos de Xcode ejecutando:

    xcode-select --install

    Si ves un error, reinicia la configuración de Xcode con:

    xcode-select -r

    Luego instala Homebrew con:

    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

    Paso 2: Instalar iTerm2

    iTerm2 es una alternativa moderna al Terminal de macOS, repleta de funcionalidades avanzadas.

    Instálalo usando Homebrew:

    brew install --cask iterm2

    Paso 3: Instalar ZSH

    macOS ya trae ZSH instalado en /bin/zsh, pero conviene actualizarlo con Brew:

    brew install zsh

    Paso 4: Instalar Oh My Zsh

    Oh My Zsh es un marco de configuración de ZSH impulsado por la comunidad. Hace que la personalización de ZSH sea simple y poderosa.

    Instálalo con:

    sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

    Verifica la versión instalada:

    zsh --version

    Y, si deseas actualizar en el futuro:

    upgrade_oh_my_zsh

    ¡Reinicia iTerm2 para comenzar a usar tu nueva configuración de ZSH!

    Paso 5: Cambiar el tema por defecto

    Oh My Zsh incluye muchos temas. El tema predeterminado es robbyrussell, pero puedes cambiarlo fácilmente.

    Para editar tu configuración:

    nano ~/.zshrc

    O usa tu editor favorito:

    cursor ~/.zshrc

    Busca la línea que dice:

    ZSH_THEME="robbyrussell"

    y cámbiala, por ejemplo, a:

    ZSH_THEME="agnoster"

    Guarda los cambios y recarga la configuración:

    source ~/.zshrc

    Usar un tema personalizado (Ejemplo: Powerlevel9k)

    Si deseas un tema no preinstalado:

    git clone https://github.com/bhilburn/powerlevel9k.git ~/.oh-my-zsh/custom/themes/powerlevel9k
    

    Luego actualiza tu .zshrc:

    ZSH_THEME="powerlevel9k/powerlevel9k"
    

    Y recarga:

    source ~/.zshrc

    Paso 6: Instalar fuentes para Powerline

    Algunos temas (como Powerlevel9k) requieren fuentes especiales (Powerline Fonts).

    Instálalas ejecutando:

    git clone https://github.com/powerline/fonts.git --depth=1
    cd fonts
    ./install.sh
    

    Luego, en iTerm2 ve a: Preferences > Profiles > Text > Change Font
    y selecciona una fuente como Inconsolata o FiraCode (esta última soporta ligaduras).

    Paso 7: Instalar esquemas de colores

    Personaliza aún más tu terminal instalando un esquema de colores:

    1. Descarga los esquemas desde iTerm2 Color Schemes.

    2. Extrae el ZIP.

    3. En iTerm2, ve a:
      Preferences > Profiles > Colors > Color Presets > Import

    4. Importa los archivos .itermcolors desde la carpeta descargada.

    Paso 8: Instalar plugins para ZSH

    Oh My Zsh viene con varios plugins (como git), pero puedes agregar más.

    Por ejemplo, para agregar soporte a Docker:

    git clone https://github.com/zsh-users/zsh-docker.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-docker
    

    Luego edita ~/.zshrc y añade zsh-docker en la sección de plugins:

    plugins=(git zsh-docker)

    Recarga:

    source ~/.zshrc

    Paso 9: Agregar alias personalizados

    Los alias son comandos abreviados. Puedes agregarlos en tu .zshrc.

    Por ejemplo:

    alias dckimgs="docker images"
    

    Así, cada vez que escribas dckimgs verás el listado de imágenes de Docker.

    ¡Listo!

    Ahora tienes un terminal hermoso, funcional y altamente productivo en macOS, listo para acompañarte en todos tus proyectos.

    Si conoces otros trucos o configuraciones útiles para ZSH, ¡Contáctame!