stefanuebe/vaadin-fullcalendar

Feature request: context menu for an calendar entry

Closed this issue · 3 comments

It would be nice, if an entry in the calendar can get a context menu like other Vaadin components.
For a requirement to copy the text of the tooltip into the clipboard, I put a small snippet in the event handler for "mouseenter"of my fullcalendar-tooltip.js but maybe there are additional requirements in the future for what a context menu for such an entry would be more convenient.

...
let tooltip = e.event.getCustomProperty("tooltip", e.event.title);

// Moderne Clipboard API
navigator.clipboard.writeText(tooltip)
    .then(() => {
        console.log('Text copied');
    })
    .catch(err => {
        console.error('error on copy text:', err);
    });

e.el._tippy = tippy(e.el, {
...

Addition: check, if it is possible and suitable to have a general possibility to allow client side events beside the already existing ones. Maybe FC already allows the defintion of native/custom events per event, but I am not aware of that atM.

With the latest 6.2.0 it is now possible to register event handlers for native javascript events. With that you can create a custom context menu based on the clicked entry. Using the Vaadin Context Menu will most likely not be possible as that requires a Vaadin component to be targeted. Entries are not components. But you may use some overlay on your own to create a context menu like popup, e.g. with the Popup Addon

https://github.com/stefanuebe/vaadin-fullcalendar/wiki/FullCalendar-Examples#use-native-javascript-events-for-entries

This is a sample on how to integrate a context menu for entries. It is optimizable, but should provide a good foundation for any addition content :)

@Route(...)
public void MyCalendarView extends VerticalLayout {
    
    private Popup popup;
   
    public MyCalendarView() {

        FullCalendar calender = new FullCalendar();
        // adds a contextmenu / right client event listener, that calls our openContextMenu. 
        // "this" is the fc object, "this.el" is the Flow element and "this.el.parentElement" is our current view.
        // This hierarchy access may changed, when you nest the FC into other containers. 

        calendar.addEntryNativeEventListener("contextmenu",
                "e => {" +
                "   e.preventDefault(); " +
                "   this.el.parentElement.$server.openContextMenu(info.event.id);" +
                "}");

        // by default, the entry element has no id attribute. Therefore we have to add it ourselves, using the 
        // entry id, that is by default an auto generated UUID
        calendar.setEntryDidMountCallback("""
                function(info) {
                    info.el.id = "entry-" + info.event.id;
                }""");

    }

    @ClientCallable
    public void openContextMenu (String id){
        initPopup(); // init the popp

        popup.removeAll(); // remove old content


        // setup the context menu
        // (side note: the list box shows a checkmark, when selecting an item, therefore you may want to use a different 
        // component for a real application or hide the checkmark with CSS)
        ListBox<String> listBox = new ListBox<>();
        listBox.setItems("Option A", "Option B", "Option C");
        listBox.addValueChangeListener(event -> {
            Notification.show("Selected " + event.getValue());
            popup.hide();
        });

        popup.add(listBox);
        popup.setFor("entry-" + id);

        popup.show();
    }

    private void initPopup () {
        if (popup == null) {
            popup = new Popup();
            popup.setFocusTrap(true);
            add(popup);
        }
    }
}