|
|
- /**
- A control that displays a repeating list of rows, suitable for displaying
- medium-sized lists (up to ~100 items). A flyweight strategy is employed to
- render one set of row controls, as needed, for as many rows as are contained
- in the repeater.
-
- The FlyweightRepeater's _components_ block contains the controls to be used
- for a single row. This set of controls will be rendered for each row. You
- may customize row rendering by handling the _onSetupItem_ event.
-
- The controls inside a FlyweightRepeater are non-interactive. This means that
- calling methods that would normally cause rendering to occur (e.g.,
- _setContent_) will not do so. However, you can force a row to render by
- calling _renderRow(inRow)_.
-
- In addition, you can force a row to be temporarily interactive by calling
- _prepareRow(inRow)_. Call the _lockRow_ method when the interaction is
- complete.
-
- For more information, see the documentation on
- [Lists](https://github.com/enyojs/enyo/wiki/Lists)
- in the Enyo Developer Guide.
- */
- enyo.kind({
- name: "enyo.FlyweightRepeater",
- published: {
- //* Number of rows to render
- count: 0,
- //* If true, multiple selections are allowed
- multiSelect: false,
- //* If true, the selected item will toggle
- toggleSelected: false
- },
- events: {
- /**
- Fires once per row at render time, with event object:
- _{index: <index of row>, selected: <true if row is selected>}_
- */
- onSetupItem: ""
- },
- components: [
- {kind: "Selection", onSelect: "selectDeselect", onDeselect: "selectDeselect"},
- {name: "client"}
- ],
- rowOffset: 0,
- bottomUp: false,
- //* @protected
- create: function() {
- this.inherited(arguments);
- this.multiSelectChanged();
- },
- multiSelectChanged: function() {
- this.$.selection.setMulti(this.multiSelect);
- },
- setupItem: function(inIndex) {
- this.doSetupItem({index: inIndex, selected: this.isSelected(inIndex)});
- },
- //* Renders the list.
- generateChildHtml: function() {
- var h = "";
- this.index = null;
- // note: can supply a rowOffset
- // and indicate if rows should be rendered top down or bottomUp
- for (var i=0, r=0; i<this.count; i++) {
- r = this.rowOffset + (this.bottomUp ? this.count - i-1 : i);
- this.setupItem(r);
- this.$.client.setAttribute("index", r);
- h += this.inherited(arguments);
- this.$.client.teardownRender();
- }
- return h;
- },
- previewDomEvent: function(inEvent) {
- var i = this.index = this.rowForEvent(inEvent);
- inEvent.rowIndex = inEvent.index = i;
- inEvent.flyweight = this;
- },
- decorateEvent: function(inEventName, inEvent, inSender) {
- // decorate event with index found via dom iff event does not already contain an index.
- var i = (inEvent && inEvent.index != null) ? inEvent.index : this.index;
- if (inEvent && i != null) {
- inEvent.index = i;
- inEvent.flyweight = this;
- }
- this.inherited(arguments);
- },
- tap: function(inSender, inEvent) {
- if (this.toggleSelected) {
- this.$.selection.toggle(inEvent.index);
- } else {
- this.$.selection.select(inEvent.index);
- }
- },
- selectDeselect: function(inSender, inEvent) {
- this.renderRow(inEvent.key);
- },
- //* @public
- //* Returns the repeater's _selection_ component.
- getSelection: function() {
- return this.$.selection;
- },
- //* Gets the selection state for the given row index.
- isSelected: function(inIndex) {
- return this.getSelection().isSelected(inIndex);
- },
- //* Renders the row specified by _inIndex_.
- renderRow: function(inIndex) {
- //this.index = null;
- var node = this.fetchRowNode(inIndex);
- if (node) {
- this.setupItem(inIndex);
- node.innerHTML = this.$.client.generateChildHtml();
- this.$.client.teardownChildren();
- }
- },
- //* Fetches the DOM node for the given row index.
- fetchRowNode: function(inIndex) {
- if (this.hasNode()) {
- var n$ = this.node.querySelectorAll('[index="' + inIndex + '"]');
- return n$ && n$[0];
- }
- },
- //* Fetches the DOM node for the given event.
- rowForEvent: function(inEvent) {
- var n = inEvent.target;
- var id = this.hasNode().id;
- while (n && n.parentNode && n.id != id) {
- var i = n.getAttribute && n.getAttribute("index");
- if (i !== null) {
- return Number(i);
- }
- n = n.parentNode;
- }
- return -1;
- },
- //* Prepares the row specified by _inIndex_ such that changes made to the
- //* controls inside the repeater will be rendered for the given row.
- prepareRow: function(inIndex) {
- var n = this.fetchRowNode(inIndex);
- enyo.FlyweightRepeater.claimNode(this.$.client, n);
- },
- //* Prevents rendering of changes made to controls inside the repeater.
- lockRow: function() {
- this.$.client.teardownChildren();
- },
- //* Prepares the row specified by _inIndex_ such that changes made to the
- //* controls in the row will be rendered in the given row; then performs the
- //* function _inFunc_, and, finally, locks the row.
- performOnRow: function(inIndex, inFunc, inContext) {
- if (inFunc) {
- this.prepareRow(inIndex);
- enyo.call(inContext || null, inFunc);
- this.lockRow();
- }
- },
- statics: {
- //* Associates a flyweight rendered control (_inControl_) with a
- //* rendering context specified by _inNode_.
- claimNode: function(inControl, inNode) {
- var n = inNode && inNode.querySelectorAll("#" + inControl.id);
- n = n && n[0];
- // FIXME: consider controls generated if we found a node or tag: null, the later so can teardown render
- inControl.generated = Boolean(n || !inControl.tag);
- inControl.node = n;
- if (inControl.node) {
- inControl.rendered();
- } else {
- //enyo.log("Failed to find node for", inControl.id, inControl.generated);
- }
- for (var i=0, c$=inControl.children, c; c=c$[i]; i++) {
- this.claimNode(c, inNode);
- }
- }
- }
- });
|