ExpandoPane.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.layout.ExpandoPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.layout.ExpandoPane"] = true;
  8. dojo.provide("dojox.layout.ExpandoPane");
  9. dojo.experimental("dojox.layout.ExpandoPane"); // just to show it can be done?
  10. dojo.require("dijit.layout.ContentPane");
  11. dojo.require("dijit._Templated");
  12. dojo.require("dijit._Contained");
  13. dojo.declare("dojox.layout.ExpandoPane",
  14. [dijit.layout.ContentPane, dijit._Templated, dijit._Contained, dijit._Container],
  15. {
  16. // summary: An experimental collapsing-pane for dijit.layout.BorderContainer
  17. //
  18. // description:
  19. // Works just like a ContentPane inside of a borderContainer. Will expand/collapse on
  20. // command, and supports having Layout Children as direct descendants
  21. //
  22. //maxHeight: "",
  23. //maxWidth: "",
  24. //splitter: false,
  25. attributeMap: dojo.delegate(dijit.layout.ContentPane.prototype.attributeMap, {
  26. title: { node: "titleNode", type: "innerHTML" }
  27. }),
  28. templateString: dojo.cache("dojox.layout", "resources/ExpandoPane.html", "<div class=\"dojoxExpandoPane\">\n\t<div dojoAttachPoint=\"titleWrapper\" class=\"dojoxExpandoTitle\">\n\t\t<div class=\"dojoxExpandoIcon\" dojoAttachPoint=\"iconNode\" dojoAttachEvent=\"onclick:toggle\"><span class=\"a11yNode\">X</span></div>\t\t\t\n\t\t<span class=\"dojoxExpandoTitleNode\" dojoAttachPoint=\"titleNode\">${title}</span>\n\t</div>\n\t<div class=\"dojoxExpandoWrapper\" dojoAttachPoint=\"cwrapper\" dojoAttachEvent=\"ondblclick:_trap\">\n\t\t<div class=\"dojoxExpandoContent\" dojoAttachPoint=\"containerNode\"></div>\n\t</div>\n</div>\n"),
  29. // easeOut: String|Function
  30. // easing function used to hide pane
  31. easeOut: "dojo._DefaultEasing",
  32. // easeIn: String|Function
  33. // easing function use to show pane
  34. easeIn: "dojo._DefaultEasing",
  35. // duration: Integer
  36. // duration to run show/hide animations
  37. duration: 420,
  38. // startExpanded: Boolean
  39. // Does this widget start in an open (true) or closed (false) state
  40. startExpanded: true,
  41. // previewOpacity: Float
  42. // A value from 0 .. 1 indicating the opacity to use on the container
  43. // when only showing a preview
  44. previewOpacity: 0.75,
  45. // previewOnDblClick: Boolean
  46. // If true, will override the default behavior of a double-click calling a full toggle.
  47. // If false, a double-click will cause the preview to popup
  48. previewOnDblClick: false,
  49. baseClass: "dijitExpandoPane",
  50. postCreate: function(){
  51. this.inherited(arguments);
  52. this._animConnects = [];
  53. this._isHorizontal = true;
  54. if(dojo.isString(this.easeOut)){
  55. this.easeOut = dojo.getObject(this.easeOut);
  56. }
  57. if(dojo.isString(this.easeIn)){
  58. this.easeIn = dojo.getObject(this.easeIn);
  59. }
  60. var thisClass = "", rtl = !this.isLeftToRight();
  61. if(this.region){
  62. switch(this.region){
  63. case "trailing" :
  64. case "right" :
  65. thisClass = rtl ? "Left" : "Right";
  66. break;
  67. case "leading" :
  68. case "left" :
  69. thisClass = rtl ? "Right" : "Left";
  70. break;
  71. case "top" :
  72. thisClass = "Top";
  73. break;
  74. case "bottom" :
  75. thisClass = "Bottom";
  76. break;
  77. }
  78. dojo.addClass(this.domNode, "dojoxExpando" + thisClass);
  79. dojo.addClass(this.iconNode, "dojoxExpandoIcon" + thisClass);
  80. this._isHorizontal = /top|bottom/.test(this.region);
  81. }
  82. dojo.style(this.domNode, {
  83. overflow: "hidden",
  84. padding:0
  85. });
  86. this.connect(this.domNode, "ondblclick", this.previewOnDblClick ? "preview" : "toggle");
  87. if(this.previewOnDblClick){
  88. this.connect(this.getParent(), "_layoutChildren", dojo.hitch(this, function(){
  89. this._isonlypreview = false;
  90. }));
  91. }
  92. },
  93. _startupSizes: function(){
  94. this._container = this.getParent();
  95. this._closedSize = this._titleHeight = dojo.marginBox(this.titleWrapper).h;
  96. if(this.splitter){
  97. // find our splitter and tie into it's drag logic
  98. var myid = this.id;
  99. dijit.registry.filter(function(w){
  100. return w && w.child && w.child.id == myid;
  101. }).forEach(dojo.hitch(this,function(w){
  102. this.connect(w,"_stopDrag","_afterResize");
  103. }));
  104. }
  105. this._currentSize = dojo.contentBox(this.domNode); // TODO: can compute this from passed in value to resize(), see _LayoutWidget for example
  106. this._showSize = this._currentSize[(this._isHorizontal ? "h" : "w")];
  107. this._setupAnims();
  108. if(this.startExpanded){
  109. this._showing = true;
  110. }else{
  111. this._showing = false;
  112. this._hideWrapper();
  113. this._hideAnim.gotoPercent(99,true);
  114. }
  115. this._hasSizes = true;
  116. },
  117. _afterResize: function(e){
  118. var tmp = this._currentSize; // the old size
  119. this._currentSize = dojo.marginBox(this.domNode); // the new size
  120. var n = this._currentSize[(this._isHorizontal ? "h" : "w")]
  121. if(n > this._titleHeight){
  122. if(!this._showing){
  123. this._showing = !this._showing;
  124. this._showEnd();
  125. }
  126. this._showSize = n;
  127. this._setupAnims();
  128. }else{
  129. this._showSize = tmp[(this._isHorizontal ? "h" : "w")];
  130. this._showing = false;
  131. this._hideWrapper();
  132. this._hideAnim.gotoPercent(89,true);
  133. }
  134. },
  135. _setupAnims: function(){
  136. // summary: Create the show and hide animations
  137. dojo.forEach(this._animConnects, dojo.disconnect);
  138. var _common = {
  139. node:this.domNode,
  140. duration:this.duration
  141. },
  142. isHorizontal = this._isHorizontal,
  143. showProps = {},
  144. hideProps = {},
  145. dimension = isHorizontal ? "height" : "width"
  146. ;
  147. showProps[dimension] = {
  148. end: this._showSize
  149. };
  150. hideProps[dimension] = {
  151. end: this._closedSize
  152. };
  153. this._showAnim = dojo.animateProperty(dojo.mixin(_common,{
  154. easing:this.easeIn,
  155. properties: showProps
  156. }));
  157. this._hideAnim = dojo.animateProperty(dojo.mixin(_common,{
  158. easing:this.easeOut,
  159. properties: hideProps
  160. }));
  161. this._animConnects = [
  162. dojo.connect(this._showAnim, "onEnd", this, "_showEnd"),
  163. dojo.connect(this._hideAnim, "onEnd", this, "_hideEnd")
  164. ];
  165. },
  166. preview: function(){
  167. // summary: Expand this pane in preview mode (does not affect surrounding layout)
  168. if(!this._showing){
  169. this._isonlypreview = !this._isonlypreview;
  170. }
  171. this.toggle();
  172. },
  173. toggle: function(){
  174. // summary: Toggle this pane's visibility
  175. if(this._showing){
  176. this._hideWrapper();
  177. this._showAnim && this._showAnim.stop();
  178. this._hideAnim.play();
  179. }else{
  180. this._hideAnim && this._hideAnim.stop();
  181. this._showAnim.play();
  182. }
  183. this._showing = !this._showing;
  184. },
  185. _hideWrapper: function(){
  186. // summary: Set the Expando state to "closed"
  187. dojo.addClass(this.domNode, "dojoxExpandoClosed");
  188. dojo.style(this.cwrapper,{
  189. visibility: "hidden",
  190. opacity: "0",
  191. overflow: "hidden"
  192. });
  193. },
  194. _showEnd: function(){
  195. // summary: Common animation onEnd code - "unclose"
  196. dojo.style(this.cwrapper, {
  197. opacity: 0,
  198. visibility:"visible"
  199. });
  200. dojo.anim(this.cwrapper, {
  201. opacity: this._isonlypreview ? this.previewOpacity : 1
  202. }, 227);
  203. dojo.removeClass(this.domNode, "dojoxExpandoClosed");
  204. if(!this._isonlypreview){
  205. setTimeout(dojo.hitch(this._container, "layout"), 15);
  206. }else{
  207. this._previewShowing = true;
  208. this.resize();
  209. }
  210. },
  211. _hideEnd: function(){
  212. // summary: Callback for the hide animation - "close"
  213. // every time we hide, reset the "only preview" state
  214. if(!this._isonlypreview){
  215. setTimeout(dojo.hitch(this._container, "layout"), 25);
  216. }else{
  217. this._previewShowing = false;
  218. }
  219. this._isonlypreview = false;
  220. },
  221. resize: function(/* Object? */newSize){
  222. // summary:
  223. // we aren't a layout widget, but need to act like one:
  224. // newSize: Object
  225. // The size object to resize to
  226. if(!this._hasSizes){ this._startupSizes(newSize); }
  227. // compute size of container (ie, size left over after title bar)
  228. var currentSize = dojo.marginBox(this.domNode);
  229. this._contentBox = {
  230. w: newSize && "w" in newSize ? newSize.w : currentSize.w,
  231. h: (newSize && "h" in newSize ? newSize.h : currentSize.h) - this._titleHeight
  232. };
  233. dojo.style(this.containerNode, "height", this._contentBox.h + "px");
  234. if(newSize){
  235. dojo.marginBox(this.domNode, newSize);
  236. }
  237. this._layoutChildren();
  238. },
  239. _trap: function(e){
  240. // summary: Trap stray events
  241. dojo.stopEvent(e);
  242. }
  243. });
  244. }