Skip to content
No description, website, or topics provided.
TypeScript JavaScript CSS HTML
Branch: master
Clone or download

Latest commit

Latest commit 9a20111 Apr 3, 2020

Files

Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
example Make all the de examples dragables Apr 2, 2020
readme Update GIF Mar 25, 2020
src Make all the de examples dragables Apr 2, 2020
.editorconfig init create-react-library@2.6.7 Feb 13, 2020
.eslintignore MVP Feb 13, 2020
.eslintrc MVP Feb 13, 2020
.gitignore init create-react-library@2.6.7 Feb 13, 2020
.travis.yml init create-react-library@2.6.7 Feb 13, 2020
README.md Update readme Apr 2, 2020
package-lock.json Bump version Apr 1, 2020
package.json Bump version Apr 2, 2020
rollup.config.js MVP Feb 13, 2020
tsconfig.json init create-react-library@2.6.7 Feb 13, 2020
tsconfig.test.json init create-react-library@2.6.7 Feb 13, 2020

README.md

react-snaplist-carousel

react-snaplist-carousel

A modern way to do a classic thing.

  • Less than 3K gzipped size.
  • Made 100% in React, no porting.
  • No dependencies.
  • Typescript ready.
  • Using it in production.
  • Using native browser snap option.
  • No magic, you get the control thanks to the hooks.

Demo

react-snaplist-carousel react-snaplist-carousel react-snaplist-carousel

LIVE DEMO

Install

npm install --save react-snaplist-carousel
import {
  SnapList,
  SnapItem,
  useVisibleElements,
  useScroll,
} from 'react-snaplist-carousel';

Basic Example

import * as React from 'react';

import { SnapList, SnapItem } from 'react-snaplist-carousel';

const MyItem = ({ children }) => (
  <div style={{ width: '70vw', height: 200, background: '#cccccc' }}>
    {children}
  </div>
);

export const App = () => (
  <SnapList>
    <SnapItem padding={{ left: '15px', right: '15px' }} snapAlign="center">
      <MyItem>Item 0</MyItem>
    </SnapItem>
    <SnapItem padding={{ left: '15px', right: '15px' }} snapAlign="center">
      <MyItem>Item 1</MyItem>
    </SnapItem>
    <SnapItem padding={{ left: '15px', right: '15px' }} snapAlign="center">
      <MyItem>Item 2</MyItem>
    </SnapItem>
    <SnapItem padding={{ left: '15px', right: '15px' }} snapAlign="center">
      <MyItem>Item 3</MyItem>
    </SnapItem>
    <SnapItem padding={{ left: '15px', right: '15px' }} snapAlign="center">
      <MyItem>Item 4</MyItem>
    </SnapItem>
  </SnapList>
);

Advanced Example

import React, { useRef } from 'react';

import {
  SnapList,
  SnapItem,
  useVisibleElements,
  useScroll,
  useDragToScroll,
  isTouchDevice,
} from 'react-snaplist-carousel';

const MyItem = ({ onClick, children, visible }) => (
  <div
    style={{
      width: '60vw',
      height: 200,
      background: visible ? '#bce6fe' : '#cccccc',
      cursor: visible ? 'default' : 'pointer',
    }}
    onClick={onClick}
  >
    {children}
  </div>
);

export const App = () => {
  const snapList = useRef(null);

  const visible = useVisibleElements(
    { debounce: 10, ref: snapList },
    ([element]) => element,
  );
  const goToSnapItem = useScroll({ ref: snapList });
  const isDragging = useDragToScroll({ ref: snapList });

  return (
    <SnapList ref={snapList}>
      <SnapItem padding={{ left: '20vw', right: '15px' }} snapAlign="center">
        <MyItem onClick={() => goToSnapItem(0)} visible={visible === 0}>
          Item 0
        </MyItem>
      </SnapItem>
      <SnapItem padding={{ left: '15px', right: '15px' }} snapAlign="center">
        <MyItem onClick={() => goToSnapItem(1)} visible={visible === 1}>
          Item 1
        </MyItem>
      </SnapItem>
      <SnapItem padding={{ left: '15px', right: '15px' }} snapAlign="center">
        <MyItem onClick={() => goToSnapItem(2)} visible={visible === 2}>
          Item 2
        </MyItem>
      </SnapItem>
      <SnapItem padding={{ left: '15px', right: '15px' }} snapAlign="center">
        <MyItem onClick={() => goToSnapItem(3)} visible={visible === 3}>
          Item 3
        </MyItem>
      </SnapItem>
      <SnapItem padding={{ left: '15px', right: '20vw' }} snapAlign="center">
        <MyItem onClick={() => goToSnapItem(4)} visible={visible === 4}>
          Item 4
        </MyItem>
      </SnapItem>
    </SnapList>
  );
};

Options

SnapList

  • direction { horizontal | vertical }: Scroll direction. *
  • disableScroll { boolean | undefined }: Disable the native scroll on swipe or mouse wheel.
  • width { string | undefined }: Width CSS property
  • height { string | undefined }: Height CSS property
  • scrollPadding { { top?: string; right?: string; bottom?: string; left?: string; } | undefined }: Use this to configure the space to see from the previous/next hidden element. See scroll-padding for more information
  • ref { React.RefObject<HTMLDivElement> | undefined }: The React.ref to the element required by the hooks.
  • className { string | undefined }: 🚑Please, use this only in case of emergency. It allows you to add/overwrite/extend all the CSS properties. If you need this, please consider opening an issue or contribute with a PR to cover your use case.

* Required fields

SnapItem

  • snapAlign { start | center | end | none }: The box’s snap position when the scroll stops. See scroll-snap-align for more information *
  • disableScroll { boolean | undefined }: Avoid the scroll to "pass over" possible snap positions. See scroll-snap-stop for more information
  • width { string | undefined }: Width CSS property
  • height { string | undefined }: Height CSS property
  • padding { { top?: string; right?: string; bottom?: string; left?: string; } | undefined }: The padding use to set the separation between the items. You can use different padding for the first and last item to get better results.
  • className { string | undefined }: 🚑Please, use this only in case of emergency. It allows you to add/overwrite/extend all the CSS properties. If you need this, please consider opening an issue or contribute with a PR to cover your use case.

* Required fields

useScroll

const snapList = useRef(null);
const goToElement = useScroll({ ref: snapList });

return (
  <SnapList ref={snapList}>
    <SnapItem snapAlign="left">
      <div onClick={() => goTo(0)}>Item 0</div>
    </SnapItem>
    <SnapItem snapAlign="left">
      <div onClick={() => goTo(1)}>Item 1</div>
    </SnapItem>
  </SnapList>
);

Response

  • A function (element:number) => void to scroll to the element.

Arguments

  • ref: { React.RefObject<HTMLDivElement> } *

* Required fields

useVisibleElements

const snapList = useRef(null);
const selected = useVisibleElements(
  { ref: snapList, debounce: 10 },
  elements => elements[0],
);
const goToElement = useScroll({ ref: snapList });

return (
  <SnapList ref={snapList}>
    <SnapItem snapAlign="left">
      <div
        onClick={() => goToElement(0)}
        style={{
          backgroundColor: selected === 0 ? 'papayawhip' : null,
        }}
      >
        Item 0
      </div>
    </SnapItem>
    <SnapItem snapAlign="left">
      <div
        onClick={() => goToElement(1)}
        style={{
          backgroundColor: selected === 1 ? 'papayawhip' : null,
        }}
      >
        Item 1
      </div>
    </SnapItem>
  </SnapList>
);

Arguments

  • ref: { React.RefObject<HTMLDivElement> } *
  • debounce: { number }. Optional (default 10). The time that the scroll is stopped before firing the visible elements check.
  • selectorFunction: { (element:number[], elementInCenter: number | null) => any }. This selector gets an array of the visible elements as an argument and the return value will be returned by the useVisibleElements. Use this function to add some logic like select only the first one, calculate if there hidden elements before or later, etc... *

* Required fields

Tip

Use many times useVisibleElements hook with different debounce values for different purposes. For instance with a SnapList to select one option, one with debounce 10 for the slider dots animation or the selected option background and another one with debounce 100 to fire a select sideEffect.

useDragToScroll

Thanks @danieljb for the contribution

const snapList = useRef(null);
const selected = useVisibleElements(
  { ref: snapList, debounce: 10 },
  elements => elements[0],
);

const isDragging = useDragToScroll({ref: snapList, disable: false});

return (
  <>
  <p>{isDragging ? 'Dragging': 'No dragging}</p>
  <SnapList ref={snapList}>
    <SnapItem snapAlign="left">
      <div
        onClick={() => goToElement(0)}
        style={{
          backgroundColor: selected === 0 ? 'papayawhip' : null,
        }}
      >
        Item 0
      </div>
    </SnapItem>
    <SnapItem snapAlign="left">
      <div
        onClick={() => goToElement(1)}
        style={{
          backgroundColor: selected === 1 ? 'papayawhip' : null,
        }}
      >
        Item 1
      </div>
    </SnapItem>
  </SnapList>
  </p>
);

Arguments

  • ref: { React.RefObject<HTMLDivElement> } *
  • disable: { booleal }. Optional (default false). The hook will be auto-disabled on touch devices but you can force it using this option.

* Required fields

isTouchDevice

This an internal util function used by useDragToScroll that can be useful for you. You can use it to modify your UI depending on the device. For example, you can show next/previous arrows only on no touch devices.

Do you want to contribute?

  • You can give a star to the project to help with the reputation
  • You can share it with your colleagues.
  • You can fork the repository and make your PR contribution.
  • You can explore using IntersectionObserver for the useVisible hook.
  • You can explore with better scrollTo polyfills.
  • You can create usefull extra elements like Dots, Thumbnails, Progress or Arrows.
  • You can create a new demo example, sky is the limit!
  • Yes, you can.

License

MIT © luispuig

You can’t perform that action at this time.