RadioGroup.js 8.8 KB

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