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.

268 lines
7.3 KiB

  1. /**
  2. _enyo.Node_ is a control that creates structured trees based on Enyo's child
  3. component hierarchy format, e.g.:
  4. {kind: "Node", icon: "images/folder-open.png", content: "Tree",
  5. expandable: true, expanded: true, components: [
  6. {icon: "images/file.png", content: "Alpha"},
  7. {icon: "images/folder-open.png", content: "Bravo",
  8. expandable: true, expanded: false, components: [
  9. {icon: "images/file.png", content: "Bravo-Alpha"},
  10. {icon: "images/file.png", content: "Bravo-Bravo"},
  11. {icon: "images/file.png", content: "Bravo-Charlie"}
  12. ]
  13. }
  14. ]
  15. }
  16. The default kind of components within a node is itself _enyo.Node_, so only
  17. the top-level node of the tree needs to be explicitly defined as such.
  18. When an expandable tree node expands, an _onExpand_ event is sent; when it
  19. is tapped, a _nodeTap_ event is sent.
  20. When the optional property _onlyIconExpands_ is set to true, expandable
  21. nodes may only be opened by tapping the icon; tapping the content label
  22. will fire the _nodeTap_ event, but will not expand the node.
  23. */
  24. enyo.kind({
  25. name: "enyo.Node",
  26. published: {
  27. //* @public
  28. //* Whether or not the Node is expandable and has child branches
  29. expandable: false,
  30. //* Open/closed state of the current Node
  31. expanded: false,
  32. //* Path to image to be used as the icon for this Node
  33. icon: "",
  34. /**
  35. Optional flag that, when true, causes the Node to expand only when
  36. the icon is tapped; not when the contents are tapped
  37. */
  38. onlyIconExpands: false,
  39. //* @protected
  40. //* Adds or removes the Enyo-selected CSS class.
  41. selected: false
  42. },
  43. style: "padding: 0 0 0 16px;",
  44. content: "Node",
  45. defaultKind: "Node",
  46. classes: "enyo-node",
  47. components: [
  48. {name: "icon", kind: "Image", showing: false},
  49. {kind: "Control", name: "caption", Xtag: "span", style: "display: inline-block; padding: 4px;", allowHtml: true},
  50. {kind: "Control", name: "extra", tag: 'span', allowHtml: true}
  51. ],
  52. childClient: [
  53. {kind: "Control", name: "box", classes: "enyo-node-box", Xstyle: "border: 1px solid orange;", components: [
  54. {kind: "Control", name: "client", classes: "enyo-node-client", Xstyle: "border: 1px solid lightblue;"}
  55. ]}
  56. ],
  57. handlers: {
  58. ondblclick: "dblclick"
  59. },
  60. events: {
  61. //* @public
  62. //* Fired when the Node is tapped
  63. onNodeTap: "nodeTap",
  64. //* Fired when the Node is double-clicked
  65. onNodeDblClick: "nodeDblClick",
  66. /**
  67. Fired when the Node expands or contracts, as indicated by the
  68. 'expanded' property in the event data
  69. */
  70. onExpand: "nodeExpand",
  71. //* Fired when the Node is destroyed
  72. onDestroyed: "nodeDestroyed"
  73. },
  74. //
  75. //* @protected
  76. create: function() {
  77. this.inherited(arguments);
  78. //this.expandedChanged();
  79. //this.levelChanged();
  80. this.selectedChanged();
  81. this.iconChanged();
  82. },
  83. destroy: function() {
  84. this.doDestroyed();
  85. this.inherited(arguments);
  86. },
  87. initComponents: function() {
  88. // TODO: optimize to create the childClient on demand
  89. //this.hasChildren = this.components;
  90. if (this.expandable) {
  91. this.kindComponents = this.kindComponents.concat(this.childClient);
  92. }
  93. this.inherited(arguments);
  94. },
  95. //
  96. contentChanged: function() {
  97. //this.$.caption.setContent((this.expandable ? (this.expanded ? "-" : "+") : "") + this.content);
  98. this.$.caption.setContent(this.content);
  99. },
  100. iconChanged: function() {
  101. this.$.icon.setSrc(this.icon);
  102. this.$.icon.setShowing(Boolean(this.icon));
  103. },
  104. selectedChanged: function() {
  105. this.addRemoveClass("enyo-selected", this.selected);
  106. },
  107. rendered: function() {
  108. this.inherited(arguments);
  109. if (this.expandable && !this.expanded) {
  110. this.quickCollapse();
  111. }
  112. },
  113. //
  114. addNodes: function(inNodes) {
  115. this.destroyClientControls();
  116. for (var i=0, n; n=inNodes[i]; i++) {
  117. this.createComponent(n);
  118. }
  119. this.$.client.render();
  120. },
  121. addTextNodes: function(inNodes) {
  122. this.destroyClientControls();
  123. for (var i=0, n; n=inNodes[i]; i++) {
  124. this.createComponent({content: n});
  125. }
  126. this.$.client.render();
  127. },
  128. //
  129. tap: function(inSender, inEvent) {
  130. if(!this.onlyIconExpands) {
  131. this.toggleExpanded();
  132. this.doNodeTap();
  133. } else {
  134. if((inEvent.target==this.$.icon.hasNode())) {
  135. this.toggleExpanded();
  136. } else {
  137. this.doNodeTap();
  138. }
  139. }
  140. return true;
  141. },
  142. dblclick: function(inSender, inEvent) {
  143. this.doNodeDblClick();
  144. return true;
  145. },
  146. //
  147. toggleExpanded: function() {
  148. this.setExpanded(!this.expanded);
  149. },
  150. quickCollapse: function() {
  151. this.removeClass("enyo-animate");
  152. this.$.box.applyStyle("height", "0");
  153. var h = this.$.client.getBounds().height;
  154. this.$.client.setBounds({top: -h});
  155. },
  156. _expand: function() {
  157. this.addClass("enyo-animate");
  158. var h = this.$.client.getBounds().height;
  159. this.$.box.setBounds({height: h});
  160. this.$.client.setBounds({top: 0});
  161. setTimeout(enyo.bind(this, function() {
  162. // things may have happened in the interim, make sure
  163. // we only fix height if we are still expanded
  164. if (this.expanded) {
  165. this.removeClass("enyo-animate");
  166. this.$.box.applyStyle("height", "auto");
  167. }
  168. }), 225);
  169. },
  170. _collapse: function() {
  171. // disable transitions
  172. this.removeClass("enyo-animate");
  173. // fix the height of our box (rather than 'auto'), this
  174. // gives webkit something to lerp from
  175. var h = this.$.client.getBounds().height;
  176. this.$.box.setBounds({height: h});
  177. // yield the thead so DOM can make those changes (without transitions)
  178. setTimeout(enyo.bind(this, function() {
  179. // enable transitions
  180. this.addClass("enyo-animate");
  181. // shrink our box to 0
  182. this.$.box.applyStyle("height", "0");
  183. // slide the contents up
  184. this.$.client.setBounds({top: -h});
  185. }), 25);
  186. },
  187. expandedChanged: function(inOldExpanded) {
  188. if (!this.expandable) {
  189. this.expanded = false;
  190. } else {
  191. var event = {expanded: this.expanded};
  192. this.doExpand(event);
  193. if (!event.wait) {
  194. this.effectExpanded();
  195. }
  196. }
  197. },
  198. effectExpanded: function() {
  199. if (this.$.client) {
  200. if (!this.expanded) {
  201. this._collapse();
  202. } else {
  203. this._expand();
  204. }
  205. }
  206. //this.contentChanged();
  207. }/*,
  208. //
  209. //
  210. levelChanged: function() {
  211. this.applyStyle("padding-left", 16 + "px");
  212. },
  213. toggleChildren: function() {
  214. if (this.$.list) {
  215. this.$.list.setShowing(this.expanded);
  216. }
  217. },
  218. renderNodes: function(inNodes) {
  219. var list = this.createComponent({name: "list", container: this});
  220. for (var i=0, n; n=inNodes[i]; i++) {
  221. n.setLevel(this.level + 1);
  222. n.setContainer(list);
  223. n.render();
  224. }
  225. list.render();
  226. },
  227. //* @public
  228. addNodes: function(inNodes) {
  229. this.renderNodes(inNodes);
  230. this.toggleChildren();
  231. },
  232. removeNodes: function() {
  233. if (this.$.list) {
  234. this.$.list.destroy();
  235. }
  236. },
  237. hasVisibleChildren: function() {
  238. return this.expanded && this.$.list && this.$.list.controls.length > 0;
  239. },
  240. fetchParent: function() {
  241. return this.level > 0 && this.container.container;
  242. },
  243. fetchChildren: function() {
  244. return this.$.list && this.$.list.controls;
  245. },
  246. fetchFirstChild: function() {
  247. return this.$.list && this.$.list.controls[0];
  248. },
  249. fetchLastChild: function() {
  250. return this.$.list && this.$.list.controls[this.$.list.controls.length-1];
  251. },
  252. fetchPrevSibling: function() {
  253. var i = this.container.controls.indexOf(this);
  254. return this.level > 0 && this.container.controls[i-1];
  255. },
  256. fetchNextSibling: function() {
  257. var i = this.container.controls.indexOf(this);
  258. return this.level > 0 && this.container.controls[i+1];
  259. },
  260. getVisibleBounds: function() {
  261. return this.$.client.getBounds();
  262. }
  263. */
  264. });