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.

174 lines
5.5 KiB

  1. /**
  2. A control that displays a repeating list of rows, suitable for displaying
  3. medium-sized lists (up to ~100 items). A flyweight strategy is employed to
  4. render one set of row controls, as needed, for as many rows as are contained
  5. in the repeater.
  6. The FlyweightRepeater's _components_ block contains the controls to be used
  7. for a single row. This set of controls will be rendered for each row. You
  8. may customize row rendering by handling the _onSetupItem_ event.
  9. The controls inside a FlyweightRepeater are non-interactive. This means that
  10. calling methods that would normally cause rendering to occur (e.g.,
  11. _setContent_) will not do so. However, you can force a row to render by
  12. calling _renderRow(inRow)_.
  13. In addition, you can force a row to be temporarily interactive by calling
  14. _prepareRow(inRow)_. Call the _lockRow_ method when the interaction is
  15. complete.
  16. For more information, see the documentation on
  17. [Lists](https://github.com/enyojs/enyo/wiki/Lists)
  18. in the Enyo Developer Guide.
  19. */
  20. enyo.kind({
  21. name: "enyo.FlyweightRepeater",
  22. published: {
  23. //* Number of rows to render
  24. count: 0,
  25. //* If true, multiple selections are allowed
  26. multiSelect: false,
  27. //* If true, the selected item will toggle
  28. toggleSelected: false
  29. },
  30. events: {
  31. /**
  32. Fires once per row at render time, with event object:
  33. _{index: <index of row>, selected: <true if row is selected>}_
  34. */
  35. onSetupItem: ""
  36. },
  37. components: [
  38. {kind: "Selection", onSelect: "selectDeselect", onDeselect: "selectDeselect"},
  39. {name: "client"}
  40. ],
  41. rowOffset: 0,
  42. bottomUp: false,
  43. //* @protected
  44. create: function() {
  45. this.inherited(arguments);
  46. this.multiSelectChanged();
  47. },
  48. multiSelectChanged: function() {
  49. this.$.selection.setMulti(this.multiSelect);
  50. },
  51. setupItem: function(inIndex) {
  52. this.doSetupItem({index: inIndex, selected: this.isSelected(inIndex)});
  53. },
  54. //* Renders the list.
  55. generateChildHtml: function() {
  56. var h = "";
  57. this.index = null;
  58. // note: can supply a rowOffset
  59. // and indicate if rows should be rendered top down or bottomUp
  60. for (var i=0, r=0; i<this.count; i++) {
  61. r = this.rowOffset + (this.bottomUp ? this.count - i-1 : i);
  62. this.setupItem(r);
  63. this.$.client.setAttribute("index", r);
  64. h += this.inherited(arguments);
  65. this.$.client.teardownRender();
  66. }
  67. return h;
  68. },
  69. previewDomEvent: function(inEvent) {
  70. var i = this.index = this.rowForEvent(inEvent);
  71. inEvent.rowIndex = inEvent.index = i;
  72. inEvent.flyweight = this;
  73. },
  74. decorateEvent: function(inEventName, inEvent, inSender) {
  75. // decorate event with index found via dom iff event does not already contain an index.
  76. var i = (inEvent && inEvent.index != null) ? inEvent.index : this.index;
  77. if (inEvent && i != null) {
  78. inEvent.index = i;
  79. inEvent.flyweight = this;
  80. }
  81. this.inherited(arguments);
  82. },
  83. tap: function(inSender, inEvent) {
  84. if (this.toggleSelected) {
  85. this.$.selection.toggle(inEvent.index);
  86. } else {
  87. this.$.selection.select(inEvent.index);
  88. }
  89. },
  90. selectDeselect: function(inSender, inEvent) {
  91. this.renderRow(inEvent.key);
  92. },
  93. //* @public
  94. //* Returns the repeater's _selection_ component.
  95. getSelection: function() {
  96. return this.$.selection;
  97. },
  98. //* Gets the selection state for the given row index.
  99. isSelected: function(inIndex) {
  100. return this.getSelection().isSelected(inIndex);
  101. },
  102. //* Renders the row specified by _inIndex_.
  103. renderRow: function(inIndex) {
  104. //this.index = null;
  105. var node = this.fetchRowNode(inIndex);
  106. if (node) {
  107. this.setupItem(inIndex);
  108. node.innerHTML = this.$.client.generateChildHtml();
  109. this.$.client.teardownChildren();
  110. }
  111. },
  112. //* Fetches the DOM node for the given row index.
  113. fetchRowNode: function(inIndex) {
  114. if (this.hasNode()) {
  115. var n$ = this.node.querySelectorAll('[index="' + inIndex + '"]');
  116. return n$ && n$[0];
  117. }
  118. },
  119. //* Fetches the DOM node for the given event.
  120. rowForEvent: function(inEvent) {
  121. var n = inEvent.target;
  122. var id = this.hasNode().id;
  123. while (n && n.parentNode && n.id != id) {
  124. var i = n.getAttribute && n.getAttribute("index");
  125. if (i !== null) {
  126. return Number(i);
  127. }
  128. n = n.parentNode;
  129. }
  130. return -1;
  131. },
  132. //* Prepares the row specified by _inIndex_ such that changes made to the
  133. //* controls inside the repeater will be rendered for the given row.
  134. prepareRow: function(inIndex) {
  135. var n = this.fetchRowNode(inIndex);
  136. enyo.FlyweightRepeater.claimNode(this.$.client, n);
  137. },
  138. //* Prevents rendering of changes made to controls inside the repeater.
  139. lockRow: function() {
  140. this.$.client.teardownChildren();
  141. },
  142. //* Prepares the row specified by _inIndex_ such that changes made to the
  143. //* controls in the row will be rendered in the given row; then performs the
  144. //* function _inFunc_, and, finally, locks the row.
  145. performOnRow: function(inIndex, inFunc, inContext) {
  146. if (inFunc) {
  147. this.prepareRow(inIndex);
  148. enyo.call(inContext || null, inFunc);
  149. this.lockRow();
  150. }
  151. },
  152. statics: {
  153. //* Associates a flyweight rendered control (_inControl_) with a
  154. //* rendering context specified by _inNode_.
  155. claimNode: function(inControl, inNode) {
  156. var n = inNode && inNode.querySelectorAll("#" + inControl.id);
  157. n = n && n[0];
  158. // FIXME: consider controls generated if we found a node or tag: null, the later so can teardown render
  159. inControl.generated = Boolean(n || !inControl.tag);
  160. inControl.node = n;
  161. if (inControl.node) {
  162. inControl.rendered();
  163. } else {
  164. //enyo.log("Failed to find node for", inControl.id, inControl.generated);
  165. }
  166. for (var i=0, c$=inControl.children, c; c=c$[i]; i++) {
  167. this.claimNode(c, inNode);
  168. }
  169. }
  170. }
  171. });