{"_id":"@allejo/react-position-sticky","_rev":"1-b56d49a2530616e57d6b9fe5dff1a37f","name":"@allejo/react-position-sticky","dist-tags":{"latest":"1.0.0-rc.1"},"versions":{"1.0.0-rc.1":{"name":"@allejo/react-position-sticky","version":"1.0.0-rc.1","description":"A React port of Eric Bidelman's implementation for having a callback on `position: sticky` changes","keywords":["react","position","sticky"],"license":"Apache-2.0","author":{"name":"Vladimir \"allejo\" Jimenez"},"homepage":"https://react-position-sticky.allejo.org/","repository":{"type":"git","url":"git+https://github.com/allejo/react-position-sticky.git"},"bugs":{"url":"https://github.com/allejo/react-position-sticky/issues"},"main":"dist/index.js","module":"dist/react-position-sticky.esm.js","scripts":{"analyze":"size-limit --why","build":"tsdx build","lint":"tsdx lint","prepare":"tsdx build","size":"size-limit","start":"tsdx watch","test":"tsdx test --passWithNoTests","prettify":"npm-run-all prettify:auto:*","prettify:auto:code":"prettier --list-different --write \"{.github,example,src,test}/**/*.{css,js,jsx,mdx,ts,tsx,scss,yaml,yml}\"","prettify:auto:package-json":"prettier-package-json --expand-users --write ./package.json","prettify:raw:code":"prettier --list-different --write","prettify:raw:package-json":"prettier-package-json --expand-users --write","watch":"tsdx watch --noClean"},"typings":"dist/index.d.ts","peerDependencies":{"react":">=16"},"devDependencies":{"@size-limit/preset-small-lib":"^4.9.1","@types/react":"^17.0.0","@types/react-dom":"^17.0.0","eslint-plugin-prettier":"^3.2.0","husky":"^4.3.5","import-sort-style-module":"^6.0.0","npm-run-all":"^4.1.5","prettier":"^2.1.1","prettier-check":"^2.0.0","prettier-package-json":"^2.1.3","prettier-plugin-import-sort":"^0.0.4","react":"^17.0.1","react-dom":"^17.0.1","size-limit":"^4.9.1","tsdx":"^0.14.1","tslib":"^2.0.3","typescript":"^4.1.2"},"engines":{"node":">=10"},"husky":{"hooks":{"pre-commit":"tsdx lint"}},"importSort":{".ts, .tsx":{"style":"module"}},"lint-staged":{"*.{css,js,jsx,mdx,ts,tsx,scss,yaml,yml}":"yarn prettify:raw:code","package.json":"yarn prettify:raw:package-json"},"prettier":{"printWidth":80,"semi":true,"singleQuote":true,"trailingComma":"es5"},"size-limit":[{"path":"dist/react-position-sticky.cjs.production.min.js","limit":"10 KB"},{"path":"dist/react-position-sticky.esm.js","limit":"10 KB"}],"gitHead":"59f54c953c5d5aed50fe2eff6443b9d177ec6df4","_id":"@allejo/react-position-sticky@1.0.0-rc.1","_nodeVersion":"14.17.0","_npmVersion":"6.14.13","dist":{"integrity":"sha512-7JCxb/Jz3o1S7icnjIiB0tm0/QW7KWLVR2gVqH9w1VZDo9SfmM3oin7okAjAu3zMZ7Q5wPWDa4HihAWirBzrVQ==","shasum":"b4f3364dce82b9503f9f6332414da5ac9aecb165","tarball":"https://registry.npmjs.org/@allejo/react-position-sticky/-/react-position-sticky-1.0.0-rc.1.tgz","fileCount":14,"unpackedSize":86735,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhyqupCRA9TVsSAnZWagAALDQP/RPyRowDGvV0uE6qztX+\nHw3siUCUFtYKEHgUn3l6lXf/hqsEXI/imedEvCGAjILYpBGLQm0GkRP7IaQz\nhwPSVjc8lPyXfAvItYWRga04F2q+3BgqKhsmFXthRVEdDkgKqVq/zt2UZvJ5\nXDY2BkRoDJIEZSEMF+WZ/D9DKCTY2+dZvaFecwa2kc1zqdfHnEZ5FWlGtWAT\nhR5DSkXG6/AL92Qpv9a6qN1C5rWr3TkSbM3qR8ZXaOQ4drM++lPLTFA+ilnV\nLz8BKO7NR5OM3rrYYsR+9Z8PSGS6fFM8IQbiEcnryYpqJj0xWy9GTV/adTuS\nYQZm/j6MuVIHc9ItCAB8oTSWuiESoqo1G/xKYzk6728qmMo6p23iNU31DQyV\n4upQHjPYOHef5nhp+iLWUVOeLq+A0MMcO6dQD5QdZEMxtxbhZBDb9Hlxz3N6\n8dYRcw4rCWC2DZiA4BWXIhZhsvVwa/79tD7KjJ5Z9/B5IuYaTK4cjwojcN8f\n+bdzKBE+lq/4XIRQkoIX6XHTW6K3cFU2SWz7ZkA3TeDG2EVtW5se1JgCY5x6\n3sxaemQlmBNk5L3xzaYjCrrXoA+PXM969cB3WGmnB4sebsP3R6h3SSEGo91F\nG9RCaaWcbyg+8znhOy+pusEQ6QsCTI3dmF4N+9qDeeY26k7SAEczAMNO56+i\n03rc\r\n=MMSY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC25PUPXHMPXYz3e1yNuA+A9alloubXSAbLi+rvVp/IzQIgJzVg+pQMEkTDihlqctM4xd2+YMpfoGoCxQZkN1KThcU="}]},"_npmUser":{"name":"allejo","email":"me@allejo.io"},"directories":{},"maintainers":[{"name":"allejo","email":"me@allejo.io"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-position-sticky_1.0.0-rc.1_1640672168934_0.9628560290770225"},"_hasShrinkwrap":false}},"time":{"created":"2021-12-28T06:16:08.858Z","1.0.0-rc.1":"2021-12-28T06:16:09.095Z","modified":"2022-04-04T13:31:47.146Z"},"maintainers":[{"name":"allejo","email":"me@allejo.io"}],"description":"A React port of Eric Bidelman's implementation for having a callback on `position: sticky` changes","homepage":"https://react-position-sticky.allejo.org/","keywords":["react","position","sticky"],"repository":{"type":"git","url":"git+https://github.com/allejo/react-position-sticky.git"},"author":{"name":"Vladimir \"allejo\" Jimenez"},"bugs":{"url":"https://github.com/allejo/react-position-sticky/issues"},"license":"Apache-2.0","readme":"# React Position Sticky\n\n[![Latest release](https://img.shields.io/github/v/release/allejo/react-position-sticky?include_prereleases)](https://github.com/allejo/react-position-sticky/releases/latest)\n[![npm](https://img.shields.io/npm/v/@allejo/react-position-sticky.svg)](https://www.npmjs.com/package/@allejo/react-position-sticky)\n\nA React port of [Eric Bidelman's usage of `IntersectionObserver` for firing a callback when a `position: sticky` element sticks and unsticks](https://developers.google.com/web/updates/2017/09/sticky-headers).\n\n## Installation\n\nIt's available via `npm`/`yarn`.\n\n```bash\nnpm i @allejo/react-position-sticky\n# or\nyarn add @allejo/react-position-sticky\n```\n\n## Usage\n\nThis project requires you to use two separate components, the `StickyViewport` and the `StickyElement` components. Both of these components do **not** render any HTML elements (they return React Fragments), instead these components will use refs to your elements.\n\n```tsx\nimport { StickyElement, StickyViewport } from '@allejo/react-position-sticky';\n\nreturn (\n\t<StickyViewport>\n\t\t<div className=\"position-relative overflow-y\">\n\t\t\t<article>\n\t\t\t\t<StickyElement\n\t\t\t\t\tonSticky={(stuck) => handleStick(stuck)}\n\t\t\t\t\tsentinels={{\n\t\t\t\t\t\ttop: {\n\t\t\t\t\t\t\ttop: '0',\n\t\t\t\t\t\t\theight: '16px',\n\t\t\t\t\t\t},\n\t\t\t\t\t\tbottom: {\n\t\t\t\t\t\t\theight: '96px',\n\t\t\t\t\t\t},\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<header className=\"position-sticky\">\n\t\t\t\t\t\t<h2>Article Title 1</h2>\n\t\t\t\t\t</header>\n\t\t\t\t</StickyElement>\n\n\t\t\t\t<p>...</p>\n\t\t\t</article>\n\t\t</div>\n\t</StickyViewport>\n);\n```\n\n## The Components\n\nAs mentioned, both of these components do **not** render any new elements in the DOM; instead they attach themselves to their respective children.\n\n### The `StickyViewport`\n\nThe `<StickyViewport>` should surround the parent div that is `position: relative`; this is the viewport of where a sticky child will be rendered and can become stuck.\n\nIn the above example, our parent div has an `overflow: y` meaning we will treat it as the viewport. However, this is not always the case; sometimes you will want to treat your browser window as the viewport. When this is the case, set the `useBrowserViewport` prop to `true`.\n\n### The `StickyElement`\n\nThe `<StickyElement>` should surround the element that is `position: sticky`. This component will automatically add a `data-stuck` attribute to the element it surrounds, which can be used for conditional CSS styling.\n\nAdditionally, it also has the `onSticky` callback with a `stuck` parameter indicating whether the element has become stuck or unstuck.\n\n## How to Determine the `sentinels` Prop\n\n[Eric Bidelman's tutorial](https://developers.google.com/web/updates/2017/09/sticky-headers) excellently details _how_ this implementation using `IntersectionObserver` works. However, if you're like me, the magic values used for sentinels didn't make sense. Therefore, here's a visualization on how to calculate the values for sentinels.\n\nIn the following images, scrollable containers are indicated by the dashed lines\n\n### The Top Sentinel\n\nThe `top` sentinel has two values: `top` and `height`; these props correspond to respective `top` and `height` CSS properties of the top sentinel.\n\nWhen thinking about how to configure your top sentinel, your goal is to make the following statement true,\n\n> Once the top sentinel starts to disappear outside of the `<StickyViewport>`, then it will be assumed that the `<StickyElement>` has become stuck.\n\nIn this example, we have a white container with `position: relative` and 16px of padding. In order to achieve the goal above, we'll state that, once the top padding of this container (indicated in red) starts to disappear, then our `<StickyElement>` has become stuck.\n\n![](.github/assets/top-sentinel-not-stuck.jpg)\n\n![](.github/assets/top-sentinel-stuck.jpg)\n\nAssuming that once the top padding of the container has begun to disappear, we can define our top sentinel as the size and location of our container's top padding. Using `top` and `height`, we define the top sentinel as being 16px high and at the very top of the parent with `top: 0`.\n\n### The Bottom Sentinel\n\nThe `bottom` sentinel has only one value: `height`; which corresponds with the CSS `height` property of the bottom sentinel.\n\nWhen thinking about how to configure your bottom sentinel, your goal is to make the following statement true,\n\n> Once the bottom sentinel has the `<StickyElement>` entirely inside of it, then it will be assumed that the `<StickyElement>` is no longer stuck.\n\nContinuing our example above, we see that the \"JavaScript\" heading is still currently stuck as it has not hit the bottom padding of the white container.\n\n![](.github/assets/bottom-sentinel-stuck.jpg)\n\nOnce the \"JavaScript\" heading has reached its specified bottom relative to the white container, it is no longer stuck.\n\n![](.github/assets/bottom-sentinel-not-stuck.jpg)\n\nUsing this assumption, we build the bottom sentinel to be as tall as the height of the heading plus the bottom padding of the white container. In this example, the heading's height is 80px and the padding is 16px, meaning our bottom sentinel's height should be 96px.\n\n## License\n\n[Apache 2.0](./LICENSE)\n","readmeFilename":"README.md"}