Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: lazy loading the events data #17

Merged
merged 6 commits into from
Jul 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
79 changes: 74 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ use Saade\FilamentFullCalendar\Widgets\FullCalendarWidget;

class CalendarWidget extends FullCalendarWidget
{

/**
* Return events that should be rendered statically on calendar first render.
*/
public function getViewData(): array
{
return [
Expand All @@ -84,10 +86,22 @@ class CalendarWidget extends FullCalendarWidget
]
];
}

/**
* FullCalendar will call this function whenever it needs new event data.
* This is triggered when the user clicks prev/next or switches views on the calendar.
*/
public function fetchEvents(array $fetchInfo, array $ignorableIds): array
{
// You can use $fetchInfo to filter events by date, and $ignorableIds to ignore already displayed events.
return [];
}
}
```

> You should return an array of FullCalendar [EventObject](https://fullcalendar.io/docs/event-object).
You can use one or both methods to fetch events.

> Both methods should retun an array of [EventObject](https://fullcalendar.io/docs/event-object).


<br>
Expand Down Expand Up @@ -132,7 +146,7 @@ return [

# Listening for events

The only events supported right now are: [EventClick](https://fullcalendar.io/docs/eventClick) and [EventDrop](https://fullcalendar.io/docs/eventDrop)
The only event-related events supported right now are: [EventClick](https://fullcalendar.io/docs/eventClick) and [EventDrop](https://fullcalendar.io/docs/eventDrop)

They're commented out by default so livewire does not spam requests without they being used. You are free to paste them in your `CalendarWidget` class. See: [FiresEvents](https://github.com/saade/filament-fullcalendar/blob/main/src/Widgets/Concerns/FiresEvents.php)

Expand Down Expand Up @@ -171,7 +185,14 @@ The process of saving and editing the event is up to you, since this plugin does

## Creating Events:

Override the `createEvent` function in your widget class, and you are ready to go!
Events can be created in two ways.

- Clicking on a day (default)
- Selecting a date range (click and drag across calendar days) (you need to opt-in for this, set `selectable => true` in the config file.)

This will open the Create Event modal.

When the create form gets submitted, it will call the `createEvent` function on your widget. Be sure to add the snippet below to your calendar class.

```php
public function createEvent(array $data): void
Expand Down Expand Up @@ -200,7 +221,11 @@ protected static function getCreateEventFormSchema(): array

## Editing Events:

Override the `editEvent` function in your widget class, and you are ready to go!
Events can be edited by clicking on an event on the calendar.

This will open the Edit Event modal.

When the edit form gets submitted, it will call the `editEvent` function on your widget. Be sure to add the snippet below to your calendar class.

```php
public function editEvent(array $data): void
Expand Down Expand Up @@ -271,6 +296,50 @@ public function yourMethod(): void

<br>

# Filtering events based on the calendar view

If you want to filter your events based on the days that are currently shown in the calendar, you can implement the `fetchInfo()` method from the [CanFetchEvents](https://github.com/saade/filament-fullcalendar/blob/main/src/Widgets/Concerns/CanFetchEvents.php) trait. Add the following code to your calendar widget:

```php
/**
* FullCalendar will call this function whenever it needs new event data.
* This is triggered when the user clicks prev/next or switches views.
*
* @see https://fullcalendar.io/docs/events-function
* @param array $fetchInfo start and end date of the current view
* @param array $ignorableIds ids of the events that are already loaded and should be ignored
*/
public function fetchEvents(array $fetchInfo, array $ignorableIds): array
{
return [];
}
```

you can filter events based on the timespan `$fetchInfo['start']` and `$fetchInfo['end']`.
> **Warning**
>
> Keep in mind that returning events that are already in the calendar, can cause duplicates. You should filter them out using the `$ignorableIds` ids.

example:
```php
public function fetchEvents(array $fetchInfo, array $ignorableIds): array
{
$schedules = Appointment::query()
->where([
['start_at', '>=', $fetchInfo['start']],
['end_at', '<', $fetchInfo['end']],
])
->whereNotIn('id', $ignorableIds)
->get();

$data = $schedules->map( ... );

return $data;
}
```

<br>

## Testing

```bash
Expand Down
32 changes: 27 additions & 5 deletions resources/views/fullcalendar.blade.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@php($locale = strtolower(str_replace('_', '-', $this->getConfig()['locale'])))
@php($locale = strtolower(str_replace('_', '-', $this->config('locale', config('app.locale')))))

<x-filament::widget>
<x-filament::card>
Expand All @@ -8,8 +8,11 @@
x-init='
document.addEventListener("DOMContentLoaded", function() {
const config = @json($this->getConfig());
const events = @json($events);
const locale = "{{ $locale }}";
const events = @json($events);
const cachedEventIds = [
...events.map(event => event.id),
];

const eventClick = function ({ event, jsEvent }) {
if( event.url ) {
Expand Down Expand Up @@ -41,21 +44,40 @@
@endif
}

const fetchEvents = function ({ start, end }, successCallback, failureCallback) {
@if( $this::canFetchEvents() )
return $wire.fetchEvents({ start, end }, cachedEventIds)
.then(events => {
// Cache fetched events
cachedEventIds.push(...events.map(event => event.id));

return successCallback(events);
})
.catch( failureCallback );
@else
return successCallback([]);
@endif
}

const calendar = new FullCalendar.Calendar($el, {
...config,
locale,
events,
eventClick,
eventDrop,
dateClick,
select,
eventSources:[
{ events },
fetchEvents
]
});

calendar.render();

window.addEventListener("filament-fullcalendar:refresh", (event) => {
window.addEventListener("filament-fullcalendar:refresh", () => {
calendar.removeAllEvents();
event.detail.data.map(event => calendar.addEvent(event));
cachedEventIds.length = 0;
calendar.refetchEvents();
});
})
'></div>
Expand Down
25 changes: 25 additions & 0 deletions src/Widgets/Concerns/CanFetchEvents.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace Saade\FilamentFullCalendar\Widgets\Concerns;

trait CanFetchEvents
{
/**
* FullCalendar will call this function whenever it needs new event data. This is triggered when the user clicks prev/next or switches views.
*
* Commented out so we can save some requests :) Feel free to extend it.
*
* @see https://fullcalendar.io/docs/events-function
* @param array $fetchInfo start and end date of the current view
* @param array $ignorableIds ids of the events that are already loaded and should be ignored
*/
// public function fetchEvents(array $fetchInfo, array $ignorableIds): array
// {
// return [];
// }

public static function canFetchEvents(): bool
{
return method_exists(static::class, 'fetchEvents');
}
}
2 changes: 1 addition & 1 deletion src/Widgets/Concerns/CanRefreshEvents.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ trait CanRefreshEvents
{
protected function refreshEvents(): void
{
$this->dispatchBrowserEvent('filament-fullcalendar:refresh', ['data' => $this->getViewData()]);
$this->dispatchBrowserEvent('filament-fullcalendar:refresh');
}
}
8 changes: 8 additions & 0 deletions src/Widgets/Contracts/LazyLoading.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Saade\FilamentFullCalendar\Widgets\Contracts;

interface LazyLoading
{
public function lazyLoadViewData($fetchInfo = null): array;
}
2 changes: 2 additions & 0 deletions src/Widgets/FullCalendarWidget.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Filament\Forms\Contracts\HasForms;
use Filament\Widgets\Widget;
use Illuminate\View\View;
use Saade\FilamentFullCalendar\Widgets\Concerns\CanFetchEvents;
use Saade\FilamentFullCalendar\Widgets\Concerns\CanManageEvents;
use Saade\FilamentFullCalendar\Widgets\Concerns\CanRefreshEvents;
use Saade\FilamentFullCalendar\Widgets\Concerns\FiresEvents;
Expand All @@ -18,6 +19,7 @@ class FullCalendarWidget extends Widget implements HasForms
}

use CanRefreshEvents;
use CanFetchEvents;
use FiresEvents;
use UsesConfig;

Expand Down