UNPKG

ares-ide

Version:

A browser-based code editor and UI designer for Enyo 2 projects

286 lines (281 loc) 10.8 kB
//*@public /** _enyo.DataList_ is an [enyo.DataRepeater](#enyo.DataRepeater) that employs a paginated scrolling scheme to enhance performance with larger datasets. The data is provided to the DataList by an [enyo.Collection](#enyo.Collection) set as the value of its _collection_ property. Note that care should be taken when deciding how to lay out the list's children. When there are a large number of child elements, the layout process can be taxing and non-performant for the browser. Avoid dynamically-updated layouts that require lots of calculations each time the data in a view is updated. Try to use CSS whenever possible. */ enyo.kind({ name: "enyo.DataList", kind: enyo.DataRepeater, /** The _enyo.DataList_ kind will emit a registered event when paging, `paging`, so that for per-page updates can be made when necessary. You can register for this event by calling _addListener()_ for the `paging` event with a callback. The callback method will be passed a reference to the _enyo.DataList_ that is paging, the name of the event (`paging`), and hash with the properties `start`, `end` and `action` that refer to the lowest active index in the dataset through the highest active index and the action that triggered the paging respectively. The `action` value can be either 'scroll' or 'reset'. */ /** The _enyo.DataList_ kind places its rows inside of a scroller. Any configurable options associated with _enyo.Scroller_ may be placed in this hash and will be set accordingly on this list's scroller. If no options are specified, the default _enyo.Scroller_ settings are used. */ scrollerOptions: null, /** The paging orientation. Valid values are _"vertical"_ and _"horizontal"_. This property will be mapped to a particular strategy governing how the list will flow. */ orientation: "vertical", /** This is typically handled automatically but some platforms may benefit from having a larger or smaller value here. If there is a number here it will be multiplied by the available viewport size (depending on orientation) to determine the minimum page size. The page size is directly related to the number of controls that will be generated at any given time (and subsequently needing update) whenever paging occurs. This number can be any rational number greater than _1.2_. */ pageSizeMultiplier: null, /** To disable the default smoothing-transitions (for supported platforms) set this flag to `false`. */ allowTransitions: true, /** Because some systems perform poorly on initialization there is a delay when attempting to actually draw the contents of a _enyo.DataList_. Usually you will not need to adjust this value (ms). If _renderDelay_ is null there will be no delay and it will be executed synchronously. Note that if set to 0 it will be executed asynchronously. */ renderDelay: 250, /** Completely resets the current list such that it scrolls to the top of the scrollable region and regenerates all of its children. This is typically necessary only on initialization, or if the entire dataset has been swapped out. */ reset: function () { // we can only reset if we've already rendered if (this.generated && this.$.scroller.generated) { this.delegate.reset(this); } }, /** Unlike _reset()_, which tears down and regenerates the entire list, this method attempts to refresh the pages as they are against the current dataset. This is much cheaper to call than _reset()_, but is primarily used internally. */ refresh: function () { if (this.hasRendered) { this.delegate.refresh(this); } }, /** Pass in an integer within the bounds of the lists's collection to have it scroll to the position of that index in the list. */ scrollToIndex: function (idx) { if (idx >= 0 && idx < this.length) { this.delegate.scrollToIndex(this, idx); } }, //*@protected constructor: enyo.inherit(function (sup) { return function () { sup.apply(this, arguments); this.metrics = {}; this.metrics.pages = {}; if (this.pageSizeMultiplier !== null && !isNaN(this.pageSizeMultiplier)) { this.pageSizeMultiplier = Math.max(1.2, this.pageSizeMultiplier); } }; }), create: enyo.inherit(function (sup) { return function () { // if we can, we use transitions this.allowTransitionsChanged(); // map the selected strategy to the correct delegate for operations // on the list, default to _vertical_ if none is provided or if it // could not be found this.delegate = this.ctor.delegates[this.orientation] || this.base.delegates.vertical; // if the delegate has an initialization routine execute it now before the // container and children are rendered if (this.delegate.initList) { this.delegate.initList(this); } sup.apply(this, arguments); // initialize the _pages_ array and add the pages to it this.pages = [this.$.page1, this.$.page2]; }; }), render: enyo.inherit(function (sup) { return function () { this.$.scroller.canGenerate = false; this.$.scroller.teardownRender(); sup.apply(this, arguments); }; }), /** Attempts to do initialization. There are only a few basic startup paths, but we need to be aware of what they are: * The view is rendered, it has a collection, and the collection has data. * The view is rendered, it has a collection with no data, and data is added later. * The view is rendered, but has no collection. Once the list itself is rendered, we check to see if we have a collection; if so, do we have any data to start rendering the rest of the list? Ultimately, the implementation decisions are decided by the delegate strategy. */ rendered: function () { // actually rendering a datalist can be taxing for some systems so // we arbitrarily delay showing for a fixed amount of time unless delay is // null in which case it will be executed immediately var startup = function () { // now that the base list is rendered, we can safely generate our scroller this.$.scroller.canGenerate = true; this.$.scroller.render(); // and now we hand over the action to our strategy to let it initialize the // way it needs to this.delegate.rendered(this); this.hasRendered = true; // now add our class to adjust visibility (if no overridden) this.addClass("rendered"); if (this.didRender) { this.didRender(); } }; if (this.renderDelay === null) { startup.call(this); } else { this.startJob("rendering", startup, this.renderDelay); // this delay will allow slower systems to keep going and get everything else // on screen before worrying about setting up the list } }, /** Overloaded to call a method of the delegate strategy. */ modelsAdded: function (c, e, props) { if (c === this.collection && this.$.scroller.canGenerate) { this.delegate.modelsAdded(this, props); } }, /** Overloaded to call a method of the delegate strategy. */ modelsRemoved: function (c, e, props) { if (c === this.collection && this.$.scroller.canGenerate) { this.delegate.modelsRemoved(this, props); } }, destroy: enyo.inherit(function (sup) { return function () { if (this.delegate && this.delegate.destroyList) { this.delegate.destroyList(this); } sup.apply(this, arguments); }; }), /** Overloaded from base kind to ensure that the container options correctly apply the scroller options before instantiating it. */ initContainer: enyo.inherit(function (sup) { return function () { var o = enyo.clone(this.get("containerOptions")), s = this.get("scrollerOptions"); if (s) { enyo.mixin(o, s, {exists: true}); } this.set("containerOptions", o); sup.apply(this, arguments); }; }), /** We let the delegate strategy manage the event, but we arbitrarily return true Because we don't want the event to propagate beyond this kind. */ didScroll: function (sender, event) { if (this.hasRendered) { this.delegate.didScroll(this, event); } return true; }, /** Special override to handle resizing in an attempt to minimize the amount of work we're doing. We don't want to waterfall the event to all children, so we hijack the normal handler. */ didResize: function (sender, event) { if (this.hasRendered) { this.delegate.didResize(this, event); } }, /** Overload to adjust the root method to be able to find the nested child based on the requested index if its page is currently active. Will return undefined if the index is out of bounds or if the control is not currently available. Also see [getChildForIndex](#getChildForIndex) which calls this method. */ childForIndex: function (i) { return this.delegate.childForIndex(this, i); }, //*@protected allowTransitionsChanged: function () { this.addRemoveClass("transitions", this.allowTransitions); }, /** The _enyo.DataList_ kind uses an overloaded container from its base kind. We set the container to a scroller and provide a way to modify the scroller options (via the _scrollerOptions_ hash). All children will reside in one of the two pages owned by the scroller. */ containerOptions: {name: "scroller", kind: "enyo.Scroller", components: [ {name: "active", classes: "active", components: [ {name: "page1", classes: "page page1"}, {name: "page2", classes: "page page2"}, {name: "buffer", classes: "buffer"} ]} ], canGenerate: false, classes: "enyo-fit enyo-data-list-scroller"}, //* We access this kind's constructor and need it to be undeferred at that time. noDefer: true, //* All of the CSS is relative to this class. classes: "enyo-data-list", //* Our initial _controlParent_ is us for the _flyweighter_ child. controlParentName: "", //* Of course we set our container to _scroller_ as needed by the base kind. containerName: "scroller", /** We have to trap the Enyo-generated _onScroll_ event and let the delegate handle it. We also need to catch the _onresize_ events so we know when to update our cached sizing. We overload the default handler so that we don't waterfall the resizing; we arbitrarily handle it to minimize the amount of work we do. */ handlers: {onScroll: "didScroll", onresize: "didResize"}, //* Add the RegisteredEventSupport mixin for the paging event mixins: [enyo.RegisteredEventSupport], //* All delegates are named elsewhere but are stored in these statics. statics: {delegates: {}}, //* An array of the actual _page_ references for easier access. pages: null }); //*@protected /** All subclasses of _enyo.DataList_ will have their own _delegates_ static hash. This is per _kind_, not per _instance_. */ enyo.DataList.subclass = function (ctor) { ctor.delegates = enyo.clone(ctor.prototype.base.delegates || this.delegates); };