not really known
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

129 lines
4.9 KiB

  1. /**
  2. _onyx.Menu_ is a subkind of <a href="#onyx.Popup">onyx.Popup</a> that
  3. displays a list of <a href="#onyx.MenuItems">onyx.MenuItems</a> and looks
  4. like a popup menu. It is meant to be used in conjunction with an
  5. <a href="#onyx.MenuDecorator">onyx.MenuDecorator</a>. The decorator couples
  6. the menu with an activating control, which may be a button or any other
  7. control with an _onActivate_ event. When the control is activated, the menu
  8. shows itself in the correct position relative to the activator.
  9. {kind: "onyx.MenuDecorator", components: [
  10. {content: "Show menu"},
  11. {kind: "onyx.Menu", components: [
  12. {content: "1"},
  13. {content: "2"},
  14. {classes: "onyx-menu-divider"},
  15. {content: "3"},
  16. ]}
  17. ]}
  18. A menu may be floated by setting the _floating_ property to true. When a
  19. menu is not floating (the default), it will scroll with the activating
  20. control, but may be obscured by surrounding content with a higher z-index.
  21. When floating, it will never be obscured, but it will not scroll with the
  22. activating button.
  23. */
  24. enyo.kind({
  25. name: "onyx.Menu",
  26. kind: "onyx.Popup",
  27. modal: true,
  28. defaultKind: "onyx.MenuItem",
  29. classes: "onyx-menu",
  30. showOnTop: false,
  31. handlers: {
  32. onActivate: "itemActivated",
  33. onRequestShowMenu: "requestMenuShow",
  34. onRequestHideMenu: "requestHide"
  35. },
  36. itemActivated: function(inSender, inEvent) {
  37. inEvent.originator.setActive(false);
  38. return true;
  39. },
  40. showingChanged: function() {
  41. this.inherited(arguments);
  42. this.adjustPosition(true);
  43. },
  44. requestMenuShow: function(inSender, inEvent) {
  45. if (this.floating) {
  46. var n = inEvent.activator.hasNode();
  47. if (n) {
  48. var r = this.activatorOffset = this.getPageOffset(n);
  49. this.applyPosition({top: r.top + (this.showOnTop ? 0 : r.height), left: r.left, width: r.width});
  50. }
  51. }
  52. this.show();
  53. return true;
  54. },
  55. applyPosition: function(inRect) {
  56. var s = ""
  57. for (n in inRect) {
  58. s += (n + ":" + inRect[n] + (isNaN(inRect[n]) ? "; " : "px; "));
  59. }
  60. this.addStyles(s);
  61. },
  62. getPageOffset: function(inNode) {
  63. // getBoundingClientRect returns top/left values which are relative to the viewport and not absolute
  64. var r = inNode.getBoundingClientRect();
  65. // IE8 doesn't return window.page{X/Y}Offset & r.{height/width}
  66. // FIXME: Perhaps use an alternate universal method instead of conditionals
  67. var pageYOffset = (window.pageYOffset === undefined) ? document.documentElement.scrollTop : window.pageYOffset;
  68. var pageXOffset = (window.pageXOffset === undefined) ? document.documentElement.scrollLeft : window.pageXOffset;
  69. var rHeight = (r.height === undefined) ? (r.bottom - r.top) : r.height;
  70. var rWidth = (r.width === undefined) ? (r.right - r.left) : r.width;
  71. return {top: r.top + pageYOffset, left: r.left + pageXOffset, height: rHeight, width: rWidth};
  72. },
  73. //* @protected
  74. /* Adjusts the menu position to fit inside the current window size.
  75. belowActivator determines whether to position the top of the menu below or on top of the activator
  76. */
  77. adjustPosition: function(belowActivator) {
  78. if (this.showing && this.hasNode()) {
  79. this.removeClass("onyx-menu-up");
  80. //reset the left position before we get the bounding rect for proper horizontal calculation
  81. this.floating ? enyo.noop : this.applyPosition({left: "auto"});
  82. var b = this.node.getBoundingClientRect();
  83. var bHeight = (b.height === undefined) ? (b.bottom - b.top) : b.height;
  84. var innerHeight = (window.innerHeight === undefined) ? document.documentElement.clientHeight : window.innerHeight;
  85. var innerWidth = (window.innerWidth === undefined) ? document.documentElement.clientWidth : window.innerWidth;
  86. //position the menu above the activator if it's getting cut off, but only if there's more room above
  87. this.menuUp = (b.top + bHeight > innerHeight) && ((innerHeight - b.bottom) < (b.top - bHeight));
  88. this.addRemoveClass("onyx-menu-up", this.menuUp);
  89. //if floating, adjust the vertical positioning
  90. if (this.floating) {
  91. var r = this.activatorOffset;
  92. //if the menu doesn't fit below the activator, move it up
  93. if (this.menuUp) {
  94. this.applyPosition({top: (r.top - bHeight + (this.showOnTop ? r.height : 0)), bottom: "auto"});
  95. }
  96. else {
  97. //if the top of the menu is above the top of the activator and there's room to move it down, do so
  98. if ((b.top < r.top) && (r.top + (belowActivator ? r.height : 0) + bHeight < innerHeight))
  99. {
  100. this.applyPosition({top: r.top + (this.showOnTop ? 0 : r.height), bottom: "auto"});
  101. }
  102. }
  103. }
  104. //adjust the horizontal positioning to keep the menu from being cut off on the right
  105. if ((b.right) > innerWidth) {
  106. if (this.floating){
  107. this.applyPosition({left: r.left-(b.left + b.width - innerWidth)});
  108. } else {
  109. this.applyPosition({left: -(b.right - innerWidth)});
  110. }
  111. }
  112. }
  113. },
  114. resizeHandler: function() {
  115. this.inherited(arguments);
  116. this.adjustPosition(true);
  117. },
  118. requestHide: function(){
  119. this.setShowing(false);
  120. }
  121. });