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
49 changes: 47 additions & 2 deletions src/jquery.contextMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,11 @@
},
// position the sub-menu
positionSubmenu: function ($menu) {
if ($menu === undefined) {
//When user hovers over item (which has sub items) handle.focusItem will call this.
//but the submenu does not exist yet if opt.items is a promise. just return, will call positionSubmenu after promise is completed.
return;
}
if ($.ui && $.ui.position) {
// .position() is provided as a jQuery UI utility
// (...and it won't work on hidden elements)
Expand Down Expand Up @@ -1159,6 +1164,8 @@
$t.addClass('context-menu-separator ' + root.classNames.notSelectable);
} else if (item.type === 'html') {
$t.addClass('context-menu-html ' + root.classNames.notSelectable);
} else if (item.type === 'sub') {
//we don't want to execute the next else-if if it is a sub.
} else if (item.type) {
$label = $('<label></label>').appendTo($t);
createNameNode(item).appendTo($label);
Expand Down Expand Up @@ -1225,11 +1232,18 @@

case 'sub':
createNameNode(item).appendTo($t);

item.appendTo = item.$node;
op.create(item, root);
$t.data('contextMenu', item).addClass('context-menu-submenu');
item.callback = null;
//if item contains items, and this is a promise, we should create it later
//check if subitems is of type promise. If it is a promise we need to create it later, after promise has been resolved
if ('function' === typeof item.items.then) {
// probably a promise, process it, when completed it will create the sub menu's.
op.processPromises(item, root, item.items);
} else {
// normal submenu.
op.create(item, root);
}
break;

case 'html':
Expand Down Expand Up @@ -1411,6 +1425,37 @@
}

return $layer;
},
processPromises: function (opt, root, promise) {
function completedPromise(opt,root,items) {
//completed promise (dev called promise.resolve)
//we now have a list of items which can be used to create the rest of the context menu.
if (items === undefined) {
//meh, null result, dev should have checked
errorPromise(undefined);//own error object
}
finishPromiseProcess(opt,root, items);
};
function errorPromise(opt,root,errorItem) {
//user called promise.reject() with an error item, if not, provide own error item.
if (errorItem === undefined) {
errorItem = { "error": { name: "No items and no error item", icon: "context-menu-icon context-menu-icon-quit" } };
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't this the errorItem as name if it is a string?

Copy link
Author

Choose a reason for hiding this comment

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

Hmm. I might had a reason for that but not sure why anymore, maybe to force users to build nice error items or something. I'll update it, one second.

if (window.console) {
(console.error || console.log).call(console, 'When you reject a promise, provide an "items" object, equal to normal sub-menu items');
}
}else if(typeof errorItem === 'string'){
errorItem = { "error": { name: errorItem } };
}
finishPromiseProcess(opt,root,errorItem);
};
function finishPromiseProcess(opt,root,items) {
opt.items = items;//override promise to items.
op.create(opt, root, true);//create submenu
op.update(opt, root);//correctly update position if user is already hovered over menu item
root.positionSubmenu.call(opt.$node, opt.$menu); //positionSubmenu, will only do anything if user already hovered over menu item that just got new subitems.
};
//wait for promise completion. .then(success, error, notify) (we don't track notify). Bind the opt and root to avoid scope problems
promise.then(completedPromise.bind(this, opt, root), errorPromise.bind(this, opt, root));
}
};

Expand Down