Skip to main content English Español Light Dark System

Data View

<gstock-data-view> | GstockDataView

Examples

El componente Data View es un orquestador que alterna entre una vista de lista (tabla) y una vista de grid (tarjetas) para mostrar datos. El consumidor controla el modo de visualización mediante la propiedad viewMode — el componente no renderiza ningún toggle de vista propio.

En modo list, renderiza internamente un gstock-data-grid. En modo grid, renderiza un gstock-data-cards.

Lista Grid
<div class="view-toggle">
  <gstock-button id="btn-list" variant="solid" size="small">
    <gstock-icon name="list" slot="prefix"></gstock-icon>
    Lista
  </gstock-button>
  <gstock-button id="btn-grid" variant="outlined" size="small">
    <gstock-icon name="grid" slot="prefix"></gstock-icon>
    Grid
  </gstock-button>
</div>

<gstock-data-view view-mode="list"></gstock-data-view>

<script type="module">
  customElements.whenDefined('gstock-data-view').then(() => {
    
    const view = document.querySelector('gstock-data-view');
    const btnList = document.querySelector('#btn-list');
    const btnGrid = document.querySelector('#btn-grid');

    view.columns = [
      { key: 'name', title: 'Nombre', sortable: true },
      { key: 'email', title: 'Correo electrónico' },
      { key: 'role', title: 'Rol' },
      { key: 'status', title: 'Estado', align: 'center' },
    ];

    view.data = [
      {
        id: 1,
        name: 'Juan Pérez',
        email: 'juan.perez@example.com',
        role: 'Administrador',
        status: 'Activo',
      },
      {
        id: 2,
        name: 'María García',
        email: 'maria.garcia@example.com',
        role: 'Usuario',
        status: 'Activo',
      },
      {
        id: 3,
        name: 'Carlos López',
        email: 'carlos.lopez@example.com',
        role: 'Editor',
        status: 'Inactivo',
      },
      {
        id: 4,
        name: 'Ana Rodríguez',
        email: 'ana.rodriguez@example.com',
        role: 'Usuario',
        status: 'Activo',
      },
      {
        id: 5,
        name: 'Luis Martín',
        email: 'luis.martin@example.com',
        role: 'Editor',
        status: 'Activo',
      },
    ];

    view.cardRenderer = (row, index, options) => {
      return `
        <div class="user-card">
          <div class="user-card__header">
            <gstock-avatar initials="${row.name.charAt(0)}" size="medium"></gstock-avatar>
            <div class="user-card__info">
              <strong>${row.name}</strong>
              <span class="user-card__role">${row.role}</span>
            </div>
          </div>
          <div class="user-card__body">
            <div class="user-card__field">
              <gstock-icon name="mail" library="default"></gstock-icon>
              <span>${row.email}</span>
            </div>
            <gstock-tag size="small" variant="${row.status === 'Activo' ? 'success' : 'neutral'}">
              ${row.status}
            </gstock-tag>
          </div>
        </div>
      `;
    };

    function updateButtons(mode) {
      btnList.variant = mode === 'list' ? 'solid' : 'outlined';
      btnGrid.variant = mode === 'grid' ? 'solid' : 'outlined';
    }

    btnList.addEventListener('click', () => {
      view.viewMode = 'list';
      updateButtons('list');
    });

    btnGrid.addEventListener('click', () => {
      view.viewMode = 'grid';
      updateButtons('grid');
    });
  });
</script>

<style>
  .view-toggle {
    display: flex;
    gap: 0.5rem;
    margin-bottom: 1rem;
  }

  .user-card {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
  }

  .user-card__header {
    display: flex;
    align-items: center;
    gap: 0.75rem;
  }

  .user-card__info {
    display: flex;
    flex-direction: column;
  }

  .user-card__info strong {
    font-size: 0.9375rem;
  }

  .user-card__role {
    font-size: 0.8125rem;
    color: var(--gstock-color-text-subtle);
  }

  .user-card__body {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
  }

  .user-card__field {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.8125rem;
    color: var(--gstock-color-text-subtle);
  }

  .user-card__field gstock-icon {
    font-size: 1rem;
  }
</style>

Con selección

La selección se preserva al cambiar entre modos de vista. Seleccione elementos en un modo y cambie al otro para verificar que la selección se mantiene.

Lista Grid
Seleccionados: 0 elementos
<gstock-data-view view-mode="list" selectable multi-select></gstock-data-view>

Con paginación

La paginación funciona en ambos modos de vista. Al cambiar de modo, la paginación se mantiene sincronizada.

Lista Grid
<div class="view-toggle">
  <gstock-button id="btn-list" variant="solid" size="small">
    <gstock-icon name="list" slot="prefix"></gstock-icon>
    Lista
  </gstock-button>
  <gstock-button id="btn-grid" variant="outlined" size="small">
    <gstock-icon name="grid" slot="prefix"></gstock-icon>
    Grid
  </gstock-button>
</div>

<gstock-data-view view-mode="list" paginated page-size="4" show-page-size-selector></gstock-data-view>

<script type="module">
  customElements.whenDefined('gstock-data-view').then(() => {
    
    const view = document.querySelector('gstock-data-view');
    const btnList = document.querySelector('#btn-list');
    const btnGrid = document.querySelector('#btn-grid');

    view.columns = [
      { key: 'name', title: 'Nombre', sortable: true },
      { key: 'sku', title: 'SKU' },
      { key: 'price', title: 'Precio', align: 'right' },
      { key: 'category', title: 'Categoría' },
    ];

    view.data = Array.from({ length: 16 }, (_, i) => ({
      id: i + 1,
      name: `Producto ${i + 1}`,
      sku: `PRD-${String(i + 1).padStart(3, '0')}`,
      price: `${(Math.random() * 99 + 1).toFixed(2)}`,
      category: ['Bebidas', 'Alimentación', 'Limpieza', 'Otros'][i % 4],
    }));

    view.cardRenderer = row => {
      return `
        <div class="item-card">
          <div class="item-card__header">
            <strong>${row.name}</strong>
            <code>${row.sku}</code>
          </div>
          <div class="item-card__footer">
            <span class="item-card__price">${row.price}</span>
            <gstock-tag size="small" variant="neutral">${row.category}</gstock-tag>
          </div>
        </div>
      `;
    };

    function updateButtons(mode) {
      btnList.variant = mode === 'list' ? 'solid' : 'outlined';
      btnGrid.variant = mode === 'grid' ? 'solid' : 'outlined';
    }

    btnList.addEventListener('click', () => {
      view.viewMode = 'list';
      updateButtons('list');
    });

    btnGrid.addEventListener('click', () => {
      view.viewMode = 'grid';
      updateButtons('grid');
    });
  });
</script>

<style>
  .view-toggle {
    display: flex;
    gap: 0.5rem;
    margin-bottom: 1rem;
  }

  .item-card {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
  }

  .item-card__header {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
  }

  .item-card__header code {
    font-size: 0.75rem;
    color: var(--gstock-color-text-subtlest);
  }

  .item-card__footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }

  .item-card__price {
    font-weight: 700;
    color: var(--gstock-color-text-brand);
  }
</style>

Con ordenamiento

El ordenamiento se aplica en la vista de lista mediante las columnas configuradas como sortable. Al cambiar a vista de grid, el estado de ordenamiento se preserva y se aplica a las tarjetas.

Lista Grid
Sin ordenar
<gstock-data-view view-mode="list" sortable hoverable></gstock-data-view>