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

Add support for writing custom JS to interact with FullCalendar's render hooks #164

Merged
merged 11 commits into from
Mar 28, 2024

Conversation

cawecoy
Copy link
Contributor

@cawecoy cawecoy commented Mar 2, 2024

Possibility to use the eventDidMount Event Render Hook:

return $painel->plugins([
    FilamentFullCalendarPlugin::make()->eventDidMount('calendarEventTooltip')
]);

In the example above we are adding a callback to my calendarEventTooltip Javascript function on the FullCalendar eventDidMount Event Render Hook parameter. The result:

const calendar = new Calendar(this.$el, {
    //...
    eventDidMount: calendarEventTooltip
});

The FullCalendar documentation says:

Customize the rendering of event elements with the following options:

(...)

eventDidMount - called right after the element has been added to the DOM. If the event data changes, this is NOT called again.

FullCalendar even has a demo with sourcecode editable in CodePen on how to use eventDidMount to easily create an event Tooltip.

Similar feature was requested twice on saade/filament-fullcalendar: #162 and #135.

Currently, one might think we could use the config(array $config) method:

return $painel->plugins([
    FilamentFullCalendarPlugin::make()->config(['eventDidMount' => 'calendarEventTooltip'])
]);

But it has a problem: the callback function is rendered as string (surrounded by quotes).

const calendar = new Calendar(this.$el, {
    //...
    eventDidMount: 'calendarEventTooltip'
});

That generates the following error: Uncaught SyntaxError: Invalid or unexpected token.

In summary, it's currently not possible to use the FullCalendar eventDidMount through this Filament Plugin and this PR will fix that.

Test: implementing the event tooltip on hover

Following the given example exposed before...

return $painel->plugins([
    FilamentFullCalendarPlugin::make()->eventDidMount('calendarEventTooltip')
]);

...additionaly, we just need to add the following Javascript code to our Filament page:

<script>
    function calendarEventTooltip(info){
        info.el.setAttribute("x-tooltip", "tooltip"); // see the tooltip options: https://github.com/ryangjchandler/alpine-tooltip?tab=readme-ov-file#modifiers
        info.el.setAttribute("x-data", "{ tooltip: '"+info.event.title+"' }"); // we can use info.event.description or anything else sent on the Calendar Widget's fetchEvents method
    }
</script>

And we are done (this example uses the Alpine Tooltip package that comes already installed with Filament by default).

image

@cawecoy
Copy link
Contributor Author

cawecoy commented Mar 13, 2024

Hi @saade! Is there any chance of this PR being accepted? Thx!

@saade
Copy link
Owner

saade commented Mar 13, 2024

I'm thinking on another aproach, what about adding a method eventDidMount on the calendar class, and use like this:

public function eventDidMount(info) {
    return <<<JS
        info.el.setAttribute("x-tooltip", "tooltip");
        info.el.setAttribute("x-data", "{ tooltip: '"+info.event.title+"' }");
    JS;
}

thoughts?

@cawecoy
Copy link
Contributor Author

cawecoy commented Mar 14, 2024

@saade it looks like a great approach. But how will the user customize it? What is the "calendar class" exactly?

@saade
Copy link
Owner

saade commented Mar 14, 2024

@saade it looks like a great approach. But how will the user customize it? What is the "calendar class" exactly?

the default definition for the function will be:

public function eventDidMount(info) {
    //
}

then the user can override this method in their own widget class that extends the FullCalendarWidget

public function eventDidMount(info) {
    // this is the custom js wrote by the user while overriding the default method
    return <<<JS
        info.el.setAttribute("x-tooltip", "tooltip");
        info.el.setAttribute("x-data", "{ tooltip: '"+info.event.title+"' }");
    JS;
}

@cawecoy
Copy link
Contributor Author

cawecoy commented Mar 14, 2024

@saade ok. Another questions:

  1. What is the "calendar class" exactly?
  2. Is your your eventDidMount method a PHP or JavaScript method? I guess it's JavaScript and you used public by mistake?

@cawecoy
Copy link
Contributor Author

cawecoy commented Mar 14, 2024

Oh no, it's PHP because of the <<<JS. But you missed $ in the parameter: public function eventDidMount($info)

How would we make $info accessible via JavaScript info.event.title?

@saade
Copy link
Owner

saade commented Mar 14, 2024

@saade ok. Another questions:

  1. What is the "calendar class" exactly?
  2. Is your your eventDidMount method a PHP or JavaScript method? I guess it's JavaScript and you used public by mistake?
<?php

namespace App\Filament\Widgets;

use Saade\FilamentFullCalendar\Widgets\FullCalendarWidget;

class CalendarWidget extends FullCalendarWidget
{
    // ....
    
    public function eventDidMount(info) {
        return <<<JS
            info.el.setAttribute("x-tooltip", "tooltip");
            info.el.setAttribute("x-data", "{ tooltip: '"+info.event.title+"' }");
        JS;
    }
    
   // ....
}
  1. eventDidMount is a method inside the above class. In the js side you just:
eventDidMount: function (info) {
   this.$wire.eventDidMount(info);
} 

@cawecoy
Copy link
Contributor Author

cawecoy commented Mar 14, 2024

@saade ok. Another questions:

  1. What is the "calendar class" exactly?
  2. Is your your eventDidMount method a PHP or JavaScript method? I guess it's JavaScript and you used public by mistake?
<?php

namespace App\Filament\Widgets;

use Saade\FilamentFullCalendar\Widgets\FullCalendarWidget;

class CalendarWidget extends FullCalendarWidget
{
    // ....
    
    public function eventDidMount(info) {
        return <<<JS
            info.el.setAttribute("x-tooltip", "tooltip");
            info.el.setAttribute("x-data", "{ tooltip: '"+info.event.title+"' }");
        JS;
    }
    
   // ....
}
  1. eventDidMount is a method inside the above class. In the js side you just:
eventDidMount: function (info) {
   this.$wire.eventDidMount(info);
} 

I've tried that, but it's not working somehow... the events are not displayed. No errors on the browser console log. Any ideas?

@saade
Copy link
Owner

saade commented Mar 14, 2024

oh shoot, yes i see why, its something along those lines, but i've expressed myself wrong while writing the snippet. Give me a couple of minutes and i i'll try to implement it

@cawecoy
Copy link
Contributor Author

cawecoy commented Mar 20, 2024

Hi @saade! Do you have any update?

@cawecoy
Copy link
Contributor Author

cawecoy commented Mar 23, 2024

@saade I finally got to make it work through the approach you suggested:

public function eventDidMount() {
    return <<<JS
        function (info){
            info.el.setAttribute("x-tooltip", "tooltip");
            info.el.setAttribute("x-data", "{ tooltip: '"+info.event.title+"' }");
        }
    JS;
}

How about that?

@KhairulAzmi21
Copy link
Contributor

I need this PR :)

@KhairulAzmi21
Copy link
Contributor

@cawecoy . do you have any idea how to styling the tooltip ?

@cawecoy
Copy link
Contributor Author

cawecoy commented Mar 26, 2024

I need this PR :)

@KhairulAzmi21 me too. Sometimes the full event content does not fit in the small event container on the calendar - it often ends up partially hidden. So the Tooltip would provide a great UX here by allowing the user to easily see the full event content just by hovering the event.

I am waiting for the maintainer @saade to review my PR (:

@cawecoy . do you have any idea how to styling the tooltip ?

I style it throught this CSS code:

.tippy-box[data-theme~=light]{
    background-color: rgb(var(--gray-700)) !important;
    color: rgb(255 255 255);
}
.tippy-box[data-theme~=light][data-placement^=top]>.tippy-arrow:before{
    border-top-color: rgb(var(--gray-700)) !important;
}
.tippy-box[data-theme~=light][data-placement^=right]>.tippy-arrow:before{
    border-right-color: rgb(var(--gray-700)) !important;
}
.tippy-box[data-theme~=light][data-placement^=bottom]>.tippy-arrow:before{
    border-bottom-color: rgb(var(--gray-700)) !important;
}
.tippy-box[data-theme~=light][data-placement^=left]>.tippy-arrow:before{
    border-left-color: rgb(var(--gray-700)) !important;
}

FilamentPHP uses Tippy.js. I've found that CSS on its official website: https://atomiks.github.io/tippyjs/v6/themes/

@saade
Copy link
Owner

saade commented Mar 26, 2024

@cawecoy could you test to see if its still working as desired?

@saade saade changed the title eventDidMount Add support for writing custom JS to interact with FullCalendarrender hooks Mar 26, 2024
@saade saade changed the title Add support for writing custom JS to interact with FullCalendarrender hooks Add support for writing custom JS to interact with FullCalendar's render hooks Mar 26, 2024
@saade
Copy link
Owner

saade commented Mar 28, 2024

@cawecoy? :)

@saade saade mentioned this pull request Mar 28, 2024
@cawecoy
Copy link
Contributor Author

cawecoy commented Mar 28, 2024

@cawecoy could you test to see if its still working as desired?

@saade sure! I'm testing it now (:

@cawecoy
Copy link
Contributor Author

cawecoy commented Mar 28, 2024

@saade I just tried all the Event Render Hooks (except eventWillUnmount), they are working as expected.

eventWillUnmount is called right before the event will be removed from the DOM. Any idea on how to remove an event via fullcalendar in order to test it?

@saade
Copy link
Owner

saade commented Mar 28, 2024

@cawecoy i think paginating through the months of the calendar will fire it. Since eventDidMount is fired under the same condition

@cawecoy
Copy link
Contributor Author

cawecoy commented Mar 28, 2024

@cawecoy i think paginating through the months of the calendar will fire it. Since eventDidMount is fired under the same condition

Great idea! It works!

@saade
Copy link
Owner

saade commented Mar 28, 2024

Cool, thank you for your contribution!

@saade saade merged commit 00784bb into saade:3.x Mar 28, 2024
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants