Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-horizontal-scrolling-menu

Package Overview
Dependencies
Maintainers
0
Versions
158
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-horizontal-scrolling-menu

Scrolling horizontal menu component for React, support mouse and touch devices.

  • 8.2.0
  • latest
  • Source
  • npm
  • Socket score

Version published
Maintainers
0
Created
Source

For hire

React horizontal scrolling menu

example

npm NPM Downloads Codacy Badge Codacy Badge Commitizen friendly npm bundle size (minified + gzip) Donate Bitcoin

Poll what you like/dislike/need from this library

Check out my new project 👉 https://github.com/asmyshlyaev177/state-in-url

Add a ⭐️ and follow me to support the project!

Proud corner

performance-dashboard-on-aws | React status code

Storybook (Faster and more convinient, new examples will be here)

Codesandbox Examples (Deprecated)

Center items

Dynamically add items when last is visible

apiRef - controling component outside

Add item and scroll to it

Loop scroll

Custom transition/animation

Swipe on mobile devices(need to run locally, codesandbox has issues)

Previous version V1

This is a highly customizable horizontal scrolling menu component for React. Can also use it for Amazon like items block or a Gallery. Menu component is responsive, just set width for parent container. Items width will be determined from CSS styles.

For navigation, you can use scrollbar, native touch scroll, mouse wheel or drag by mouse.

Component provide context with visible items and helpers.

Possible set default position on initialization.

Check out examples on Storybook or codesandbox.

:star: if you like the project :)

NextJS issues

Cannot use import statement outside a module

Quick start

npm install --save react-horizontal-scrolling-menu@7.1.1 // last version has a bug

test In project:

import React from 'react';
import { ScrollMenu, VisibilityContext } from 'react-horizontal-scrolling-menu';
import 'react-horizontal-scrolling-menu/dist/styles.css';

const getItems = () =>
  Array(20)
    .fill(0)
    .map((_, ind) => ({ id: `element-${ind}` }));

function App() {
  const [items, setItems] = React.useState(getItems);
  const [selected, setSelected] = React.useState([]);

  const isItemSelected = (id) => !!selected.find((el) => el === id);

  const handleClick =
    (id) =>
    ({ getItemById, scrollToItem }) => {
      const itemSelected = isItemSelected(id);

      setSelected((currentSelected) =>
        itemSelected
          ? currentSelected.filter((el) => el !== id)
          : currentSelected.concat(id),
      );
    };

  return (
    <ScrollMenu LeftArrow={LeftArrow} RightArrow={RightArrow}>
      {items.map(({ id }) => (
        <Card
          itemId={id} // NOTE: itemId is required for track items
          title={id}
          key={id}
          onClick={handleClick(id)}
          selected={isItemSelected(id)}
        />
      ))}
    </ScrollMenu>
  );
}

const LeftArrow = () => {
  const visibility = React.useContext < publicApiType > VisibilityContext;
  const isFirstItemVisible = visibility.useIsVisible('first', true);
  return (
    <Arrow
      disabled={isFirstItemVisible}
      onClick={() => visibility.scrollPrev()}
      className="left"
    >
      Left
    </Arrow>
  );
};

const RightArrow = () => {
  const visibility = React.useContext < publicApiType > VisibilityContext;
  const isLastItemVisible = visibility.useIsVisible('last', false);
  return (
    <Arrow
      disabled={isLastItemVisible}
      onClick={() => visibility.scrollNext()}
      className="right"
    >
      Right
    </Arrow>
  );
};

function Card({ onClick, selected, title, itemId }) {
  const visibility = React.useContext < publicApiType > VisibilityContext;
  const visible = visibility.useIsVisible(itemId, true);

  return (
    <div
      onClick={() => onClick(visibility)}
      style={{
        width: '160px',
      }}
      tabIndex={0}
    >
      <div className="card">
        <div>{title}</div>
        <div>visible: {JSON.stringify(visible)}</div>
        <div>selected: {JSON.stringify(!!selected)}</div>
      </div>
      <div
        style={{
          height: '200px',
        }}
      />
    </div>
  );
}

export default App;

Check out Example in example-nextjs folder for info how to implement more features like mouse drag or disable body scroll.

Example

You can clone repository and run demo project.

git clone https://github.com/asmyshlyaev177/react-horizontal-scrolling-menu
npm run setup
npm run demo

Storybook

Can clone repo and run storybook

git clone https://github.com/asmyshlyaev177/react-horizontal-scrolling-menu
npm run setup
npm run storybook

Helpers and api

Children of main ScrollMenu component(arrows, fotter, items) can use VisibilityContext to access state and callbacks. Function callbacks also pass context, eg onWheel, onScroll etc.

Properties and callbacks

PropSignature
LeftArrowReact component for left arrow
RightArrowReact component for right arrow
HeaderReact component Header
FooterReact component Footer
onWheel(VisibilityContext, event) => void
onScroll(VisibilityContext, event) => void, will fire before scroll
onInit(VisibilityContext) => void
apiRefReact.RefObject | React.RefCallback
optionsoptions for IntersectionObserver - rootMargin, threshold, and ratio to consider element visible
containerRefReact.RefObject | React.RefCallback
onUpdate(VisibilityContext) => void
onMouseDown(VisibilityContext) => (React.MouseEventHandler) => void
onMouseLeave(VisibilityContext) => (React.MouseEventHandler) => void
onMouseUp(VisibilityContext) => (React.MouseEventHandler) => void
onMouseMove(VisibilityContext) => (React.MouseEventHandler) => void
onTouchMove(VisibilityContext) => (React.TouchEventHandler) => void
onTouchStart(VisibilityContext) => (React.TouchEventHandler) => void
onTouchEnd(VisibilityContext) => (React.TouchEventHandler) => void
itemClassNameClassName of Item
scrollContainerClassNameClassName of scrollContainer
transitionDurationDuration of transitions in ms, default 500
transitionBehavior'smooth' |'auto' | customFunction
wrapperClassNameClassName of the outer-most div
RTLEnable Right to left direction
noPolyfillDon't use polyfill for scroll, no transitions, true by default

VisibilityContext

PropSignature
useIsVisible(itemId: string, defaultValue?: false) => boolean
getItemByIditemId => IOItem | undefined
getItemElementByIditemId => DOM Element | null
getItemByIndexindex => IOItem | undefined
getItemElementByIndexindex => DOM Element | null
getNextElement() => IOItem | undefined
getPrevElement() => IOItem | undefined
isFirstItemVisibleboolean
isItemVisibleitemId => boolean
isLastItemboolean
isLastItemVisibleboolean
menuVisible{ current: boolean }
scrollNext(behavior, inline, block, ScrollOptions) => void
scrollPrev(behavior, inline, block, ScrollOptions) => void
scrollToItem(item, behavior, inline, block, ScrollOptions) => void
itemsItemsMap class instance
scrollContainerRef

items class instance

ItemsMap class store info about all items and has methods to get currently visible items, prev/next item. Also, can subscribe to updates.

Prop/methodDescription
subscribesubscribe for events for itemId or first, last, onInit, onUpdate, eg. items.subscribe('item5', (item) => setVisible(item.visible))
unsubscribeuse in useEffect to cleanup, pass same cb instance
getVisiblereturn only visible items
toItemsreturn ids for all items
toArrreturn all items
firstreturn first item
lastreturn last item
prev(itemId | Item) => previous item | undefined
next(itemId | Item) => next item | undefined

Transition/Animation

NOTE: won't work with RTL prop

Can use transitionDuration, and transitionBehavior See example

ScrollOptions for scrollToItem, scrollPrev, scrollNext

Will override transition* options passed to ScrollMenu

{
  // target,
  behavior, // 'smooth', 'auto' or custom function
    // inline,
    // block,
    {
      duration: number, // number in milliseconds
    };
}

Other helpers

slidingWindow

Can get previous or next visible group of items with slidingWindow(allItems: string[], visibleItems: string[]) helper, e.g

slidingWindow(allItems, visibleItems)
.prev()
//.next()
getItemsPos

Can get first, center and last items, e.g.

const prevGroup = slidingWindow(allItems, visibleItems).prev()
const { first, center: centerItem, last } = getItemsPos(prevGroup)

// and scroll to center item of previous group of items
scrollToItem(getItemById(centerItem, 'smooth', 'center'))

Check out examples

apiRef

Can pass Ref object to Menu, current value will assigned as VisibilityContext. But some other values can be staled, so better use it only for firing functions like scrollToItem.

For scrolling use apiRef.scrollToItem(apiRef.getItemElementById) instead of apiRef.scrollToItem(apiRef.getItemById).

Can get item outside of context via apiRef.getItemElementById(id) or directly via document.querySelector(`[data-key='${itemId}']`). See apiRef example and Add item and scroll to it

Browser support

  • Browser must support IntersectionObserver API and requestAnimationFrame or use polyfills.
  • Only modern browsers, no IE or smart toasters

About

My first npm project. Sorry for my english.

Any contribution and correction appreciated. Just fork repo, commit and make PR, don't forget about tests.

Contributing

Changelog

Keywords

FAQs

Package last updated on 27 Oct 2024

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc