npm package: @slidy/react
Description: Simple, configurable & reusable carousel component built with ReactJS
Author: Artemiy Schukin
Latest version: 1.2.1

<div align="center">
    <a href="https://www.npmjs.com/package/@slidy/react">
        <img alt="npm package version" src="https://img.shields.io/npm/v/@slidy/react?style=flat-square" />
    </a>
    <a href="https://www.npmjs.com/package/@slidy/react">
        <img alt="types included" src="https://img.shields.io/npm/types/@slidy/react?style=flat-square" />
    </a>
    <a href="https://www.npmjs.com/package/@slidy/react">
        <img alt="downloads count" src="https://img.shields.io/npm/dm/@slidy/react?style=flat-square" />
    </a>
    <a href="https://www.npmjs.com/package/@slidy/react">
        <img alt="licence" src="https://img.shields.io/npm/l/@slidy/react?style=flat-square" />
    </a>
    <a href="https://bundlephobia.com/package/@slidy/react">
        <img alt="minzipped size" src="https://img.shields.io/bundlephobia/minzip/@slidy/react?label=minzip&style=flat-square" />
    </a>
</div>

# @slidy/react

Simple, configurable & reusable carousel component built with React based on [@slidy/core](https://github.com/Valexr/slidy/tree/master/packages/core).

Try the [demo](https://codesandbox.io/s/slidy-react-demo-64wllu?file=/src/App.tsx).

## Getting started

The package is available via [npm](https://www.npmjs.com/package/@slidy/react):

```
npm i @slidy/react
```

## Usage

The most simple way to get started is to use named import of `<Slidy />` component:

```jsx
import { Slidy } from '@slidy/react';
import '@slidy/react/dist/slidy.css';

const slides = [
    {
        id: 1,
        width: 800,
        height: 1200,
        src: 'static/img/some-image.webp',
    },
];

export default () => {
    return <Slidy slides={slides} />;
};
```

All props are optional. The only property to get started is `slides` - an array of objects with image related data.

> **You will also have to import css styles:**

```jsx
import '@slidy/react/dist/slidy.css';
```

## Core Component

`Core` is a wrapper component for [@slidy/core](https://github.com/Valexr/slidy/tree/master/packages/core) available via named import. It is best to use to build up the custom component for specific needs or when just the basic functionality is needed.

```tsx
import { Core } from '@slidy/react';

export default () => {
    return <Core />;
};
```

### Core Component API

| Property    |   Default   |                                Type                                 | Description                                                           |
| :---------- | :---------: | :-----------------------------------------------------------------: | :-------------------------------------------------------------------- |
| `animation` | `undefined` |                          `AnimationFunc `                           | Custom slide animation.                                               |
| `axis`      |    `"x"`    |                           `"x"` or `"y"`                            | The scroll direction.                                                 |
| `clamp`     |     `0`     |                              `number`                               | Clamps sliding index as `{clamp} - {index} + {clamp}`                 |
| `className` |    `""`     |                              `string`                               | Passes the `class` to the node.                                       |
| `duration`  |    `450`    |                              `number`                               | Slide transitions duration value.                                     |
| `easing`    | `undefined` |                       `(t: number => number)`                       | Inertion scroll easing behaviour.                                     |
| `gravity`   |    `1.2`    |                              `number`                               | Scroll inertia value.                                                 |
| `indent`    |     `0`     |                              `number`                               | Custom scroll indent value, calculates as `gap * indent`.             |
| `index`     |     `0`     |                              `number`                               | The index of the initial slide.                                       |
| `setIndex`  | `undefined` |           `React.Dispatch<React.SetStateAction<number>>`            | Set's index of a current slide.                                       |
| `plugins`   | `undefined` |                     `ReturnType<PluginFunc>[]`                      | Plugins to operate with the slidy instance and its options directly.  |
| `loop`      |   `false`   |                              `boolean`                              | Makes the slideshow continious.                                       |
| `sensity`   |     `5`     |                              `number`                               | Defines the sliding sensity as the number of pixels required to drag. |
| `snap`      | `undefined` |           `"start"` or `"center"` or `"end"` or `"deck"`            | Enforces the scroll stop positions.                                   |
| `tag`       |    `ol`     |                              `string`                               | The HTML tag name to render.                                          |
| `onResize`  | `undefined` |   `(event: CustomEvent<{ ROE: ResizeObserverEntry[] }>) => void`    | Listen to the core event `resize` to fire.                            |
| `onMount`   | `undefined` |               `(event: CustomEvent<Options>) => void`               | Listen to the core event `mount` to fire.                             |
| `onMove`    | `undefined` | `(event: CustomEvent<{ index: number; position: number }>) => void` | Listen to the core event `move` to fire.                              |
| `onIndex`   | `undefined` |          `(event: CustomEvent<{ index: number }>) => void`          | Listen to the core event `index` to fire.                             |
| `onKeys`    | `undefined` |               `(event: CustomEvent<string>) => void`                | Listen to the core event `keys` to fire.                              |
| `onUpdate`  | `undefined` |               `(event: CustomEvent<Options>) => void`               | Listen to the core event `update` to fire.                            |
| `onDestroy` | `undefined` |             `(event: CustomEvent<HTMLElement>) => void`             | Listen to the core event `destroy` to fire.                           |

For TypeScript users there is the `SlidyCoreOptions` interface available via named import.

## Slidy Component

`<Slidy />` component uses `<Core />` internally and provides more features expected from carousel.

### Slidy Component API

The `<Slidy />` component interface extends the `<Core />`. There are a list of additional options available:

| Property      |      Default       |                                Type                                 | Description                                                              |
| :------------ | :----------------: | :-----------------------------------------------------------------: | :----------------------------------------------------------------------- |
| `arrows`      |       `true`       |                   `boolean` or `() => JSXElement`                   | Renders the arrow button controls for accessible slide navigation.       |
| `arrow`       |    `undefined`     |                         `() => JSXElement`                          | Renders the arrow.                                                       |
| `children`    |    `undefined`     |                    `(item: Slide) => JSXElement`                    | Renders each slide.                                                      |
| `overlay`     |    `undefined`     |                         `() => JSXElement`                          | Renders the overlay.                                                     |
| `background`  |      `false`       |                              `boolean`                              | Sets `background-image` instead of `<img />` elements to display slides. |
| `classNames`  |   `SlidyStyles`    |                        `SlidyStylesDefault`                         | The class names object used over the component.                          |
| `getImgSrc`   | `item => item.src` |                             `function`                              | The slide's `src` attribute getter.                                      |
| `getThumbSrc` | `item => item.src` |                             `function`                              | The thumbnail's `src` attribute getter.                                  |
| `i18n`        |   `i18nDefaults`   |                             `I18NDict`                              | The i18n localization dictionary.                                        |
| `navigation`  |      `false`       |                              `boolean`                              | Renders the navigation controls for pagination-like slide navigation.    |
| `groups`      |        `0`         |                              `number`                               | Controls the number of items displayed per viewport.                     |
| `progress`    |      `false`       |                              `boolean`                              | Renders the progress bar.                                                |
| `slides`      |        `[]`        |                             `Slides[]`                              | An array of objects with image metadata.                                 |
| `thumbnail`   |      `false`       |                   `boolean` or `() => JSXElement`                   | Renders the thumbnail navigation panel.                                  |
| `onResize`    |    `undefined`     |   `(event: CustomEvent<{ ROE: ResizeObserverEntry[] }>) => void`    | Listen to the core event `resize` to fire.                               |
| `onMount`     |    `undefined`     |               `(event: CustomEvent<Options>) => void`               | Listen to the core event `mount` to fire.                                |
| `onMove`      |    `undefined`     | `(event: CustomEvent<{ index: number; position: number }>) => void` | Listen to the core event `move` to fire.                                 |
| `onIndex`     |    `undefined`     |          `(event: CustomEvent<{ index: number }>) => void`          | Listen to the core event `index` to fire.                                |
| `onKeys`      |    `undefined`     |               `(event: CustomEvent<string>) => void`                | Listen to the core event `keys` to fire.                                 |
| `onUpdate`    |    `undefined`     |               `(event: CustomEvent<Options>) => void`               | Listen to the core event `update` to fire.                               |
| `onDestroy`   |    `undefined`     |             `(event: CustomEvent<HTMLElement>) => void`             | Listen to the core event `destroy` to fire.                              |
| `plugins`     |    `undefined`     |                     `ReturnType<PluginFunc>[]`                      | Plugins to operate with the slidy instance and its options directly.     |

By default component works with images. Image object should contain `width` and `height` attributes to prevent layout shifts and `alt` for accessibility.

## Styling

### Extending/Overriding classes

To extend default component styles use `classNames` property. Default classes are available via object, that can be extended or overridden:

```jsx
import { Slidy, classNames } from '@slidy/react';
import '@slidy/react/dist/slidy.css';

export default () => {
    return (
        <Slidy
            classNames={{
                root: `${classNames.root} custom-class`,
                ...classNames,
            }}
        />
    );
};
```

The `classNames` consist of `{ target: className }` pairs:

| Target    |   Default class    | Description             |
| :-------- | :----------------: | :---------------------- |
| arrow     |   `slidy-arrow`    | Arrow controls.         |
| autoplay  |  `slidy-autoplay`  | Autoplay control.       |
| counter   |  `slidy-counter`   | Slide progress counter. |
| img       |    `slidy-img`     | Slide image node.       |
| nav       |    `slidy-nav`     | Slide navigation panel. |
| nav-item  |  `slidy-nav-item`  | Navigtion panel item.   |
| overlay   |  `slidy-overlay`   | Slides overlay node.    |
| progress  |  `slidy-progress`  | Slide progress bar.     |
| root      |      `slidy`       | Component's root node.  |
| slide     |   `slidy-slide`    | Slide item node.        |
| slides    |   `slidy-slides`   | Slides list node.       |
| thumbnail | `slidy-thumbnail`  | Thumbnail item.         |
| thumbnail | `slidy-thumbnails` | Thumbnails bar.         |

### Custom Properties API

For easier style customization `Slidy` provides a set of predefined custom properties to inherit:

List of available public custom properties:

| Property                            |   Default    |    Type     | Description                                          |
| :---------------------------------- | :----------: | :---------: | :--------------------------------------------------- |
| `--slidy-arrow-bg`                  |  #4e4e4ebf   |  `<color>`  | The arrow control background color.                  |
| `--slidy-arrow-bg-hover`            |  #4e4e4e54   |  `<color>`  | The arrow control hover background color.            |
| `--slidy-arrow-icon-color`          | currentColor |  `<color>`  | The arrow control icon fill color.                   |
| `--slidy-arrow-size`                |     24px     | `<length>`  | The arrow controls size.                             |
| `--slidy-autoplay-control-size`     |    2.25em    | `<length>`  | The autoplay control size.                           |
| `--slidy-autoplay-indicator-accent` |  lightpink   |   <color>   | The autoplay control indicator ring color.           |
| `--slidy-counter-bg`                |  #4e4e4ebf   |  `<color>`  | The counter's background color.                      |
| `--slidy-focus-ring-color`          |  #c9c9c9e6   |  `<color>`  | Focus ring color for all focusable elements.         |
| `--slidy-height`                    |     100%     | `<length>`  | The height of the component's node.                  |
| `--slidy-nav-item-color`            |    white     |  `<color>`  | The navigation elements color.                       |
| `--slidy-nav-item-radius`           |     50%      | `<length>`  | The navigation elements border radius.               |
| `--slidy-nav-item-size`             |     16px     | `<length>`  | The navigation elements size.                        |
| `--slidy-progress-thumb-color`      |   #c44f61    |  `<color>`  | The progress bar active track color.                 |
| `--slidy-progress-track-color`      |  #96969680   |  `<color>`  | The progress bar track color.                        |
| `--slidy-progress-track-size`       |     5px      | `<length>`  | The progress bar height.                             |
| `--slidy-slide-aspect-ratio`        |    unset     | `<int/int>` | Defines the slide aspect-ratio.                      |
| `--slidy-slide-bg-color`            |   darkgray   |  `<color>`  | The placeholder background color for loading images. |
| `--slidy-slide-gap`                 |     1rem     | `<length>`  | The gap between items in carousel.                   |
| `--slidy-slide-height`              |     100%     | `<length>`  | The carousel items height.                           |
| `--slidy-slide-object-fit`          |    cover     |      -      | The carousel items (images) resize behaviour.        |
| `--slidy-slide-radius`              |     1rem     | `<length>`  | The slide's border radius value.                     |
| `--slidy-slide-width`               |     auto     | `<length>`  | The carousel items width.                            |
| `--slidy-thumbnail-radius`          |    0.5rem    | `<length>`  | The thumbnail `border-radius` value.                 |
| `--slidy-thumbnail-size`            |     50px     | `<length>`  | The thumbnail panel size.                            |
| `--slidy-width`                     |     100%     | `<length>`  | The width of the component's node.                   |

#### Inherited custom properties

All supported custom properties starts with `--slidy-`. For example, to recolor navigation controls, let the component inherit a `--slidy-nav-item-color` custom property from any parent:

```css
.parent {
    --slidy-navigation-color: red;
}
```

```jsx
export default () => {
    return (
        <div class="parent">
            <Slidy />
        </div>
    );
};
```

Or just pass a class with a set of custom properties:

```css
.some-class {
    --slidy-navigation-color: red;
    --slidy-nav-item-size: 1rem;
}
```

```jsx
import { Slidy, classNames } from '@slidy/react';
import '@slidy/react/dist/slidy.css';

export default () => {
    return (
        <Slidy
            classNames={{
                root: `${classNames.root} .some-class`,
                ...classNames,
            }}
        />
    );
};
```

## Slots

### `arrow`

Customizes the content of the default arrow controls.

### `arrows`

Provides a slot for custom arrow buttons.

If the nodes are `<button /> ` and the `data-step` attribute is present, the event listener is not needed. Just provide the values `-1` and `1` for `data-step` on custom buttons.

Also, there are `grid-area` is present in the layout for this custom controls: `prev-slide` and `next-slide` respectively.

```css
button:first-of-type {
    grid-area: prev-slide;
}

button:last-of-type {
    grid-area: next-slide;
}
```

```jsx
export default () => {
    return (
        <Slidy
            arrows={() => (
                <>
                    <button data-step="-1"> Show the previous slide </button>
                    <button data-step="1"> Show the next slide </button>
                </>
            )}
        />
    );
};
```

### `default`

Usually the default markup is not enough. The `default` slot solves this problem. To use custom slide markup slot expose each `slides` prop item `item` argument.

```jsx
export default () => {
    return (
        <Slidy>
            {(item) => (
                <figure>
                    <img src={item.src} alt={item.figcaption} />
                    <figcaption>{item.figcaption}</figcaption>
                </figure>
            )}
        </Slidy>
    );
};
```

### `overlay`

Slot to display content overlaid content. It covers the slides area and can be customized by overriding the `.slidy-overlay`. For example, it is used to display the counter.

```jsx
export default () => {
    return <Slidy overlay={() => <button> Share </button>} />;
};
```

## i18n

To modify all texts used in the component use pass the dictionary as `i18n` prop. For the sake of accessibility, it is recommended translating defaults:

| Key        | Default                         |                          Event detail                          |
| :--------- | :------------------------------ | :------------------------------------------------------------: |
| `carousel` | "carousel"                      |                `aria-label` of a root element.                 |
| `counter`  | "%s of %s"                      | `aria-label` of each slide as {slide number} of {slide length} |
| `first`    | "Go to the first slide"         |       `aria-label` of the first item at the navigation.        |
| `last`     | "Go to the last slide"          |        `aria-label` of the last item at the navigation.        |
| `next`     | "Go to the next slide"          |               `aria-label` of the arrow control.               |
| `play`     | "Start autoplay"                |             `aria-label` of the autoplay control.              |
| `prev`     | "Return back to previous slide" |               `aria-label` of the arrow control.               |
| `slide`    | "Slide"                         |           `aria-roledescription` of each slide item.           |
| `slideN`   | "Go to the slide %s"            |         `aria-label` of pagination of each slide item.         |
| `stop`     | "Stop autoplay"                 |             `aria-label` of the autoplay control.              |

## Recipes

### External controls

It is possible to control the navigation of the `Slidy` instance from the parent component via binding.

There are two variables available to control the component externally: `index` and `position`. Declare the variables to hold the values and bind them to the instance for the carousel control.

```jsx
import { Slidy } from '@slidy/react';
import { useState } from 'react';

import '@slidy/react/dist/slidy.css';

export default () => {
    const [index, setIndex] = useState(0);

    return (
        <>
            <Slidy slides={slides} index={index} setIndex={setIndex} />
            <button onClick={() => setIndex((prev) => prev + 1)}>Next slide</button>
        </>
    );
};
```

## Possible issues

-   Slides should not have `absolute` positioning, otherwise the core script won't get correct dimentions;
-   Using the `background` option usually is not recommended. In case you need to use it, specify the slide sizes with custom properties: `width` and `height`, or just `aspect-ratio`.

## License

Slidy is distributed under the MIT license