# @sendsay-ru/guidely-react

React bindings for `@sendsay-ru/guidely`.

The package provides `GuidelyProvider` and `useGuidely`. It is SSR-safe: importing the package
does not touch the DOM, and the core Guidely instance is created only after the provider mounts.

## Install

```bash
npm install @sendsay-ru/guidely @sendsay-ru/guidely-react
```

```bash
yarn add @sendsay-ru/guidely @sendsay-ru/guidely-react
```

`@sendsay-ru/guidely` is a peer dependency and provides the core config, engine, and renderer.

## Usage

```tsx
import { GuidelyProvider, useGuidely } from "@sendsay-ru/guidely-react";
import type { TFlowState } from "@sendsay-ru/guidely";

const stateByFlow = new Map<string, TFlowState>();

function App() {
  return (
    <GuidelyProvider
      config={config}
      adapter={{
        getState(flowId, context) {
          // Use context.startsAt if your app gates tours by date.
          return stateByFlow.get(flowId) ?? null;
        },
        setState(flowId, state) {
          stateByFlow.set(flowId, state);
        },
      }}
    >
      <StartTourButton />
    </GuidelyProvider>
  );
}

function StartTourButton() {
  const guidely = useGuidely();

  return (
    <button type="button" onClick={() => void guidely.start("welcome-tour")}>
      Start tour
    </button>
  );
}
```

See the `@sendsay-ru/guidely` README for the full config shape.

## Provider Props

`GuidelyProvider` accepts the same options as `createGuidely`, plus `children` and an optional
`enabled` flag:

```tsx
<GuidelyProvider
  config={config}
  adapter={adapter}
  onEvent={handleEvent}
  locale="en"
  labels={labels}
  theme={theme}
  container={container}
  autoPromote={canShowAutomaticPrompts}
  path={pathname}
>
  <App />
</GuidelyProvider>
```

The provider creates the core Guidely instance after mount and destroys it on unmount. If `config`,
`adapter`, `onEvent`, `labels`, `theme`, `container`, `targetResolver`, or `autoPromote` changes
identity, the provider recreates the core instance with the new options.

`locale` and `path` change while the application runs, so the provider passes their new values to
`setLocale` and `setPath` instead: a running tour keeps going in the new language or on the new
page.

`path` is the page the user is on, and flows with a route trigger start on matching pages. With
react-router, take it from `useLocation()`:

```tsx
import type { ReactNode } from "react";
import { useLocation } from "react-router-dom";

function TourProvider({ children }: { children: ReactNode }) {
  const { pathname } = useLocation();

  return (
    <GuidelyProvider config={config} adapter={adapter} path={pathname}>
      {children}
    </GuidelyProvider>
  );
}
```

Use `enabled={false}` while the host loads an adapter. The provider and its children stay mounted,
but no core runtime is created. In that state `useGuidely().isReady` is `false`, `start` resolves to
`{ started: false, reason: "not_ready" }`, and other commands are no-ops. This makes it safe to
keep `GuidelyProvider` as a stable application wrapper:

```tsx
<GuidelyProvider config={config} adapter={adapter} enabled={Boolean(adapter)}>
  <App />
</GuidelyProvider>
```

`autoPromote={false}` is different from `enabled={false}`: the runtime remains ready and manual
`start()` calls keep working, but target-triggered prompts, immediate flows, and route flows do not
start automatically. This is useful for support users, embedded contexts, and feature flags.

## Hook Result

`useGuidely()` returns:

```ts
{
  activeFlow,
  promptedFlow,
  isReady,
  snapshot,
  start,
  next,
  prev,
  skip,
  complete,
  goTo,
  stop,
  setLocale,
}
```

`activeFlow` is either `null` or `{ flowId, stepId, index }`. `promptedFlow` is either `null` or
`{ flowId, stepId }` and represents the single discovery beacon that has not started a flow yet.
`snapshot` also includes both values, the active locale, runtime status, and `isDestroyed` flag.

The hook subscribes with `use-sync-external-store/shim`, so it works with React 17 and React 18.
Its command functions have stable identities while the same provider is mounted.

Use `useGuidelyEvent` when a component needs runtime events without routing them through the
provider props:

```tsx
import { useGuidelyEvent } from "@sendsay-ru/guidely-react";

function TourAnalytics() {
  useGuidelyEvent((event) => {
    if (event.type === "flow:complete") {
      // Send analytics or start the next product-specific action.
    }
  });

  return null;
}
```

For contextual automatic discovery, declare a target trigger in config. No host effect or queue is
needed:

```ts
const config = {
  version: 1,
  flows: [
    {
      id: "new-toolbar",
      trigger: { type: "target", priority: 10 },
      steps: [
        {
          id: "toolbar",
          type: "hotspot",
          target: { type: "data-id", value: "toolbar", scope: "document" },
          content: { en: { body: "Try the new toolbar." } },
        },
      ],
    },
  ],
};
```

For tours about a page, declare a route trigger and pass the current path to the provider:

```ts
trigger: { type: "route", path: "/campaigns/:id" }
```

See the core README for prompt lease, cooldown, availability cycles, ordering, session limits,
route path patterns, and lifecycle details.
