본문으로 이동

Toast

현재 작업과 함께 짧은 피드백을 순서대로 알립니다.

용법

자동 닫힘

잠시 표시한 알림을 자동으로 닫되 사용자가 바로 닫을 수 있는 버튼도 함께 제공합니다.

잠시 표시한 알림을 자동으로 닫되 사용자가 바로 닫을 수 있는 버튼도 함께 제공합니다.

계속 유지되는 알림

사용자가 직접 닫을 때까지 알림을 계속 표시합니다.

사용자가 직접 닫을 때까지 알림을 계속 표시합니다.

개수 제한

기존 값을 유지하면서 설정한 항목 수나 화면 표시 개수를 지킵니다.

기존 값을 유지하면서 설정한 항목 수나 화면 표시 개수를 지킵니다.

setup에서 호출

useToast()ToastProvider의 슬롯 하위 컴포넌트 setup에서 호출합니다. 반환된 state와 함수는 템플릿이나 이벤트·비동기 함수에서 사용할 수 있습니다. Context를 사용하는 컴포넌트를 Provider 슬롯 아래에 두면 같은 트리에서 toast 상태와 동작을 공유합니다.

template
<!-- AppShell.vue -->
<ToastProvider v-slot="{ toasts }">
  <RequestButton />
  <ToastViewport class="toast-viewport">
    <ToastRoot v-for="item in toasts" :key="item.id" :value="item.id" class="toast-item">
      <ToastTitle />
      <ToastDescription />
      <ToastClose>닫기</ToastClose>
    </ToastRoot>
  </ToastViewport>
</ToastProvider>
ts
// RequestButton.vue <script setup>
const { toast, update } = useToast()

async function save() {
  const id = crypto.randomUUID()
  toast({ id, title: '저장 중', kind: 'deployment-pending', durationMs: null })
  try {
    const result = await saveRelease()
    update(id, { title: '저장 완료', kind: 'deployment-complete', durationMs: 3_000 })
    return result
  } catch (error) {
    update(id, { title: '저장 실패', description: '다시 시도해 주세요.', kind: 'error', durationMs: 5_000 })
    throw error
  }
}

애플리케이션이 요청을 실행하고 pending·성공·실패 문구, 시간, 오류 노출 정책을 결정합니다. markup, 아이콘, class, 위치, 모션도 Provider의 compound parts를 조립하는 애플리케이션이 소유합니다.

kind는 사용자 정의 문자열입니다. 생략하거나 빈 문자열이면 info가 되고, 그 외 값은 data-kind까지 그대로 전달됩니다. 기존 접근성 호환성을 위해 정확히 error인 항목만 role="alert", 나머지는 role="status"를 사용합니다.

CSS로 직접 스타일링

아이콘과 별도 설정을 생략하는 구성에서는 data-kind 선택자만 사용합니다.

css
.toast-item[data-kind='deployment-pending'] {
  background: var(--toast-pending-background);
}

.toast-item[data-kind='deployment-complete'] {
  background: var(--toast-complete-background);
}

class와 아이콘 등록

애플리케이션의 일반 객체에 kind 표시 정보를 등록합니다. 미등록 값은 명시적인 fallback으로 처리합니다.

ts
// toast-kinds.ts
import type { Component } from 'vue'
import InfoIcon from './InfoIcon.vue'
import SpinnerIcon from './SpinnerIcon.vue'
import SuccessIcon from './SuccessIcon.vue'
import ErrorIcon from './ErrorIcon.vue'

interface ToastKindPresentation {
  readonly class: string
  readonly icon: Component
}

export const toastKinds = {
  info: { class: 'toast--info', icon: InfoIcon },
  'deployment-pending': { class: 'toast--pending', icon: SpinnerIcon },
  'deployment-complete': { class: 'toast--complete', icon: SuccessIcon },
  error: { class: 'toast--error', icon: ErrorIcon },
} as const satisfies Record<string, ToastKindPresentation>

export type AppToastKind = keyof typeof toastKinds

const fallbackKind: ToastKindPresentation = {
  class: 'toast--unknown',
  icon: InfoIcon,
}

export function resolveToastKind(kind: string): ToastKindPresentation {
  return kind in toastKinds
    ? toastKinds[kind as AppToastKind]
    : fallbackKind
}
template
<ToastRoot
  v-for="item in toasts"
  :key="item.id"
  :value="item.id"
  :class="resolveToastKind(item.kind).class"
>
  <component :is="resolveToastKind(item.kind).icon" />
  <ToastTitle />
  <ToastDescription />
</ToastRoot>

애플리케이션 내부에서 kind 제한

Sectile은 모든 문자열을 허용하지만 애플리케이션 wrapper에서는 등록된 kind만 받도록 좁힐 수 있습니다.

ts
// use-app-toast.ts
import type { ToastInput } from '@sectile/vue/toast'
import { useToast } from '@sectile/vue/toast'
import type { AppToastKind } from './toast-kinds'

type AppToastInput = Omit<ToastInput<string>, 'kind'> & {
  readonly kind?: AppToastKind
}

export function useAppToast() {
  const api = useToast()
  return {
    ...api,
    toast(input: AppToastInput) {
      api.toast(input)
    },
  }
}

API

Vue 패키지: @sectile/vue/toast

컴포넌트
  • ToastProvider
  • ToastPortal
  • ToastViewport
  • ToastRoot
  • ToastTitle
  • ToastDescription
  • ToastClose

함수

useToast

ts
function useToast(): UseToastReturn

Props

ToastProviderProps

closeLabel

각 알림 닫기 작업에 제공할 접근 가능한 이름입니다.

defaultDurationMs

밀리초 단위의 초기 타이머 길이입니다.

dismissOnEscape

Escape 키로 포커스된 알림을 닫을지 여부입니다.

hotkey

알림 표시 영역으로 포커스를 옮길 문서 단축키입니다. false는 단축키를 해제합니다.

initialToasts

Provider가 처음 마운트될 때 존재할 알림입니다.

maxVisible

한 번에 표시할 수 있는 최대 알림 수입니다.

pauseOnWindowBlur

브라우저 창이 비활성 상태일 때 자동 닫기 시간을 멈출지 여부입니다.

swipeDirection

포인터로 알림을 밀어 닫을 방향입니다.

swipeThreshold

밀어서 닫을 때 필요한 포인터 이동 거리(픽셀)입니다.

toasts

부모가 Provider를 제어할 때 사용할 현재 알림 목록입니다.

ToastPartProps

as

이 파트가 렌더링할 요소 또는 컴포넌트입니다.

asChild

하나뿐인 자식 요소에 파트 속성을 직접 합칠지 여부입니다.

ToastPortalProps

defer

Teleport 대상을 현재 mount 또는 update tick이 끝날 때 찾을지 여부입니다.

disabled

사용자 조작을 막을지 여부입니다.

to

포털 콘텐츠를 옮길 대상입니다.

ToastRootProps

as

이 파트가 렌더링할 요소 또는 컴포넌트입니다.

asChild

하나뿐인 자식 요소에 파트 속성을 직접 합칠지 여부입니다.

value

이 계약이 노출하는 현재 값입니다.

슬롯

ToastProviderSlotProps

dismiss

알림 하나를 닫는 함수입니다.

dismissAll

모든 알림을 닫는 함수입니다.

paused

자동 갱신이 멈춘 상태인지 여부입니다.

toast

이 항목이 나타내는 알림입니다.

toasts

현재 알림 컬렉션입니다.

update

식별자를 유지하면서 알림 하나를 갱신하는 함수입니다.

ToastRootSlotProps

open

연결된 팝업이나 펼침 영역이 열려 있는지 여부입니다.

toast

이 항목이 나타내는 알림입니다.

이벤트

ToastProvider

update:toasts

Provider가 새 외부 제어 알림 목록을 요청할 때 발생합니다.

기타 타입

UseToastReturn

이름타입필수
toastsComputedRef<readonly ToastItem<string>[]>필수
pausedComputedRef<boolean>필수
toastvoid필수
updatevoid필수
dismissvoid필수
dismissAllvoid필수

파트

공통 범위: [data-scope="toast"]. 컴포넌트 내부로 스타일을 제한할 때 파트 선택자와 함께 사용합니다.

파트선택자역할추가 속성
viewport[data-part="viewport"]현재 보이는 콘텐츠를 배치하고 경계를 정합니다.
root[data-part="root"]컴포넌트 경계와 내부 파트를 묶습니다.
title[data-part="title"]연결된 콘텐츠의 제목을 표시합니다.
description[data-part="description"]연결된 콘텐츠나 결정 내용을 설명합니다.
close[data-part="close"]현재 화면을 닫거나 해제합니다.

키보드 동작

동작
F8알림 표시 영역으로 포커스를 옮깁니다.
Tab / Shift+Tab알림 작업 컨트롤 사이에서 포커스를 이동합니다.
Escape포커스된 알림을 닫습니다.
Pointer swipe설정한 거리보다 멀리 알림을 밀면 닫습니다.

접근성

표시 영역이 알림 순서와 키보드 접근을 유지하며 각 알림에 지역화된 닫기 작업을 제공하고 사용자 조작이나 창 상태에 따라 자동 닫기를 멈춥니다.

MIT 라이선스로 배포합니다.