can
Version:
MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.
294 lines (277 loc) • 5.57 kB
HTML
<body>
<style>
* {
font: 13px/16px "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;
color: #333;
}
table {
margin: 5px auto;
margin-bottom: 15px;
width: 95%;
}
td {
border-top: 1px solid #eee;
padding: 5px;
}
tr:last-child {
border-bottom: 1px solid #eee;
}
tr:nth-child(odd) {
background-color: #fcfcf1;
}
thead th {
background-color: #ccc;
color: #666;
padding: 5px;
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
}
next-prev { margin: 5px auto;text-align: center;display: block; }
next-prev a {
border-radius: 3px;
padding: 4px 14px;
text-decoration: none;
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
background-color: #f5f5f5;
border: 1px solid #bbbbbb;
border-color: #e6e6e6 #e6e6e6 #bfbfbf;
}
next-prev a.enabled {
color: #ffffff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
background-color: #49afcd;
border-color: #2f96b4 #2f96b4 #1f6377;
}
page-count {
display: block;
}
page-count, page-count * {
font-size: 11px;
color: #ccc;
text-align: center;
}
.waiting {
opacity: 0.5;
}
</style>
<div id="out">
<my-app></my-app>
</div>
<script src="../../node_modules/steal/steal.js" dev-bundle main="@empty">
import {DefineMap, DefineList, Component, fixture, realtimeRestModel} from "can";
const Website = DefineMap.extend({
id: "number",
name: "string",
url: "string"
});
Website.List = DefineList.extend({
"#": Website,
count: "number"
});
realtimeRestModel({
Map: Website,
List: Website.List,
url: "/websites"
});
const websiteStore = fixture.store([{
id: 1,
name: "CanJS",
url: "http://canjs.us"
}, {
id: 2,
name: "jQuery++",
url: "http://jquerypp.com"
}, {
id: 3,
name: "JavaScriptMVC",
url: "http://javascriptmvc.com"
}, {
id: 4,
name: "Bitovi",
url: "http://bitovi.com"
}, {
id: 5,
name: "FuncUnit",
url: "http://funcunit.com"
}, {
id: 6,
name: "StealJS",
url: "http://stealjs.com"
}, {
id: 7,
name: "jQuery",
url: "http://jquery.com"
}, {
id: 8,
name: "Mootools",
url: "http://mootools.com"
}, {
id: 9,
name: "Dojo",
url: "http://dojo.com"
}, {
id: 10,
name: "YUI",
url: "http://yui.com"
}, {
id: 11,
name: "DoneJS",
url: "http://donejs.com"
}, {
id: 12,
name: "Mindjet Connect",
url: "http://connect.mindjet.com"
}, {
id: 13,
name: "JSFiddle",
url: "http://jsfiddle.net"
}, {
id: 14,
name: "Zepto",
url: "http://zepto.com"
}, {
id: 15,
name: "Spill",
url: "http://spill.com"
}, {
id: 16,
name: "Github",
url: "http://github.com"
}] );
fixture("/websites/{id}", websiteStore);
fixture.delay = 2000;
const Paginate = DefineMap.extend({
count: {
default: Infinity
},
offset: {
default: 0,
set: function(newOffset) {
return newOffset < 0 ?
0 :
Math.min(newOffset, !isNaN(this.count - 1) ?
this.count - 1 :
Infinity);
}
},
limit: {
default: 5
},
page: {
get: function() {
return Math.floor(this.offset / this.limit) + 1;
},
set: function(newVal) {
this.offset = (parseInt(newVal, 10) - 1) * this.limit;
}
},
pageCount: {
get: function() {
return this.count ?
Math.ceil(this.count / this.limit) : null;
}
},
get start(){
return this.offset;
},
get end(){
return this.offset + this.limit - 1;
},
next: function() {
this.offset = this.offset + this.limit;
},
prev: function() {
this.offset = this.offset - this.limit;
},
canNext: function() {
return this.offset < this.count - this.limit;
},
canPrev: function() {
return this.offset > 0;
}
});
Component.extend({
tag: "my-grid",
view: `<table>
<tbody {{# if(this.waiting) }}class='waiting'{{/ if }}>
<content></content>
</tbody>
</table>`,
ViewModel: {
promiseData: "any",
items: {
get: function(lastSet, resolve) {
this.promiseData.then(resolve);
}
},
waiting: {
get: function(lastSet, resolve) {
const promise = this.promiseData;
promise.then(function() {
resolve(false);
});
return true;
}
}
},
leakScope: true
});
Component.extend({
tag: "next-prev",
view:
`<a href="javascript://"
class="prev {{# this.paginate.canPrev() }}enabled{{/ }}"
on:click="paginate.prev()">Prev</a>
<a href="javascript://"
class="next {{# this.paginate.canNext() }}enabled{{/ }}"
on:click="paginate.next()">Next</a>`
});
Component.extend({
tag: "page-count",
view: `Page <span>{{ this.page }}</span> of <span>{{ this.count }}</span>.`
});
Component.extend({
tag: "my-app",
view: `
<my-grid promiseData:from='websitesPromise'>
{{# for( item of this.items) }}
<tr>
<td width='40%'>{{ item.name }}</td>
<td width='70%'>{{ item.url }}</td>
</tr>
{{/ for }}
</my-grid>
<next-prev paginate:from='this.paginate'></next-prev>
<page-count page:from='this.paginate.page' count:from='this.paginate.pageCount'></page-count>`,
ViewModel: {
paginate: {
default() {
return new Paginate({
limit: 5
});
}
},
websitesCount: {
get(lastValue, setValue) {
this.websitesPromise.then(function(websites) {
setValue(websites.count);
});
}
},
get websitesPromise() {
return Website.getList({
page: {
start: this.paginate.start,
end: this.paginate.end
}
});
},
connectedCallback() {
this.listenTo('websitesCount', function(event, count) {
this.paginate.count = count;
});
return this.stopListening.bind(this);
}
}
});
</script>
</body>