Przejdź do treści

Toggle Group

Zestaw przycisków przełączających do wyboru jednej opcji — albo kilku — z małego, widocznego zbioru.

Kod źródłowyBase UIRejestr
import {
  AlignCenterIcon,
  AlignJustifyIcon,
  AlignLeftIcon,
  AlignRightIcon,
} from "lucide-react"

import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"

export default function ToggleGroupDemo() {
  return (
    <ToggleGroup
      variant="outline"
      spacing={0}
      defaultValue={["left"]}
      aria-label="Wyrównanie tekstu"
    >
      <ToggleGroupItem value="left" aria-label="Wyrównaj do lewej">
        <AlignLeftIcon />
      </ToggleGroupItem>
      <ToggleGroupItem value="center" aria-label="Wyśrodkuj">
        <AlignCenterIcon />
      </ToggleGroupItem>
      <ToggleGroupItem value="right" aria-label="Wyrównaj do prawej">
        <AlignRightIcon />
      </ToggleGroupItem>
      <ToggleGroupItem value="justify" aria-label="Wyjustuj">
        <AlignJustifyIcon />
      </ToggleGroupItem>
    </ToggleGroup>
  )
}

Anatomia

  1. 1GrupaJedna kontrolka i jedno miejsce w kolejności tabulacji: strzałki przenoszą fokus między elementami, a Tab przechodzi dalej. Ustala wariant, rozmiar i odstępy wszystkich elementów.
  2. 2Wciśnięty elementWybrane opcje mają wypełnienie stanu wciśnięcia i zgłaszają aria-pressed. Grupa może dopuszczać jeden wybór albo kilka.
  3. 3ElementPrzycisk przełączający. Elementy z samą ikoną potrzebują aria-label, jak każdy przycisk z ikoną.

Instalacja

$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/toggle-group.json

Użycie

import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
<ToggleGroup defaultValue={["left"]} aria-label="Text alignment">
  <ToggleGroupItem value="left" aria-label="Align left">
    <AlignLeftIcon />
  </ToggleGroupItem>
  <ToggleGroupItem value="center" aria-label="Align center">
    <AlignCenterIcon />
  </ToggleGroupItem>
</ToggleGroup>

Wartość jest zawsze tablicą wartości wciśniętych elementów — ["left"] przy pojedynczym wyborze, ["bold", "italic"] przy kilku.

Przykłady

Wielokrotny wybór

Dodaj multiple, żeby można było wcisnąć naraz dowolną liczbę elementów — jak na pasku formatowania.

import {
  BoldIcon,
  ItalicIcon,
  StrikethroughIcon,
  UnderlineIcon,
} from "lucide-react"

import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"

export default function ToggleGroupMultiple() {
  return (
    <ToggleGroup
      multiple
      defaultValue={["bold", "italic"]}
      aria-label="Styl tekstu"
    >
      <ToggleGroupItem value="bold" aria-label="Pogrubienie">
        <BoldIcon />
      </ToggleGroupItem>
      <ToggleGroupItem value="italic" aria-label="Kursywa">
        <ItalicIcon />
      </ToggleGroupItem>
      <ToggleGroupItem value="underline" aria-label="Podkreślenie">
        <UnderlineIcon />
      </ToggleGroupItem>
      <ToggleGroupItem value="strikethrough" aria-label="Przekreślenie">
        <StrikethroughIcon />
      </ToggleGroupItem>
    </ToggleGroup>
  )
}

Kontrolka segmentowa

spacing={0} w połączeniu z wariantem outline scala elementy w jedną kontrolkę ze wspólnymi obramowaniami — kontrolkę segmentową do przełączania między kilkoma widokami tej samej treści.

import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"

export default function ToggleGroupSegmented() {
  return (
    <ToggleGroup
      variant="outline"
      size="sm"
      spacing={0}
      defaultValue={["week"]}
      aria-label="Zakres kalendarza"
    >
      <ToggleGroupItem value="day">Dzień</ToggleGroupItem>
      <ToggleGroupItem value="week">Tydzień</ToggleGroupItem>
      <ToggleGroupItem value="month">Miesiąc</ToggleGroupItem>
      <ToggleGroupItem value="year">Rok</ToggleGroupItem>
    </ToggleGroup>
  )
}

Przełącznik widoku

W grupie jednokrotnego wyboru ponowne naciśnięcie aktywnego elementu zwalnia go i nic nie zostaje wybrane. Gdy jedna opcja musi być zawsze aktywna, kontroluj wartość i ignoruj puste aktualizacje.

Projekty
  • Atlas
  • Beacon
  • Cinder
  • Delta
"use client"

import * as React from "react"
import { LayoutGridIcon, ListIcon } from "lucide-react"

import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
import { cn } from "@/lib/utils"

const projects = ["Atlas", "Beacon", "Cinder", "Delta"]

export default function ToggleGroupViewSwitcher() {
  const [view, setView] = React.useState("grid")

  return (
    <div className="flex w-full max-w-sm flex-col gap-4">
      <div className="flex items-center justify-between">
        <span id="view-label" className="text-sm font-medium">
          Projekty
        </span>
        <ToggleGroup
          variant="outline"
          size="sm"
          spacing={0}
          value={[view]}
          // Single selection: ignore attempts to deselect the active view.
          onValueChange={(value) => value.length > 0 && setView(value[0])}
          aria-labelledby="view-label"
        >
          <ToggleGroupItem value="grid" aria-label="Widok siatki">
            <LayoutGridIcon />
          </ToggleGroupItem>
          <ToggleGroupItem value="list" aria-label="Widok listy">
            <ListIcon />
          </ToggleGroupItem>
        </ToggleGroup>
      </div>
      <ul
        className={cn(
          "grid gap-2",
          view === "grid" ? "grid-cols-2" : "grid-cols-1"
        )}
      >
        {projects.map((project) => (
          <li
            key={project}
            className={cn(
              "rounded-lg border bg-card px-3 text-sm shadow-raised",
              view === "grid"
                ? "flex h-16 items-end pb-2"
                : "flex h-10 items-center"
            )}
          >
            {project}
          </li>
        ))}
      </ul>
    </div>
  )
}
const [view, setView] = React.useState("grid")

<ToggleGroup
  value={[view]}
  onValueChange={(value) => value.length > 0 && setView(value[0])}
  aria-label="View"
>
  …
</ToggleGroup>

Układ pionowy

orientation="vertical" układa elementy jeden pod drugim i przełącza nawigację klawiaturą na strzałki w górę i w dół — to układ palety narzędzi.

import {
  MousePointer2Icon,
  PenToolIcon,
  SquareIcon,
  TypeIcon,
} from "lucide-react"

import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"

export default function ToggleGroupVertical() {
  return (
    <ToggleGroup
      orientation="vertical"
      defaultValue={["select"]}
      aria-label="Narzędzia"
      className="rounded-xl border bg-card p-1 shadow-raised"
    >
      <ToggleGroupItem value="select" aria-label="Zaznaczanie">
        <MousePointer2Icon />
      </ToggleGroupItem>
      <ToggleGroupItem value="pen" aria-label="Pióro">
        <PenToolIcon />
      </ToggleGroupItem>
      <ToggleGroupItem value="shape" aria-label="Prostokąt">
        <SquareIcon />
      </ToggleGroupItem>
      <ToggleGroupItem value="text" aria-label="Tekst">
        <TypeIcon />
      </ToggleGroupItem>
    </ToggleGroup>
  )
}

Rozmiary

size dotyczy wszystkich elementów: sm 32, default 36, lg 40.

import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"

const sizes = ["sm", "default", "lg"] as const

export default function ToggleGroupSizes() {
  return (
    <div className="flex flex-col items-center gap-4">
      {sizes.map((size) => (
        <ToggleGroup
          key={size}
          variant="outline"
          size={size}
          spacing={0}
          defaultValue={["monthly"]}
          aria-label={`Okres rozliczeniowy (${size})`}
        >
          <ToggleGroupItem value="monthly">Miesięcznie</ToggleGroupItem>
          <ToggleGroupItem value="yearly">Rocznie</ToggleGroupItem>
        </ToggleGroup>
      ))}
    </div>
  )
}

Wytyczne

Kiedy używać

  • Do przełączania między dwoma–pięcioma widokami lub trybami tej samej treści: Dzień · Tydzień · Miesiąc, siatka albo lista.
  • Na paskach narzędzi z powiązanymi opcjami — jednokrotnego wyboru (wyrównanie) lub wielokrotnego (styl tekstu).

Kiedy nie używać

  • Do wyborów w formularzu, który zostanie wysłany później — użyj Radio Group: jest odbierany jako pole formularza i obsługuje opisy.
  • Przy więcej niż pięciu–sześciu opcjach albo długich etykietach — użyj Select.
  • Do niepowiązanych akcji — użyj Button Group. Grupa przycisków przełączających sugeruje wspólny stan.
  • Do przełączania widoków, z których każdy ma własny panel — użyj Tabs. Do nawigacji między stronami służą linki.

Krótkie i jednorodne etykiety

Segmenty powinny być tego samego rodzaju i mieć zbliżoną długość — wtedy kontrolka jest odbierana jako jeden zestaw. Ikony sprawdzają się na paskach narzędzi; przy trybach lepiej działają słowa.

DobrzeKrótkie, jednorodne opcje tego samego rodzaju.
ŹleRóżne długości i akcja wśród opcji — to już nie wygląda na jeden wybór.

Dostępność

  • Grupa renderuje role="group"; nazwij ją przez aria-label albo aria-labelledby wskazujące widoczną etykietę.
  • Każdy element to przycisk przełączający z aria-pressed. Elementy z samą ikoną potrzebują aria-label.
  • Grupa zajmuje jedno miejsce w kolejności tabulacji, a fokus wędruje w jej obrębie (roving focus): Tab przenosi fokus do grupy, strzałki przesuwają go między elementami, a na końcach fokus przeskakuje na przeciwny koniec grupy (loopFocus).
KlawiszDziałanie
Tab
Przenosi fokus do grupy; kolejne naciśnięcie ją opuszcza.
→↓
Przenosi fokus na następny element (w grupach pionowych — w dół).
←↑
Przenosi fokus na poprzedni element (w grupach pionowych — w górę).
SpacjaEnter
Wciska lub zwalnia element, na którym jest fokus.

Dokumentacja API

ToggleGroup

Renderuje <div> z role="group". Przyjmuje wszystkie propsy komponentu Toggle Group z Base UI.

PropTypDomyślnie
value

Wartości wciśniętych elementów. Kontrolowany odpowiednik defaultValue.

string[]Brak wartości domyślnej
defaultValue

Elementy wciśnięte na początku, w trybie niekontrolowanym.

string[]Brak wartości domyślnej
onValueChange

Wywoływana, gdy zmieniają się wciśnięte elementy.

(value: string[], eventDetails) => voidBrak wartości domyślnej
multiple

Pozwala wcisnąć więcej niż jeden element.

booleanfalse
variant

Stosowany do wszystkich elementów.

"default" | "outline"Brak wartości domyślnej
size

Stosowany do wszystkich elementów.

"sm" | "default" | "lg"Brak wartości domyślnej
spacing

Odstęp między elementami w jednostkach skali odstępów. 0 scala elementy w jedną kontrolkę.

number1
orientation

Kierunek układu i oś nawigacji strzałkami.

"horizontal" | "vertical""horizontal"
loopFocus

Przenosi fokus klawiatury z ostatniego elementu z powrotem na pierwszy.

booleantrue
disabled

Wyłącza wszystkie elementy.

booleanfalse

ToggleGroupItem

Toggle, który zgłasza grupie swoje value. Renderuje <button>.

PropTypDomyślnie
valuewymagany

Identyfikuje element w tablicy wartości grupy.

stringBrak wartości domyślnej
disabled

Wyłącza tylko ten element.

booleanfalse
variant

Używany, gdy grupa nie ustawia własnego.

"default" | "outline""default"
size

Używany, gdy grupa nie ustawia własnego.

"sm" | "default" | "lg""default"