Skip to content

Commit dcb344f

Browse files
Docs: Added package docs.
1 parent efe40e7 commit dcb344f

File tree

5 files changed

+281
-25
lines changed

5 files changed

+281
-25
lines changed

README.md

Lines changed: 248 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,286 @@
11
<p align="center">
22
<img src="art/package-art.png" alt="Art">
33
<p align="center">
4-
<a href="https://github.com/codelabmw/package-name/actions"><img alt="GitHub Workflow Status (master)" src="https://github.com/codelabmw/package-name/actions/workflows/run-tests.yml/badge.svg"></a>
5-
<a href="https://packagist.org/packages/codelabmw/package-name"><img alt="Total Downloads" src="https://img.shields.io/packagist/dt/codelabmw/package-name"></a>
6-
<a href="https://packagist.org/packages/codelabmw/package-name"><img alt="Latest Version" src="https://img.shields.io/packagist/v/codelabmw/package-name"></a>
7-
<a href="https://packagist.org/packages/codelabmw/package-name"><img alt="License" src="https://img.shields.io/packagist/l/codelabmw/package-name"></a>
4+
<a href="https://github.com/codelabmw/laravel-infinite-scroll/actions"><img alt="GitHub Workflow Status (master)" src="https://github.com/codelabmw/laravel-infinite-scroll/actions/workflows/run-tests.yml/badge.svg"></a>
5+
<a href="https://packagist.org/packages/codelabmw/laravel-infinite-scroll"><img alt="Total Downloads" src="https://img.shields.io/packagist/dt/codelabmw/laravel-infinite-scroll"></a>
6+
<a href="https://packagist.org/packages/codelabmw/laravel-infinite-scroll"><img alt="Latest Version" src="https://img.shields.io/packagist/v/codelabmw/laravel-infinite-scroll"></a>
7+
<a href="https://packagist.org/packages/codelabmw/laravel-infinite-scroll"><img alt="License" src="https://img.shields.io/packagist/l/codelabmw/laravel-infinite-scroll"></a>
88
</p>
99
</p>
1010

11-
A description of what the package is and what it does.
11+
# Laravel Infinite Scroll
12+
13+
A Laravel package for easily implementing infinite scroll in your Inertia.js applications, regardless of frontend stack (React, Vue, Svelte, etc.).
14+
15+
This package is designed to be stack-agnostic: each frontend stack (React, Vue, Svelte, etc.) can have its own Infinite Scroll component. The package provides a convenient mechanism to install and use the appropriate component for your stack.
16+
17+
---
18+
19+
## 📚 Table of Contents
20+
21+
- [Overview](#overview)
22+
- [Features](#features)
23+
- [Requirements](#requirements)
24+
- [Installation](#installation)
25+
- [Quick Start](#quick-start)
26+
- [Usage](#usage)
27+
- [Backend: InfiniteScroll Facade](#backend-infinitescroll-facade)
28+
- [Frontend: React Component](#frontend-react-component)
29+
- [API Reference](#api-reference)
30+
- [InfiniteScroll::make](#infinitescrollmake)
31+
- [Console Commands](#console-commands)
32+
- [Stacks & Extensibility](#stacks--extensibility)
33+
- [Troubleshooting & FAQ](#troubleshooting--faq)
34+
- [Contributing](#contributing)
35+
- [Changelog](#changelog)
36+
- [License](#license)
37+
38+
---
39+
40+
## Overview
41+
42+
**Laravel Infinite Scroll** brings a plug-and-play infinite scrolling experience to your Inertia.js powered Laravel apps. It seamlessly integrates backend pagination and frontend rendering, making your lists and feeds truly dynamic.
43+
44+
## Features
45+
46+
- Effortless infinite scroll for Eloquent queries, Paginators, and CursorPaginators
47+
- Stack-agnostic: works with any Inertia frontend adapter (React, Vue, Svelte, etc.)
48+
- Comes with ready-to-use components for common stacks (currently React; others can be contributed)
49+
- Artisan command to install stack-specific components with stack detection and custom path support
50+
- Fully typed, tested, and extensible
51+
52+
## Requirements
53+
54+
- PHP ^8.3
55+
- Laravel 10+
56+
- Inertia.js (any frontend adapter: React, Vue, Svelte, etc.)
1257

1358
## Installation
1459

15-
> Requires PHP ^8.3
60+
```bash
61+
composer require codelabmw/laravel-infinite-scroll
62+
```
1663

17-
You can install the package via composer:
64+
Publish the frontend infinite scroll component for your stack:
1865

1966
```bash
20-
composer require codelabmw/package-name
67+
php artisan install:infinite-scroll
2168
```
2269

23-
## Usage
70+
The install command will list all supported stacks and prompt you to choose your stack and the path where to publish the component. It will also try to detect your current stack and set it as the default in the selection menu.
71+
72+
> **Note:** Currently, only React stack components are included. Support for other stacks (Vue, Svelte, etc.) is planned for future versions and community contributions are welcome!
2473
25-
Instructions on how the package should and can be used including code examples.
74+
## Quick Start
75+
76+
### Backend: Add Infinite Scroll to Your Controller
2677

2778
```php
2879
<?php
2980

3081
declare(strict_types=1);
3182

32-
namespace Codelabmw\Package;
83+
namespace App\Http\Controllers;
84+
85+
use App\Models\Article;
86+
use Codelabmw\InfiniteScroll\Facades\InfiniteScroll;
87+
use Inertia\Inertia;
88+
89+
final class ArticleController extends Controller
90+
{
91+
public function index()
92+
{
93+
$articles = Article::query();
94+
// You can use a Builder, CursorPaginator, or Paginator
95+
return Inertia::render('articles/index', InfiniteScroll::make('articles', $articles));
96+
}
97+
}
98+
```
99+
100+
### Frontend: Use the Infinite Scroll Component
101+
102+
Depending on your stack, the component will be installed in a different location. For React, it will be installed in `resources/js/components/infinite-scroll.tsx` unless specified otherwise.
103+
104+
```tsx
105+
import InfiniteScroll from "@/components/infinite-scroll";
106+
107+
interface Props {
108+
articles: { data: Article[] };
109+
}
110+
111+
export default function ArticlesIndex({ articles }: Props) {
112+
return (
113+
<div>
114+
<InfiniteScroll data="articles">
115+
{articles.data.map((article) => (
116+
<div key={article.id}>{article.title}</div>
117+
))}
118+
</InfiniteScroll>
119+
</div>
120+
);
121+
}
122+
```
123+
124+
---
125+
126+
## Usage
127+
128+
### Backend: InfiniteScroll Facade
129+
130+
The core backend API is the `InfiniteScroll` facade:
131+
132+
#### `InfiniteScroll::make(string $key, Builder|CursorPaginator|Paginator $data, int $perPage = 15, array $columns = ['*']): array`
133+
134+
- **$key**: The name of the prop (e.g. `'articles'`) as will be used in the frontend component.
135+
- **$data**: An Eloquent Builder, CursorPaginator, or Paginator (LengthAwarePaginator|SimplePaginator) instance.
136+
- **$perPage**: Items per page (default: 15). _Used if $data is a Builder_.
137+
- **$columns**: Columns to select (default: all). _Used if $data is a Builder_.
138+
139+
**Returns:**
140+
An array of props for Inertia, including:
141+
142+
- `$key` (deferred data) - The data that was passed to the facade.
143+
- `type` (pagination type) - Either `cursor` or `paged`
144+
- `cursor` or `page` (depending on paginator) - If cursor pagination is used, this will be the cursor to fetch the next page. If paged pagination is used, this will be the page number of the current page.
145+
- `has_more` (bool) - Whether there are more pages to fetch.
146+
- `per_page` (int) - The number of items per page.
147+
148+
**Example:**
149+
150+
```php
151+
InfiniteScroll::make('articles', Article::query());
152+
```
153+
154+
### Frontend: Use the Infinite Scroll Component
155+
156+
After publishing, import and use the `InfiniteScroll` component for your stack in your Inertia pages. The component will handle fetching more data as the user scrolls.
157+
158+
---
159+
160+
## API Reference
161+
162+
### InfiniteScroll::make
33163

34-
final class Example
164+
See [Backend Usage](#backend-infinitescroll-facade) for signature and example.
165+
166+
### Console Commands
167+
168+
#### `php artisan install:infinite-scroll`
169+
170+
Publishes infinite scroll components of your chosen stack to your resources directory.
171+
172+
### Stacks & Extensibility
173+
174+
- **Stack detection:** The install command tries to auto-detect your current stack (e.g., React) and sets it as the default in the selection menu.
175+
- **Multiple stacks supported:** The package is designed to support any Inertia frontend stack. Each stack implementation is responsible for its own frontend component.
176+
- **Adding custom stacks:** You can extend the package by implementing the `Stack` contract and registering your stack and component. Community contributions for additional stacks (Vue, Svelte, etc.) are encouraged!
177+
178+
---
179+
180+
## Troubleshooting & FAQ
181+
182+
- **Q: The infinite scroll component is not loading?**
183+
- A: Make sure you have run the install command and imported the component correctly.
184+
- A: Make sure you do not have a type error when specifying the data key.
185+
- **Q: How do I customize the frontend component?**
186+
- A: Use component props (`whileLoading` | `whileNoMoreData`) to override default behavior.
187+
- A: Edit the published component in your resources directory or where you published it.
188+
- **Q: Does this work with other stacks?**
189+
- A: Currently, only React components are supported out of the box. You can add other stacks by contributing to the package.
190+
191+
---
192+
193+
## Contributing
194+
195+
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines and run the test suite before submitting a PR.
196+
197+
### Adding Support for a New Stack
198+
199+
To add support for a new Inertia frontend stack (e.g., Vue, Svelte):
200+
1. **Implement the `Stack` contract** in `src/Contracts/Stack.php` for your stack.
201+
2. **Provide the frontend component** for your stack (e.g., Vue or Svelte InfiniteScroll component) in stubs directory.
202+
3. **Register your stack** in the package so it appears in the install command selection menu in `src/SupportedStacks.php`.
203+
4. **Test your integration** and update documentation/examples as needed.
204+
205+
Community contributions for new stacks are highly encouraged!
206+
207+
#### Example
208+
209+
```php
210+
namespace Codelabmw\InfiniteScroll\Stacks;
211+
212+
use Codelabmw\InfiniteScroll\Contracts\Stack;
213+
214+
final class MyStack implements Stack
35215
{
36216
/**
37-
* Creates Example instance.
217+
* The display name of the stack.
218+
*/
219+
public function getLabel(): string
220+
{
221+
return 'MyStack';
222+
}
223+
224+
/**
225+
* The default installation path of components for this stack.
38226
*/
39-
public function __construct(private readonly Package $package)
227+
public function getDefaultInstallationPath(): string
40228
{
41-
$package->doSomething();
229+
return 'resources/js/components';
230+
}
231+
232+
/**
233+
* The paths of the stubs to copy.
234+
*
235+
* @return Collection<int, string>
236+
*/
237+
public function getStubs(): Collection
238+
{
239+
return collect([
240+
FileSystem::stubs('my-stack/infinite-scroll.ext'),
241+
]);
242+
}
243+
244+
/**
245+
* Whether this stack is the current one.
246+
*/
247+
public function isCurrent(): bool
248+
{
249+
return $this->isTheCurrentStackInUseByApplication();
42250
}
43251
}
44252
```
45253

46-
## Changelog
254+
Place your components in `stubs/stack/stub-name.ext`.
47255

48-
Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
256+
Then, register your stack in `src/SupportedStacks.php`:
49257

50-
## Contributing
258+
```php
259+
//...
260+
261+
class SupportedStacks
262+
{
263+
/**
264+
* @return array<int, class-string<Stack>>
265+
*/
266+
public function get(): array
267+
{
268+
return [
269+
//...
270+
MyStack::class,
271+
];
272+
}
273+
}
274+
```
51275

52-
Please see [CONTRIBUTING](CONTRIBUTING.md) for details.
276+
Now your stack will appear in the install command menu!
277+
---
53278

54-
## Credits
279+
## Changelog
280+
281+
Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
55282

56-
- [Chikondi Kamwendo](https://github.com/kondi3)
57-
- [All Contributors](../../contributors)
283+
---
58284

59285
## License
60286

art/package-art.png

773 Bytes
Loading

src/Stacks/React.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ final class React implements Stack
1515
*/
1616
public function getLabel(): string
1717
{
18-
return 'React';
18+
return 'React-TS';
1919
}
2020

2121
/**

tests/Feature/Console/Commands/InstallCommandTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ public function getStubs(): Collection
4444
});
4545

4646
it('requires input', function (): void {
47+
// Arrange
48+
$mock = Mockery::mock($this->reactStack);
49+
50+
$this->app->bind(React::class, fn () => $mock);
51+
4752
// Act & Assert
4853
$this->artisan('install:infinite-scroll')
4954
->expectsQuestion('Which stack are you using?', 'React')
@@ -79,6 +84,31 @@ public function getStubs(): Collection
7984
->assertExitCode(1);
8085
});
8186

87+
it('does not overwrite existing files', function (): void {
88+
// Arrange
89+
$mock = Mockery::mock($this->reactStack);
90+
$stubFile = FileSystem::stubs('components/react/ts/infinite-scroll.tsx');
91+
$destinationDir = FileSystem::tests('Fixtures/Storage');
92+
$destinationFile = $destinationDir.'/infinite-scroll.tsx';
93+
94+
FileSystem::ensureDirectoryExists($destinationDir);
95+
file_put_contents($destinationFile, 'original content');
96+
97+
$mock->shouldReceive('getStubs')->andReturn(collect([$stubFile]));
98+
$this->app->bind(React::class, fn () => $mock);
99+
100+
// Act & Assert
101+
$this->artisan('install:infinite-scroll')
102+
->expectsQuestion('Which stack are you using?', 'React')
103+
->expectsQuestion('Where do you want to install components?', $destinationDir)
104+
->assertExitCode(0);
105+
106+
expect(file_get_contents($destinationFile))->toBe('original content');
107+
108+
// Cleanup
109+
FileSystem::delete($destinationFile);
110+
});
111+
82112
it('installs proper files', function (): void {
83113
// Arrange
84114
$mock = Mockery::mock($this->reactStack);

tests/Unit/Stacks/ReactTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@
3232
}
3333
});
3434

35-
test('getLabel returns React', function (): void {
35+
test('getLabel returns React-TS', function (): void {
3636
// Act
3737
$label = $this->react->getLabel();
3838

3939
// Assert
40-
expect($label)->toBe('React');
40+
expect($label)->toBe('React-TS');
4141
});
4242

4343
test('getDefaultInstallationPath returns expected path', function (): void {

0 commit comments

Comments
 (0)