View Switcher

Bascule basee sur un parametre d'URL entre des vues nommees avec un style pilule ou onglet. Installez View Switcher depuis le registre VLLNT UI avec la CLI shadcn.

Signaler un bug

Bascule basee sur un parametre d'URL entre des vues nommees avec un style pilule ou onglet. Fait partie de la famille navigation dans VLLNT UI, il est publie comme entree de registre lisible par machine — copiez la source directement dans votre application avec la CLI shadcn et possedez-la, sans dependance d'execution a une bibliotheque de composants.

Apercu

Basculez entre clair et sombre pour inspecter l'apercu Storybook integre.

Installation

Ajoutez View Switcher a votre projet avec la CLI shadcn. La source arrive dans votre code, prete a etre adaptee :

pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/view-switcher.json

Source

"use client"; import { memo, Suspense } from "react"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { cn } from "../../lib/utils"; type ViewOption = { key: string; label: string; }; type ViewSwitcherProps = { className?: string; defaultKey?: string; options: ViewOption[]; paramName?: string; }; function ViewSwitcherInner({ className, defaultKey, options, paramName: parameterName = "view", }: ViewSwitcherProps) { const router = useRouter(); const pathname = usePathname(); const searchParameters = useSearchParams(); const resolvedDefault = defaultKey ?? options[0]?.key ?? ""; const currentKey = searchParameters.get(parameterName) ?? resolvedDefault; function handleSelect(key: string): void { const parameters = new URLSearchParams(searchParameters.toString()); if (key === resolvedDefault) { parameters.delete(parameterName); } else { parameters.set(parameterName, key); } const query = parameters.toString(); router.push(query ? `${pathname}?${query}` : pathname, { scroll: false }); } return ( <div className={cn( "inline-flex items-center rounded-lg border bg-muted p-1", className, )} role="tablist" > {options.map((option) => ( <button aria-selected={currentKey === option.key} className={cn( "rounded-md px-3 py-1.5 text-sm font-medium transition-colors", currentKey === option.key ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground", )} key={option.key} onClick={() => { handleSelect(option.key); }} role="tab" type="button" > {option.label} </button> ))} </div> ); } function ViewSwitcherFallback({ className, defaultKey, options, }: ViewSwitcherProps) { const resolvedDefault = defaultKey ?? options[0]?.key ?? ""; return ( <div className={cn( "inline-flex items-center rounded-lg border bg-muted p-1", className, )} role="tablist" > {options.map((option) => ( <button aria-selected={resolvedDefault === option.key} className={cn( "rounded-md px-3 py-1.5 text-sm font-medium transition-colors", resolvedDefault === option.key ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground", )} key={option.key} role="tab" type="button" > {option.label} </button> ))} </div> ); } const ViewSwitcher = memo(function ViewSwitcher(props: ViewSwitcherProps) { return ( <Suspense fallback={<ViewSwitcherFallback {...props} />}> <ViewSwitcherInner {...props} /> </Suspense> ); }); ViewSwitcher.displayName = "ViewSwitcher"; export { ViewSwitcher }; export type { ViewOption, ViewSwitcherProps };

Stories

Explorez chaque variante et etat dans le Storybook interactif :

Apercu

Basculez entre clair et sombre pour inspecter l'apercu Storybook integre.

Dependances

  • @vllnt/ui@^0.3.0