Add hover effect on event title

I have a drupal event calendar, and I need the below functionality:

When I will hover on an event it should show event title and description in tooltip popup.
Is there any way/hook to achieve this?

Proposed resolution

Remaining tasks

User interface changes

API changes

Data model changes

Comments

maheshv created an issue. See original summary.

nitesh624’s picture

Issue summary: View changes
jennypanighetti’s picture

I'm looking for this functionality as well. It is in the Fullcalendar JS library, but is it in this module?

nitesh624’s picture

I don't think this module have this feature. Even if we try to add if try to implement through custom approach. Looks like html tags are getting stripped off in the view.

nitesh624’s picture

I think hovering feature is a must have feature show show title and description When we hover onto a title. Suppose if title is two long (Suppose 7-10 words ) then it will only show few 3-4 words only on calendar. There is no way to read the complete title.

I tried to implement through custom code by using hook_views_plugins_alter() hook to set my custom class like below and then overriding prepareEvent function to include description as extended property use them in Javascript

<?php
/**
 * Implements hook_views_plugins_alter().
 */
function YOUR_MODULE_views_plugins_alter(array &$plugins) {
  if (isset($plugins['style']['fullcalendar'])) {
    $plugins['style']['fullcalendar']['class'] = \Drupal\YOUR_MODULE\Plugin\views\style\FullCalendarCustom::class;
  }
}

and then created a class FullCalendarCustom in my custom mdoule to add description field in

<?php
namespace Drupal\YOUR_MODULE\Plugin\views\style;

use Drupal\fullcalendar\Plugin\views\style\FullCalendar as FullCalendarBase;
use Drupal\Core\Entity\EntityInterface;

class FullCalendarCustom extends FullCalendarBase {

  /**
   * {@inheritdoc}
   */
  // You can copy the original method and modify as needed.
  // Example: Add a custom property to the event array.
  protected function prepareEvent(EntityInterface $entity, string $title, array $fields, int $delta): array {
    $event = parent::prepareEvent($entity, $title, $fields, $delta);

    // --- Your custom logic here ---
    $event['descrption'] = striptags($entity->body->value);
    return $event;
  }

}

But since parent::prepareEvent() is set as private. So its not allowing me to override.

nitesh624’s picture

So Now I am able to get this functionality working and below is the code snippet I had to write to get this working

function custom_calendar_preprocess_views_view__fullcalendar(array &$variables): void {
  if (isset($variables['rows']['#attached']['drupalSettings']['fullcalendar']['js-view-dom-id-' . $variables['dom_id']]['options']['eventSources'])) {
    $events = $variables['rows']['#attached']['drupalSettings']['fullcalendar']['js-view-dom-id-' . $variables['dom_id']]['options']['eventSources'][0];
    // Update the events to include descriptions from nodes.
    $events = array_map(function ($event) {
      // Load the node by ID and set the description from body field.
      $node = \Drupal::entityTypeManager()->getStorage('node')->load($event['id']);
      if ($node instanceof NodeInterface && $node->hasField('body') && !$node->get('body')->isEmpty()) {
        $description = $node->body->value;
        // Trim the description to 160 chars.
        $event['description'] = mb_substr(strip_tags($description), 0, 160);
      }
      else {
        $event['description'] = '';
      }
      return $event;
    }, $events);
    // Update the events in the drupalSettings.
    $variables['rows']['#attached']['drupalSettings']['fullcalendar']['js-view-dom-id-' . $variables['dom_id']]['options']['eventSources'][0] = $events;
  }
}

Then include tippyjs library through CDN

https://unpkg.com/@popperjs/core@2/dist/umd/popper.min.js
https://unpkg.com/tippy.js@6/dist/tippy-bundle.umd.js

and below is the js file content

(function eventTooltip(Drupal, drupalSettings, once, tippy, FullCalendar) {
  Drupal.behaviors.calendarTooltip = {
    attach(context) {
      // Resets the "once" behavior for .fullcalendar--wrapper
      once.remove('fullCalendarBuild', '.fullcalendar--wrapper', context);
      once('fullCalendarBuild', '.fullcalendar--wrapper', context).forEach(
        (calendarEl) => {
          const domId = calendarEl.id;
          if (drupalSettings.fullcalendar.hasOwnProperty(domId)) {
            const settings = drupalSettings.fullcalendar[domId];
            const calendarOptions = settings.options;
              calendarOptions.eventDidMount = function addTooltip(info) {
                // Format the date as "MMM d" (e.g., "Jan 1")
                const formattedDate = info.event.start.toLocaleDateString(undefined, { month: 'long', day: 'numeric' });
                // Custom tooltip logic
                tippy(info.el, {
                  content: `<div class="event-tooltip-title">${info.event.title}</div><div class="event-tooltip-date">${formattedDate}</div><div class="event-tooltip-description">${info.event.extendedProps.description}</div>`,
                  allowHTML: true,
                });
              };
            // Render the calendar as usual
            const calendar = new FullCalendar.Calendar(calendarEl, calendarOptions);
            calendar.render();
          }
        }
      );
    }
  };
})(Drupal, drupalSettings, once, window.tippy, window.FullCalendar);

One major drawback of this approach is first I have to stop the rendering of existing calendar using below line
once.remove('fullCalendarBuild', '.fullcalendar--wrapper', context);

Then update the calendar option to attach callback and then create the new object of calendar and re-rendering the calendar like below

 const calendar = new FullCalendar.Calendar(calendarEl, calendarOptions);
 calendar.render();
nitesh624’s picture

I am not able to find any way to reuse the existing calendar object and add the callback function before rendering.

mortona2k’s picture

nitesh624’s picture

yes that option I already tried. but I could not see anything to check if calendar is already rendered or not

mandclu’s picture

Providing some form of tooltip display option is definitely a priority for this module. There seem to be quite a few examples online (including on FullCalendar.io) that show using Tippy.js, but it doesn't look like that library's repo has seen any updates in over 3 years and has now been marked as read only, which doesn't give me confidence. Floating UI looks to be actively maintained which makes it look like a better candidate.

As to how this could be configured for Drupal, I would favour allowing a site builder to choose a view mode that would be used to render the content that would appear within the tooltip. That should allow virtually limitless customization options (including the use of custom templates, if desired) but does have the potential drawback that a misconfiguration (for example, choosing view mode that hasn't been set up for a content type) could cause full nodes to be rendered for the tooltip displays, causing the view render to be huge.

nitesh624’s picture

Yes this could be the good option

erutan’s picture

There's built in functionality in fullcalendar that does this already that might be worth looking at vs recreating with a third party solution:

https://fullcalendar.io/docs/event-popover

https://www.drupal.org/project/fullcalendar/issues/3491161 appears to be a dupe of this issue.

mandclu’s picture

The event popover seems designed to handle the use case of there being too many events to be shown within the calendar cell. While very useful, that seems different from what is being discussed here. I do agree that #3491161: Allow opening of events in a modal dialog is a duplicate, so thank you for closing that.

One thing from that issue to bring here is the potential approach of just using Drupal's AJAX API. I'm not 100% sure that simply adding the markup will work, but it's definitely worth a try. A big advantage of that approach would be not having to worry about additional rendering or markup in the page.

That said, there are potential advantages to something more customizable that's designed specifically to show an embedded view of the event information within the calendar. But if it works, the AJAX API version could be a good first step with low overhead.

erutan’s picture

Good catch on the popover, to be fair I just skimmed it.

I've been using (abusing?) the ajax API on a project to load form modes and whatnot into a model and having a ?destination= redirect back to the page the view on for doing larger content edits on the fly. I have it in views rewrites as well as templates. :)

<a href="/storage/{{ id }}/edit/?display=FOO&destination=/BAR" class="use-ajax" data-dialog-type="dialog">✍️</a>

This could simply be documented somewhere.

fullcalendar_view iirc has an option where you check a box and then the last field in the view will get loaded in a modal. With rewrites and custom text fields this is a nice workable option.

mandclu’s picture

Yeah the existing JS in this module leverages those properties already for the feature that double-clicking on a time slot or date opens a form to create a new event, and I can verify that it's very convenient. The challenge is that passing it into FullCalendar is not as straightforward. The class is no problem but data attributes aren't as well documented. I think adding it within extendedProps might work.

coaston’s picture

Hi guys, is there any update ?

I really need this featute. Came here because migrating from module fullcalendar_view because of perfomance issues and really missing this feature.

I have tried #14 and replaced "title" with ajax link but it works elsewhere but not with fullcalendar display mode it seems.

Any patch?