Przejdź do treści

Dialog

Stabilny

Okno nałożone na stronę, które skupia uwagę na jednym zadaniu i blokuje wszystko inne, dopóki zadanie nie zostanie zakończone albo okno zamknięte.

Instalacja

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

Użycie

import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"
<Dialog>
  <DialogTrigger render={<Button variant="outline" />}>Edit profile</DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Edit profile</DialogTitle>
      <DialogDescription>Changes are visible to everyone in your workspace.</DialogDescription>
    </DialogHeader>
    {/* … */}
    <DialogFooter>
      <DialogClose render={<Button variant="outline" />}>Cancel</DialogClose>
      <Button>Save changes</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>

DialogContent sam renderuje portal, przyciemnione tło i okno. Wyzwalacze i przyciski zamykania przyjmują prop render, więc element, który otwiera okno, jest prawdziwym Button z prfct, a nie opakowaniem wokół niego.

Przykłady

Formularz

Najczęstsze okno dialogowe: krótki formularz, który edytuje jedną rzecz. Po otwarciu okna fokus trafia do pierwszego pola, a po zamknięciu wraca do wyzwalacza. Ogranicz się do kilku pól — wszystko dłuższe zasługuje na osobną stronę albo Sheet. Umieść pola i stopkę w jednym elemencie <form>, żeby Enter wysyłał formularz.

Przewijana treść

Gdy treść jest wyższa niż obszar widoku, przewijaj tylko środkową część okna, żeby tytuł i akcje pozostały widoczne. Samo okno nigdy nie jest większe niż obszar widoku pomniejszony o margines 1 rem z każdej strony.

Tryb kontrolowany

Okna otwierane z pozycji menu, skrótem klawiszowym albo po zakończeniu operacji asynchronicznej nie mają własnego wyzwalacza. Wyrenderuj okno poza menu i steruj nim za pomocą openonOpenChange.

Planowanie Q3
const [open, setOpen] = React.useState(false)

<DropdownMenuItem onClick={() => setOpen(true)}>Rename…</DropdownMenuItem>

<Dialog open={open} onOpenChange={setOpen}>
  <DialogContent>{/* … */}</DialogContent>
</Dialog>

DialogFooter to pas z delikatnie zabarwionym tłem, przylegający do dolnej krawędzi. Akcje umieszczaj po prawej, dodatkowy kontekst po lewej, a gdy okno potrzebuje tylko drogi wyjścia, użyj showCloseButton.

Szerokość

Domyślna maksymalna szerokość to max-w-lg (32 rem) — w sam raz dla formularza. Dla treści pomocniczych, np. listy skrótów, poszerz okno klasą max-w-*, ale zachowaj boczny margines: okno ma zawsze najwyżej calc(100% - 2rem) szerokości.

Wytyczne

Kiedy używać

  • Aby zebrać niewielką ilość danych bez opuszczania strony: zmiana nazwy, zaproszenie, edycja kilku pól.
  • Aby pokazać informację, którą trzeba potwierdzić, zanim przejdzie się dalej.
  • Aby potwierdzić zadanie rozpoczęte na bieżącej stronie, z kontekstem wystarczającym do podjęcia decyzji.

Kiedy nie używać

  • Do potwierdzania akcji nieodwracalnych lub niszczących dane — użyj Alert Dialog, którego nie da się zamknąć kliknięciem obok.
  • Do długich formularzy, procesów wieloetapowych i wszystkiego, co trzeba porównywać z zawartością strony — użyj Sheet albo osobnej strony.
  • Do komunikatu o powodzeniu akcji — użyj Toast. Przerywanie pracy tylko po to, żeby powiedzieć, że wszystko poszło dobrze, to podatek od każdego zadania.
  • Do kontrolek kontekstowych powiązanych z jednym elementem — użyj Popover.

Wybór nakładki

Każda nakładka to kompromis między skupieniem uwagi a zachowaniem kontekstu. Wybierz najlżejszą, która wystarczy do zadania.

KomponentBlokuje stronęZamyka się po kliknięciu obokZastosowanie
DialogTakTakZadanie wymagające skupienia: krótki formularz, decyzja z kontekstem.
Alert DialogTakNiePotwierdzanie akcji nieodwracalnych lub niszczących dane.
SheetTakTakDłuższe formularze, szczegóły i filtry, przy których strona pozostaje w polu widzenia.
DrawerTakTak, także gestem przesunięciaPanele dolne projektowane z myślą o urządzeniach mobilnych, z gestami i punktami przyciągania.
PopoverNieTakNiewielka interaktywna treść zakotwiczona przy wyzwalaczu.
Hover CardNiePo odsunięciu kursoraPodglądy linków dla widzących użytkowników myszy.
TooltipNiePo odsunięciu kursoraKrótka tekstowa etykieta ikony lub kontrolki.

Jak pisać

Tytuł okna powinien mówić, co ono robi, a nie zadawać pytanie, które potem powtarzasz: Edytuj profil, Zaproś do zespołu. Główny przycisk nazwij od rezultatu i powtórz kluczowy rzeczownik, jeśli to pomaga: Zapisz zmiany, Wyślij 3 zaproszenia. Opis jest opcjonalny, ale zwykle warto poświęcić mu jedno zdanie kontekstu.

Zaproś do zespołuDostaną e-mail z zaproszeniem do Acme.
Dobrze.Tytuł nazywa zadanie, a przyciski — rezultaty.
Czy na pewno?Potwierdź, aby kontynuować.
Źle.Ogólnikowe tytuły i para OK/Anuluj zmuszają do ponownego czytania wszystkiego.

Jedno okno naraz

Nigdy nie otwieraj okna dialogowego na innym oknie dialogowym. Jeśli zadanie wymaga drugiego kroku, podmień zawartość pierwszego okna albo przenieś cały proces na osobną stronę.

Dostępność

Okno realizuje wzorzec WAI-ARIA dialog. Base UI renderuje je z role="dialog"aria-modal, nadaje mu nazwę z DialogTitle i opis z DialogDescription.

KlawiszDziałanie
EnterSpacja
Na wyzwalaczu: otwiera okno i przenosi do niego fokus.
Tab
Przenosi fokus do następnego elementu, który może go przyjąć. Fokus nie wychodzi poza okno.
ShiftTab
Przenosi fokus do poprzedniego elementu, który może go przyjąć; na początku przeskakuje na koniec.
Esc
Zamyka okno i przywraca fokus do elementu, który je otworzył.
  • Zawsze dodawaj tytuł. DialogTitle nadaje oknu dostępną nazwę. Jeśli projekt nie przewiduje widocznego tytułu, zostaw go i dodaj className="sr-only".
  • Fokus. Po otwarciu fokus trafia do pierwszego elementu, który może go przyjąć — a przy otwarciu dotykiem do samego okna, żeby nie wysuwała się klawiatura ekranowa. Zmienisz to propsami initialFocusfinalFocus na DialogContent.
  • Reszta strony jest nieaktywna. Gdy okno modalne jest otwarte, przewijanie strony jest zablokowane, a treść poza oknem jest nieosiągalna dla myszy, klawiatury i czytnika ekranu.
  • Droga wyjścia. Zostaw przycisk zamykania (showCloseButton, domyślnie włączony) albo przycisk DialogClose w stopce. Osoby korzystające z czytnika ekranu na urządzeniu dotykowym nie mogą nacisnąć Esc.
  • Ruch. Okno pojawia się z przenikaniem i skalowaniem w ciągu 320 ms, z krzywą przejścia enter, a znika szybciej, niż się pojawiło. Przy ograniczonym ruchu pojawia się natychmiast.

Dokumentacja API

Dialog

Komponent główny. Nie renderuje żadnego elementu. Przyjmuje wszystkie propsy Dialog.Root z Base UI.

PropTypDomyślnie
open

Czy okno jest otwarte. Używaj razem z onOpenChange, żeby sterować oknem.

booleanBrak wartości domyślnej
defaultOpen

Czy okno jest początkowo otwarte, w trybie niekontrolowanym.

booleanfalse
onOpenChange

Wywoływana, gdy okno się otwiera lub zamyka. details.reason podaje przyczynę: trigger-press, outside-press, escape-key, close-press…

(open: boolean, details) => voidBrak wartości domyślnej
onOpenChangeComplete

Wywoływana po zakończeniu animacji wejścia lub wyjścia.

(open: boolean) => voidBrak wartości domyślnej
modal

true blokuje przewijanie i interakcję poza oknem. "trap-focus" tylko zatrzymuje fokus w oknie. false pozwala na interakcję ze stroną.

boolean | "trap-focus"true
disablePointerDismissal

Zapobiega zamknięciu po kliknięciu poza oknem.

booleanfalse
actionsRef

Imperatywny uchwyt do zamknięcia lub odmontowania okna.

RefObject<{ close, unmount }>Brak wartości domyślnej

DialogTrigger

Otwiera okno. Renderuje <button>; użyj render, żeby wyrenderować Button z prfct.

PropTypDomyślnie
render

Podmienia renderowany element, nie zmieniając działania.

ReactElement | (props, state) => ReactElementBrak wartości domyślnej
nativeButton

Ustaw false, gdy render zwraca element inny niż przycisk.

booleantrue

DialogContent

Renderuje portal, przyciemnione tło i samo okno.

PropTypDomyślnie
showCloseButton

Renderuje w prawym górnym rogu przycisk z ikoną, który zamyka okno.

booleantrue
initialFocus

Element, który dostaje fokus po otwarciu okna. Domyślnie pierwszy element osiągalny klawiszem Tab.

boolean | RefObject | (interaction) => HTMLElement | booleanBrak wartości domyślnej
finalFocus

Element, który dostaje fokus po zamknięciu okna. Domyślnie wyzwalacz.

boolean | RefObject | (interaction) => HTMLElement | booleanBrak wartości domyślnej
className

Łączone z klasami okna. Szerokość nadpiszesz klasą max-w-*.

stringBrak wartości domyślnej

DialogHeader, DialogTitle, DialogDescription

DialogHeader układa tytuł i opis jeden pod drugim i rezerwuje miejsce na przycisk zamykania. DialogTitle renderuje <h2> i nadaje oknu nazwę; DialogDescription renderuje <p> i je opisuje.

DialogFooter

Pas z delikatnie zabarwionym tłem, przylegający do dolnej krawędzi okna. Na małych ekranach układa akcje jedna pod drugą, od sm wzwyż wyrównuje je do prawej.

PropTypDomyślnie
showCloseButton

Dodaje na końcu przycisk zamykania w wariancie outline.

booleanfalse

DialogClose

Zamyka okno. Renderuje <button>; użyj render, żeby wyrenderować Button z prfct.