RadioGroup.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  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.RadioGroup"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.layout.RadioGroup"] = true;
  8. dojo.provide("dojox.layout.RadioGroup");
  9. dojo.experimental("dojox.layout.RadioGroup");
  10. //
  11. // dojox.layout.RadioGroup - an experimental (probably poorly named) Layout widget extending StackContainer
  12. // that accepts ContentPanes as children, and applies aesthetically pleasing responsive transition animations
  13. // attached to :hover of the Buttons created.
  14. //
  15. // FIXME: take the Buttons out of the root template, and allow layoutAlign or similar attrib to use a different
  16. // template, or build the template dynamically?
  17. //
  18. dojo.require("dijit._Widget");
  19. dojo.require("dijit._Templated");
  20. dojo.require("dijit._Contained");
  21. dojo.require("dijit.layout.StackContainer");
  22. dojo.require("dojo.fx.easing");
  23. dojo.declare("dojox.layout.RadioGroup",
  24. [dijit.layout.StackContainer,dijit._Templated],
  25. {
  26. // summary: A Container that turns its Layout Children into a single Pane and transitions between states
  27. // onHover of the button
  28. //
  29. // duration: Integer
  30. // used for Fade and Slide RadioGroup's, the duration to run the transition animation. does not affect anything
  31. // in default RadioGroup
  32. duration: 750,
  33. // hasButtons: Boolean
  34. // toggles internal button making on or off
  35. hasButtons: false,
  36. // buttonClass: String
  37. // The full declared className of the Button widget to use for hasButtons
  38. buttonClass: "dojox.layout._RadioButton",
  39. // templateString: String
  40. // the template for our container
  41. templateString: '<div class="dojoxRadioGroup">'
  42. +' <div dojoAttachPoint="buttonHolder" style="display:none;">'
  43. +' <table class="dojoxRadioButtons"><tbody><tr class="dojoxRadioButtonRow" dojoAttachPoint="buttonNode"></tr></tbody></table>'
  44. +' </div>'
  45. +' <div class="dojoxRadioView" dojoAttachPoint="containerNode"></div>'
  46. +'</div>',
  47. startup: function(){
  48. // summary: scan the container for children, and make "tab buttons" for them
  49. this.inherited(arguments);
  50. this._children = this.getChildren();
  51. this._buttons = this._children.length;
  52. this._size = dojo.coords(this.containerNode);
  53. if(this.hasButtons){
  54. dojo.style(this.buttonHolder, "display", "block");
  55. }
  56. },
  57. _setupChild: function(/* dijit._Widget */child){
  58. // summary: Creates a hover button for a child node of the RadioGroup
  59. dojo.style(child.domNode, "position", "absolute");
  60. if(this.hasButtons){
  61. var tmp = this.buttonNode.appendChild(dojo.create('td'));
  62. var n = dojo.create("div", null, tmp),
  63. _Button = dojo.getObject(this.buttonClass),
  64. tmpw = new _Button({
  65. label: child.title,
  66. page: child
  67. }, n)
  68. ;
  69. dojo.mixin(child, { _radioButton: tmpw });
  70. tmpw.startup();
  71. }
  72. child.domNode.style.display = "none";
  73. },
  74. removeChild: function(child){
  75. if(this.hasButtons && child._radioButton){
  76. child._radioButton.destroy();
  77. delete child._radioButton;
  78. }
  79. this.inherited(arguments);
  80. },
  81. // FIXME: shouldn't have to rewriting these, need to take styling out of _showChild and _hideChild
  82. // and use classes on the domNode in _transition or something similar (in StackContainer)
  83. _transition: function(/*dijit._Widget*/ newWidget, /*dijit._Widget*/ oldWidget){
  84. // summary: called when StackContainer receives a selectChild call, used to transition the panes.
  85. this._showChild(newWidget);
  86. if(oldWidget){
  87. this._hideChild(oldWidget);
  88. }
  89. // Size the new widget, in case this is the first time it's being shown,
  90. // or I have been resized since the last time it was shown.
  91. // page must be visible for resizing to work
  92. if(this.doLayout && newWidget.resize){
  93. newWidget.resize(this._containerContentBox || this._contentBox);
  94. }
  95. },
  96. _showChild: function(/*dijit._Widget*/ page){
  97. // summary: show the selected child widget
  98. var children = this.getChildren();
  99. page.isFirstChild = (page == children[0]);
  100. page.isLastChild = (page == children[children.length-1]);
  101. page.selected = true;
  102. page.domNode.style.display="";
  103. if(page._onShow){
  104. page._onShow(); // trigger load in ContentPane
  105. }else if(page.onShow){
  106. page.onShow();
  107. }
  108. },
  109. _hideChild: function(/*dijit._Widget*/ page){
  110. // summary: hide the specified child widget
  111. page.selected = false;
  112. page.domNode.style.display="none";
  113. if(page.onHide){
  114. page.onHide();
  115. }
  116. }
  117. });
  118. dojo.declare("dojox.layout.RadioGroupFade",
  119. dojox.layout.RadioGroup,
  120. {
  121. // summary: An extension on a stock RadioGroup, that fades the panes.
  122. _hideChild: function(page){
  123. // summary: hide the specified child widget
  124. dojo.fadeOut({
  125. node:page.domNode,
  126. duration:this.duration,
  127. onEnd: dojo.hitch(this,"inherited", arguments, arguments)
  128. }).play();
  129. },
  130. _showChild: function(page){
  131. // summary: show the specified child widget
  132. this.inherited(arguments);
  133. dojo.style(page.domNode, "opacity", 0);
  134. dojo.fadeIn({
  135. node:page.domNode,
  136. duration:this.duration
  137. }).play();
  138. }
  139. });
  140. dojo.declare("dojox.layout.RadioGroupSlide",
  141. dojox.layout.RadioGroup,
  142. {
  143. // summary: A Sliding Radio Group
  144. // description:
  145. // An extension on a stock RadioGroup widget, sliding the pane
  146. // into view from being hidden. The entry direction is randomized
  147. // on each view
  148. //
  149. // easing: Function
  150. // A hook to override the default easing of the pane slides.
  151. easing: "dojo.fx.easing.backOut",
  152. // zTop: Integer
  153. // A z-index to apply to the incoming pane
  154. zTop: 99,
  155. constructor: function(){
  156. if(dojo.isString(this.easing)){
  157. this.easing = dojo.getObject(this.easing);
  158. }
  159. },
  160. _positionChild: function(page){
  161. // summary: set the child out of view immediately after being hidden
  162. if(!this._size){ return; } // FIXME: is there a real "size" floating around always?
  163. // there should be a contest: obfuscate this function as best you can.
  164. var rA = true, rB = true;
  165. switch(page.slideFrom){
  166. case "bottom" : rB = !rB; break;
  167. case "right" : rA = !rA; rB = !rB; break;
  168. case "top" : break;
  169. case "left" : rA = !rA; break;
  170. default:
  171. rA = Math.round(Math.random());
  172. rB = Math.round(Math.random());
  173. break;
  174. }
  175. var prop = rA ? "top" : "left",
  176. val = (rB ? "-" : "") + (this._size[rA ? "h" : "w" ] + 20) + "px";
  177. dojo.style(page.domNode, prop, val);
  178. },
  179. _showChild: function(page){
  180. // summary: Slide in the selected child widget
  181. var children = this.getChildren();
  182. page.isFirstChild = (page == children[0]);
  183. page.isLastChild = (page == children[children.length-1]);
  184. page.selected = true;
  185. dojo.style(page.domNode,{
  186. zIndex: this.zTop, display:""
  187. })
  188. if(this._anim && this._anim.status()=="playing"){
  189. this._anim.gotoPercent(100,true);
  190. }
  191. this._anim = dojo.animateProperty({
  192. node:page.domNode,
  193. properties: {
  194. left: 0,
  195. top: 0
  196. },
  197. duration: this.duration,
  198. easing: this.easing,
  199. onEnd: dojo.hitch(page, function(){
  200. if(this.onShow){ this.onShow(); }
  201. if(this._onShow){ this._onShow(); }
  202. }),
  203. beforeBegin: dojo.hitch(this, "_positionChild", page)
  204. });
  205. this._anim.play();
  206. },
  207. _hideChild: function(page){
  208. // summary: reset the position of the hidden pane out of sight
  209. page.selected = false;
  210. page.domNode.style.zIndex = this.zTop - 1;
  211. if(page.onHide){
  212. page.onHide();
  213. }
  214. }
  215. });
  216. dojo.declare("dojox.layout._RadioButton",
  217. [dijit._Widget,dijit._Templated,dijit._Contained],
  218. {
  219. // summary: The Buttons for a RadioGroup
  220. //
  221. // description: A private widget used to manipulate the StackContainer (RadioGroup*). Don't create directly.
  222. //
  223. // label: String
  224. // the Text Label of the button
  225. label: "",
  226. // domNode to tell parent to select
  227. page: null,
  228. templateString: '<div dojoAttachPoint="focusNode" class="dojoxRadioButton"><span dojoAttachPoint="titleNode" class="dojoxRadioButtonLabel">${label}</span></div>',
  229. startup: function(){
  230. // summary: start listening to mouseOver
  231. this.connect(this.domNode, "onmouseenter", "_onMouse");
  232. },
  233. _onMouse: function(/* Event */e){
  234. // summary: set the selected child on hover, and set our hover state class
  235. this.getParent().selectChild(this.page);
  236. this._clearSelected();
  237. dojo.addClass(this.domNode,"dojoxRadioButtonSelected");
  238. },
  239. _clearSelected: function(){
  240. // summary: remove hover state class from sibling Buttons. This is easier (and more reliable)
  241. // than setting up an additional connection to onMouseOut
  242. // FIXME: this relies on the template being [div][span]node[/span][/div]
  243. dojo.query(".dojoxRadioButtonSelected", this.domNode.parentNode.parentNode)
  244. .removeClass("dojoxRadioButtonSelected")
  245. ;
  246. }
  247. });
  248. dojo.extend(dijit._Widget,{
  249. // slideFrom: String
  250. // A parameter needed by RadioGroupSlide only. An optional paramter to force
  251. // the ContentPane to slide in from a set direction. Defaults
  252. // to "random", or specify one of "top", "left", "right", "bottom"
  253. // to slideFrom top, left, right, or bottom.
  254. slideFrom: "random"
  255. })
  256. }