E-commerceecommerceshoptoolbarsortcatalog
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 componente React:
tsx
import { ProductToolbar } from 'react-component-kit-ui/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/ProductToolbar.tsx + ProductToolbar.css:
ProductToolbar.tsx
'use client';
import ModernSelect from '../forms/ModernSelect';
import './ProductToolbar.css';
/** class:list → className */
function cx(...args: unknown[]): string {
const out: string[] = [];
const walk = (v: unknown) => {
if (!v) return;
if (typeof v === 'string' || typeof v === 'number') out.push(String(v));
else if (Array.isArray(v)) v.forEach(walk);
else if (typeof v === 'object') for (const [k, on] of Object.entries(v as Record<string, unknown>)) if (on) out.push(k);
};
args.forEach(walk);
return out.join(' ');
}
/**
* 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.
*/
export interface ProductToolbarProps {
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;
}
export function ProductToolbar({
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 = '',
}: ProductToolbarProps) {
const Heading: any = headingLevel;
return (
<>
<div className="p-toolbar-cq" data-rk-e742d8="">
<div className={cx(['p-toolbar', `p-toolbar--${variant}`, className])} data-rk-e742d8="">
<div className="p-toolbar__text" data-rk-e742d8="">
<Heading className="p-toolbar__title" data-rk-e742d8="">{title}</Heading>
{description && <p className="p-toolbar__desc" data-rk-e742d8="">{description}</p>}
<p className="p-toolbar__count" aria-live="polite" data-rk-e742d8="">
<span data-result-count={target} data-rk-e742d8="">{total}</span> {countLabel}
</p>
</div>
<div className="p-toolbar__sort" data-rk-e742d8="">
<ModernSelect label="Sort by" id={`${filtersId}-sort`} name="sort" options={sortOptions} />
</div>
</div>
</div>
</>
);
}
export default ProductToolbar;ProductToolbar.css
/* ProductToolbar — ported from astro-component-kit (ecommerce/ProductToolbar.astro) */
.p-toolbar-cq:where([data-rk-e742d8]) { container-type: inline-size; }
.p-toolbar:where([data-rk-e742d8]) {
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:where([data-rk-e742d8]) { flex-direction: column; align-items: stretch; }
.p-toolbar__title:where([data-rk-e742d8]) { 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:where([data-rk-e742d8]) { max-width: 40rem; margin: var(--sp-2, 0.5rem) 0 0; color: var(--c-text-2, #a3a3a3); }
.p-toolbar__count:where([data-rk-e742d8]) { margin: var(--sp-2, 0.5rem) 0 0; font-size: 0.85rem; color: var(--c-text-2, #a3a3a3); }
.p-toolbar__count:where([data-rk-e742d8]) span:where([data-rk-e742d8]) { color: var(--c-text-1, #fff); font-weight: 700; }
.p-toolbar__sort:where([data-rk-e742d8]) { width: 220px; flex-shrink: 0; }
@container (max-width: 640px) {
.p-toolbar:where([data-rk-e742d8]) { flex-direction: column; align-items: stretch; }
.p-toolbar__sort:where([data-rk-e742d8]) { width: 100%; }
}Información Rápida
- Categoría
- E-commerce
- Nombre de archivo
ProductToolbar.tsx- Dependencias
- Ninguna — React + CSS puro
- Etiquetas
- ecommerceshoptoolbarsortcatalog