Skip to content

Context menu async loading sub-menu's #431

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

Merged
merged 9 commits into from
Oct 24, 2016
Merged

Context menu async loading sub-menu's #431

merged 9 commits into from
Oct 24, 2016

Conversation

Ruud-cb
Copy link

@Ruud-cb Ruud-cb commented Jul 22, 2016

Hi,

Issue: #429
With this feature it is possible to load sub-menu items AFTER the context menu has been opened. By providing a Jquery promise for the "items". After the promise will be resolved or rejected the items will be shown in the context menu.

Example usage:

    var errorItems = { "errorItem": { name: "Could not load items" },};//example usage if you want to reject promise
    var loadItems = function () {//example method that will eventually do an async call
        var dfd = jQuery.Deferred();
        setTimeout(function () {
            dfd.resolve(subItems);
        }, 1000);
        //setTimeout(function () {
        //    dfd.reject(errorItems);
        //}, 1000); //could be used to reject items, providing the same format list.
        return dfd.promise();//return a jquery promise, see http://api.jquery.com/deferred.promise/
    };

    var subItems = {
        "sub1": { name: "Submenu1", icon: "edit" },
        "sub2": { name: "Submenu2", icon: "cut" },
    };
    //normal context menu initialization.
    $.contextMenu({
        selector: '.context-menu-one',
        build: function ($trigger, e) {
            return {
                callback: function (key, options) {
                    var m = "clicked: " + key;
                    console.log(m);
                },
                items: {
                    "edit": { name: "Edit", icon: "edit" },
                    "cut": { name: "Cut", icon: "cut" },
                    "status": {
                        name: "Status",
                        icon: "delete",
                        items: loadItems(),//providing promise instead of items
                    },
                }
            };
        }
    });

working plunker:
https://embed.plnkr.co/iEkk5Ohrja86xf4UKObA/

Implementation
Looking at the code changes, which is commented quite extensively anyway: It checks on create for all sub-items if item.items is a promise. If so it will use another function processPromises within operations to deal with this. There, it will wait for either promise.resolve or promise.reject. If one of these occur it will override the items of the current node (opt) which will now contain actual items. Thereafter it will create the sub-menu.
We also call op.update() to put the $node on a correct x and y position, thereafter we can call root.positionSubmenu(), which will correctly position the sub-menu, only when the user already is hovering over the menu item.
Two more adjustments: inside positionSubmenu we have to check if $menu is undefined. Because focusItem calls this function anyway. This just prevents an console error.
And: When appending css styles when creating the context (sub) menu, add an extra check if it already has a 'sub' type. This because we call create again with an item that already has sub and should not execute the last else-if.

TODO:

  • console error when closing context menu before promise is resolved
  • Unit tests need to be created for this.
  • optional nice to have: loading icon when promise is being resolved.

@bbrala bbrala mentioned this pull request Aug 21, 2016
@@ -1227,7 +1234,7 @@
createNameNode(item).appendTo($t);

item.appendTo = item.$node;
op.create(item, root);
//op.create(item, root); decide later, might be a promise.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why isn't the logic for creating the submenu called here but evaluated later, outside the case statement?

@bbrala
Copy link
Member

bbrala commented Oct 11, 2016

I've started on cleanup and docs here.

https://github.com/swisnl/jQuery-contextMenu/tree/Ruud-cb-master

  • Loading icon by using a class for a submenu waiting for a promise.
  • Check if menu is still open after promise is resolved
  • Add documentation
  • Add tests.

@spr1ne
Copy link
Contributor

spr1ne commented Oct 12, 2016

@Ruud-cb Thank you! Very useful!

@vherasme
Copy link

Hi,

I need to implement dynamic submenu items based on some properties of the content. Can I use this branch to do this ? Regards,

Victor

@bbrala
Copy link
Member

bbrala commented Oct 19, 2016

You could use this, but it is not finished yet, you could also use the build option which lets you build the moment you right click.

@vherasme
Copy link

To be more specific. Can I use build to construct a list of items that will be of variable size ? Maybe I need to read more the documentation, but I am not sure I can do this. Could you please point me to an example ?

@bbrala
Copy link
Member

bbrala commented Oct 19, 2016

Sorry i was on mobile and getting a link was a bit hard.

https://swisnl.github.io/jQuery-contextMenu/demo/dynamic-create.html

@vherasme
Copy link

I think this has a fixed number of subitems:

"edit": {name: "Edit", icon: "edit"}, "cut": {name: "Cut", icon: "cut"}, "copy": {name: "Copy", icon: "copy"}, "paste": {name: "Paste", icon: "paste"}, "delete": {name: "Delete", icon: "delete"}, "sep1": "---------", "quit": {name: "Quit", icon: function($element, key, item){ return 'context-menu-icon context-menu-icon-quit'; }}

In my code, I will right click and depending on the properties I find, I will have to create an item for each of those properties. Also, that item's name will be the same as the found property. For example:

If i find, dummy1, dummy2, etc. I will create:

"dummy1": {name: "dummy1"}, "dummy2": {name: "dummy2"},

And of course, the names will always change as well.

Thanks in advance for your patience. I know you take your time and effort to give this great plugin in for other developers. Cheers,

@bbrala
Copy link
Member

bbrala commented Oct 20, 2016

You could use the arguments from the build option (like $trigger) to figure out what menu items you want to show. The structure isn't set, your can return any menu you like in that function.

@bbrala bbrala merged commit 1cd7d07 into swisnl:master Oct 24, 2016
@spr1ne
Copy link
Contributor

spr1ne commented Oct 24, 2016

@bbrala Hooray! Thank you! Can you poke me in documentation, how i can use loading icon?

@bbrala
Copy link
Member

bbrala commented Oct 24, 2016

I think the docs are only updated on release. You could check demo/async-promise.md in the repository for a demo.

@bbrala
Copy link
Member

bbrala commented Oct 24, 2016

Documentation/demo/async-promise.md I mean

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.

4 participants