|
11 | 11 | * Licensed under
|
12 | 12 | * MIT License http://www.opensource.org/licenses/mit-license
|
13 | 13 | *
|
14 |
| - * Date: 2017-08-30T12:16:04.336Z |
| 14 | + * Date: 2017-11-24T18:02:43.224Z |
15 | 15 | */
|
16 | 16 |
|
17 | 17 | // jscs:disable
|
|
116 | 116 | // flag denoting if a second trigger should simply move (true) or rebuild (false) an open menu
|
117 | 117 | // as long as the trigger happened on one of the trigger-element's child nodes
|
118 | 118 | reposition: true,
|
| 119 | + // Flag denoting if a second trigger should close the menu, as long as |
| 120 | + // the trigger happened on one of the trigger-element's child nodes. |
| 121 | + // This overrides the reposition option. |
| 122 | + hideOnSecondTrigger: false, |
119 | 123 |
|
120 | 124 | //ability to select submenu
|
121 | 125 | selectableSubMenu: false,
|
122 | 126 |
|
| 127 | + hasOverboundaryScroll: false, |
| 128 | + |
123 | 129 | // Default classname configuration to be able avoid conflicts in frameworks
|
124 | 130 | classNames: {
|
125 | 131 | hover: 'context-menu-hover', // Item hover
|
|
200 | 206 | opt.$menu.css(offset);
|
201 | 207 | },
|
202 | 208 | // position the sub-menu
|
203 |
| - positionSubmenu: function ($menu) { |
| 209 | + positionSubmenu: function ($menu, $root) { |
204 | 210 | if (typeof $menu === 'undefined') {
|
205 | 211 | // When user hovers over item (which has sub items) handle.focusItem will call this.
|
206 | 212 | // but the submenu does not exist yet if opt.items is a promise. just return, will
|
|
217 | 223 | collision: 'flipfit fit'
|
218 | 224 | }).css('display', '');
|
219 | 225 | } else {
|
220 |
| - // determine contextMenu position |
221 |
| - var offset = { |
222 |
| - top: -9, |
223 |
| - left: this.outerWidth() - 5 |
224 |
| - }; |
| 226 | + var offset = {}; |
| 227 | + if($root && $root.hasOverboundaryScroll){ |
| 228 | + var parentOffset = this.offset(); |
| 229 | + // determine contextMenu position |
| 230 | + offset = { |
| 231 | + top: parentOffset.top, |
| 232 | + left: parentOffset.left + this.outerWidth() |
| 233 | + }; |
| 234 | + } else { |
| 235 | + offset = { |
| 236 | + top: -9, |
| 237 | + left: this.outerWidth() - 5 |
| 238 | + }; |
| 239 | + } |
225 | 240 | $menu.css(offset);
|
| 241 | + if ($root && $root.hasOverboundaryScroll) |
| 242 | + op.activated($root, $menu); |
226 | 243 | }
|
227 | 244 | },
|
228 | 245 | // offset to add to zIndex
|
|
236 | 253 | // events
|
237 | 254 | events: {
|
238 | 255 | show: $.noop,
|
239 |
| - hide: $.noop |
| 256 | + hide: $.noop, |
| 257 | + activated: $.noop |
240 | 258 | },
|
241 | 259 | // default callback
|
242 | 260 | callback: null,
|
|
471 | 489 | $(target).trigger(e);
|
472 | 490 | root.$layer.show();
|
473 | 491 | }
|
474 |
| - |
| 492 | + |
| 493 | + if (root.hideOnSecondTrigger && triggerAction && root.$menu !== null && typeof root.$menu !== 'undefined') { |
| 494 | + root.$menu.trigger('contextmenu:hide'); |
| 495 | + return; |
| 496 | + } |
| 497 | + |
475 | 498 | if (root.reposition && triggerAction) {
|
476 | 499 | if (document.elementFromPoint) {
|
477 | 500 | if (root.$trigger.is(target)) {
|
|
936 | 959 |
|
937 | 960 | // position sub-menu - do after show so dumb $.ui.position can keep up
|
938 | 961 | if (opt.$node) {
|
939 |
| - root.positionSubmenu.call(opt.$node, opt.$menu); |
| 962 | + root.positionSubmenu.call(opt.$node, opt.$menu, root); |
940 | 963 | }
|
941 | 964 | },
|
942 | 965 | // blur <command>
|
|
997 | 1020 | // position and show context menu
|
998 | 1021 | opt.$menu.css(css)[opt.animation.show](opt.animation.duration, function () {
|
999 | 1022 | $trigger.trigger('contextmenu:visible');
|
| 1023 | + |
| 1024 | + op.activated(opt,opt.$menu); |
| 1025 | + opt.events.activated(); |
1000 | 1026 | });
|
1001 | 1027 | // make options available and set state
|
1002 | 1028 | $trigger
|
|
1104 | 1130 | }
|
1105 | 1131 |
|
1106 | 1132 | // create contextMenu
|
1107 |
| - opt.$menu = $('<ul class="context-menu-list"></ul>').addClass(opt.className || '').data({ |
| 1133 | + opt.$menu = $('<ul class="context-menu-list ' + (opt.hasOverboundaryScroll ? 'overBoundary' : '') + '"></ul>').addClass(opt.className || '').data({ |
1108 | 1134 | 'contextMenu': opt,
|
1109 | 1135 | 'contextMenuRoot': root
|
1110 | 1136 | });
|
|
1521 | 1547 | opt.items = items;
|
1522 | 1548 | op.create(opt, root, true); // Create submenu
|
1523 | 1549 | op.update(opt, root); // Correctly update position if user is already hovered over menu item
|
1524 |
| - root.positionSubmenu.call(opt.$node, opt.$menu); // positionSubmenu, will only do anything if user already hovered over menu item that just got new subitems. |
| 1550 | + root.positionSubmenu.call(opt.$node, opt.$menu, root); // positionSubmenu, will only do anything if user already hovered over menu item that just got new subitems. |
1525 | 1551 | }
|
1526 | 1552 |
|
1527 | 1553 | // Wait for promise completion. .then(success, error, notify) (we don't track notify). Bind the opt
|
1528 | 1554 | // and root to avoid scope problems
|
1529 | 1555 | promise.then(completedPromise.bind(this, opt, root), errorPromise.bind(this, opt, root));
|
| 1556 | + }, |
| 1557 | + // operation that will run after contextMenu showed on screen |
| 1558 | + activated: function(opt,menu){ |
| 1559 | + if(!opt.hasOverboundaryScroll) |
| 1560 | + return; |
| 1561 | + var $menu = menu; |
| 1562 | + var win = $(window); |
| 1563 | + var $menuOffset = $menu.offset(); |
| 1564 | + var winHeight = win.height(); |
| 1565 | + var winWidth = win.width(); |
| 1566 | + var winScrollTop = win.scrollTop(); |
| 1567 | + var menuHeight = $menu.outerHeight(); |
| 1568 | + var menuWidth = $menu.outerWidth(); |
| 1569 | + if(menuHeight > winHeight){ |
| 1570 | + $menu.css({ |
| 1571 | + 'height': winHeight - |
| 1572 | + ((parseInt($menu.css('padding-top'))*2)+(parseInt($menu.css('margin-top'))*2))+'px', |
| 1573 | + 'overflow-x':'hidden', |
| 1574 | + 'overflow-y':'auto', |
| 1575 | + 'top':winScrollTop+'px' |
| 1576 | + }); |
| 1577 | + } else if($menuOffset.top < winScrollTop){ |
| 1578 | + $menu.css({ |
| 1579 | + 'top':'0px' |
| 1580 | + }); |
| 1581 | + } else if($menuOffset.top+menuHeight > winScrollTop + winHeight){ |
| 1582 | + $menu.css({ |
| 1583 | + 'top':$menuOffset.top - Math.abs((winScrollTop+winHeight)-($menuOffset.top+menuHeight)) -((parseInt($menu.css('padding-top'))*2)+(parseInt($menu.css('margin-top'))*2))+'px' |
| 1584 | + }); |
| 1585 | + } |
| 1586 | + if($menuOffset.left + menuWidth > winWidth){ |
| 1587 | + var newLeftPosition = $menuOffset.left - Math.abs(($menuOffset.left+menuWidth) - winWidth); |
| 1588 | + var parent = $menu.parents('ul.context-menu-list').first(); |
| 1589 | + if(parent.length){ |
| 1590 | + if(newLeftPosition <= parent.offset().left + parent.outerWidth() |
| 1591 | + && newLeftPosition >= parent.offset().left){ |
| 1592 | + $menu.css({ |
| 1593 | + 'left':parent.offset().left - $menu.outerWidth() + 'px' |
| 1594 | + }); |
| 1595 | + }else{ |
| 1596 | + $menu.css({ |
| 1597 | + 'left':$menuOffset.left-Math.abs(($menuOffset.left+menuWidth) - winWidth) + 'px' |
| 1598 | + }); |
| 1599 | + } |
| 1600 | + }else{ |
| 1601 | + $menu.css({ |
| 1602 | + 'left':$menuOffset.left-Math.abs(($menuOffset.left+menuWidth) - winWidth) + 'px' |
| 1603 | + }); |
| 1604 | + } |
| 1605 | + } |
1530 | 1606 | }
|
1531 | 1607 | };
|
1532 | 1608 |
|
|
1616 | 1692 | }
|
1617 | 1693 |
|
1618 | 1694 | switch (operation) {
|
| 1695 | + |
| 1696 | + case 'update': |
| 1697 | + // Updates visibility and such |
| 1698 | + if(_hasContext){ |
| 1699 | + op.update($context); |
| 1700 | + } else { |
| 1701 | + for(var menu in menus){ |
| 1702 | + if(menus.hasOwnProperty(menu)){ |
| 1703 | + op.update(menus[menu]); |
| 1704 | + } |
| 1705 | + } |
| 1706 | + } |
| 1707 | + break; |
| 1708 | + |
1619 | 1709 | case 'create':
|
1620 | 1710 | // no selector no joy
|
1621 | 1711 | if (!o.selector) {
|
|
1905 | 1995 | disabled: !!$node.attr('disabled'),
|
1906 | 1996 | callback: (function () {
|
1907 | 1997 | return function () {
|
1908 |
| - $node.get(0).click() |
| 1998 | + $node.get(0).click(); |
1909 | 1999 | };
|
1910 | 2000 | })()
|
1911 | 2001 | };
|
|
1924 | 2014 | icon: $node.attr('icon'),
|
1925 | 2015 | callback: (function () {
|
1926 | 2016 | return function () {
|
1927 |
| - $node.get(0).click() |
| 2017 | + $node.get(0).click(); |
1928 | 2018 | };
|
1929 | 2019 | })()
|
1930 | 2020 | };
|
|
0 commit comments