본문으로 건너뛰기

Tour

이미 만들어진 페이지 위에서 단계별로 안내합니다. 처음 온 독자에게 한 번은 보여 줘야 하는 것들을, 실제로 있는 자리에서 가리킵니다.

tsx
import { Tour } from 'neba';

<Tour
  open={open}
  onOpenChange={setOpen}
  steps={[
    { target: '#search', title: 'Find anything', content: 'Everything is behind this field.' },
    { target: '#deploy', title: 'Ship it', content: 'Builds the current branch.', side: 'left' }
  ]}
/>;

Props

Prop타입기본값설명
steps * readonly TourStep[]순서대로 놓인 정거장들
openbooleantour가 실행 중인지. onOpenChange와 함께 쓰면 controlled 컴포넌트가 됩니다
defaultOpenbooleanfalse실행 중인 채로 시작할지 (uncontrolled)
onOpenChange(open: boolean) => void시작하고 끝날 때마다 호출됩니다
stepnumber몇 번째 정거장인지 (0부터)
defaultStepnumber0처음 시작하는 정거장 (uncontrolled)
onStepChange(step: number) => void단계가 바뀔 때마다 호출됩니다
onFinish() => void마지막 단계의 버튼을 눌렀을 때, tour가 닫히기 전에 호출됩니다
maskbooleantrue페이지를 어둡게 하고 대상만 도려냅니다. 어두운 층은 포인터를 가로채지 않습니다
skippablebooleantrue카운터 옆에 Skip 버튼을 그립니다
dismissiblebooleantrueEscape로 tour를 끝낼 수 있는지
scrollIntoViewbooleantruetour가 각 단계에 닿을 때 그 대상을 화면 안으로 스크롤합니다
size공통'xs' | 'sm' | 'md' | 'lg' | 'xl''md'카드의 타입 스케일과 너비 상한
color공통'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info''primary'카드의 가장자리와 버튼의 색 역할
density공통'default' | 'compact''default'카드 안쪽 여백만 바꿉니다
localestringBCP 47 태그. 버튼과 카운터를 이 언어로 씁니다. 지원하지 않는 태그는 영어로
previousLabelReactNodePrevious 버튼의 문구
nextLabelReactNodeNext 버튼의 문구
doneLabelReactNode마지막 단계에서 Next가 되는 문구
skipLabelReactNodeSkip 버튼의 문구
classNamestring카드에 붙는 class
classNamesNebaSlots<'mask' | 'title' | 'description' | 'close' | 'footer'>루트 뒤에 있는 각 파트의 class. 루트 자체는 className이 맡으므로 root 키는 없습니다

TourStep

Prop타입기본값설명
targetstring이 단계가 가리키는 것의 CSS selector. 없으면 아무것도 도려내지 않고 가운데에 놓입니다
titleReactNode단계의 제목
contentReactNode단계가 하는 말
side공통'top' | 'right' | 'bottom' | 'left''bottom'카드가 놓이는 대상의 모서리
align공통'start' | 'center' | 'end''center'그 모서리 위에서의 위치
paddingnumber6도려낸 구멍이 대상보다 얼마나 넓어지는지 (px)

HowToSteps를 뒤집은 것입니다. 그쪽은 설명을 페이지 안에 두고 독자가 따라가게 하고, 이쪽은 페이지를 그대로 둔 채 그 위에 섭니다. 그래서 단계는 selector로 지정합니다. tour가 말하는 대상은 이미 화면에 있고, 카드 안에서 한 번 더 설명하면 관리해야 할 사본이 둘이 됩니다.

예시

steps · target

각 단계는 CSS selector로 대상을 지목하며, 그 시점의 페이지에 대해 조회됩니다. target이 없는 단계는 아무것도 도려내지 않은 채 가운데에 놓입니다. 환영 단계와 마무리 단계가 그런 것입니다.

open · step

open은 tour를 실행하고 step은 몇 번째 지점인지입니다. 둘 다 uncontrolled 짝이 있고 둘 다 변화를 보고합니다. onFinish는 마지막 단계의 버튼을 눌렀을 때, tour가 닫히기 전에 호출됩니다.

mask

페이지를 어둡게 하고 대상만 그 어둠에서 도려냅니다. 어두운 층은 포인터를 가로채지 않으므로 가리키는 대상을 그대로 쓸 수 있습니다. 이것이 tour와 dialog의 연속을 가르는 차이입니다.

locale과 라벨

버튼과 카운터의 문구는 locale에서 옵니다. previousLabel, nextLabel, doneLabel, skipLabel로 각각을 직접 쓸 수 있습니다.

className · classNames

className은 카드(각 step이 쓰이는 popup)에 붙습니다. 그 뒤의 dimming은 popup의 자손이 아니라 형제라서 classNames.mask로만 닿습니다.

tsx
<Tour
  steps={steps}
  className="max-w-sm"
  classNames={{ mask: 'bg-black/70', footer: 'justify-between' }}
/>

slot은 mask, title, description, close, footer입니다. 넘긴 class가 컴포넌트 자신의 class와 어떻게 겨루는지는 prop 규약을 보세요.

접근성

  • 카드는 제목이 이름이 되고 본문이 설명이 되는 dialog이며, 각 단계가 열릴 때 focus가 그 안으로 옮겨갑니다.
  • dismissible을 끄지 않는 한 Escape로 tour가 끝납니다. 바깥을 누르는 것으로는 끝나지 않습니다. 페이지를 쓰는 것이 tour의 목적이기 때문입니다.
  • tour가 어떤 것에 이르는 유일한 통로가 되어서는 안 됩니다. tour가 가리키는 것은 tour 없이도 찾을 수 있어야 합니다. 이미 닫아버린 독자나 애초에 보지 못한 독자에게 두 번째 기회는 없습니다.

Released under the MIT License