TourKit
@tour-kit/announcements

Type Reference

TypeScript types for Announcement, AnnouncementVariant, QueueConfig, AudienceRule, and the full announcements API surface

Type Reference

Complete TypeScript type definitions for the announcements package.


Core Types

AnnouncementConfig

Main configuration object for an announcement:

interface AnnouncementConfig<TMetadata = any> {
  // Required
  id: string;
  variant: AnnouncementVariant;

  // Content
  title?: string;
  description?: string | ReactNode;
  media?: AnnouncementMedia;

  // Actions
  primaryAction?: AnnouncementAction;
  secondaryAction?: AnnouncementAction;

  // Behavior
  priority?: AnnouncementPriority;
  frequency?: FrequencyRule;
  audience?: AudienceCondition[];

  // Variant Options
  modalOptions?: ModalOptions;
  slideoutOptions?: SlideoutOptions;
  bannerOptions?: BannerOptions;
  toastOptions?: ToastOptions;
  spotlightOptions?: SpotlightOptions;

  // Callbacks
  onShow?: () => void;
  onDismiss?: (reason: DismissalReason) => void;
  onComplete?: () => void;

  // Custom data
  metadata?: TMetadata;
}

AnnouncementVariant

type AnnouncementVariant = 'modal' | 'slideout' | 'banner' | 'toast' | 'spotlight';

AnnouncementPriority

type AnnouncementPriority = 'low' | 'normal' | 'high' | 'critical';

Content Types

AnnouncementMedia

interface AnnouncementMedia {
  type: 'image' | 'video' | 'custom';
  src?: string;
  alt?: string;
  poster?: string;        // For videos
  autoPlay?: boolean;     // For videos
  component?: ReactNode;  // For custom
}

Examples:

// Image
media: {
  type: 'image',
  src: '/feature.png',
  alt: 'New feature screenshot',
}

// Video
media: {
  type: 'video',
  src: '/demo.mp4',
  poster: '/demo-poster.jpg',
  autoPlay: true,
}

// Custom component
media: {
  type: 'custom',
  component: <MyCustomMedia />,
}

AnnouncementAction

interface AnnouncementAction {
  label: string;
  onClick: () => void;
  href?: string;          // Optional link
  variant?: 'primary' | 'secondary' | 'ghost';
  disabled?: boolean;
}

Example:

primaryAction: {
  label: 'Try It Now',
  onClick: () => router.push('/feature'),
  variant: 'primary',
}

Frequency Types

FrequencyRule

type FrequencyRule =
  | 'once'                                    // Show once ever
  | 'session'                                 // Once per session
  | 'always'                                  // Every time
  | { type: 'times'; count: number }          // N times total
  | { type: 'interval'; days: number };       // Every N days

Examples:

frequency: 'once'

frequency: 'session'

frequency: { type: 'times', count: 3 }

frequency: { type: 'interval', days: 7 }

Audience Types

AudienceCondition

type AudienceCondition<T = any> =
  | FieldCondition<T>
  | CustomCondition<T>;

FieldCondition

interface FieldCondition<T = any> {
  field: keyof T;
  operator: ComparisonOperator;
  value: any;
}

type ComparisonOperator =
  | 'equals'
  | 'notEquals'
  | 'in'
  | 'notIn'
  | 'greaterThan'
  | 'lessThan'
  | 'contains'
  | 'notContains';

Examples:

{ field: 'plan', operator: 'equals', value: 'pro' }

{ field: 'role', operator: 'in', value: ['admin', 'owner'] }

{ field: 'credits', operator: 'greaterThan', value: 100 }

CustomCondition

interface CustomCondition<T = any> {
  type: 'custom';
  check: (context: T) => boolean;
}

Example:

{
  type: 'custom',
  check: (user) => user.isActive && user.hasFeature('analytics'),
}

State Types

AnnouncementState

Internal state for each announcement:

interface AnnouncementState {
  id: string;
  isVisible: boolean;
  isActive: boolean;
  isDismissed: boolean;
  viewCount: number;
  lastViewedAt: Date | null;
  dismissedAt: Date | null;
  dismissalReason: DismissalReason | null;
}

DismissalReason

type DismissalReason =
  | 'close_button'       // User clicked close button
  | 'overlay_click'      // User clicked overlay
  | 'escape_key'         // User pressed Escape
  | 'primary_action'     // User clicked primary action
  | 'secondary_action'   // User clicked secondary action
  | 'auto_dismiss'       // Auto-dismissed (toasts)
  | 'programmatic';      // Dismissed via code

Variant Options

ModalOptions

interface ModalOptions {
  size?: 'sm' | 'md' | 'lg' | 'xl';
  closeOnOverlay?: boolean;
  closeOnEscape?: boolean;
  showCloseButton?: boolean;
  centered?: boolean;
}

Defaults:

{
  size: 'md',
  closeOnOverlay: true,
  closeOnEscape: true,
  showCloseButton: true,
  centered: true,
}

SlideoutOptions

interface SlideoutOptions {
  position?: 'left' | 'right';
  size?: 'sm' | 'md' | 'lg';
  width?: number;
  closeOnOverlay?: boolean;
  closeOnEscape?: boolean;
  showCloseButton?: boolean;
}

Defaults:

{
  position: 'right',
  size: 'md',
  closeOnOverlay: true,
  closeOnEscape: true,
  showCloseButton: true,
}

BannerOptions

interface BannerOptions {
  position?: 'top' | 'bottom';
  sticky?: boolean;
  dismissible?: boolean;
  intent?: 'info' | 'success' | 'warning' | 'error';
}

Defaults:

{
  position: 'top',
  sticky: false,
  dismissible: true,
  intent: 'info',
}

ToastOptions

interface ToastOptions {
  position?: ToastPosition;
  autoDismiss?: boolean;
  autoDismissDelay?: number;  // milliseconds
  showCloseButton?: boolean;
  intent?: 'info' | 'success' | 'warning' | 'error';
}

type ToastPosition =
  | 'top-left'
  | 'top-center'
  | 'top-right'
  | 'bottom-left'
  | 'bottom-center'
  | 'bottom-right';

Defaults:

{
  position: 'bottom-right',
  autoDismiss: true,
  autoDismissDelay: 5000,
  showCloseButton: true,
  intent: 'info',
}

SpotlightOptions

interface SpotlightOptions {
  target: string;                          // CSS selector
  targetElement?: HTMLElement;             // Or direct element ref
  placement?: Placement;
  offset?: number;
  padding?: number;
  borderRadius?: number;
  closeOnClickOutside?: boolean;
  closeOnEscape?: boolean;
  showCloseButton?: boolean;
}

type Placement =
  | 'top'
  | 'top-start'
  | 'top-end'
  | 'bottom'
  | 'bottom-start'
  | 'bottom-end'
  | 'left'
  | 'left-start'
  | 'left-end'
  | 'right'
  | 'right-start'
  | 'right-end';

Defaults:

{
  placement: 'bottom',
  offset: 8,
  padding: 8,
  borderRadius: 4,
  closeOnClickOutside: true,
  closeOnEscape: true,
  showCloseButton: true,
}

Provider Types

AnnouncementsProviderProps

interface AnnouncementsProviderProps<TUserContext = any> {
  // Required
  announcements: AnnouncementConfig[];
  children: ReactNode;

  // Optional
  userContext?: TUserContext;
  queueConfig?: QueueConfig;
  storage?: StorageType;
  storageKey?: string;

  // Callbacks
  onShow?: (id: string) => void;
  onDismiss?: (id: string, reason: DismissalReason) => void;
  onComplete?: (id: string) => void;
  onQueueChange?: (queue: AnnouncementConfig[]) => void;

  // Debug
  debug?: boolean;
}

QueueConfig

interface QueueConfig {
  maxConcurrent?: number;    // Max announcements shown at once
  autoShow?: boolean;        // Auto-show next in queue
  delayBetween?: number;     // Delay between announcements (ms)
  respectVariants?: boolean; // Allow one per variant
}

Defaults:

{
  maxConcurrent: 1,
  autoShow: true,
  delayBetween: 0,
  respectVariants: false,
}

StorageType

type StorageType = 'localStorage' | 'sessionStorage' | 'memory';

Hook Return Types

UseAnnouncementReturn

interface UseAnnouncementReturn {
  // State
  state: AnnouncementState;
  config: AnnouncementConfig;
  isVisible: boolean;
  isActive: boolean;
  isDismissed: boolean;
  canShow: boolean;
  viewCount: number;

  // Actions
  show: () => void;
  hide: () => void;
  dismiss: (reason?: DismissalReason) => void;
  complete: () => void;
  reset: () => void;
}

UseAnnouncementsReturn

interface UseAnnouncementsReturn {
  // All announcements
  announcements: {
    all: AnnouncementState[];
    visible: AnnouncementState[];
    queued: AnnouncementState[];
    dismissed: AnnouncementState[];
  };

  // Actions
  showNext: () => void;
  hideAll: () => void;
  resetAll: () => void;
}

UseAnnouncementQueueReturn

interface UseAnnouncementQueueReturn {
  // Queue state
  queue: AnnouncementConfig[];
  queueSize: number;
  activeCount: number;

  // Actions
  showNext: () => void;
  clearQueue: () => void;
}

Component Props

AnnouncementModal Props

interface AnnouncementModalProps {
  id?: string;                    // Announcement ID
  config?: AnnouncementConfig;    // Or inline config
  onDismiss?: (reason: DismissalReason) => void;
}

AnnouncementSlideout Props

interface AnnouncementSlideoutProps {
  id?: string;
  config?: AnnouncementConfig;
  onDismiss?: (reason: DismissalReason) => void;
}

AnnouncementBanner Props

interface AnnouncementBannerProps {
  id?: string;
  config?: AnnouncementConfig;
  onDismiss?: (reason: DismissalReason) => void;
}

AnnouncementToast Props

interface AnnouncementToastProps {
  id?: string;
  config?: AnnouncementConfig;
  onDismiss?: (reason: DismissalReason) => void;
}

AnnouncementSpotlight Props

interface AnnouncementSpotlightProps {
  id?: string;
  config?: AnnouncementConfig;
  onDismiss?: (reason: DismissalReason) => void;
}

Headless Component Props

HeadlessModal Props

interface HeadlessModalProps {
  id?: string;
  config?: AnnouncementConfig;
  children: (props: HeadlessModalRenderProps) => ReactNode;
}

interface HeadlessModalRenderProps {
  state: AnnouncementState;
  config: AnnouncementConfig;
  isOpen: boolean;
  dismiss: (reason: DismissalReason) => void;
  complete: () => void;
}

HeadlessSlideout Props

interface HeadlessSlideoutProps {
  id?: string;
  config?: AnnouncementConfig;
  children: (props: HeadlessSlideoutRenderProps) => ReactNode;
}

interface HeadlessSlideoutRenderProps {
  state: AnnouncementState;
  config: AnnouncementConfig;
  isOpen: boolean;
  dismiss: (reason: DismissalReason) => void;
  complete: () => void;
}

HeadlessBanner Props

interface HeadlessBannerProps {
  id?: string;
  config?: AnnouncementConfig;
  children: (props: HeadlessBannerRenderProps) => ReactNode;
}

interface HeadlessBannerRenderProps {
  state: AnnouncementState;
  config: AnnouncementConfig;
  isVisible: boolean;
  dismiss: (reason: DismissalReason) => void;
  complete: () => void;
}

HeadlessToast Props

interface HeadlessToastProps {
  id?: string;
  config?: AnnouncementConfig;
  children: (props: HeadlessToastRenderProps) => ReactNode;
}

interface HeadlessToastRenderProps {
  state: AnnouncementState;
  config: AnnouncementConfig;
  isVisible: boolean;
  dismiss: (reason: DismissalReason) => void;
  complete: () => void;
  progress: number;  // 0-100 for auto-dismiss
}

HeadlessSpotlight Props

interface HeadlessSpotlightProps {
  id?: string;
  config?: AnnouncementConfig;
  children: (props: HeadlessSpotlightRenderProps) => ReactNode;
}

interface HeadlessSpotlightRenderProps {
  state: AnnouncementState;
  config: AnnouncementConfig;
  isVisible: boolean;
  targetRect: DOMRect | null;
  dismiss: (reason: DismissalReason) => void;
  complete: () => void;
}

Utility Types

Generic Metadata

Extend announcement configs with custom metadata:

interface CustomMetadata {
  campaignId: string;
  source: 'email' | 'in-app' | 'push';
  experimentId?: string;
}

const config: AnnouncementConfig<CustomMetadata> = {
  id: 'promo',
  variant: 'modal',
  title: 'Special Offer',
  metadata: {
    campaignId: 'summer-2024',
    source: 'in-app',
  },
};

Generic User Context

Type-safe user context:

interface UserContext {
  plan: 'free' | 'pro' | 'enterprise';
  role: 'user' | 'admin';
  features: string[];
}

<AnnouncementsProvider<UserContext>
  userContext={{
    plan: 'pro',
    role: 'user',
    features: ['analytics'],
  }}
  announcements={announcements}
/>

Import Paths

// Types
import type {
  AnnouncementConfig,
  AnnouncementVariant,
  AnnouncementPriority,
  FrequencyRule,
  AudienceCondition,
  DismissalReason,
  QueueConfig,
  UseAnnouncementReturn,
  UseAnnouncementsReturn,
  UseAnnouncementQueueReturn,
} from '@tour-kit/announcements';

// Components
import {
  AnnouncementsProvider,
  AnnouncementModal,
  AnnouncementSlideout,
  AnnouncementBanner,
  AnnouncementToast,
  AnnouncementSpotlight,
} from '@tour-kit/announcements';

// Headless
import {
  HeadlessModal,
  HeadlessSlideout,
  HeadlessBanner,
  HeadlessToast,
  HeadlessSpotlight,
} from '@tour-kit/announcements/headless';

// Hooks
import {
  useAnnouncement,
  useAnnouncements,
  useAnnouncementQueue,
} from '@tour-kit/announcements';

On this page