{"_id":"@alexkurasa/react-horizontal-scrolling-menu","name":"@alexkurasa/react-horizontal-scrolling-menu","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@alexkurasa/react-horizontal-scrolling-menu","version":"1.0.0","author":{"name":"alexkurasa"},"description":"Scrolling horizontal menu component for React, support mouse and touch devices.","license":"ISC","main":"dist/index.umd.js","browser":"dist/index.umd.js","module":"dist/index.esm.js","unpkg":"dist/index.umd.js","types":"dist/types/index.d.ts","repository":{"type":"git","url":"git://github.com/alexkurasa/react-horizontal-scrolling-menu.git"},"registry":"https://registry.npmjs.org/@alexkurasa","exports":{".":{"browser":"./dist/index.umd.js","module":"./dist/index.esm.js","import":"./dist/index.esm.js","umd":"./dist/index.umd.js","require":"./dist/index.umd.js"}},"typesVersions":{"*":{"*":["dist/types/"]}},"scripts":{"test:unit":"jest","test:e2e":"run-p -r demo:test cypress:run-with-wait","cypress:run-with-wait":"run-s wait cypress:run","cypress:run":"cypress run","dev":"cross-env IS_DEVELOPMENT=true rollup -c rollup.config.js -w","dev2":"webpack -w","kill-rollup":"ps aux | grep rollup | awk '{print $2}' | xargs kill -9 &","postbuild":"sleep 15 && yarn kill-rollup","build":"rm -rf dist && rollup -c rollup.config.js &","example":"cd example-nextjs && yarn dev","example:test":"cd example-nextjs && NEXT_PUBLIC_IS_TEST=true CHOKIDAR_USEPOLLING=1 yarn dev","wait":"wait-on http://localhost:3003 && sleep 10","demo":"run-p dev example","demo:test":"run-s build example:test","commit":"cz","release":"yarn test:unit && yarn test:e2e && standard-version","publish":"yarn build && git push --follow-tags origin main && npm publish","setup-yarn":"yarn cache clean && yarn set version 1.22.1","install-deps":"cd example-nextjs && yarn install && cd ../ && yarn install","setup-husky":"husky install","cz-install":"commitizen init cz-conventional-changelog --dev --exact --yarn","link-react":"rm -rf node_modules/react && rm -rf node_modules/react-dom && cd example-nextjs/node_modules/react && yarn link && cd ../react-dom && yarn link && cd ../../../ && yarn link react && yarn link react-dom && rm -rf node_modules/react && rm -rf node_modules/react-dom","prepare":"yarn link-react","setup":"yarn setup-yarn && yarn install-deps && yarn setup-husky && yarn cz-install","cz-husky-init":"husky add .husky/commit-msg 'npx --no-install commitlint --edit \"$1\"'","deploy-gp":"babel-node deploy-gp.js"},"keywords":["react","react-component","menu","navigation","gallery","horizontal","scroll","scrolling","scrolling-menu"],"browserslist":["last 5 Chrome versions","last 5 ChromeAndroid versions","last 5 Firefox versions","last 5 FirefoxAndroid versions","Firefox ESR","last 3 Safari major versions","last 2 iOS major versions"],"dependencies":{"smooth-scroll-into-view-if-needed":"^1.1.32"},"devDependencies":{"@babel/core":"^7.14.6","@babel/node":"^7.14.7","@babel/plugin-transform-runtime":"^7.14.5","@babel/preset-env":"^7.14.7","@babel/preset-react":"^7.14.5","@commitlint/cli":"^13.1.0","@commitlint/config-conventional":"^13.1.0","@rollup/plugin-commonjs":"^19.0.0","@rollup/plugin-node-resolve":"^13.0.0","@rollup/plugin-typescript":"^8.2.1","@testing-library/jest-dom":"^5.12.0","@testing-library/react":"^11.2.7","@testing-library/react-hooks":"^7.0.2","@types/babel__core":"^7","@types/babel__plugin-transform-runtime":"^7","@types/babel__preset-env":"^7","@types/gh-pages":"^3","@types/jest":"^26.0.23","@types/react":"18.0.0","@types/react-dom":"18.0.0","@typescript-eslint/eslint-plugin":"^4.26.1","@typescript-eslint/parser":"^4.26.1","babel-plugin-dynamic-import-node":"^2.3.3","babel-plugin-transform-react-remove-prop-types":"^0.4.24","browserslist":"^4.20.3","commitizen":"^4.2.4","cross-env":"^7.0.3","css-loader":"^5.2.6","cypress":"^9.6.0","cz":"^1.8.2","cz-conventional-changelog":"3.3.0","eslint":"^7.27.0","eslint-config-prettier":"^8.3.0","eslint-plugin-cypress":"^2.11.3","eslint-plugin-disable":"^2.0.1","eslint-plugin-jest":"^23.20.0","eslint-plugin-jsx-a11y":"^6.3.1","eslint-plugin-prettier":"^3.4.0","eslint-plugin-radar":"^0.2.1","eslint-plugin-react":"^7.20.6","eslint-plugin-react-hooks":"^4.1.2","eslint-plugin-unicorn":"^32.0.1","gh-pages":"^3.2.3","husky":"^7.0.2","jest":"^27.0.1","jest-dev-server":"^4.4.0","lint-staged":"^10.2.2","node-sass":"^6.0.0","npm-run-all":"^4.1.5","postcss":"^8.3.0","prettier":"^2.3.0","prop-types":"^15.7.2","react":"^18.1.0","rollup":"^2.70.2","rollup-plugin-filesize":"^9.1.2","rollup-plugin-ignore":"^1.0.9","rollup-plugin-postcss":"^4.0.0","rollup-plugin-sourcemaps":"^0.6.3","rollup-plugin-terser":"^7.0.2","semver":"^7.3.2","standard-version":"^9.3.1","style-loader":"^2.0.0","ts-jest":"^27.0.5","ts-loader":"^9.2.3","typescript":"4.4.2","wait-on":"^6.0.0","webpack":"^5.72.0","webpack-cli":"^4.7.2","webpack-merge":"^5.8.0"},"peerDependencies":{"react":">=16.8"},"husky":{"hooks":{"pre-commit":"lint-staged"}},"lint-staged":{"*.{js,jsx}":["eslint --fix","git add"],"*.{html,css,less,ejs,json}":["prettier --write","git add"]},"config":{"commitizen":{"path":"cz-conventional-changelog"}},"packageManager":"yarn@1.22.1","gitHead":"c024c491c61bef12d73c08882fc04768edc2960a","bugs":{"url":"https://github.com/alexkurasa/react-horizontal-scrolling-menu/issues"},"homepage":"https://github.com/alexkurasa/react-horizontal-scrolling-menu#readme","_id":"@alexkurasa/react-horizontal-scrolling-menu@1.0.0","_nodeVersion":"14.19.3","_npmVersion":"6.14.17","dist":{"integrity":"sha512-LJww0wILnz4oHfZB/MaKd3tRsU+hExp7rbkfDy1V5Qt8broCtpJiVaJLmVR7Qpahchw9WSOa3Vz3+oYLoJsVRw==","shasum":"05e2900bbc3a3066b85b89b34f5f669ec9cc68fe","tarball":"https://registry.npmjs.org/@alexkurasa/react-horizontal-scrolling-menu/-/react-horizontal-scrolling-menu-1.0.0.tgz","fileCount":39,"unpackedSize":77235,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCPLkB2k4pB77J8kQ/GyTvqwJgbWYEKBeewMck9Xw9rXQIhAM441G2kslyoNQii+LR33d+LR3ewwIdGpjITFZpi6AOr"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi/5IGACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmr01g/9FQiRmo6FDJK3d5yMpcU4jfSxt1jhQ7egtNHbmHRXN69mZzdK\r\n3HANbK07R7WuZZy9Vc9DA2UzMyQK71rHURLLasJhVCli3wyyGftTMTwLApsl\r\navDBgyPaAjq7yu7rqpPKnCKbjCbyB5zxqS/YkAXk+bgvPC8hApLglJmXk2EC\r\njBTWzlllszfuI8TXjjg1MQZ7bEovpXeWGSo0AFBBZrMVmPsDP7qtybaHybes\r\nIkuvO/3nZ1TUSFRult6NnamlrJENCSaPdPvPhT3FCcIjVaYeHdFp85UvjMU3\r\nLtin6rFSA0NJFlPOTAamz2Ye07uBoB5it6ghRaIwYl+HrYR+3Q2GsZC7ifQM\r\nGi+sNFB6ncihK/72ATxwetDaivav1sDbSp8FmlwUu58IbcqpTIBx6lyMTSA/\r\nUCedrY1pp16fqJw/CP0ONFAmasYm5LcfKw31RY6DQjypsbku+UprmUsyVX9g\r\nwt9+ZufgcXTWTwtintUa77BTNRiDQSTtQ+AMNSllpiuK0CRZ8bDGeOq4F1bv\r\nJap9nRVOCnrr922BSuyU1k7NOd9ytjqQ2ef77OeNK2xtf42rJKUs88R92sAO\r\n0uh/K9QScqEajzUfZlPg5B3fG9uzf87uI9OZHFgNKTdldi+7sGM4iWjKQ9bG\r\nbKsbsEYMJr83mmmpsXWq31LTGexil+Susgo=\r\n=ZCip\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"alexkurasa","email":"alexkurasa@gmail.com"},"directories":{},"maintainers":[{"name":"alexkurasa","email":"alexkurasa@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-horizontal-scrolling-menu_1.0.0_1660916230716_0.6924967353821048"},"_hasShrinkwrap":false}},"time":{"created":"2022-08-19T13:37:10.667Z","1.0.0":"2022-08-19T13:37:10.921Z","modified":"2022-08-19T13:37:11.048Z"},"maintainers":[{"name":"alexkurasa","email":"alexkurasa@gmail.com"}],"description":"Scrolling horizontal menu component for React, support mouse and touch devices.","homepage":"https://github.com/alexkurasa/react-horizontal-scrolling-menu#readme","keywords":["react","react-component","menu","navigation","gallery","horizontal","scroll","scrolling","scrolling-menu"],"repository":{"type":"git","url":"git://github.com/alexkurasa/react-horizontal-scrolling-menu.git"},"author":{"name":"alexkurasa"},"bugs":{"url":"https://github.com/alexkurasa/react-horizontal-scrolling-menu/issues"},"license":"ISC","readme":"# React horizontal scrolling menu\n\n[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](https://war.ukraine.ua)\n\n![example](/sample.gif)\n\n[![npm](https://img.shields.io/npm/v/react-horizontal-scrolling-menu.svg)](https://www.npmjs.com/package/react-horizontal-scrolling-menu)\n[![Tests](https://github.com/asmyshlyaev177/react-horizontal-scrolling-menu/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/asmyshlyaev177/react-horizontal-scrolling-menu/actions/workflows/tests.yml)\n[![Codacy Badge](https://app.codacy.com/project/badge/Grade/433d9b4a8a374109a9f96b8faf3c175d)](https://www.codacy.com/gh/asmyshlyaev177/react-horizontal-scrolling-menu/dashboard?utm_source=github.com&utm_medium=referral&utm_content=asmyshlyaev177/react-horizontal-scrolling-menu&utm_campaign=Badge_Grade)\n[![Codacy Badge](https://app.codacy.com/project/badge/Coverage/433d9b4a8a374109a9f96b8faf3c175d)](https://www.codacy.com/gh/asmyshlyaev177/react-horizontal-scrolling-menu/dashboard?utm_source=github.com&utm_medium=referral&utm_content=asmyshlyaev177/react-horizontal-scrolling-menu&utm_campaign=Badge_Coverage)\n[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](https://github.com/asmyshlyaev177/react-horizontal-scrolling-menu/)\n![npm bundle size (minified + gzip)](https://img.shields.io/bundlephobia/minzip/react-horizontal-scrolling-menu.svg)\n[![Donate with Bitcoin](https://en.cryptobadges.io/badge/micro/18va9nD2mMRbGs8Mb7ob19dXmQ4Kbu7B96)](https://en.cryptobadges.io/donate/18va9nD2mMRbGs8Mb7ob19dXmQ4Kbu7B96)\n\n### Proud corner\n\n[performance-dashboard-on-aws\n](https://github.com/awslabs/performance-dashboard-on-aws/blob/49ce2517a29569a9761dec8f212f25cf85a394af/frontend/src/components/Tabs.tsx#L3) |\n[React status code](https://react.statuscode.com/issues/257)\n\n### Examples\n\n[Demo](https://asmyshlyaev177.github.io/react-horizontal-scrolling-menu)\n\n[Basic example](https://codesandbox.io/s/react-horizontal-scrolling-menu-v2-basic-example-swg0y?file=/src/index.tsx)\n\n[Hidden scrollbar and arrows on bottom](https://codesandbox.io/s/no-scrollbar-and-buttons-position-c3kn5?file=/src/index.tsx)\n\n[Select item](https://codesandbox.io/s/react-horizontal-scrolling-menu-v2-basic-with-select-item-qtxwt?file=/src/index.tsx)\n\n[Drag by mouse](https://codesandbox.io/s/react-horizontal-scrolling-menu-v2-drag-by-mouse-o3u2t?file=/src/index.tsx)\n\n[Click and select multiple items](https://codesandbox.io/s/react-horizontal-scrolling-menu-v2-item-selection-and-click-0oze7?file=/src/index.tsx)\n\n[Scroll by 1 item](https://codesandbox.io/s/react-horizontal-scrolling-menu-v2-scroll-one-item-6hn8q?file=/src/index.tsx)\n\n[Center items](https://codesandbox.io/s/center-items-on-click-drag-e8cyph?file=/src/index.tsx)\n\n[Dynamically add items when last is visible](https://codesandbox.io/s/react-horizontal-scrolling-menu-v2-dynamically-add-items-38ted?file=/src/index.tsx)\n\n[apiRef - controling component outside](https://codesandbox.io/s/react-horizontal-scrolling-menu-v2-apiref-vdr0d?file=/src/index.tsx)\n\n[Add item and scroll to it](https://codesandbox.io/s/basic-example-forked-3j0xm?file=/src/index.tsx)\n\n[RTL](https://codesandbox.io/s/rtl-8lix35?file=/src/index.tsx)\n\n[Custom transition/animation](https://codesandbox.io/s/custom-transition-animation-n2pyn)\n\n### Previous version [V1](https://github.com/asmyshlyaev177/react-horizontal-scrolling-menu/tree/v1)\n\nThis is a horizontal scrolling menu component for React.\nMenu component has adaptive width, just set width for parent container.\nItems width will be determined from CSS styles.\n\nFor navigation, you can use scrollbar, native touch scroll, mouse wheel or drag by mouse.\n\nComponent provide context with visible items and helpers.\n\nPossible set default position on initialization.\n\n:star: if you like the project :)\n\n## Quick start\n\n```bash\nyarn add react-horizontal-scrolling-menu\n```\n\nIn project:\n\n```javascript\nimport React from 'react';\nimport { ScrollMenu, VisibilityContext } from 'react-horizontal-scrolling-menu';\n\nconst getItems = () =>\n  Array(20)\n    .fill(0)\n    .map((_, ind) => ({ id: `element-${ind}` }));\n\nfunction App() {\n  const [items, setItems] = React.useState(getItems);\n  const [selected, setSelected] = React.useState([]);\n  const [position, setPosition] = React.useState(0);\n\n  const isItemSelected = (id) => !!selected.find((el) => el === id);\n\n  const handleClick =\n    (id) =>\n    ({ getItemById, scrollToItem }) => {\n      const itemSelected = isItemSelected(id);\n\n      setSelected((currentSelected) =>\n        itemSelected\n          ? currentSelected.filter((el) => el !== id)\n          : currentSelected.concat(id)\n      );\n    };\n\n  return (\n    <ScrollMenu LeftArrow={LeftArrow} RightArrow={RightArrow}>\n      {items.map(({ id }) => (\n        <Card\n          itemId={id} // NOTE: itemId is required for track items\n          title={id}\n          key={id}\n          onClick={handleClick(id)}\n          selected={isItemSelected(id)}\n        />\n      ))}\n    </ScrollMenu>\n  );\n}\n\nfunction LeftArrow() {\n  const { isFirstItemVisible, scrollPrev } =\n    React.useContext(VisibilityContext);\n\n  return (\n    <Arrow disabled={isFirstItemVisible} onClick={() => scrollPrev()}>\n      Left\n    </Arrow>\n  );\n}\n\nfunction RightArrow() {\n  const { isLastItemVisible, scrollNext } = React.useContext(VisibilityContext);\n\n  return (\n    <Arrow disabled={isLastItemVisible} onClick={() => scrollNext()}>\n      Right\n    </Arrow>\n  );\n}\n\nfunction Card({ onClick, selected, title, itemId }) {\n  const visibility = React.useContext(VisibilityContext);\n\n  return (\n    <div\n      onClick={() => onClick(visibility)}\n      style={{\n        width: '160px',\n      }}\n      tabIndex={0}\n    >\n      <div className=\"card\">\n        <div>{title}</div>\n        <div>visible: {JSON.stringify(!!visibility.isItemVisible(itemId))}</div>\n        <div>selected: {JSON.stringify(!!selected)}</div>\n      </div>\n      <div\n        style={{\n          height: '200px',\n        }}\n      />\n    </div>\n  );\n}\n\nexport default App;\n```\n\nCheck out Example in `example-nextjs` folder for info how to implement more features like mouse drag or disable body scroll.\n\n## Example\n\nYou can clone repository and run demo project.\n\n```bash\ngit clone https://github.com/asmyshlyaev177/react-horizontal-scrolling-menu\nyarn install\nyarn run demo\n```\n\n### Helpers and api\n\nChildren of main ScrollMenu component(arrows, fotter, items) can use **VisibilityContext** to access state and callbacks.\nFunction callbacks also pass context, eg `onWheel`, `onScroll` etc.\n\n## Properties and callbacks\n\n| Prop                     | Signature                                                     |\n| ------------------------ | ------------------------------------------------------------- |\n| LeftArrow                | React component for left arrow                                |\n| RightArrow               | React component for right arrow                               |\n| Header                   | React component Header                                        |\n| Footer                   | React component Footer                                        |\n| onWheel                  | (VisibilityContext, event) => void                            |\n| onScroll                 | (VisibilityContext, event) => void, will fire _before_ scroll |\n| onInit                   | (VisibilityContext) => void                                   |\n| apiRef                   | React.RefObject                                               |\n| onUpdate                 | (VisibilityContext) => void                                   |\n| onMouseDown              | (VisibilityContext) => (React.MouseEventHandler) => void      |\n| onMouseUp                | (VisibilityContext) => (React.MouseEventHandler) => void      |\n| onMouseMove              | (VisibilityContext) => (React.MouseEventHandler) => void      |\n| itemClassName            | ClassName of Item                                             |\n| separatorClassName       | ClassName of Item's separator                                 |\n| scrollContainerClassName | ClassName of scrollContainer                                  |\n| transitionDuration       | Duration of transitions in ms, default 500                    |\n| transitionBehavior       | 'smooth' \\|'auto' \\| customFunction                           |\n| transitionEase           | Ease function, eg t => t\\*(2-t)                               |\n| wrapperClassName         | ClassName of the outer-most div                               |\n| RTL                      | Enable Right to left direction                                |\n| noPolyfill               | Don't use polyfill for scroll, no transitions                 |\n\n### VisibilityContext\n\n| Prop                          | Signature                                              |\n| ----------------------------- | ------------------------------------------------------ |\n| getItemById                   | itemId => IOItem \\| undefined                          |\n| getItemElementById            | itemId => DOM Element \\| null                          |\n| getItemByIndex                | index => IOItem \\| undefined                           |\n| getItemElementByIndex         | index => DOM Element \\| null                           |\n| getNextItem                   | () => IOItem \\| undefined)                             |\n| getPrevItem                   | () => IOItem \\| undefined                              |\n| initComplete                  | boolean                                                |\n| isFirstItemVisible            | boolean                                                |\n| isItemVisible                 | itemId => boolean                                      |\n| isLastItem                    | boolean                                                |\n| isLastItemVisible             | boolean                                                |\n| scrollNext                    | (behavior, inline, block, ScrollOptions) => void       |\n| scrollPrev                    | (behavior, inline, block, ScrollOptions) => void       |\n| scrollToItem                  | (item, behavior, inline, block, ScrollOptions) => void |\n| visibleItemsWithoutSeparators | ['item1', 'item2']                                     |\n| initComplete                  | boolean                                                |\n| items                         | ItemsMap class instance                                |\n| scrollContainer               | Ref<OuterContainer>                                    |\n| visibleItems                  | ['item1', 'item1-separator', 'item2']                  |\n\n### Transition/Animation\n\nNOTE: won't work with RTL prop\n\nCan use `transitionDuration`, `transitionEase` and `transitionBehavior`\nSee [example](https://codesandbox.io/s/custom-transition-animation-n2pyn)\n\n#### ScrollOptions for scrollToItem, scrollPrev, scrollNext\n\nWill override transition\\* options passed to ScrollMenu\n\n```javascript\n{\n  // target,\n  behavior, // 'smooth', 'auto' or custom function\n    // inline,\n    // block,\n    {\n      duration: number, // number in milliseconds\n      ease: (t) => t, // ease function, more https://gist.github.com/gre/1650294#file-easing-js\n    };\n}\n```\n\n### Other helpers\n\n#### slidingWindow\n\nCan get previous or next visible group of items with `slidingWindow(allItems: string[], visibleItems: string[])` helper, e.g\n\n```\nslidingWindow(allItems, visibleItems)\n.prev()\n//.next()\n```\n\n#### getItemsPos\n\nCan get first, center and last items, e.g.\n\n```\nconst prevGroup = slidingWindow(allItems, visibleItems).prev()\nconst { first, center: centerItem, last } = getItemsPos(prevGroup)\n\n// and scroll to center item of previous group of items\nscrollToItem(getItemById(centerItem, 'smooth', 'center'))\n\n```\n\nCheck out [examples](#examples)\n\n### apiRef\n\nCan pass Ref object to Menu, current value will assigned as VisibilityContext. But `visibleItems` and some other values can be staled, so better use it only for firing functions like `scrollToItem`.\n\nFor scrolling use `apiRef.scrollToItem(apiRef.getItemElementById)` instead of `apiRef.scrollToItem(apiRef.getItemById)`.\n\nCan get item outside of context via `apiRef.getItemElementById(id)` or directly via `` document.querySelector(`[data-key='${itemId}']`) ``.\nSee [`apiRef` example and `Add item and scroll to it`](#examples)\n\n## Browser support\n\n- Browser must support **IntersectionObserver API** and **requestAnimationFrame** or use polyfills.\n- Only modern browsers, no IE or smart toasters\n\n## About\n\nMy first npm project. Sorry for my english.\n\nAny contribution and correction appreciated. Just fork repo, commit and make PR, don't forget about tests.\n\n## [Contributing](https://github.com/asmyshlyaev177/react-horizontal-scrolling-menu/blob/main/CONTRIBUTING.md)\n\n## [Changelog](https://github.com/asmyshlyaev177/react-horizontal-scrolling-menu/blob/main/CHANGELOG.md)\n","readmeFilename":"README.md"}