| Index: sky/examples/city-list/city-list.sky
|
| diff --git a/sky/examples/city-list/city-list.sky b/sky/examples/city-list/city-list.sky
|
| deleted file mode 100644
|
| index 4d8e55012f491f0ae6c8b6d31d1a1aeb30937c06..0000000000000000000000000000000000000000
|
| --- a/sky/examples/city-list/city-list.sky
|
| +++ /dev/null
|
| @@ -1,532 +0,0 @@
|
| -<!--
|
| -// Copyright 2014 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| --->
|
| -<import src="../../framework/sky-element/sky-element.sky" as="SkyElement" />
|
| -<import src="city-data-service.sky" as="CityDataService" />
|
| -<import src="city-sequence.sky" as="CitySequence" />
|
| -
|
| -<sky-element name="state-header">
|
| -<template>
|
| - <style>
|
| - div {
|
| - font-size: 16px;
|
| - color: #FFF;
|
| - background-color: #333;
|
| - padding: 4px 4px 4px 12px;
|
| - display: paragraph;
|
| - }
|
| - </style>
|
| - <div>{{ state }}</div>
|
| -</template>
|
| -<script>
|
| -module.exports.StateHeaderElement = class extends SkyElement {
|
| - created() {
|
| - this.state = "";
|
| - }
|
| - set datum(datum) {
|
| - this.state = datum.state;
|
| - }
|
| -}.register();
|
| -</script>
|
| -</sky-element>
|
| -
|
| -<sky-element name="letter-header">
|
| -<template>
|
| - <style>
|
| - div {
|
| - font-size: 12px;
|
| - font-weight: bold;
|
| - padding: 2px 4px 4px 12px;
|
| - background-color: #DDD;
|
| - display: paragraph;
|
| - }
|
| - </style>
|
| - <div>{{ letter }}</div>
|
| -</template>
|
| -<script>
|
| -module.exports.LetterHeaderElement = class extends SkyElement {
|
| - created() {
|
| - this.letter = "";
|
| - }
|
| - set datum(datum) {
|
| - this.letter = datum.letter;
|
| - }
|
| -}.register();
|
| -</script>
|
| -</sky-element>
|
| -
|
| -<sky-element name="city-item">
|
| -<template>
|
| - <style>
|
| - :host {
|
| - display: flex;
|
| - font-size: 13px;
|
| - padding: 8px 4px 4px 12px;
|
| - border-bottom: 1px solid #EEE;
|
| - line-height: 15px;
|
| - overflow: hidden;
|
| - }
|
| -
|
| - div {
|
| - display: paragraph;
|
| - }
|
| -
|
| - span {
|
| - display: inline;
|
| - }
|
| -
|
| - #name {
|
| - font-weight: bold
|
| - }
|
| -
|
| - #population {
|
| - color: #AAA;
|
| - }
|
| - </style>
|
| - <div>
|
| - <span id="name">{{ name }}</span>
|
| - <t>, </t>
|
| - <span id="population">population {{ population }}</span>
|
| - </div>
|
| -</template>
|
| -<script>
|
| -module.exports.CityItemElement = class extends SkyElement {
|
| - created() {
|
| - this.name = "";
|
| - this.population = "";
|
| - }
|
| - set datum(datum) {
|
| - this.name = datum.name;
|
| - this.population = datum.population;
|
| - }
|
| -}.register();
|
| -</script>
|
| -</sky-element>
|
| -
|
| -<sky-element name="city-list">
|
| -<template>
|
| - <style>
|
| -
|
| - :host {
|
| - overflow: hidden;
|
| - position: absolute;
|
| - top: 0;
|
| - right: 0;
|
| - bottom: 0;
|
| - left: 0;
|
| - display: block;
|
| - background-color: #fff;
|
| - }
|
| -
|
| - #contentarea {
|
| - will-change: transform;
|
| - }
|
| -
|
| - .position {
|
| - position: absolute;
|
| - left: 0;
|
| - right: 0;
|
| - }
|
| -
|
| - </style>
|
| - <div id="contentarea">
|
| - </div>
|
| -</template>
|
| -<script>
|
| -
|
| -(function(global) {
|
| - "use strict";
|
| -
|
| - var LOAD_LENGTH = 20;
|
| - var LOAD_BUFFER_PRE = LOAD_LENGTH * 4;
|
| - var LOAD_BUFFER_POST = LOAD_LENGTH * 4;
|
| -
|
| - function Loader(cityList) {
|
| - this.cityList = cityList;
|
| - this.loadingData = false;
|
| - this.data = null;
|
| - this.zeroIndex = 0;
|
| - this.loadIndex = 0;
|
| - }
|
| -
|
| - Loader.prototype.localIndex = function(externalIndex) {
|
| - return externalIndex + this.zeroIndex;
|
| - }
|
| -
|
| - Loader.prototype.externalIndex = function(localIndex) {
|
| - return localIndex - this.zeroIndex;
|
| - }
|
| -
|
| - Loader.prototype.getItems = function() {
|
| - return this.data ? this.data.items : [];
|
| - }
|
| -
|
| - Loader.prototype.maybeLoadMoreData =
|
| - function(dataloadedCallback, firstVisible) {
|
| - if (this.loadingData)
|
| - return;
|
| -
|
| - if (firstVisible) {
|
| - this.loadIndex = this.externalIndex(
|
| - this.data.items.indexOf(firstVisible));
|
| - }
|
| -
|
| - var localIndex = this.localIndex(this.loadIndex);
|
| - var loadedPre = 0;
|
| - var loadedPost = 0;
|
| -
|
| - if (this.data) {
|
| - loadedPre = localIndex;
|
| - loadedPost = this.data.items.length - loadedPre;
|
| - }
|
| -
|
| - var loadTime;
|
| - if (loadedPre >= LOAD_BUFFER_PRE &&
|
| - loadedPost >= LOAD_BUFFER_POST) {
|
| -
|
| - var cityList = this.cityList;
|
| - setTimeout(function() {
|
| - cityList.dispatchEvent(new Event('load'));
|
| - });
|
| -
|
| - if (window.startLoad) {
|
| - loadTime = new Date().getTime() - window.startLoad;
|
| - console.log('Load: ' + loadTime + 'ms');
|
| - window.startLoad = undefined;
|
| - }
|
| - return;
|
| - }
|
| -
|
| - this.loadingData = true;
|
| -
|
| - var loadIndex;
|
| -
|
| - if (!this.data) {
|
| - // Initial batch
|
| - loadIndex = 0;
|
| - } else if (loadedPost < LOAD_BUFFER_POST) {
|
| - // Load forward first
|
| - loadIndex = this.data.items.length;
|
| - } else {
|
| - // Then load backward
|
| - loadIndex = -LOAD_LENGTH;
|
| - }
|
| -
|
| - var self = this;
|
| - var externalIndex = this.externalIndex(loadIndex);
|
| -
|
| - try {
|
| - CityDataService.service.then(function(cityService) {
|
| - return cityService.get(externalIndex, LOAD_LENGTH)
|
| - .then(function(cities) {
|
| - var indexOffset = 0;
|
| - var newData = new CitySequence(cities);
|
| - if (!self.data) {
|
| - self.data = newData;
|
| - } else if (loadIndex > 0) {
|
| - self.data.append(newData);
|
| - } else {
|
| - self.zeroIndex += LOAD_LENGTH;
|
| - indexOffset = LOAD_LENGTH;
|
| - newData.append(self.data);
|
| - self.data = newData;
|
| - }
|
| -
|
| - self.loadingData = false;
|
| - dataloadedCallback(self.data, indexOffset);
|
| - });
|
| - }).catch(function(ex) {
|
| - console.log(ex.stack);
|
| - });
|
| - } catch (ex) {
|
| - console.log(ex.stack);
|
| - }
|
| - }
|
| -
|
| - function Scroller() {
|
| - this.contentarea = null;
|
| - this.scrollTop = 0;
|
| - this.scrollHeight = -1;
|
| - }
|
| -
|
| - Scroller.prototype.setup = function(scrollHeight, contentarea) {
|
| - this.scrollHeight = scrollHeight;
|
| - this.contentarea = contentarea;
|
| - }
|
| -
|
| - Scroller.prototype.scrollBy = function(amount) {
|
| - this.scrollTop += amount;
|
| - this.scrollTo();
|
| - }
|
| -
|
| - Scroller.prototype.scrollTo = function() {
|
| - var transform = 'translateY(' + -this.scrollTop.toFixed(2) + 'px)';
|
| - this.contentarea.style.transform = transform;
|
| - }
|
| -
|
| - // Current position and height of the scroller, that could
|
| - // be used (by Tiler, for example) to reason about where to
|
| - // place visible things.
|
| - Scroller.prototype.getCurrentFrame = function() {
|
| - return { top: this.scrollTop, height: this.scrollHeight };
|
| - }
|
| -
|
| - Scroller.prototype.hasFrame = function() {
|
| - return this.scrollHeight != -1;
|
| - }
|
| -
|
| - function Tile(datum, element, viewType, index) {
|
| - this.datum = datum;
|
| - this.element = element;
|
| - this.viewType = viewType;
|
| - this.index = index;
|
| - }
|
| -
|
| - function Tiler(contentArea, views, viewHeights) {
|
| - this.contentArea = contentArea;
|
| - this.drawTop = 0;
|
| - this.drawBottom = 0;
|
| - this.firstItem = -1;
|
| - this.tiles = [];
|
| - this.viewHeights = viewHeights;
|
| - this.views = views;
|
| - }
|
| -
|
| - Tiler.prototype.setupViews = function(scrollFrame) {
|
| - for (var type in this.viewHeights) {
|
| - this.initializeViewType(scrollFrame, type, this.viewHeights[type]);
|
| - }
|
| - }
|
| -
|
| - Tiler.prototype.initializeViewType = function(scrollFrame, viewType,
|
| - height) {
|
| - var count = Math.ceil(scrollFrame.height / height) * 2;
|
| - var viewCache = this.views[viewType] = {
|
| - indices: [],
|
| - elements: []
|
| - };
|
| -
|
| - var protoElement;
|
| - switch (viewType) {
|
| - case 'stateHeader':
|
| - protoElement = document.createElement('state-header');
|
| - break;
|
| - case 'letterHeader':
|
| - protoElement = document.createElement('letter-header');
|
| - break;
|
| - case 'cityItem':
|
| - protoElement = document.createElement('city-item');
|
| - break;
|
| - default:
|
| - console.warn('Unknown viewType: ' + viewType);
|
| - }
|
| - protoElement.style.display = 'none';
|
| - protoElement.style.height = height;
|
| - protoElement.classList.add('position');
|
| -
|
| - for (var i = 0; i < count; i++) {
|
| - var clone = protoElement.cloneNode(false);
|
| - this.contentArea.appendChild(clone);
|
| - viewCache.elements.push(clone);
|
| - viewCache.indices.push(i);
|
| - }
|
| - }
|
| -
|
| - Tiler.prototype.checkoutTile = function(viewType, datum, top) {
|
| - var viewCache = this.views[viewType];
|
| - var index = viewCache.indices.pop();
|
| - var element = viewCache.elements[index];
|
| - element.datum = datum;
|
| - element.style.display = '';
|
| - element.style.top = top + 'px';
|
| -
|
| - return new Tile(datum, element, viewType, index);
|
| - }
|
| -
|
| - Tiler.prototype.checkinTile = function(tile) {
|
| - if (!tile.element)
|
| - return;
|
| -
|
| - tile.element.style.display = 'none';
|
| - this.views[tile.viewType].indices.push(tile.index);
|
| - }
|
| -
|
| - Tiler.prototype.getFirstVisibleDatum = function(scrollFrame) {
|
| - var tiles = this.tiles;
|
| - var viewHeights = this.viewHeights;
|
| -
|
| - var itemTop = this.drawTop - scrollFrame.top;
|
| - if (itemTop >= 0 && tiles.length)
|
| - return tiles[0].datum;
|
| -
|
| - var tile;
|
| - for (var i = 0; i < tiles.length && itemTop < 0; i++) {
|
| - tile = tiles[i];
|
| - var height = viewHeights[tile.viewType];
|
| - itemTop += height;
|
| - }
|
| -
|
| - return tile ? tile.datum : null;
|
| - }
|
| -
|
| - Tiler.prototype.viewType = function(datum) {
|
| - switch (datum.headerOrder) {
|
| - case 1: return 'stateHeader';
|
| - case 2: return 'letterHeader';
|
| - default: return 'cityItem';
|
| - }
|
| - }
|
| -
|
| - Tiler.prototype.drawTiles = function(scrollFrame, data) {
|
| - var tiles = this.tiles;
|
| - var viewHeights = this.viewHeights;
|
| -
|
| - var buffer = Math.round(scrollFrame.height / 2);
|
| - var targetTop = scrollFrame.top - buffer;
|
| - var targetBottom = scrollFrame.top + scrollFrame.height + buffer;
|
| -
|
| - // Collect down to targetTop
|
| - var first = tiles[0];
|
| - while (tiles.length &&
|
| - targetTop > this.drawTop + viewHeights[first.viewType]) {
|
| -
|
| - var height = viewHeights[first.viewType];
|
| - this.drawTop += height;
|
| -
|
| - this.firstItem++;
|
| - this.checkinTile(tiles.shift());
|
| -
|
| - first = tiles[0];
|
| - }
|
| -
|
| - // Collect up to targetBottom
|
| - var last = tiles[tiles.length - 1];
|
| - while(tiles.length &&
|
| - targetBottom < this.drawBottom - viewHeights[last.viewType]) {
|
| -
|
| - var height = viewHeights[last.viewType];
|
| - this.drawBottom -= height;
|
| -
|
| - this.checkinTile(tiles.pop());
|
| -
|
| - last = tiles[tiles.length - 1];
|
| - }
|
| -
|
| - // Layout up to targetTop
|
| - while (this.firstItem > 0 &&
|
| - targetTop < this.drawTop) {
|
| -
|
| - var datum = data[this.firstItem - 1];
|
| - var type = this.viewType(datum);
|
| - var height = viewHeights[type];
|
| -
|
| - this.drawTop -= height;
|
| -
|
| - var tile = targetBottom < this.drawTop ?
|
| - new Tile(datum, null, datum.viewType, -1) : // off-screen
|
| - this.checkoutTile(type, datum, this.drawTop);
|
| -
|
| - this.firstItem--;
|
| - tiles.unshift(tile);
|
| - }
|
| -
|
| - // Layout down to targetBottom
|
| - while (this.firstItem + tiles.length < data.length - 1 &&
|
| - targetBottom > this.drawBottom) {
|
| -
|
| - var datum = data[this.firstItem + tiles.length];
|
| - var type = this.viewType(datum);
|
| - var height = viewHeights[type];
|
| -
|
| - this.drawBottom += height;
|
| -
|
| - var tile = targetTop > this.drawBottom ?
|
| - new Tile(datum, null, datum.viewType, -1) : // off-screen
|
| - this.checkoutTile(type, datum, this.drawBottom - height);
|
| -
|
| - tiles.push(tile);
|
| - }
|
| -
|
| - // Debug validate:
|
| - // for (var i = 0; i < tiles.length; i++) {
|
| - // if (tiles[i].datum !== data[this.firstItem + i])
|
| - // throw Error('Invalid')
|
| - // }
|
| - }
|
| -
|
| - // FIXME: Needs better name.
|
| - Tiler.prototype.updateFirstItem = function(offset) {
|
| - var tiles = this.tiles;
|
| -
|
| - if (!tiles.length) {
|
| - this.firstItem = 0;
|
| - } else {
|
| - this.firstItem += offset;
|
| - }
|
| - }
|
| -
|
| - module.exports.CityListElement = class extends SkyElement {
|
| -
|
| - created() {
|
| - this.loader = null;
|
| - this.scroller = null;
|
| - this.tiler = null;
|
| - this.date = null;
|
| - this.month = null;
|
| - this.views = null;
|
| - }
|
| -
|
| - attached() {
|
| - this.views = {};
|
| - this.loader = new Loader(this);
|
| - this.scroller = new Scroller();
|
| - this.tiler = new Tiler(
|
| - this.shadowRoot.getElementById('contentarea'), this.views, {
|
| - stateHeader: 27,
|
| - letterHeader: 18,
|
| - cityItem: 30
|
| - });
|
| -
|
| - var self = this;
|
| - this.addEventListener('wheel', function(event) {
|
| - self.scrollBy(-event.offsetY)
|
| - });
|
| -
|
| - setTimeout(function() {
|
| - self.domReady();
|
| - self.loader.maybeLoadMoreData(self.dataLoaded.bind(self));
|
| - });
|
| - }
|
| -
|
| - domReady() {
|
| - this.scroller.setup(this.clientHeight,
|
| - this.shadowRoot.getElementById('contentarea'));
|
| - var scrollFrame = this.scroller.getCurrentFrame();
|
| - this.tiler.setupViews(scrollFrame);
|
| - }
|
| -
|
| - updateView(data, scrollChanged) {
|
| - var scrollFrame = this.scroller.getCurrentFrame();
|
| - this.tiler.drawTiles(scrollFrame, data);
|
| - var datum = scrollChanged ?
|
| - this.tiler.getFirstVisibleDatum(scrollFrame) : null;
|
| - this.loader.maybeLoadMoreData(this.dataLoaded.bind(this), datum);
|
| - }
|
| -
|
| - dataLoaded(data, indexOffset) {
|
| - var scrollFrame = this.scroller.getCurrentFrame();
|
| - this.tiler.updateFirstItem(indexOffset);
|
| - this.updateView(data.items, false);
|
| - }
|
| -
|
| - scrollBy(amount) {
|
| - this.scroller.scrollBy(amount);
|
| - this.updateView(this.loader.getItems(), true);
|
| - }
|
| - }.register();
|
| -
|
| -})(this);
|
| -</script>
|
| -</sky-element>
|
|
|