├── .editorconfig
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ ├── codeql-analysis.yml
│ └── main.yml
├── .gitignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── cypress.config.ts
├── cypress
├── e2e
│ └── spec.cy.ts
├── fixtures
│ └── example.json
├── support
│ ├── commands.ts
│ └── e2e.ts
└── tsconfig.json
├── docs
└── demo.gif
├── example
├── .gitignore
├── README.md
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
├── src
│ ├── App.test.tsx
│ ├── App.tsx
│ ├── index.css
│ ├── index.tsx
│ ├── logo.svg
│ ├── random-color-generator.ts
│ ├── react-app-env.d.ts
│ ├── reportWebVitals.ts
│ └── setupTests.ts
└── tsconfig.json
├── package-lock.json
├── package.json
├── renovate.json
├── src
└── index.tsx
└── tsconfig.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 |
5 | ---
6 |
7 | **Describe the bug**
8 | A clear and concise description of what the bug is.
9 |
10 | **To Reproduce**
11 | Steps to reproduce the behavior:
12 | 1. Go to '...'
13 | 2. Click on '....'
14 | 3. Scroll down to '....'
15 | 4. See error
16 |
17 | **Expected behavior**
18 | A clear and concise description of what you expected to happen.
19 |
20 | **Screenshots**
21 | If applicable, add screenshots to help explain your problem.
22 |
23 | **Desktop (please complete the following information):**
24 | - OS: [e.g. iOS]
25 | - Browser [e.g. chrome, safari]
26 | - Version [e.g. 22]
27 |
28 | **Smartphone (please complete the following information):**
29 | - Device: [e.g. iPhone6]
30 | - OS: [e.g. iOS8.1]
31 | - Browser [e.g. stock browser, safari]
32 | - Version [e.g. 22]
33 |
34 | **Additional context**
35 | Add any other context about the problem here.
36 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 |
5 | ---
6 |
7 | **Is your feature request related to a problem? Please describe.**
8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
9 |
10 | **Describe the solution you'd like**
11 | A clear and concise description of what you want to happen.
12 |
13 | **Describe alternatives you've considered**
14 | A clear and concise description of any alternative solutions or features you've considered.
15 |
16 | **Additional context**
17 | Add any other context or screenshots about the feature request here.
18 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | name: "CodeQL"
2 |
3 | on:
4 | push:
5 | branches: [master, ]
6 | pull_request:
7 | # The branches below must be a subset of the branches above
8 | branches: [master]
9 | schedule:
10 | - cron: '0 2 * * 2'
11 |
12 | jobs:
13 | analyze:
14 | name: Analyze
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - name: Checkout repository
19 | uses: actions/checkout@v2
20 | with:
21 | # We must fetch at least the immediate parents so that if this is
22 | # a pull request then we can checkout the head.
23 | fetch-depth: 2
24 |
25 | # If this run was triggered by a pull request event, then checkout
26 | # the head of the pull request instead of the merge commit.
27 | - run: git checkout HEAD^2
28 | if: ${{ github.event_name == 'pull_request' }}
29 |
30 | # Initializes the CodeQL tools for scanning.
31 | - name: Initialize CodeQL
32 | uses: github/codeql-action/init@v1
33 | # Override language selection by uncommenting this and choosing your languages (go, javascript, csharp, python, cpp, java)
34 | with:
35 | languages: javascript
36 |
37 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
38 | # If this step fails, then you should remove it and run the build manually (see below)
39 | - name: Autobuild
40 | uses: github/codeql-action/autobuild@v1
41 |
42 | # ℹ️ Command-line programs to run using the OS shell.
43 | # 📚 https://git.io/JvXDl
44 |
45 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
46 | # and modify them (or add more) to build your code if your project
47 | # uses a compiled language
48 |
49 | #- run: |
50 | # make bootstrap
51 | # make release
52 |
53 | - name: Perform CodeQL Analysis
54 | uses: github/codeql-action/analyze@v1
55 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | branches: [ master ]
4 | pull_request:
5 | branches: [ master ]
6 | # Allows to run this workflow manually from the Actions tab
7 | workflow_dispatch:
8 |
9 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
10 | permissions:
11 | contents: read
12 | pages: write
13 | id-token: write
14 |
15 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
16 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
17 | concurrency:
18 | group: "pages"
19 | cancel-in-progress: false
20 |
21 | jobs:
22 | build:
23 | runs-on: ubuntu-latest
24 |
25 | strategy:
26 | matrix:
27 | node-version: [18.x, 20.x]
28 |
29 | steps:
30 | - uses: actions/checkout@v3
31 | - name: Use Node.js ${{ matrix.node-version }}
32 | uses: actions/setup-node@v3
33 | with:
34 | node-version: ${{ matrix.node-version }}
35 | - run: env | sort
36 |
37 | - name: Install React
38 | run: npm install react react-dom prop-types
39 | - run: npm install
40 | - name: Install example
41 | run: cd ./example && npm install && cd ..
42 |
43 | - run: npm run build
44 | - name: Run Cypress tests
45 | run: |
46 | npm run build &
47 | cd ./example && npm run start:silent &
48 | npm run cypress:run -- --record --key ${{ secrets.CYPRESS_KEY }}
49 |
50 | buildDemo:
51 | runs-on: ubuntu-latest
52 | steps:
53 | - uses: actions/checkout@v3
54 | - name: Install React
55 | run: npm install react react-dom prop-types
56 | - run: npm install
57 | - run: npm run build
58 | - name: Install example
59 | run: cd ./example && npm install && cd ..
60 |
61 | # By default, react builds the index.html links with absolute paths. However, the github pages deployment is under a folder name (usually the repo name). Therefore, we must make sure that the index.html links are relative
62 | - name: Set public url
63 | run: cd ./example && echo "PUBLIC_URL=." >> $GITHUB_ENV
64 |
65 | - name: Build demo
66 | run: cd ./example && npm run build && cd ..
67 | - name: Publish demo artifact
68 | uses: actions/upload-pages-artifact@v3
69 | with:
70 | path: ./example/build
71 |
72 | deployGithubPages:
73 | needs: buildDemo
74 | if: github.ref == 'refs/heads/master'
75 |
76 | environment:
77 | name: github-pages
78 | url: ${{ steps.deployment.outputs.page_url }}
79 |
80 | runs-on: ubuntu-latest
81 | steps:
82 | - name: Deploy to GitHub Pages
83 | id: deployment
84 | uses: actions/deploy-pages@v4
85 |
86 |
87 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # See https://help.github.com/ignore-files/ for more about ignoring files.
3 |
4 | # dependencies
5 | node_modules
6 |
7 | # builds
8 | build
9 | dist
10 | .rpt2_cache
11 |
12 | # misc
13 | .DS_Store
14 | .env
15 | .env.local
16 | .env.development.local
17 | .env.test.local
18 | .env.production.local
19 |
20 | npm-debug.log*
21 | yarn-debug.log*
22 | yarn-error.log*
23 |
24 | .idea
25 |
26 | cypress/videos
27 | cypress/screenshots
28 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 |
4 | ## [2.0.2](https://github.com/dizco/react-scrollable-feed/compare/v2.0.1...v2.0.2) (2024-05-11)
5 | * Add debug flag
6 |
7 |
8 | ## [2.0.1](https://github.com/dizco/react-scrollable-feed/compare/v2.0.0...v2.0.1) (2023-12-31)
9 | * Remove src and other directories from the published package
10 |
11 |
12 | ## [2.0.0](https://github.com/dizco/react-scrollable-feed/compare/v1.3.2...v2.0.0) (2023-12-31)
13 | * Fix issue with children props typing ([#87](https://github.com/dizco/react-scrollable-feed/issues/87))
14 | * Updated dependencies
15 | * Build with Node 18 and Node 20
16 | * Rebuild package bundling with [developit/microbundle ](https://github.com/developit/microbundle) instead of Rollup
17 | * Updated license to BSD 3-clause
18 |
19 |
20 | ## [1.3.2](https://github.com/dizco/react-scrollable-feed/compare/v1.3.1...v1.3.2) (2023-01-14)
21 | * Updated dependencies
22 |
23 |
24 | ## [1.3.1](https://github.com/dizco/react-scrollable-feed/compare/v1.3.0...v1.3.1) (2021-05-16)
25 | * Updated dependencies
26 |
27 |
28 | ## [1.3.0](https://github.com/dizco/react-scrollable-feed/compare/v1.2.0...v1.3.0) (2021-02-11)
29 | * Added `scrollToBottom` public method
30 | * Fix issue with scrolling when wrapper has fixed height ([#34](https://github.com/dizco/react-scrollable-feed/issues/34))
31 | * Updated dependencies
32 |
33 |
34 | ## [1.2.0](https://github.com/dizco/react-scrollable-feed/compare/v1.1.2...v1.2.0) (2020-09-30)
35 | * Added `onScroll` prop
36 | * Updated dependencies
37 |
38 |
39 | ## [1.1.2](https://github.com/dizco/react-scrollable-feed/compare/v1.1.1...v1.1.2) (2020-01-01)
40 | * Updated dependencies
41 |
42 |
43 | ## [1.1.1](https://github.com/dizco/react-scrollable-feed/compare/v1.1.0...v1.1.1) (2019-12-03)
44 | * Fix issue with scrolling on Edge, Firefox ([#20](https://github.com/dizco/react-scrollable-feed/issues/20))
45 |
46 |
47 | ## [1.1.0](https://github.com/dizco/react-scrollable-feed/compare/v1.0.4...v1.1.0) (2019-11-10)
48 | * Added `className` prop
49 | * Updated dependencies
50 |
51 |
52 | ## [1.0.4](https://github.com/dizco/react-scrollable-feed/compare/v1.0.3...v1.0.4) (2019-07-09)
53 | * Updated dependencies
54 |
55 |
56 | ## [1.0.3](https://github.com/dizco/react-scrollable-feed/compare/v1.0.2...v1.0.3) (2019-01-30)
57 | * Fixed issue with the automatic bottom detection ([#7](https://github.com/dizco/react-scrollable-feed/issues/7))
58 |
59 |
60 | ## [1.0.2](https://github.com/dizco/react-scrollable-feed/compare/v1.0.1...v1.0.2) (2018-11-25)
61 | * Updated dependencies
62 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Development
2 |
3 | Local development is broken into two parts (ideally using two tabs).
4 |
5 | First, run microbundle to watch your `src/` module and automatically recompile it into `dist/` whenever you make changes.
6 |
7 | ```bash
8 | npm run dev # runs with watch flag
9 | ```
10 |
11 | The second part will be running the `example/` create-react-app that's linked to the local version of your module.
12 |
13 | ```bash
14 | # (in another tab)
15 | cd example
16 | npm start # runs create-react-app dev server
17 | ```
18 |
19 | Now, anytime you make a change to your library in `src/` or to the example app's `example/src`, `create-react-app` will live-reload your local dev server so you can iterate on your component in real-time.
20 |
21 | 
22 |
23 |
24 | #### Publishing to npm
25 |
26 | ```bash
27 | npm publish
28 | ```
29 |
30 | This builds `cjs`, `es` and 'modern' versions of your module to `dist/` and then publishes your module to `npm`.
31 |
32 | Make sure that any npm modules you want as peer dependencies are properly marked as `peerDependencies` in `package.json`. The microbundle config will automatically recognize them as peers and not try to bundle them in your module.
33 |
34 |
35 | #### Deploying to Github Pages
36 |
37 | ```bash
38 | npm run deploy
39 | ```
40 |
41 | This creates a production build of the example `create-react-app` that showcases your library and then runs `gh-pages` to deploy the resulting bundle.
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2023, Gabriel Bourgault
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | 1. Redistributions of source code must retain the above copyright notice, this
9 | list of conditions and the following disclaimer.
10 |
11 | 2. Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | 3. Neither the name of the copyright holder nor the names of its
16 | contributors may be used to endorse or promote products derived from
17 | this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
17 |
18 | UX-wise, asking a user to scroll down manually a chat box when new messages arrive is quite painful. **react-scrollable-feed** aims to alleviate the burden of managing scrolling concerns from React developers. The same concept applies to any other kind of feed where new content arrives dynamically.
19 |
20 | ## Demo
21 |
22 | View a live demo [here](https://dizco.github.io/react-scrollable-feed/).
23 |
24 | 
25 |
26 | ## Install
27 |
28 | ```bash
29 | npm install --save react-scrollable-feed
30 | ```
31 |
32 | ## Usage
33 |
34 | ```tsx
35 | import * as React from 'react'
36 |
37 | import ScrollableFeed from 'react-scrollable-feed'
38 |
39 | class App extends React.Component {
40 | render() {
41 | const items = ['Item 1', 'Item 2'];
42 |
43 | return (
44 |
45 | {items.map((item, i) =>
{item}
)}
46 |
47 | );
48 | }
49 | }
50 | ```
51 |
52 | ## Options
53 |
54 | ### forceScroll
55 |
56 | - Type: `boolean`
57 | - Default: `false`
58 |
59 | If set to true, will scroll to the bottom after each update on the component. By default, if the scrollable section is not at the bottom _before_ the update occurs, it will leave the scroll at the same position.
60 |
61 | ### animateScroll
62 |
63 | - Type: `(element: HTMLElement, offset: number) => void`
64 | - Default:
65 | ```ts
66 | if (element.scrollBy) {
67 | element.scrollBy({ top: offset });
68 | }
69 | else {
70 | element.scrollTop = offset;
71 | }
72 | ```
73 |
74 | Allows to override the scroll animation by any implementation.
75 |
76 | ### onScrollComplete
77 |
78 | - Type: `() => void`
79 | - Default: `() => {}`
80 |
81 | Is called after the scroll animation has been executed.
82 |
83 | ### changeDetectionFilter
84 |
85 | - Type: `(previousProps: ScrollableFeedComponentProps, newProps: ScrollableFeedComponentProps) => boolean`
86 | - Default: `() => true`
87 |
88 | Allows to customize _when_ the scroll should occur. This will be called everytime a `componentDidUpdate` happens, which means everytime one of the props changes. You will receive as parameters the previous and the new props.
89 |
90 | Note: `ScrollableFeedComponentProps` is defined as `React.PropsWithChildren`
91 |
92 | If you want to compare the last children from both the previous and new props, you could do something like this :
93 |
94 | ```tsx
95 | import * as React from 'react'
96 |
97 | import ScrollableFeed from 'react-scrollable-feed'
98 |
99 | class App extends React.Component {
100 | changeDetectionFilter(previousProps, newProps) {
101 | const prevChildren = previousProps.children;
102 | const newChildren = newProps.children;
103 |
104 | return prevChildren !== newChildren
105 | && prevChildren[prevChildren.length - 1] !== newChildren[newChildren.length - 1];
106 | }
107 |
108 | render() {
109 | const items = ['Item 1', 'Item 2'];
110 |
111 | return (
112 |
115 | {items.map((item, i) =>