# Eko Gallery React

A library for integrating the [eko](https://eko.com) gallery into your react based site.

## Installation

```bash
npm install @ekolabs/eko-gallery-react
```

OR

```bash
yarn add @ekolabs/eko-gallery-react
```

## Usage

### EkoGallery Component

The `EkoGallery` component is used to render the eko gallery on your site. It should replace your existing product gallery component.

```jsx
import { EkoGallery } from '@ekolabs/eko-gallery-react';

<EkoGallery
    className="mb-4"
    config={config}
    variantId={variantId}
    activeItem={activeItem}
    ref={galleryRef}
    onEvent={onEkoGalleryEvent}
>
    {/* Your existing product gallery component that will serve as a fallback */}
    <CustomerGalley />
</EkoGallery>
```

This component accepts the following props:

| Prop       | Type     | Description                                                                                                                                     |
|------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------|
| className  | String   | _Optional._ class names to add to the `EkoGallery` component, for styling purposes for example.                                                 |
| config     | Object   | The eko gallery configuration. This object will be published via the eko platform and exposed via an api endpoint (see below).                  |
| variantId  | String   | _Optional._ The selected variant (if applicable). It is used to switch to the relevant variant gallery assets when the product variant changes. |
| activeItem | Object   | _Optional._ Updates the displayed item and step of the EkoGallery.                                                                              |
| ref        | Object   | _Optional._ Exposes the next() and prev() functions that allow navigation between the steps                                                     |
| onEvent    | Function | _Optional._ Handler for events occurred in the eko gallery, for third party tools tracking for example.                                         |

> When passing the `onEvent` prop, make sure to use the `useCallback()` hook to avoid unnecessary re-renders.

### Enabling SSR

To enable SSR of the EkoGallery you'll need to add the `ssrHtml` prop to `<EkoGallery>`.

To get `ssrHtml` on the server, import `getSsrHtml` from `@ekolabs/eko-gallery-react/server` and pass it to `<EkoGallery>`.

```jsx
// Next.js example
import { EkoGallery } from '@ekolabs/eko-gallery-react';

const EKO_SALES_CHANNEL_ID = process.env.EKO_SALES_CHANNEL_ID;

export async function getStaticProps({ params: { id } }) {
    const { getSsrHtml } = await import('@ekolabs/eko-gallery-react/server');

    const ekoProductConfigUrl = getEkoProductConfigUrl(EKO_SALES_CHANNEL_ID);
    const ekoProductResponse = await fetch(ekoProductConfigUrl).then(res => res.json());
    const ekoProductConfigs = ekoProductResponse.data;
    const config = ekoProductConfigs[id];
    const { ssrHtml } = await getSsrHtml(config);
    return {
        props: {
            ekoGalleryProps: {
                config,
                ssrHtml
            }
        }
    };
}

export default function ProductPage({ ekoGalleryProps }) {
    return (
        <EkoGallery
            {...ekoGalleryProps}
            // rest of EkoGallery props...
        >
    );
}
```

The dynamic import in `getStaticProps` ensures that `@ekolabs/eko-gallery-react/server` doesn't get bundled into your next.js client's code.

#### getSsrHtml(config, isForMobile)

| Arg         | Type    | Description                                                                                                         |
|-------------|---------|---------------------------------------------------------------------------------------------------------------------|
| config      | Object  | The config for the product as recieved from eko                                                                     |
| isForMobile | Boolean | If you know on the server that the client is a mobile device pass `true` so the resulting layout will be for mobile |


### Switching the gallery's active item externally example

First, initialize a variable using React's `useRef()` hook.
Then, pass it to the `ref` property of the `EkoGallery` component.
This enables to call two functions:
1. `next()` - Will cycle to the next step of an active item. When called on the last step of an active item - the gallery will cycle to the first step of the next index.
2. `prev()` - Will cycle to the previous step of an active item. When called on the first step of an active item - the gallery will cycle to last step of the previous index.


```js
    import { useRef } from 'react';

    const galleryRef = useRef(null);

    // Call the gallery's next() method to cycle to the next step.
    function onNextClick() {
        galleryRef.current.next();
    }

    // Call the gallery's prev() method to cycle to the previous step.
    function onPrevClick() {
        galleryRef.current.prev();
    }
```


### Config data fetching

To get the eko product configs, you should use the `getEkoProductConfigUrl()` function to get the url, fetch the data and pass it to the `EkoGallery` component.

The `getEkoProductConfigUrl()` receives the eko sales channel id and returns the url to fetch the eko product configs from.

```js
import { getEkoProductConfigUrl } from '@ekolabs/eko-gallery-react';

const EKO_SALES_CHANNEL_ID = process.env.EKO_SALES_CHANNEL_ID;

const ekoProductConfigUrl = getEkoProductConfigUrl(EKO_SALES_CHANNEL_ID);
const ekoProductResponse = await fetch(ekoProductConfigUrl).then(res => res.json());
const ekoProductConfigs = ekoProductResponse.data;
```

### Analytics setup

Our 1st party tracking tool should be added in order to track events on the site.

#### The eko analytics snippet

The eko analytics snippet should be added to the site's head tag, here is an example in `Next.js`:

```jsx
// _document.tsx

import Script from 'next/script';
import { getEkoAnalyticsSnippet } from '@ekolabs/eko-gallery-react';

const IS_PRODUCTION = process.env.NODE_ENV === 'production';

render() {
    return (
        <Html lang="en">
            <Head>
                <Script id="eko-analytics-snippet" strategy="beforeInteractive">
                    {getEkoAnalyticsSnippet(IS_PRODUCTION)}
                </Script>
            </Head>
            <body>
                <Main />
                <NextScript />
            </body>
        </Html>
    );
}
```
### Events

The eko gallery dispatches events through the `onEvent` handler. Use this to track user interactions and gallery lifecycle events.

#### Event Types

| Event               | Description                                                       |
|---------------------|-------------------------------------------------------------------|
| `galleryinit`       | Fired when the gallery component initializes                      |
| `galleryloaded`     | Fired when the interactive smart gallery is fully loaded          |
| `activeitemchanged` | Fired when the active gallery item changes                        |
| `click`             | Fired when the user click on a non-interactive part of the video  |
| `swipe`             | Fired when the user swipes within the interactive video           |
| `interaction`       | Fired on any user interaction (click, swipe, etc.) with interaction metadata |

#### Event Payloads

##### `activeitemchanged` event

Fired when the displayed gallery item changes (via thumbnail click, carousel scroll, or internal navigation).

| Property | Type | Description |
| -------- | ---- | ----------- |
| `index` | Number | The zero-based index of the newly active item |
| `step` | Number | The zero-based index of the newly active item's step/sub-item |

##### `click` event

| Property      | Type   | Description                |
|---------------|--------|----------------------------|
| `x`           | Number | x value of the click event |
| `y`           | Number | y value of the click event |

##### `swipe` event

Fired when the user swipes within the interactive video.

##### `interaction` event

Fired on any user interaction, providing structured metadata about the interaction.

| Property          | Type   | Description                                                        |
|-------------------|--------|--------------------------------------------------------------------|
| `elementid`       | String | Identifier of the interacted element                               |
| `elementtype`     | String | Type of the interacted element                                     |
| `elementname`     | String | Name of the interacted element                                     |
| `interactiontype` | String | Type of interaction (e.g. `swipe_next`, `swipe_prev`)              |

##### Lifecycle events (`galleryinit`, `gallerycoverdisplayed`, `galleryloaded`)

These events are fired without a payload and indicate gallery state transitions.

#### Example

```jsx
const onEkoGalleryEvent = useCallback((event, data) => {
    switch (event) {
        case 'galleryinit':
            console.log('Gallery initialized');
            break;
        case 'galleryloaded':
            console.log('Interactive gallery ready');
            break;
        case 'activeitemchanged':
            console.log(`Active item changed to: ${data.index} ${data.step}`);
            break;
        case 'click':
            console.log(`Click: ${data}`);
            break;
        case 'swipe':
            console.log('User swiped in interactive video', data);
            break;
        case 'interaction':
            console.log(`Interaction: ${data.interactiontype} on ${data.elementid}`);
            break;
    }
}, []);
```

#### ekoWebPixel

The `ekoWebPixel` API is used to track the events on your site.

Init once via the `init()` method:

```jsx
import { ekoWebPixel } from '@ekolabs/eko-gallery-react';

const IS_PRODUCTION = process.env.NODE_ENV === 'production';

useEffect(() => {
    ekoWebPixel.init(IS_PRODUCTION);
}, []);
```

Track events via the `track()` method:

```jsx
import { ekoWebPixel } from '@ekolabs/eko-gallery-react';

ekoWebPixel.track('pixel.page_viewed');
ekoWebPixel.track('pixel.product_viewed', { ... });
ekoWebPixel.track('pixel.cart.add', { ... });
ekoWebPixel.track('pixel.cart.remove', { ... });

// For traffic allocation:
ekoWebPixel.track('trafficallocation.decision', { ... });

// For non shopify backend stores:
ekoWebPixel.track('pixel.checkout', { ... });
ekoWebPixel.track('pixel.order', { ... });
```

Report on route changes:

```jsx
import { ekoWebPixel } from '@ekolabs/eko-gallery-react';

ekoWebPixel.onRouteChanged();
```
