diff options
Diffstat (limited to 'html/lib/layout/list')
-rw-r--r-- | html/lib/layout/list/package.js | 3 | ||||
-rw-r--r-- | html/lib/layout/list/source/AlphaJumpList.js | 37 | ||||
-rw-r--r-- | html/lib/layout/list/source/AlphaJumper.css | 36 | ||||
-rw-r--r-- | html/lib/layout/list/source/AlphaJumper.js | 70 | ||||
-rw-r--r-- | html/lib/layout/list/source/FlyweightRepeater.js | 175 | ||||
-rw-r--r-- | html/lib/layout/list/source/List.css | 15 | ||||
-rw-r--r-- | html/lib/layout/list/source/List.js | 368 | ||||
-rw-r--r-- | html/lib/layout/list/source/PulldownList.css | 60 | ||||
-rw-r--r-- | html/lib/layout/list/source/PulldownList.js | 180 | ||||
-rw-r--r-- | html/lib/layout/list/source/Selection.js | 141 | ||||
-rw-r--r-- | html/lib/layout/list/source/package.js | 7 | ||||
-rw-r--r-- | html/lib/layout/list/source/wip-package.js | 6 |
12 files changed, 1098 insertions, 0 deletions
diff --git a/html/lib/layout/list/package.js b/html/lib/layout/list/package.js new file mode 100644 index 0000000..f1de4f2 --- /dev/null +++ b/html/lib/layout/list/package.js @@ -0,0 +1,3 @@ +enyo.depends( + "source/" +);
\ No newline at end of file diff --git a/html/lib/layout/list/source/AlphaJumpList.js b/html/lib/layout/list/source/AlphaJumpList.js new file mode 100644 index 0000000..6b6e326 --- /dev/null +++ b/html/lib/layout/list/source/AlphaJumpList.js @@ -0,0 +1,37 @@ +/** + A control that presents an alphabetic panel that you can select from, in + order to perform actions based on the item selected. + + {kind: "AlphaJumpList", onSetupItem: "setupItem", + onAlphaJump: "alphaJump", + components: [ + {name: "divider"}, + {kind: "onyx.Item"} + ] + } + +*/ +enyo.kind({ + name: "enyo.AlphaJumpList", + kind: "List", + //* @protected + scrollTools: [ + {name: "jumper", kind: "AlphaJumper"} + ], + initComponents: function() { + this.createChrome(this.scrollTools); + this.inherited(arguments); + }, + rendered: function() { + this.inherited(arguments); + this.centerJumper(); + }, + resizeHandler: function() { + this.inherited(arguments); + this.centerJumper(); + }, + centerJumper: function() { + var b = this.getBounds(), sb = this.$.jumper.getBounds(); + this.$.jumper.applyStyle("top", ((b.height - sb.height) / 2) + "px"); + } +});
\ No newline at end of file diff --git a/html/lib/layout/list/source/AlphaJumper.css b/html/lib/layout/list/source/AlphaJumper.css new file mode 100644 index 0000000..df1265c --- /dev/null +++ b/html/lib/layout/list/source/AlphaJumper.css @@ -0,0 +1,36 @@ +.enyo-alpha-jumper { + text-transform: uppercase; + font-size: 11px; + xborder-radius: 12px; + position: absolute; + xbackground: white; + text-align: center; + right: 10px; + z-index: 1; + color: #333; + padding: 0 14px; +} + +.enyo-alpha-jumper > *:first-child { + padding-top: 4px; + border-radius: 12px 12px 0 0; + border-top: 1px solid #333; +} + +.enyo-alpha-jumper > *:last-child { + padding-bottom: 4px; + border-radius: 0 0 12px 12px; + border-bottom: 1px solid #333; +} + +.enyo-alpha-jumper > * { + background: #eee; + padding: 1px 4px; + border-right: 1px solid #333; + border-left: 1px solid #333; +} + +.enyo-alpha-jumper > .active { + background: #333; + color: white; +}
\ No newline at end of file diff --git a/html/lib/layout/list/source/AlphaJumper.js b/html/lib/layout/list/source/AlphaJumper.js new file mode 100644 index 0000000..2f85d00 --- /dev/null +++ b/html/lib/layout/list/source/AlphaJumper.js @@ -0,0 +1,70 @@ +enyo.kind({ + name: "enyo.AlphaJumper", + classes: "enyo-alpha-jumper enyo-border-box", + published: { + marker: null + }, + events: { + onAlphaJump: "" + }, + handlers: { + ondown: "down", + onmove: "move", + onup: "up" + }, + initComponents: function() { + for (var s="A".charCodeAt(0), i=s; i<s+26; i++) { + this.createComponent({content: String.fromCharCode(i)}); + } + this.inherited(arguments); + }, + down: function(inSender, inEvent) { + if (this.tracking) { + enyo.dispatcher.release(); + } + this.tracking = true; + if (this.hasNode()) { + var b = this.node.getBoundingClientRect(); + // IE8 does not return width + var w = (b.width === undefined) ? (b.right - b.left) : b.width; + this.x = b.left + w/2; + } + enyo.dispatcher.capture(this); + this.track(inEvent); + }, + move: function(inSender, inEvent) { + if (this.tracking) { + this.track(inEvent); + } + }, + up: function() { + if (this.tracking) { + enyo.dispatcher.release(); + this.setMarker(null); + this.tracking = false; + } + }, + track: function(inEvent) { + var n = document.elementFromPoint(this.x, inEvent.pageY); + var c = this.nodeToControl(n); + if (c) { + this.setMarker(c); + } + }, + nodeToControl: function(inNode) { + for (var i=0, c$=this.controls, c; c=c$[i]; i++) { + if (inNode == c.hasNode()) { + return c; + } + } + }, + markerChanged: function(inLast) { + if (inLast) { + inLast.removeClass("active"); + } + if (this.marker) { + this.marker.addClass("active"); + this.doAlphaJump({letter: this.marker.getContent(), index: this.marker.indexInContainer()}); + } + } +});
\ No newline at end of file diff --git a/html/lib/layout/list/source/FlyweightRepeater.js b/html/lib/layout/list/source/FlyweightRepeater.js new file mode 100644 index 0000000..01e6d80 --- /dev/null +++ b/html/lib/layout/list/source/FlyweightRepeater.js @@ -0,0 +1,175 @@ +/** + 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); + } + } + } +});
\ No newline at end of file diff --git a/html/lib/layout/list/source/List.css b/html/lib/layout/list/source/List.css new file mode 100644 index 0000000..72da968 --- /dev/null +++ b/html/lib/layout/list/source/List.css @@ -0,0 +1,15 @@ +.enyo-list { + position: relative; +} + +.enyo-list-port { + overflow: hidden; + position: relative; + height: 10000000px; +} + +.enyo-list-page { + position: absolute; + left: 0; + right: 0; +}
\ No newline at end of file diff --git a/html/lib/layout/list/source/List.js b/html/lib/layout/list/source/List.js new file mode 100644 index 0000000..c4252b9 --- /dev/null +++ b/html/lib/layout/list/source/List.js @@ -0,0 +1,368 @@ +/** + A control that displays a scrolling list of rows, suitable for displaying + very large lists. _enyo.List_ is optimized such that only a small portion of + the list is rendered at a given time. A flyweight pattern is employed, in + which controls placed inside the list are created once, but rendered for + each list item. For this reason, it's best to use only simple controls in + a List, such as <a href="#enyo.Control">enyo.Control</a> and + <a href="#enyo.Image">enyo.Image</a>. + + A List'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. + + Events fired from within list rows contain the _index_ property, which may + be used to identify the row from which the event originated. + + The controls inside a List 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.List", + kind: "Scroller", + classes: "enyo-list", + published: { + /** + The number of rows contained in the list. Note that as the amount of + list data changes, _setRows_ can be called to adjust the number of + rows. To re-render the list at the current position when the count + has changed, call the _refresh_ method. + */ + count: 0, + /** + The number of rows to be shown on a given list page segment. + There is generally no need to adjust this value. + */ + rowsPerPage: 50, + /** + If true, renders the list such that row 0 is at the bottom of the + viewport and the beginning position of the list is scrolled to the + bottom + */ + bottomUp: false, + //* If true, multiple selections are allowed + multiSelect: false, + //* If true, the selected item will toggle + toggleSelected: false, + //* If true, the list will assume all rows have the same height for optimization + fixedHeight: false + }, + events: { + /** + Fires once per row at render time, with event object: + _{index: <index of row>}_ + */ + onSetupItem: "" + }, + handlers: { + onAnimateFinish: "animateFinish" + }, + //* @protected + rowHeight: 0, + listTools: [ + {name: "port", classes: "enyo-list-port enyo-border-box", components: [ + {name: "generator", kind: "FlyweightRepeater", canGenerate: false, components: [ + {tag: null, name: "client"} + ]}, + {name: "page0", allowHtml: true, classes: "enyo-list-page"}, + {name: "page1", allowHtml: true, classes: "enyo-list-page"} + ]} + ], + create: function() { + this.pageHeights = []; + this.inherited(arguments); + this.getStrategy().translateOptimized = true; + this.bottomUpChanged(); + this.multiSelectChanged(); + this.toggleSelectedChanged(); + }, + createStrategy: function() { + this.controlParentName = "strategy"; + this.inherited(arguments); + this.createChrome(this.listTools); + this.controlParentName = "client"; + this.discoverControlParent(); + }, + rendered: function() { + this.inherited(arguments); + this.$.generator.node = this.$.port.hasNode(); + this.$.generator.generated = true; + this.reset(); + }, + resizeHandler: function() { + this.inherited(arguments); + this.refresh(); + }, + bottomUpChanged: function() { + this.$.generator.bottomUp = this.bottomUp; + this.$.page0.applyStyle(this.pageBound, null); + this.$.page1.applyStyle(this.pageBound, null); + this.pageBound = this.bottomUp ? "bottom" : "top"; + if (this.hasNode()) { + this.reset(); + } + }, + multiSelectChanged: function() { + this.$.generator.setMultiSelect(this.multiSelect); + }, + toggleSelectedChanged: function() { + this.$.generator.setToggleSelected(this.toggleSelected); + }, + countChanged: function() { + if (this.hasNode()) { + this.updateMetrics(); + } + }, + updateMetrics: function() { + this.defaultPageHeight = this.rowsPerPage * (this.rowHeight || 100); + this.pageCount = Math.ceil(this.count / this.rowsPerPage); + this.portSize = 0; + for (var i=0; i < this.pageCount; i++) { + this.portSize += this.getPageHeight(i); + } + this.adjustPortSize(); + }, + generatePage: function(inPageNo, inTarget) { + this.page = inPageNo; + var r = this.$.generator.rowOffset = this.rowsPerPage * this.page; + var rpp = this.$.generator.count = Math.min(this.count - r, this.rowsPerPage); + var html = this.$.generator.generateChildHtml(); + inTarget.setContent(html); + var pageHeight = inTarget.getBounds().height; + // if rowHeight is not set, use the height from the first generated page + if (!this.rowHeight && pageHeight > 0) { + this.rowHeight = Math.floor(pageHeight / rpp); + this.updateMetrics(); + } + // update known page heights + if (!this.fixedHeight) { + var h0 = this.getPageHeight(inPageNo); + if (h0 != pageHeight && pageHeight > 0) { + this.pageHeights[inPageNo] = pageHeight; + this.portSize += pageHeight - h0; + } + } + }, + update: function(inScrollTop) { + var updated = false; + // get page info for position + var pi = this.positionToPageInfo(inScrollTop); + // zone line position + var pos = pi.pos + this.scrollerHeight/2; + // leap-frog zone position + var k = Math.floor(pos/Math.max(pi.height, this.scrollerHeight) + 1/2) + pi.no; + // which page number for page0 (even number pages)? + var p = k % 2 == 0 ? k : k-1; + if (this.p0 != p && this.isPageInRange(p)) { + //this.log("update page0", p); + this.generatePage(p, this.$.page0); + this.positionPage(p, this.$.page0); + this.p0 = p; + updated = true; + } + // which page number for page1 (odd number pages)? + p = k % 2 == 0 ? Math.max(1, k-1) : k; + // position data page 1 + if (this.p1 != p && this.isPageInRange(p)) { + //this.log("update page1", p); + this.generatePage(p, this.$.page1); + this.positionPage(p, this.$.page1); + this.p1 = p; + updated = true; + } + if (updated && !this.fixedHeight) { + this.adjustBottomPage(); + this.adjustPortSize(); + } + }, + updateForPosition: function(inPos) { + this.update(this.calcPos(inPos)); + }, + calcPos: function(inPos) { + return (this.bottomUp ? (this.portSize - this.scrollerHeight - inPos) : inPos); + }, + adjustBottomPage: function() { + var bp = this.p0 >= this.p1 ? this.$.page0 : this.$.page1; + this.positionPage(bp.pageNo, bp); + }, + adjustPortSize: function() { + this.scrollerHeight = this.getBounds().height; + var s = Math.max(this.scrollerHeight, this.portSize); + this.$.port.applyStyle("height", s + "px"); + }, + positionPage: function(inPage, inTarget) { + inTarget.pageNo = inPage; + var y = this.pageToPosition(inPage); + inTarget.applyStyle(this.pageBound, y + "px"); + }, + pageToPosition: function(inPage) { + var y = 0; + var p = inPage; + while (p > 0) { + p--; + y += this.getPageHeight(p); + } + return y; + }, + positionToPageInfo: function(inY) { + var page = -1; + var p = this.calcPos(inY); + var h = this.defaultPageHeight; + while (p >= 0) { + page++; + h = this.getPageHeight(page); + p -= h; + } + //page = Math.min(page, this.pageCount-1); + return {no: page, height: h, pos: p+h}; + }, + isPageInRange: function(inPage) { + return inPage == Math.max(0, Math.min(this.pageCount-1, inPage)); + }, + getPageHeight: function(inPageNo) { + return this.pageHeights[inPageNo] || this.defaultPageHeight; + }, + invalidatePages: function() { + this.p0 = this.p1 = null; + // clear the html in our render targets + this.$.page0.setContent(""); + this.$.page1.setContent(""); + }, + invalidateMetrics: function() { + this.pageHeights = []; + this.rowHeight = 0; + this.updateMetrics(); + }, + scroll: function(inSender, inEvent) { + var r = this.inherited(arguments); + this.update(this.getScrollTop()); + return r; + }, + //* @public + scrollToBottom: function() { + this.update(this.getScrollBounds().maxTop); + this.inherited(arguments); + }, + setScrollTop: function(inScrollTop) { + this.update(inScrollTop); + this.inherited(arguments); + this.twiddle(); + }, + getScrollPosition: function() { + return this.calcPos(this.getScrollTop()); + }, + setScrollPosition: function(inPos) { + this.setScrollTop(this.calcPos(inPos)); + }, + //* Scrolls to a specific row. + scrollToRow: function(inRow) { + var page = Math.floor(inRow / this.rowsPerPage); + var pageRow = inRow % this.rowsPerPage; + var h = this.pageToPosition(page); + // update the page + this.updateForPosition(h); + // call pageToPosition again and this time should return the right pos since the page info is populated + h = this.pageToPosition(page); + this.setScrollPosition(h); + if (page == this.p0 || page == this.p1) { + var rowNode = this.$.generator.fetchRowNode(inRow); + if (rowNode) { + // calc row offset + var offset = rowNode.offsetTop; + if (this.bottomUp) { + offset = this.getPageHeight(page) - rowNode.offsetHeight - offset; + } + var y = this.getScrollPosition() + offset; + this.setScrollPosition(y); + } + } + }, + //* Scrolls to the beginning of the list. + scrollToStart: function() { + this[this.bottomUp ? "scrollToBottom" : "scrollToTop"](); + }, + //* Scrolls to the end of the list. + scrollToEnd: function() { + this[this.bottomUp ? "scrollToTop" : "scrollToBottom"](); + }, + //* Re-renders the list at the current position. + refresh: function() { + this.invalidatePages(); + this.update(this.getScrollTop()); + this.stabilize(); + + //FIXME: Necessary evil for Android 4.0.4 refresh bug + if (enyo.platform.android === 4) { + this.twiddle(); + } + }, + //* Re-renders the list from the beginning. + reset: function() { + this.getSelection().clear(); + this.invalidateMetrics(); + this.invalidatePages(); + this.stabilize(); + this.scrollToStart(); + }, + /** + Returns the _selection_ component that manages the selection state for + this list. + */ + getSelection: function() { + return this.$.generator.getSelection(); + }, + //* Sets the selection state for the given row index. + select: function(inIndex, inData) { + return this.getSelection().select(inIndex, inData); + }, + //* Gets the selection state for the given row index. + isSelected: function(inIndex) { + return this.$.generator.isSelected(inIndex); + }, + /** + Re-renders the specified row. Call after making modifications to a row, + to force it to render. + */ + renderRow: function(inIndex) { + this.$.generator.renderRow(inIndex); + }, + //* Prepares the row to become interactive. + prepareRow: function(inIndex) { + this.$.generator.prepareRow(inIndex); + }, + //* Restores the row to being non-interactive. + lockRow: function() { + this.$.generator.lockRow(); + }, + /** + Performs a set of tasks by running the function _inFunc_ on a row (which + must be interactive at the time the tasks are performed). Locks the row + when done. + */ + performOnRow: function(inIndex, inFunc, inContext) { + this.$.generator.performOnRow(inIndex, inFunc, inContext); + }, + //* @protected + animateFinish: function(inSender) { + this.twiddle(); + return true; + }, + // FIXME: Android 4.04 has issues with nested composited elements; for example, a SwipeableItem, + // can incorrectly generate taps on its content when it has slid off the screen; + // we address this BUG here by forcing the Scroller to "twiddle" which corrects the bug by + // provoking a dom update. + twiddle: function() { + var s = this.getStrategy(); + enyo.call(s, "twiddle"); + } +}); diff --git a/html/lib/layout/list/source/PulldownList.css b/html/lib/layout/list/source/PulldownList.css new file mode 100644 index 0000000..89f6da0 --- /dev/null +++ b/html/lib/layout/list/source/PulldownList.css @@ -0,0 +1,60 @@ +.enyo-list-pulldown { + position: absolute; + bottom: 100%; + left: 0; + right: 0; +} + +.enyo-puller { + position: relative; + height: 50px; + font-size: 22px; + color: #444; + padding: 20px 0 0px 34px; +} + +.enyo-puller-text { + position: absolute; + left: 80px; + top: 22px; +} + +.enyo-puller-arrow { + position: relative; + background: #444; + width: 7px; + height: 28px; + transition: transform 0.3s; + -webkit-transition: -webkit-transform 0.3s; + -moz-transition: -moz-transform 0.3s; + -o-transition: -o-transform 0.3s; + -ms-transition: -ms-transform 0.3s; +} + +.enyo-puller-arrow:after { + content: " "; + height: 0; + width: 0; + position: absolute; + border: 10px solid transparent; + border-bottom-color: #444; + bottom: 100%; + left: 50%; + margin-left: -10px; +} + +.enyo-puller-arrow-up { + transform: rotate(0deg); + -webkit-transform: rotate(0deg); + -moz-transform: rotate(0deg); + -o-transform: rotate(0deg); + -ms-transform: rotate(0deg); +} + +.enyo-puller-arrow-down { + transform: rotate(180deg); + -webkit-transform: rotate(180deg); + -moz-transform: rotate(180deg); + -o-transform: rotate(180deg); + -ms-transform: rotate(180deg); +}
\ No newline at end of file diff --git a/html/lib/layout/list/source/PulldownList.js b/html/lib/layout/list/source/PulldownList.js new file mode 100644 index 0000000..8d0dab1 --- /dev/null +++ b/html/lib/layout/list/source/PulldownList.js @@ -0,0 +1,180 @@ +/** +A list that provides a pull-to-refresh feature, which allows new data to be +retrieved and updated in the list. + +PulldownList provides the onPullRelease event to allow an application to start +retrieving new data. The onPullComplete event indicates that the pull is +complete and it's time to update the list with the new data. + + {name: "list", kind: "PulldownList", onSetupItem: "setupItem", + onPullRelease: "pullRelease", onPullComplete: "pullComplete", + components: [ + {name: "item"} + ]} + + pullRelease: function() { + this.search(); + }, + processSearchResults: function(inRequest, inResponse) { + this.results = inResponse.results; + this.$.list.setCount(this.results.length); + this.$.list.completePull(); + }, + pullComplete: function() { + this.$.list.reset(); + } +*/ +enyo.kind({ + name: "enyo.PulldownList", + kind: "List", + touch: true, + pully: null, + pulldownTools: [ + {name: "pulldown", classes: "enyo-list-pulldown", components: [ + {name: "puller", kind: "Puller"} + ]} + ], + events: { + onPullStart: "", + onPullCancel: "", + onPull: "", + onPullRelease: "", + onPullComplete: "" + }, + handlers: { + onScrollStart: "scrollStartHandler", + onScroll: "scrollHandler", + onScrollStop: "scrollStopHandler", + ondragfinish: "dragfinish" + }, + // + pullingMessage: "Pull down to refresh...", + pulledMessage: "Release to refresh...", + loadingMessage: "Loading...", + // + pullingIconClass: "enyo-puller-arrow enyo-puller-arrow-down", + pulledIconClass: "enyo-puller-arrow enyo-puller-arrow-up", + loadingIconClass: "", + //* @protected + create: function() { + var p = {kind: "Puller", showing: false, text: this.loadingMessage, iconClass: this.loadingIconClass, onCreate: "setPully"}; + this.listTools.splice(0, 0, p); + this.inherited(arguments); + this.setPulling(); + }, + initComponents: function() { + this.createChrome(this.pulldownTools); + this.accel = enyo.dom.canAccelerate(); + this.translation = this.accel ? "translate3d" : "translate"; + this.inherited(arguments); + }, + setPully: function(inSender, inEvent) { + this.pully = inEvent.originator; + }, + scrollStartHandler: function() { + this.firedPullStart = false; + this.firedPull = false; + this.firedPullCancel = false; + }, + scrollHandler: function(inSender) { + if (this.completingPull) { + this.pully.setShowing(false); + } + var s = this.getStrategy().$.scrollMath; + var over = s.y; + if (s.isInOverScroll() && over > 0) { + enyo.dom.transformValue(this.$.pulldown, this.translation, "0," + over + "px" + (this.accel ? ",0" : "")); + if (!this.firedPullStart) { + this.firedPullStart = true; + this.pullStart(); + this.pullHeight = this.$.pulldown.getBounds().height; + } + if (over > this.pullHeight && !this.firedPull) { + this.firedPull = true; + this.firedPullCancel = false; + this.pull(); + } + if (this.firedPull && !this.firedPullCancel && over < this.pullHeight) { + this.firedPullCancel = true; + this.firedPull = false; + this.pullCancel(); + } + } + }, + scrollStopHandler: function() { + if (this.completingPull) { + this.completingPull = false; + this.doPullComplete(); + } + }, + dragfinish: function() { + if (this.firedPull) { + var s = this.getStrategy().$.scrollMath; + s.setScrollY(s.y - this.pullHeight); + this.pullRelease(); + } + }, + //* @public + //* To signal that the list should execute pull completion. This usually be called after the application has received the new data. + completePull: function() { + this.completingPull = true; + this.$.strategy.$.scrollMath.setScrollY(this.pullHeight); + this.$.strategy.$.scrollMath.start(); + }, + //* @protected + pullStart: function() { + this.setPulling(); + this.pully.setShowing(false); + this.$.puller.setShowing(true); + this.doPullStart(); + }, + pull: function() { + this.setPulled(); + this.doPull(); + }, + pullCancel: function() { + this.setPulling(); + this.doPullCancel(); + }, + pullRelease: function() { + this.$.puller.setShowing(false); + this.pully.setShowing(true); + this.doPullRelease(); + }, + setPulling: function() { + this.$.puller.setText(this.pullingMessage); + this.$.puller.setIconClass(this.pullingIconClass); + }, + setPulled: function() { + this.$.puller.setText(this.pulledMessage); + this.$.puller.setIconClass(this.pulledIconClass); + } +}); + +enyo.kind({ + name: "enyo.Puller", + classes: "enyo-puller", + published: { + text: "", + iconClass: "" + }, + events: { + onCreate: "" + }, + components: [ + {name: "icon"}, + {name: "text", tag: "span", classes: "enyo-puller-text"} + ], + create: function() { + this.inherited(arguments); + this.doCreate(); + this.textChanged(); + this.iconClassChanged(); + }, + textChanged: function() { + this.$.text.setContent(this.text); + }, + iconClassChanged: function() { + this.$.icon.setClasses(this.iconClass); + } +});
\ No newline at end of file diff --git a/html/lib/layout/list/source/Selection.js b/html/lib/layout/list/source/Selection.js new file mode 100644 index 0000000..8581f8d --- /dev/null +++ b/html/lib/layout/list/source/Selection.js @@ -0,0 +1,141 @@ +/** + _enyo.Selection_ is used to manage row selection state for lists. It + provides selection state management for both single-select and multi-select + lists. + + // The following is an excerpt from enyo.FlyweightRepeater. + enyo.kind({ + name: "enyo.FlyweightRepeater", + ... + components: [ + {kind: "Selection", onSelect: "selectDeselect", onDeselect: "selectDeselect"}, + ... + ], + tap: function(inSender, inEvent) { + ... + // mark the tapped row as selected + this.$.selection.select(inEvent.index); + ... + }, + selectDeselect: function(inSender, inEvent) { + // this is where a row selection highlight might be applied + this.renderRow(inEvent.key); + } + ... + }) +*/ +enyo.kind({ + name: "enyo.Selection", + kind: enyo.Component, + published: { + //* If true, multiple selections are allowed. + multi: false + }, + events: { + /** + Fires when an item is selected. + + {kind: "Selection", onSelect: "selectRow"... + ... + selectRow: function(inSender, inKey, inPrivateData) { + ... + + _inKey_ is whatever key was used to register + the selection (usually a row index). + + _inPrivateData_ references data registered + with this key by the code that made the original selection. + */ + onSelect: "", + /** + Fires when an item is deselected. + + {kind: "Selection", onSelect: "deselectRow"... + ... + deselectRow: function(inSender, inKey, inPrivateData) + ... + + _inKey_ is whatever key was used to request + the deselection (usually a row index). + + _inPrivateData_ references data registered + with this key by the code that made the selection. + */ + onDeselect: "", + //* Sent when selection changes (but not when the selection is cleared). + onChange: "" + }, + //* @protected + create: function() { + this.clear(); + this.inherited(arguments); + }, + multiChanged: function() { + if (!this.multi) { + this.clear(); + } + this.doChange(); + }, + highlander: function(inKey) { + if (!this.multi) { + this.deselect(this.lastSelected); + } + }, + //* @public + //* Removes all selections. + clear: function() { + this.selected = {}; + }, + //* Returns true if the _inKey_ row is selected. + isSelected: function(inKey) { + return this.selected[inKey]; + }, + //* Manually sets a row's state to selected or unselected. + setByKey: function(inKey, inSelected, inData) { + if (inSelected) { + this.selected[inKey] = (inData || true); + this.lastSelected = inKey; + this.doSelect({key: inKey, data: this.selected[inKey]}); + } else { + var was = this.isSelected(inKey); + delete this.selected[inKey]; + this.doDeselect({key: inKey, data: was}); + } + this.doChange(); + }, + //* Deselects a row. + deselect: function(inKey) { + if (this.isSelected(inKey)) { + this.setByKey(inKey, false); + } + }, + /** + Selects a row. If the _multi_ property is set to false, _select_ will + also deselect the previous selection. + */ + select: function(inKey, inData) { + if (this.multi) { + this.setByKey(inKey, !this.isSelected(inKey), inData); + } else if (!this.isSelected(inKey)) { + this.highlander(); + this.setByKey(inKey, true, inData); + } + }, + /** + Toggles selection state for a row. If the _multi_ property is set to + false, toggling a selection on will deselect the previous selection. + */ + toggle: function(inKey, inData) { + if (!this.multi && this.lastSelected != inKey) { + this.deselect(this.lastSelected); + } + this.setByKey(inKey, !this.isSelected(inKey), inData); + }, + /** + Returns the selection as a hash in which each selected item has a value; + unselected items are undefined. + */ + getSelected: function() { + return this.selected; + } +}); diff --git a/html/lib/layout/list/source/package.js b/html/lib/layout/list/source/package.js new file mode 100644 index 0000000..391e38e --- /dev/null +++ b/html/lib/layout/list/source/package.js @@ -0,0 +1,7 @@ +enyo.depends( + "FlyweightRepeater.js", + "List.css", + "List.js", + "PulldownList.css", + "PulldownList.js" +);
\ No newline at end of file diff --git a/html/lib/layout/list/source/wip-package.js b/html/lib/layout/list/source/wip-package.js new file mode 100644 index 0000000..bafc093 --- /dev/null +++ b/html/lib/layout/list/source/wip-package.js @@ -0,0 +1,6 @@ +enyo.depends( + // Add wip controls here + "AlphaJumper.css", + "AlphaJumper.js", + "AlphaJumpList.js" +);
\ No newline at end of file |