Skip to main content
userTourKit
@tour-kit/announcements

Type Reference

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

domidex01Published

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';

Component Composition Props

Compound parts used to build the styled variants. Most consumers use the variant components directly (Modal, Slideout, Banner, Toast, Spotlight); these props matter when you compose your own layout.

AnnouncementOverlayProps

interface AnnouncementOverlayProps
  extends React.HTMLAttributes<HTMLDivElement>,
    VariantProps<typeof overlayVariants> {
  /** Whether the overlay is visible */
  open?: boolean
  /** Callback when overlay is clicked */
  onClose?: () => void
}

AnnouncementContentProps

interface AnnouncementContentProps extends React.HTMLAttributes<HTMLDivElement> {
  title?: string
  description?: React.ReactNode
  media?: AnnouncementMedia
  titleProps?: React.HTMLAttributes<HTMLHeadingElement>
  descriptionProps?: React.HTMLAttributes<HTMLDivElement>
}

AnnouncementActionsProps

interface AnnouncementActionsProps extends React.HTMLAttributes<HTMLDivElement> {
  primaryAction?: AnnouncementAction
  secondaryAction?: AnnouncementAction
  onAction?: (type: 'primary' | 'secondary') => void
  /** Fired when action.dismissOnClick is true */
  onDismiss?: () => void
  direction?: 'horizontal' | 'vertical'
}

AnnouncementCloseProps

interface AnnouncementCloseProps
  extends Omit<React.ComponentPropsWithoutRef<'button'>, 'children'> {
  asChild?: boolean
  children?: React.ReactNode | RenderProp
  onClose?: () => void
}

Context Types

AnnouncementsContext

Raw React context. Prefer useAnnouncementsContext — it throws when used outside <AnnouncementsProvider>.

declare const AnnouncementsContext: React.Context<AnnouncementsContextValue | null>

AnnouncementsContextValue

interface AnnouncementsContextValue {
  announcements: Map<string, AnnouncementState>
  activeAnnouncement: string | null
  queue: string[]
  queueConfig: QueueConfig
  register: (config: AnnouncementConfig) => void
  unregister: (id: string) => void
  show: (id: string) => void
  hide: (id: string) => void
  dismiss: (id: string, reason?: DismissalReason) => void
  complete: (id: string) => void
  reset: (id: string) => void
  resetAll: () => void
  getState: (id: string) => AnnouncementState | undefined
  getConfig: (id: string) => AnnouncementConfig | undefined
  canShow: (id: string) => boolean
  showNext: () => void
  clearQueue: () => void
}

Queue Types

QueueItem

interface QueueItem {
  id: string
  priority: AnnouncementPriority
  addedAt: number
  weight: number
  /** Monotonic sequence number for ordering items with same timestamp */
  sequence: number
}

DEFAULT_QUEUE_CONFIG

declare const DEFAULT_QUEUE_CONFIG: QueueConfig

The default queue configuration the provider falls back to when no queueConfig is supplied. Use as a base when building a partial override.

AnnouncementScheduler

Class that manages the queue and gating decisions internally. Exported for advanced use (custom orchestrators, test fixtures); typical apps never construct it directly.

declare class AnnouncementScheduler {
  constructor(config: QueueConfig)
  canShow(config, state, userContext?): boolean
  shouldQueue(config, state, userContext?): boolean
  enqueue(config: AnnouncementConfig): number
  getNext(): string | undefined
  peekNext(): string | undefined
  remove(id: string): boolean
  isQueued(id: string): boolean
  getQueuePosition(id: string): number
  getQueuedIds(): string[]
  get queueSize(): number
  canShowMore(): boolean
  markActive(): void
  markInactive(): void
  get currentActiveCount(): number
  clearQueue(): void
  resetActive(): void
  updateConfig(config: QueueConfig): void
  get delayBetween(): number
  get autoShow(): boolean
}

Storage

AnnouncementStorageAdapter

Pluggable persistence layer. Default storage uses localStorage; pass an adapter on the provider to swap implementations (memory, cookies, SQLite, ...).

interface AnnouncementStorageAdapter {
  getItem(key: string): string | null | Promise<string | null>
  setItem(key: string, value: string): void | Promise<void>
  removeItem(key: string): void | Promise<void>
}

Analytics Event Types

The provider's onEvent callback receives an AnnouncementEvent, which is a discriminated union of seven concrete shapes.

AnnouncementEventType

type AnnouncementEventType =
  | 'announcement_registered'
  | 'announcement_shown'
  | 'announcement_dismissed'
  | 'announcement_completed'
  | 'announcement_action_clicked'
  | 'announcement_queued'
  | 'announcement_dequeued'

BaseAnnouncementEvent

interface BaseAnnouncementEvent {
  type: AnnouncementEventType
  announcementId: string
  variant: AnnouncementVariant
  timestamp: number
  metadata?: Record<string, unknown>
}

Concrete events

interface AnnouncementRegisteredEvent extends BaseAnnouncementEvent {
  type: 'announcement_registered'
}

interface AnnouncementShownEvent extends BaseAnnouncementEvent {
  type: 'announcement_shown'
  viewCount: number
  fromQueue: boolean
}

interface AnnouncementDismissedEvent extends BaseAnnouncementEvent {
  type: 'announcement_dismissed'
  reason: DismissalReason
  viewDuration: number
}

interface AnnouncementCompletedEvent extends BaseAnnouncementEvent {
  type: 'announcement_completed'
  viewDuration: number
}

interface AnnouncementActionClickedEvent extends BaseAnnouncementEvent {
  type: 'announcement_action_clicked'
  actionType: 'primary' | 'secondary'
  actionLabel: string
}

interface AnnouncementQueuedEvent extends BaseAnnouncementEvent {
  type: 'announcement_queued'
  queuePosition: number
}

interface AnnouncementDequeuedEvent extends BaseAnnouncementEvent {
  type: 'announcement_dequeued'
  reason: 'shown' | 'expired' | 'removed'
}

type AnnouncementEvent =
  | AnnouncementRegisteredEvent
  | AnnouncementShownEvent
  | AnnouncementDismissedEvent
  | AnnouncementCompletedEvent
  | AnnouncementActionClickedEvent
  | AnnouncementQueuedEvent
  | AnnouncementDequeuedEvent

Discriminate on event.type — TypeScript narrows the union to the per-event payload.

Cross-Cutting

TypeNotes
UILibrary'radix-ui' | 'base-ui' — see Unified Slot
RenderPropGeneric render-prop callback shape — see Unified Slot