ToggleSplitter.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  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.ToggleSplitter"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.layout.ToggleSplitter"] = true;
  8. dojo.provide("dojox.layout.ToggleSplitter");
  9. dojo.experimental("dojox.layout.ToggleSplitter");
  10. dojo.require("dijit.layout.BorderContainer");
  11. dojo.declare("dojox.layout.ToggleSplitter", [ dijit.layout._Splitter ],
  12. {
  13. // summary:
  14. // A draggable and toggle-to-close/open spacer between two items in a BorderContainer
  15. //
  16. // description:
  17. // Extends the dijit.layout._Splitter to add a toggling behavior
  18. // on double-click
  19. //
  20. /*=====
  21. container: null,
  22. child: null,
  23. region: null,
  24. =====*/
  25. // open: Boolean
  26. // the initial and current state of the splitter (and its attached pane)
  27. open: true,
  28. // closedThreshold: Integer
  29. // how small the attached pane can be before its considered closed
  30. closedThreshold: 5,
  31. // openSize: String
  32. // the css height/width value to apply by default when the attached pane is open
  33. openSize: "",
  34. // _closedSize: String
  35. // the css height/width value to apply by default when the attached pane is closed
  36. _closedSize: "0",
  37. templateString: '<div class="dijitSplitter dojoxToggleSplitter" dojoAttachEvent="onkeypress:_onKeyPress,onmousedown:_onMouseDown" tabIndex="0" role="separator"><div dojoAttachPoint="toggleNode" class="dijitSplitterThumb dojoxToggleSplitterIcon"></div></div>',
  38. postCreate: function(){
  39. this._started = false;
  40. this.inherited(arguments);
  41. // add a region css hook
  42. var region = this.region;
  43. dojo.addClass(this.domNode, "dojoxToggleSplitter"+region.charAt(0).toUpperCase() + region.substring(1));
  44. // hook up double-clicks to toggle the splitter -
  45. this.connect(this, "onDblClick", "_toggleMe");
  46. },
  47. startup: function(){
  48. this.inherited(arguments);
  49. // we have to wait until startup to be sure the child exists in the dom
  50. // and has non-zero size (if its supposed to be showing)
  51. var paneNode = this.child.domNode,
  52. intPaneSize = dojo.style(paneNode, (this.horizontal ? "height" : "width"));
  53. // creation of splitters is an opaque process in BorderContainer,
  54. // so if we want to get init params, we have to retrieve them from the attached BC child
  55. // NOTE: for this to work we have to extend the prototype of dijit._Widget (some more)
  56. dojo.forEach(["toggleSplitterOpen", "toggleSplitterClosedThreshold", "toggleSplitterOpenSize"], function(name){
  57. var pname = name.substring("toggleSplitter".length);
  58. pname = pname.charAt(0).toLowerCase() + pname.substring(1);
  59. if(name in this.child){
  60. this[pname] = this.child[name];
  61. }
  62. }, this);
  63. if(!this.openSize){
  64. // store the current size as the openSize if none was provided
  65. // dojo.style always returns a integer (pixel) value for height/width
  66. // use an arbirary default if a pane was initalized closed and no openSize provided
  67. this.openSize = (this.open) ? intPaneSize + "px" : "75px";
  68. }
  69. this._openStyleProps = this._getStyleProps(paneNode, true);
  70. // update state
  71. this._started = true;
  72. this.set("open", this.open);
  73. return this;
  74. },
  75. _onMouseUp: function(evt){
  76. dojo.disconnect(this._onMoveHandle);
  77. dojo.disconnect(this._onUpHandle);
  78. delete this._onMoveHandle;
  79. delete this._onUpHandle;
  80. delete this._startPosn;
  81. },
  82. _onPrelimMouseMove: function(evt){
  83. // only start dragging when a mouse down AND a significant mousemove occurs
  84. var startPosn = this._startPosn || 0;
  85. // allow a little fudging in a click before we consider a drag started
  86. var dragThreshold = 3;
  87. var offset = Math.abs( startPosn - (this.horizontal ? evt.clientY : evt.clientX) );
  88. if(offset >= dragThreshold){
  89. // treat as a drag and dismantle this preliminary handlers
  90. dojo.disconnect(this._onMoveHandle);
  91. this._startDrag(evt);
  92. }
  93. },
  94. _onMouseDown: function(evt){
  95. // summary:
  96. // handle mousedown events from the domNode
  97. if(!this.open){
  98. // ignore mousedown while closed
  99. // - this has the effect of preventing dragging while closed, which is the prefered behavior (for now)
  100. return;
  101. }
  102. // Mousedown can fire more than once (!)
  103. // ..so check before connecting
  104. if(!this._onUpHandle){
  105. this._onUpHandle = dojo.connect(dojo.body(), "onmouseup", this, "_onMouseUp");
  106. }
  107. if(!this._onMoveHandle){
  108. this._startPosn = this.horizontal ? evt.clientY : evt.clientX;
  109. // start listening for mousemove
  110. this._onMoveHandle = dojo.connect(dojo.body(), "onmousemove", this, "_onPrelimMouseMove");
  111. }
  112. },
  113. _handleOnChange: function(){
  114. // summary
  115. // effect the state change with the new value of this.open
  116. // TODO: animate the open/close
  117. var paneNode = this.child.domNode,
  118. openProps,
  119. dim = this.horizontal ? "height" : "width";
  120. if(this.open){
  121. // change to open state
  122. var styleProps = dojo.mixin({
  123. display: "block",
  124. overflow: "auto",
  125. visibility: "visible"
  126. }, this._openStyleProps);
  127. styleProps[dim] = (this._openStyleProps && this._openStyleProps[dim]) ? this._openStyleProps[dim] : this.openSize;
  128. dojo.style(paneNode, styleProps);
  129. // and re-hook up the mouse event handler
  130. this.connect(this.domNode, "onmousedown", "_onMouseDown");
  131. } else {
  132. // change to closed state
  133. // FIXME: this wont work in a drag-to-closed scenario
  134. var paneStyle = dojo.getComputedStyle(paneNode);
  135. openProps = this._getStyleProps(paneNode, true, paneStyle);
  136. var closedProps = this._getStyleProps(paneNode, false, paneStyle);
  137. this._openStyleProps = openProps;
  138. dojo.style(paneNode, closedProps);
  139. }
  140. this._setStateClass();
  141. if(this.container._started){
  142. this.container._layoutChildren(this.region);
  143. }
  144. },
  145. _getStyleProps: function(paneNode, open, paneStyle){
  146. // summary:
  147. // create an object with the style property name: values
  148. // that will need to be applied to the child pane render the given state
  149. if(!paneStyle){
  150. paneStyle = dojo.getComputedStyle(paneNode);
  151. }
  152. var styleProps = {},
  153. dim = this.horizontal ? "height" : "width";
  154. styleProps["overflow"] = (open) ? paneStyle["overflow"] : "hidden";
  155. styleProps["visibility"] = (open) ? paneStyle["visibility"] : "hidden";
  156. // use the inline width/height style value, in preference to the computedStyle
  157. // for the open width/height
  158. styleProps[dim] = (open) ? paneNode.style[dim] || paneStyle[dim] : this._closedSize;
  159. // We include the padding,border,margin width values for restoring on open
  160. var edgeNames = ["Top", "Right", "Bottom", "Left"];
  161. dojo.forEach(["padding","margin","border"], function(pname){
  162. for(var i=0; i<edgeNames.length; i++){
  163. var fullname = pname+edgeNames[i];
  164. if(pname=="border"){
  165. pname+="Width";
  166. }
  167. if(undefined !== paneStyle[fullname]){
  168. styleProps[fullname] = (open) ?
  169. paneStyle[fullname] : 0;
  170. }
  171. }
  172. });
  173. return styleProps;
  174. },
  175. _setStateClass: function(){
  176. // sumamry:
  177. // apply the appropriate classes for the current open state
  178. if(this.open){
  179. dojo.removeClass(this.domNode, "dojoxToggleSplitterClosed");
  180. dojo.addClass(this.domNode, "dojoxToggleSplitterOpen");
  181. dojo.removeClass(this.toggleNode, "dojoxToggleSplitterIconClosed");
  182. dojo.addClass(this.toggleNode, "dojoxToggleSplitterIconOpen");
  183. } else {
  184. dojo.addClass(this.domNode, "dojoxToggleSplitterClosed");
  185. dojo.removeClass(this.domNode, "dojoxToggleSplitterOpen");
  186. dojo.addClass(this.toggleNode, "dojoxToggleSplitterIconClosed");
  187. dojo.removeClass(this.toggleNode, "dojoxToggleSplitterIconOpen");
  188. }
  189. },
  190. _setOpenAttr: function(/*Boolean*/ value){
  191. // summary:
  192. // setter for the open property
  193. if(!this._started) {
  194. return;
  195. }
  196. this.open = value;
  197. this._handleOnChange(value, true);
  198. var evt = this.open ? "onOpen" : "onClose";
  199. this[evt](this.child);
  200. },
  201. onOpen: function(){
  202. // stub
  203. },
  204. onClose: function(){
  205. // stub
  206. },
  207. _toggleMe: function(evt){
  208. // summary:
  209. // event handle, toggle the open state
  210. if(evt){
  211. dojo.stopEvent(evt);
  212. }
  213. this.set("open", !this.open);
  214. },
  215. _onKeyPress: function(/*Event*/ e){
  216. this.inherited(arguments);
  217. // TODO: add support for space, enter to cause toggle
  218. }
  219. });
  220. // As BC places no constraints on what kind of widgets can be children
  221. // we have to extend the base class to ensure the properties we need can be set (both in markup and programatically)
  222. dojo.extend(dijit._Widget, {
  223. // toggleSplitterOpen: Boolean
  224. toggleSplitterOpen: true,
  225. // toggleSplitterClosedThreshold: Integer
  226. toggleSplitterClosedThreshold: 5,
  227. // toggleSplitterClosedThreshold: String
  228. // a css size value (e.g. "100px")
  229. toggleSplitterOpenSize: ""
  230. });
  231. }