saade / filament-fullcalendar

The Most Popular JavaScript Calendar as a Filament Widget
MIT License
295 stars 88 forks source link

events not captured after months change #197

Open lorenzoalulithos opened 5 months ago

lorenzoalulithos commented 5 months ago

I report a strange bug.

I am using the eventDidMount and eventDidMount methods to try to improve the smartphone responsive nature of the calendar.

So far so good, except that I have noticed that when I change the month, the events are no longer captured and the changes made to the DOM are restored. The bug is triggered by increasing the months by 2 or decreasing them to more than 5.

This is my implementation:

   /**
     * @return string
     */
    public function eventDidMount(): string
    {
        return <<<JS
                function({ event, timeText, isStart, isEnd, isMirror, isPast, isFuture, isToday, el, view }){
                    el.setAttribute("x-tooltip", "tooltip");
                    el.setAttribute("x-data", "{ tooltip: '"+event.title+"' }");

                    el.style.backgroundColor = event.extendedProps.is_sent ? 'green' : 'red';

                    const weekButton = document.querySelector('button[title="Settimana"]');

                    if (weekButton) {
                        weekButton.textContent = 'Sett';

                        weekButton.addEventListener('click', () => {
                            if (weekButton.textContent.trim() !== 'Sett') {
                                weekButton.textContent = 'Sett';
                            }
                        });
                    }

                    const dayButton = document.querySelector('button[title="Giorno"]');

                    if (dayButton) {
                        dayButton.addEventListener('click', () => {
                            if (weekButton.textContent.trim() !== 'Sett') {
                                weekButton.textContent = 'Sett';
                            }
                        });
                    }
                }
            JS;
    }

     /**
     * @return string
     */
    public function eventContent(): string
    {
        return <<<JS
                function({ event, timeText, isStart, isEnd, isMirror, isPast, isFuture, isToday, el, view }){
                    let currentDate = document.getElementById('current-date');console.log(event._context.calendarApi.currentData, currentDate)
                    currentDate.innerText = event._context.calendarApi.currentData.viewTitle
                }
            JS;
    }

on load:

Screenshot 2024-07-02 alle 11 19 24

after 1 click

Screenshot 2024-07-02 alle 11 19 30

after 2 click:

Screenshot 2024-07-02 alle 11 19 35

as you can see, the month disappears and the button is not updated.

Using the console.log inside the methods nothing is printed out after the 2nd click.

EDIT: I would add that I have found that the method is not triggered if there is no model in the selected month.

saade commented 4 months ago

I could not even render the calendar properly with the code you've provided. Can you make a simple reproduction repository so i can have a look?

lorenzoalulithos commented 4 months ago

@saade

I developed a workaround:

   return Visit::withDataRangeAndRoles(
            Arr::get($info, 'start'),
            Arr::get($info, 'end')
        )
                    ->get()
                    ->map(
                        fn (Visit $visit) => [
                            'id' => $visit->id,
                            'title' => $visit->company_name,
                            'start' => $visit->arrival_date,
                            'end' => $visit->departure_date,
                            'url' => $visit->resource_detail_url,
                            'shouldOpenUrlInNewTab' => false,
                            'is_sent' => $visit->is_sent,
                        ]
                    )
                    ->whenEmpty(function () use ($info) {
                        return collect([
                            [
                                'id' => 0,
                                'title' => 'fake-model',
                                'start' => Arr::get($info, 'start'),
                                'end' => Arr::get($info, 'end'),
                            ],
                        ]);
                    })
                    ->all();
    }

When the method returns an empty array I create a dummy object, which I remove in the 'eventDidMount' method

 public function eventDidMount(): string
    {
        return <<<JS
                function({ event, timeText, isStart, isEnd, isMirror, isPast, isFuture, isToday, el, view }){
                    el.setAttribute("x-tooltip", "tooltip");
                    el.setAttribute("x-data", "{ tooltip: '"+event.title+"' }");

                    if(event.title === 'fake-model'){
                        el.style.display = 'none'
                    }

I also added this property to retrieve the value of the month.


final class CalendarWidget extends FullCalendarWidget
{
    /**
     * {@inheritDoc}
     */
    public $defaultAction;

    public string|Model|null $model = Visit::class;

    /**
     * @var string
     */
    public string $mountLabel = '';
@php
    use \Saade\FilamentFullCalendar\FilamentFullCalendarPlugin;
    use \App\Filament\Widgets\CalendarWidget;
    use \Filament\Support\Facades\FilamentAsset;

    /** @var FilamentFullCalendarPlugin $plugin */
    $plugin = FilamentFullCalendarPlugin::get();

    /** @var CalendarWidget $calendar */
    $calendar = $this;
@endphp

<x-filament-widgets::widget>
    <x-filament::section class="full-height-calendar-container">
        <div class="flex justify-between flex-1 mb-4">
            <div>{{$this->mountLabel}}</div>

            <x-filament-actions::actions :actions="$this->getCachedHeaderActions()" class="shrink-0"/>
        </div>

        <div class="filament-fullcalendar full-height-calendar-section" wire:ignore ax-load
             ax-load-src="{{ FilamentAsset::getAlpineComponentSrc('filament-fullcalendar-alpine', 'saade/filament-fullcalendar') }}"
             ax-load-css="{{ FilamentAsset::getStyleHref('filament-fullcalendar-styles', 'saade/filament-fullcalendar') }}"
             x-ignore x-data="fullcalendar({
                locale: @js($plugin->getLocale()),
                plugins: @js($plugin->getPlugins()),
                schedulerLicenseKey: @js($plugin->getSchedulerLicenseKey()),
                timeZone: @js($plugin->getTimezone()),
                config: @js($this->getConfig()),
                editable: @json($plugin->isEditable()),
                selectable: @json($plugin->isSelectable()),
                eventClassNames: {!! htmlspecialchars($this->eventClassNames(), ENT_COMPAT) !!},
                eventContent: {!! htmlspecialchars($this->eventContent(), ENT_COMPAT) !!},
                eventDidMount: {!! htmlspecialchars($this->eventDidMount(), ENT_COMPAT) !!},
                eventWillUnmount: {!! htmlspecialchars($this->eventWillUnmount(), ENT_COMPAT) !!},
            })">
        </div>
    </x-filament::section>

    <x-filament-actions::modals/>
</x-filament-widgets::widget>

I know that by setting the configurations I can have the name of the month automatically. But I had to do this to fidx the responsive from all devices :)