1 estrella en GitHub = 1 componente nuevo.

Dar una estrella
E-commerce ecommerceshoptoolbarsortcatalog

Product Toolbar

Cabecera de catálogo con contador de resultados en vivo y selector de orden (ModernSelect) conectado a FilterSidebar.

Vista Previa en Vivo

All products

0 products

Uso

Copia y pega este código directamente en tu página de Astro:

astro
---
import { ProductToolbar } from 'astro-component-kit/ecommerce';
---

<ProductToolbar title="All products" total={12} target="shop-grid" filtersId="shop-filters" />
--- import { ProductToolbar } from 'astro-component-kit/ecommerce'; --- <ProductToolbar title="All products" total={12} target="shop-grid" filtersId="shop-filters" />

Instalación Manual

Si no estás usando el paquete npm, crea un archivo nuevo y pega el siguiente código: src/components/lib/ProductToolbar.astro:

astro
---
/**
 * ProductToolbar — Catalog heading bar: title, live result count and a sort select
 * (built on ModernSelect) that drives FilterSidebar's sorting.
 *
 * @param {string} title - Page heading. Default 'All products'.
 * @param {string} description - Optional supporting copy.
 * @param {number} total - Initial product count. Default 0.
 * @param {string} target - ProductGrid id. Default 'product-grid'.
 * @param {string} filtersId - FilterSidebar form id (sort select becomes `${filtersId}-sort`). Default 'catalog-filters'.
 * @param {Array<{label: string, value: string}>} sortOptions - Sort choices (values: featured|price-asc|price-desc|rating|name).
 * @param {string} countLabel - Text after the count. Default 'products'.
 * @param {string} headingLevel - Title element (h1|h2). Default 'h1'.
 * @param {string} variant - Layout (split|stacked). Default 'split'.
 * @param {string} className - Extra class.
 *
 * Accessibility: the count lives in a polite live region so filter changes are announced.
 */
import ModernSelect from '../forms/ModernSelect.astro';

interface Props {
  title?: string;
  description?: string;
  total?: number;
  target?: string;
  filtersId?: string;
  sortOptions?: Array<{ label: string; value: string }>;
  countLabel?: string;
  headingLevel?: 'h1' | 'h2';
  variant?: 'split' | 'stacked';
  className?: string;
}

const {
  title = 'All products',
  description,
  total = 0,
  target = 'product-grid',
  filtersId = 'catalog-filters',
  sortOptions = [
    { label: 'Featured', value: 'featured' },
    { label: 'Price: low to high', value: 'price-asc' },
    { label: 'Price: high to low', value: 'price-desc' },
    { label: 'Top rated', value: 'rating' },
    { label: 'Name A–Z', value: 'name' },
  ],
  countLabel = 'products',
  headingLevel = 'h1',
  variant = 'split',
  className = '',
} = Astro.props;

const Heading = headingLevel;
---

<div class="p-toolbar-cq">
<div class:list={['p-toolbar', `p-toolbar--${variant}`, className]}>
  <div class="p-toolbar__text">
    <Heading class="p-toolbar__title">{title}</Heading>
    {description && <p class="p-toolbar__desc">{description}</p>}
    <p class="p-toolbar__count" aria-live="polite">
      <span data-result-count={target}>{total}</span> {countLabel}
    </p>
  </div>
  <div class="p-toolbar__sort">
    <ModernSelect label="Sort by" id={`${filtersId}-sort`} name="sort" options={sortOptions} />
  </div>
</div>
</div>

<style>
  .p-toolbar-cq { container-type: inline-size; }
  .p-toolbar {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--sp-6, 1.5rem);
    padding-bottom: var(--sp-5, 1.25rem);
    margin-bottom: var(--sp-6, 1.5rem);
    border-bottom: 1px solid var(--c-border, rgb(var(--ink-rgb, 255 255 255) / 0.05));
  }
  .p-toolbar--stacked { flex-direction: column; align-items: stretch; }
  .p-toolbar__title { margin: 0; font-size: clamp(1.8rem, 4vw, 2.6rem); font-weight: 900; letter-spacing: -0.035em; color: var(--c-text-1, #fff); }
  .p-toolbar__desc { max-width: 40rem; margin: var(--sp-2, 0.5rem) 0 0; color: var(--c-text-2, #a3a3a3); }
  .p-toolbar__count { margin: var(--sp-2, 0.5rem) 0 0; font-size: 0.85rem; color: var(--c-text-2, #a3a3a3); }
  .p-toolbar__count span { color: var(--c-text-1, #fff); font-weight: 700; }
  .p-toolbar__sort { width: 220px; flex-shrink: 0; }

  @container (max-width: 640px) {
    .p-toolbar { flex-direction: column; align-items: stretch; }
    .p-toolbar__sort { width: 100%; }
  }
</style>
--- /** * ProductToolbar — Catalog heading bar: title, live result count and a sort select * (built on ModernSelect) that drives FilterSidebar's sorting. * * @param {string} title - Page heading. Default 'All products'. * @param {string} description - Optional supporting copy. * @param {number} total - Initial product count. Default 0. * @param {string} target - ProductGrid id. Default 'product-grid'. * @param {string} filtersId - FilterSidebar form id (sort select becomes `${filtersId}-sort`). Default 'catalog-filters'. * @param {Array<{label: string, value: string}>} sortOptions - Sort choices (values: featured|price-asc|price-desc|rating|name). * @param {string} countLabel - Text after the count. Default 'products'. * @param {string} headingLevel - Title element (h1|h2). Default 'h1'. * @param {string} variant - Layout (split|stacked). Default 'split'. * @param {string} className - Extra class. * * Accessibility: the count lives in a polite live region so filter changes are announced. */ import ModernSelect from '../forms/ModernSelect.astro'; interface Props { title?: string; description?: string; total?: number; target?: string; filtersId?: string; sortOptions?: Array<{ label: string; value: string }>; countLabel?: string; headingLevel?: 'h1' | 'h2'; variant?: 'split' | 'stacked'; className?: string; } const { title = 'All products', description, total = 0, target = 'product-grid', filtersId = 'catalog-filters', sortOptions = [ { label: 'Featured', value: 'featured' }, { label: 'Price: low to high', value: 'price-asc' }, { label: 'Price: high to low', value: 'price-desc' }, { label: 'Top rated', value: 'rating' }, { label: 'Name A–Z', value: 'name' }, ], countLabel = 'products', headingLevel = 'h1', variant = 'split', className = '', } = Astro.props; const Heading = headingLevel; --- <div class="p-toolbar-cq"> <div class:list={['p-toolbar', `p-toolbar--${variant}`, className]}> <div class="p-toolbar__text"> <Heading class="p-toolbar__title">{title}</Heading> {description && <p class="p-toolbar__desc">{description}</p>} <p class="p-toolbar__count" aria-live="polite"> <span data-result-count={target}>{total}</span> {countLabel} </p> </div> <div class="p-toolbar__sort"> <ModernSelect label="Sort by" id={`${filtersId}-sort`} name="sort" options={sortOptions} /> </div> </div> </div> <style> .p-toolbar-cq { container-type: inline-size; } .p-toolbar { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--sp-6, 1.5rem); padding-bottom: var(--sp-5, 1.25rem); margin-bottom: var(--sp-6, 1.5rem); border-bottom: 1px solid var(--c-border, rgb(var(--ink-rgb, 255 255 255) / 0.05)); } .p-toolbar--stacked { flex-direction: column; align-items: stretch; } .p-toolbar__title { margin: 0; font-size: clamp(1.8rem, 4vw, 2.6rem); font-weight: 900; letter-spacing: -0.035em; color: var(--c-text-1, #fff); } .p-toolbar__desc { max-width: 40rem; margin: var(--sp-2, 0.5rem) 0 0; color: var(--c-text-2, #a3a3a3); } .p-toolbar__count { margin: var(--sp-2, 0.5rem) 0 0; font-size: 0.85rem; color: var(--c-text-2, #a3a3a3); } .p-toolbar__count span { color: var(--c-text-1, #fff); font-weight: 700; } .p-toolbar__sort { width: 220px; flex-shrink: 0; } @container (max-width: 640px) { .p-toolbar { flex-direction: column; align-items: stretch; } .p-toolbar__sort { width: 100%; } } </style>

Información Rápida

Categoría
E-commerce
Nombre de archivo
ProductToolbar.astro
Dependencias
Ninguna — Astro nativo + CSS puro
Etiquetas
ecommerceshoptoolbarsortcatalog