{"version":3,"file":"react.mjs","names":["portals: ReactNode[]","portals"],"sources":["../src/react.tsx"],"sourcesContent":["/** @jsxImportSource @opentui/react */\n\nimport {\n  BoxRenderable,\n  type KeyEvent,\n  type RenderContext,\n} from \"@opentui/core\";\nimport {\n  createPortal,\n  useKeyboard,\n  useRenderer,\n  useTerminalDimensions,\n} from \"@opentui/react\";\nimport {\n  createContext,\n  type ReactNode,\n  useCallback,\n  useContext,\n  useEffect,\n  useLayoutEffect,\n  useMemo,\n  useState,\n  useSyncExternalStore,\n} from \"react\";\nimport { JSX_CONTENT_KEY } from \"./constants\";\nimport { DialogManager } from \"./manager\";\nimport type {\n  AlertContext,\n  ChoiceContext,\n  ConfirmContext,\n  DialogState,\n  PromptContext,\n} from \"./prompts\";\nimport { DialogContainerRenderable } from \"./renderables\";\nimport type {\n  BaseAlertOptions,\n  BaseChoiceOptions,\n  BaseConfirmOptions,\n  BaseDialogActions,\n  BasePromptOptions,\n  DialogContainerOptions,\n  DialogId,\n  DialogShowOptions,\n  InternalDialog,\n  InternalDialogShowOptions,\n} from \"./types\";\n\ninterface DialogWithJsx extends InternalDialog {\n  [JSX_CONTENT_KEY]?: ReactNode;\n}\n\n/** Internal type for show options that include JSX bridging keys */\ninterface DialogShowOptionsWithJsx extends InternalDialogShowOptions {\n  [JSX_CONTENT_KEY]?: ReactNode;\n}\n\n/**\n * Content factory that returns React elements.\n * Must be a function to ensure consistent behavior with Solid adapter\n * and prevent timing issues with JSX evaluation.\n */\nexport type ContentFactory = () => ReactNode;\n\nexport interface ShowOptions extends Omit<DialogShowOptions, \"content\"> {\n  /** Must be a function returning JSX: `() => <MyDialog />` */\n  content: ContentFactory;\n}\n\n// ============================================================================\n// React Prompt Types\n// ============================================================================\n// These extend the generic base types with React-specific content signatures.\n\n/** Content factory for prompt dialogs. */\ntype PromptContent<T> = (ctx: PromptContext<T>) => ReactNode;\n\n/** Content factory for confirm dialogs. */\ntype ConfirmContent = (ctx: ConfirmContext) => ReactNode;\n\n/** Content factory for alert dialogs. */\ntype AlertContent = (ctx: AlertContext) => ReactNode;\n\n/** Content factory for choice dialogs. */\ntype ChoiceContent<K> = (ctx: ChoiceContext<K>) => ReactNode;\n\n/**\n * Options for a generic prompt dialog.\n * @template T The type of value the prompt resolves to.\n */\nexport interface PromptOptions<T>\n  extends BasePromptOptions<T, PromptContent<T>> {}\n\n/**\n * Options for a confirm dialog.\n */\nexport interface ConfirmOptions extends BaseConfirmOptions<ConfirmContent> {}\n\n/**\n * Options for an alert dialog.\n */\nexport interface AlertOptions extends BaseAlertOptions<AlertContent> {}\n\n/**\n * Options for a choice dialog.\n * @template K The type of keys for the available choices.\n */\nexport interface ChoiceOptions<K>\n  extends BaseChoiceOptions<ChoiceContent<K>, K> {}\n\n/**\n * Dialog actions for showing, closing, and managing dialogs.\n * Extends BaseDialogActions with async prompt methods.\n */\nexport interface DialogActions extends BaseDialogActions<ShowOptions> {\n  /** Show a generic prompt dialog and wait for a response. */\n  prompt: <T>(options: PromptOptions<T>) => Promise<T | undefined>;\n  /** Show a confirmation dialog and wait for the user to confirm or cancel. */\n  confirm: (options: ConfirmOptions) => Promise<boolean>;\n  /** Show an alert dialog and wait for the user to dismiss it. */\n  alert: (options: AlertOptions) => Promise<void>;\n  /** Show a choice dialog and wait for the user to select an option. */\n  choice: <K>(options: ChoiceOptions<K>) => Promise<K | undefined>;\n}\n\nconst DialogContext = createContext<DialogManager | null>(null);\n\nconst createPlaceholderContent = () => (ctx: RenderContext) =>\n  new BoxRenderable(ctx, { id: \"~jsx-placeholder\" });\n\n/**\n * Helper to build dialog show options for React adapter.\n * Handles both direct show/replace calls and async prompt methods.\n *\n * @param content - () => ReactNode or (ctx) => ReactNode\n * @param rest - Dialog options excluding content\n * @param ctx - Optional context for async prompts (prompt, confirm, alert, choice)\n */\nfunction buildShowOptions(\n  content: ContentFactory,\n  rest: Omit<DialogShowOptions, \"content\">,\n): DialogShowOptionsWithJsx;\nfunction buildShowOptions<TCtx>(\n  content: (ctx: TCtx) => ReactNode,\n  rest: Omit<DialogShowOptions, \"content\">,\n  ctx: TCtx,\n): DialogShowOptionsWithJsx;\nfunction buildShowOptions(\n  content: (...args: unknown[]) => unknown,\n  rest: Omit<DialogShowOptions, \"content\">,\n  ctx?: unknown,\n): DialogShowOptionsWithJsx {\n  const resolvedContent = ctx !== undefined ? content(ctx) : content();\n\n  return {\n    ...rest,\n    content: createPlaceholderContent(),\n    [JSX_CONTENT_KEY]: resolvedContent,\n    deferred: true,\n  } as DialogShowOptionsWithJsx;\n}\n\nfunction useDialogManager(): DialogManager {\n  const manager = useContext(DialogContext);\n\n  if (!manager) {\n    throw new Error(\n      \"useDialog/useDialogState must be used within a DialogProvider.\\n\\n\" +\n        \"Wrap your app with <DialogProvider>:\\n\\n\" +\n        \"  import { DialogProvider } from '@opentui-ui/dialog/react';\\n\\n\" +\n        \"  function App() {\\n\" +\n        \"    return (\\n\" +\n        \"      <DialogProvider>\\n\" +\n        \"        <YourContent />\\n\" +\n        \"      </DialogProvider>\\n\" +\n        \"    );\\n\" +\n        \"  }\",\n    );\n  }\n\n  return manager;\n}\n\n/**\n * Access dialog actions within a DialogProvider.\n *\n * For reactive state, use `useDialogState()` instead.\n *\n * @example\n * ```tsx\n * const dialog = useDialog();\n *\n * // Show a dialog (content must be a function)\n * dialog.show({ content: () => <text>Hello</text> });\n *\n * // Close the top dialog\n * dialog.close();\n *\n * // Close a specific dialog\n * dialog.close(dialogId);\n *\n * // Close all dialogs\n * dialog.closeAll();\n * ```\n */\nexport function useDialog(): DialogActions {\n  const manager = useDialogManager();\n\n  return useMemo<DialogActions>(\n    () => ({\n      show: (options: ShowOptions) => {\n        const { content, ...rest } = options;\n        return manager.show(buildShowOptions(content, rest));\n      },\n\n      close: (id?: DialogId) => manager.close(id),\n\n      closeAll: () => manager.closeAll(),\n\n      replace: (options: ShowOptions) => {\n        const { content, ...rest } = options;\n        return manager.replace(buildShowOptions(content, rest));\n      },\n\n      // =====================================================================\n      // Async Prompt Methods (delegate to manager with factory pattern)\n      // =====================================================================\n\n      prompt: <T,>(options: PromptOptions<T>): Promise<T | undefined> => {\n        const { content, fallback, ...rest } = options;\n        return manager.prompt<T>((ctx) => ({\n          ...buildShowOptions(content, rest, ctx),\n          fallback,\n        }));\n      },\n\n      confirm: (options: ConfirmOptions): Promise<boolean> => {\n        const { content, fallback, ...rest } = options;\n        return manager.confirm((ctx) => ({\n          ...buildShowOptions(content, rest, ctx),\n          fallback,\n        }));\n      },\n\n      alert: (options: AlertOptions): Promise<void> => {\n        const { content, ...rest } = options;\n        return manager.alert((ctx) => buildShowOptions(content, rest, ctx));\n      },\n\n      choice: <K,>(options: ChoiceOptions<K>): Promise<K | undefined> => {\n        const { content, fallback, ...rest } = options;\n        return manager.choice<K>((ctx) => ({\n          ...buildShowOptions(content, rest, ctx),\n          fallback,\n        }));\n      },\n    }),\n    [manager],\n  );\n}\n\n/**\n * Subscribe to reactive dialog state with a selector.\n *\n * Only re-renders when the selected value changes (using reference equality).\n *\n * @example\n * ```tsx\n * // Subscribe to specific state\n * const isOpen = useDialogState(s => s.isOpen);\n * const count = useDialogState(s => s.count);\n * const topDialog = useDialogState(s => s.topDialog);\n * const dialogs = useDialogState(s => s.dialogs);\n *\n * // Use in your component\n * if (isOpen) {\n *   console.log(`${count} dialog(s) open`);\n * }\n * ```\n */\nexport function useDialogState<T>(selector: (state: DialogState) => T): T {\n  const manager = useDialogManager();\n\n  const subscribe = useMemo(\n    () => (onStoreChange: () => void) => manager.subscribe(onStoreChange),\n    [manager],\n  );\n\n  const getSnapshot = useCallback(() => {\n    const dialogs = manager.getDialogs();\n    const state: DialogState = {\n      isOpen: dialogs.length > 0,\n      dialogs,\n      topDialog: dialogs.length > 0 ? dialogs[dialogs.length - 1] : undefined,\n      count: dialogs.length,\n    };\n    return selector(state);\n  }, [manager, selector]);\n\n  return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * A keyboard hook for dialog content that only fires when the dialog is topmost.\n *\n * This prevents keyboard events from affecting stacked dialogs that are not focused.\n * Use this instead of `useKeyboard` inside dialog content components.\n *\n * @param handler - Keyboard event handler (only called when dialog is topmost)\n * @param dialogId - The dialog's ID from context (e.g., `ctx.dialogId`)\n *\n * @example\n * ```tsx\n * function DeleteConfirmDialog({ resolve, dialogId }: ConfirmContext) {\n *   useDialogKeyboard((key) => {\n *     if (key.name === \"return\") resolve(true);\n *     if (key.name === \"escape\") resolve(false);\n *   }, dialogId);\n *\n *   return <text>Press Enter to confirm</text>;\n * }\n * ```\n */\nexport function useDialogKeyboard(\n  handler: (key: KeyEvent) => void | Promise<void>,\n  dialogId: DialogId,\n): void {\n  const isTopmost = useDialogState((s) => s.topDialog?.id === dialogId);\n\n  useKeyboard((key) => {\n    if (isTopmost) {\n      handler(key);\n    }\n  });\n}\n\nexport interface DialogProviderProps extends DialogContainerOptions {\n  children: ReactNode;\n}\n\n/**\n * Provides dialog functionality to children via useDialog() and useDialogState() hooks.\n *\n * @example\n * ```tsx\n * <DialogProvider size=\"medium\">\n *   <App />\n * </DialogProvider>\n * ```\n */\nexport function DialogProvider(props: DialogProviderProps) {\n  const { children, ...containerOptions } = props;\n\n  const renderer = useRenderer();\n  const dimensions = useTerminalDimensions();\n\n  const [manager] = useState(() => new DialogManager(renderer));\n\n  const [container] = useState(\n    () =>\n      new DialogContainerRenderable(renderer, {\n        manager,\n        ...containerOptions,\n      }),\n  );\n\n  const dialogs = useSyncExternalStore(\n    (onStoreChange) => manager.subscribe(onStoreChange),\n    () => manager.getDialogs(),\n    () => manager.getDialogs(),\n  );\n\n  useEffect(() => {\n    renderer.root.add(container);\n\n    return () => {\n      container.destroyRecursively();\n      renderer.root.remove(container.id);\n      manager.destroy();\n    };\n  }, [container, manager, renderer]);\n\n  useEffect(() => {\n    container.updateDimensions(dimensions.width);\n  }, [container, dimensions.width]);\n\n  const portals = useMemo(() => {\n    if (!dialogs || dialogs.length === 0) {\n      return [];\n    }\n\n    const portals: ReactNode[] = [];\n\n    for (const [id, dialogRenderable] of container.getDialogRenderables()) {\n      const dialogWithJsx = dialogRenderable.dialog as DialogWithJsx;\n      const jsxContent = dialogWithJsx[JSX_CONTENT_KEY];\n\n      if (jsxContent !== undefined) {\n        portals.push(createPortal(jsxContent, dialogRenderable, id));\n      }\n    }\n\n    return portals;\n  }, [container, dialogs]);\n\n  // Set dialog visibility\n  // requestAnimationFrame is polyfilled by @opentui/core\n  useLayoutEffect(() => {\n    // dialogs triggers re-run when dialog state changes\n    void dialogs;\n\n    const raf = (globalThis as Record<string, unknown>)\n      .requestAnimationFrame as (callback: () => void) => number;\n\n    for (const [, dialogRenderable] of container.getDialogRenderables()) {\n      raf(() => {\n        dialogRenderable.visible = true;\n      });\n    }\n  }, [container, dialogs]);\n\n  return (\n    <DialogContext.Provider value={manager}>\n      {children}\n      {portals}\n    </DialogContext.Provider>\n  );\n}\n\n// =============================================================================\n// Re-exports for convenience\n// =============================================================================\n\nexport type {\n  AlertContext,\n  ChoiceContext,\n  ConfirmContext,\n  DialogState,\n  PromptContext,\n} from \"./prompts\";\nexport { type DialogTheme, themes } from \"./themes\";\nexport type {\n  DialogContainerOptions,\n  DialogId,\n  DialogSize,\n  DialogStyle,\n} from \"./types\";\n"],"mappings":"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"}