<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="styles.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<script src="app.js"></script>
<script src="fsListView.js"></script>
</head>
<body ng-app="app">
<app>
Loading...
</app>
</body>
</html>
<div class="app">
<div class="examples-control">
<label>
Delay: <input type="number" ng-model="$ctrl.delay" min="10" max="5000" placeholder="Delay" />
</label>
<label>
Number of items
<input type="number" ng-model="$ctrl.items" min="0" max="50" placeholder="Number of items" />
</label>
<button ng-click="$ctrl.rebuild()">Apply</button>
</div>
<div class="examples">
<div class="examples__item">
<fs-list-view source="$ctrl.dataSource"
on-select="$ctrl.selectedItem = selectedItem"
selected="$ctrl.selectedItem">
<list-item>
<p>Name: {{ $parent.listItem.name }}</p>
<p>Description: {{ $parent.listItem.description }}</p>
</list-item>
</fs-list-view>
</div>
<div class="examples__item">
<fs-list-view source="$ctrl.dataSource"
on-select="$ctrl.selectedItem = selectedItem"
selected="$ctrl.selectedItem">
<list-item class="modified">
<strong>{{ $parent.listItem.name }}</strong>
{{ $parent.listItem.description }}
</list-item>
<list-empty></list-empty>
<list-loading>
<div id="floatingCirclesG">
<div class="f_circleG" id="frotateG_01"></div>
<div class="f_circleG" id="frotateG_02"></div>
<div class="f_circleG" id="frotateG_03"></div>
<div class="f_circleG" id="frotateG_04"></div>
<div class="f_circleG" id="frotateG_05"></div>
<div class="f_circleG" id="frotateG_06"></div>
<div class="f_circleG" id="frotateG_07"></div>
<div class="f_circleG" id="frotateG_08"></div>
</div>
</list-loading>
<list-empty>Nothing to see :(</list-empty>
</fs-list-view>
</div>
<div class="examples__item">
<fs-list-view source="$ctrl.dataSource"
on-select="$ctrl.selectedItem = selectedItem"
selected="$ctrl.selectedItem">
<list-item>
<p>What? {{ $parent.listItem.name }}</p>
<p>Why?: {{ $parent.listItem.description }}</p>
</list-item>
<list-loading>
<div class="windows8">
<div class="wBall" id="wBall_1">
<div class="wInnerBall"></div>
</div>
<div class="wBall" id="wBall_2">
<div class="wInnerBall"></div>
</div>
<div class="wBall" id="wBall_3">
<div class="wInnerBall"></div>
</div>
<div class="wBall" id="wBall_4">
<div class="wInnerBall"></div>
</div>
<div class="wBall" id="wBall_5">
<div class="wInnerBall"></div>
</div>
</div>
</list-loading>
<list-empty>(╯°□°)╯︵ ┻━┻)</list-empty>
</fs-list-view>
</div>
</div>
</div>
"use babel";
class AppComponent {
constructor($timeout) {
this.$timeout = $timeout;
this.delay = 500;
this.items = 6;
this.rebuild();
}
rebuild() {
this.dataSource = this.$timeout(this.delay).then(() => {
return range(this.items).map((id) => {
return {
name: `Test ${id}`,
description: `Description ${id}`
}
})
});
}
}
function range(n) {
return Array.apply(null, Array(n)).map((_, i) => i + 1);
}
angular
.module('app', [])
.component('app', {
templateUrl: 'app.html',
controller: AppComponent
})
"use babel";
class FsListViewComponent {
constructor() {
"ngInject";
this.collection = [];
}
get source() {
return this.$source;
}
set source(source) {
this.$source = source;
this.collection = [];
if (source && angular.isFunction (source.then)) {
this.$handleSource(source);
}
}
get isEmpty () {
return !this.isLoading && !this.collection.length;
}
set selectedListItem(item) {
this.$selectedItem = item;
}
selectItem(item) {
// наш компонент не содержит состояния
// таким образом решать "выделять" объект или нет
// решать должен использующий код
this.onSelect({
selectedItem: item
});
}
$handleSource() {
this.isLoading = true;
this.$source
.then(
(data) => this.collection = data,
(reason) => this.collection = []
)
.then(() => this.isLoading = false);
}
}
angular.module('app').component('fsListView', {
bindings: {
source: '<',
selectedListItem: '<selected',
onSelect: '&'
},
transclude: {
listItem: 'listItem',
listEmpty: '?listEmpty',
listLoading: '?listLoading'
},
template: `
<div class="list-view">
<div class="list-view__loading" ng-show="$ctrl.isLoading" ng-transclude="listLoading">Loading...</div>
<div class="list-view__empty" ng-show="$ctrl.isEmpty" ng-transclude="listEmpty">No Data</div>
<div class="list-view__scroll-view" ng-if="$ctrl.collection.length">
<div class="list-view__item"
ng-class="{'list-view__item--selected': listItem === $ctrl.$selectedItem}"
ng-click="$ctrl.selectItem(listItem)"
ng-repeat="listItem in $ctrl.collection"
ng-transclude="listItem"
></div>
</div>
</div>
`,
controller: FsListViewComponent
});
html,
body {
height: 100%;
margin: 0;
padding: 0;
font-family: verdana, arial;
}
app {
display: block;
height: 100%;
}
app, .app {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
}
.examples-control {
height: 50px;
padding: 10px;
font-size: 16px;
}
.examples-control input[type=number] {
margin: 10px;
}
.examples {
background: #efefef;
flex: 1;
display: flex;
flex-direction: row;
padding: 0 5px;
}
.examples__item {
margin: 10px 5px;
flex: 1;
display: flex;
}
fs-list-view {
display: flex;
flex: 1;
width: 100%;
}
.list-view {
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
}
.list-view__loading {
font-size: 24px;
text-align: center;
}
.list-view__empty {
font-size: 24px;
text-align: center;
}
.list-view__scroll-view {
flex: 1;
padding: 0 10px;
overflow: scroll;
}
.list-view__item {
font-size: 16px;
padding: 10px;
margin: 10px 0;
background: #fefefe;
border: solid 1px #efefef;
border-radius: 3px;
box-shadow: 3px 3px 7px rgba(0,0,0,0.20);
}
.list-view__item--selected {
background-color: #426daa;
border-color: #334faa;
color: #fff;
}
.windows8 {
position: relative;
width: 44px;
height: 44px;
margin: auto;
}
.windows8 .wBall {
position: absolute;
width: 42px;
height: 42px;
opacity: 0;
transform: rotate(225deg);
-o-transform: rotate(225deg);
-ms-transform: rotate(225deg);
-webkit-transform: rotate(225deg);
-moz-transform: rotate(225deg);
animation: orbit 5.7425s infinite;
-o-animation: orbit 5.7425s infinite;
-ms-animation: orbit 5.7425s infinite;
-webkit-animation: orbit 5.7425s infinite;
-moz-animation: orbit 5.7425s infinite;
}
.windows8 .wBall .wInnerBall {
position: absolute;
width: 5px;
height: 5px;
background: #000;
left: 0px;
top: 0px;
border-radius: 5px;
}
.windows8 #wBall_1 {
animation-delay: 1.256s;
-o-animation-delay: 1.256s;
-ms-animation-delay: 1.256s;
-webkit-animation-delay: 1.256s;
-moz-animation-delay: 1.256s;
}
.windows8 #wBall_2 {
animation-delay: 0.243s;
-o-animation-delay: 0.243s;
-ms-animation-delay: 0.243s;
-webkit-animation-delay: 0.243s;
-moz-animation-delay: 0.243s;
}
.windows8 #wBall_3 {
animation-delay: 0.5065s;
-o-animation-delay: 0.5065s;
-ms-animation-delay: 0.5065s;
-webkit-animation-delay: 0.5065s;
-moz-animation-delay: 0.5065s;
}
.windows8 #wBall_4 {
animation-delay: 0.7495s;
-o-animation-delay: 0.7495s;
-ms-animation-delay: 0.7495s;
-webkit-animation-delay: 0.7495s;
-moz-animation-delay: 0.7495s;
}
.windows8 #wBall_5 {
animation-delay: 1.003s;
-o-animation-delay: 1.003s;
-ms-animation-delay: 1.003s;
-webkit-animation-delay: 1.003s;
-moz-animation-delay: 1.003s;
}
@-o-keyframes orbit {
0% {
opacity: 1;
z-index: 99;
-o-transform: rotate(180deg);
-o-animation-timing-function: ease-out;
}
7% {
opacity: 1;
-o-transform: rotate(300deg);
-o-animation-timing-function: linear;
-o-origin: 0%;
}
30% {
opacity: 1;
-o-transform: rotate(410deg);
-o-animation-timing-function: ease-in-out;
-o-origin: 7%;
}
39% {
opacity: 1;
-o-transform: rotate(645deg);
-o-animation-timing-function: linear;
-o-origin: 30%;
}
70% {
opacity: 1;
-o-transform: rotate(770deg);
-o-animation-timing-function: ease-out;
-o-origin: 39%;
}
75% {
opacity: 1;
-o-transform: rotate(900deg);
-o-animation-timing-function: ease-out;
-o-origin: 70%;
}
76% {
opacity: 0;
-o-transform: rotate(900deg);
}
100% {
opacity: 0;
-o-transform: rotate(900deg);
}
}
@-ms-keyframes orbit {
0% {
opacity: 1;
z-index: 99;
-ms-transform: rotate(180deg);
-ms-animation-timing-function: ease-out;
}
7% {
opacity: 1;
-ms-transform: rotate(300deg);
-ms-animation-timing-function: linear;
-ms-origin: 0%;
}
30% {
opacity: 1;
-ms-transform: rotate(410deg);
-ms-animation-timing-function: ease-in-out;
-ms-origin: 7%;
}
39% {
opacity: 1;
-ms-transform: rotate(645deg);
-ms-animation-timing-function: linear;
-ms-origin: 30%;
}
70% {
opacity: 1;
-ms-transform: rotate(770deg);
-ms-animation-timing-function: ease-out;
-ms-origin: 39%;
}
75% {
opacity: 1;
-ms-transform: rotate(900deg);
-ms-animation-timing-function: ease-out;
-ms-origin: 70%;
}
76% {
opacity: 0;
-ms-transform: rotate(900deg);
}
100% {
opacity: 0;
-ms-transform: rotate(900deg);
}
}
@-webkit-keyframes orbit {
0% {
opacity: 1;
z-index: 99;
-webkit-transform: rotate(180deg);
-webkit-animation-timing-function: ease-out;
}
7% {
opacity: 1;
-webkit-transform: rotate(300deg);
-webkit-animation-timing-function: linear;
-webkit-origin: 0%;
}
30% {
opacity: 1;
-webkit-transform: rotate(410deg);
-webkit-animation-timing-function: ease-in-out;
-webkit-origin: 7%;
}
39% {
opacity: 1;
-webkit-transform: rotate(645deg);
-webkit-animation-timing-function: linear;
-webkit-origin: 30%;
}
70% {
opacity: 1;
-webkit-transform: rotate(770deg);
-webkit-animation-timing-function: ease-out;
-webkit-origin: 39%;
}
75% {
opacity: 1;
-webkit-transform: rotate(900deg);
-webkit-animation-timing-function: ease-out;
-webkit-origin: 70%;
}
76% {
opacity: 0;
-webkit-transform: rotate(900deg);
}
100% {
opacity: 0;
-webkit-transform: rotate(900deg);
}
}
@-moz-keyframes orbit {
0% {
opacity: 1;
z-index: 99;
-moz-transform: rotate(180deg);
-moz-animation-timing-function: ease-out;
}
7% {
opacity: 1;
-moz-transform: rotate(300deg);
-moz-animation-timing-function: linear;
-moz-origin: 0%;
}
30% {
opacity: 1;
-moz-transform: rotate(410deg);
-moz-animation-timing-function: ease-in-out;
-moz-origin: 7%;
}
39% {
opacity: 1;
-moz-transform: rotate(645deg);
-moz-animation-timing-function: linear;
-moz-origin: 30%;
}
70% {
opacity: 1;
-moz-transform: rotate(770deg);
-moz-animation-timing-function: ease-out;
-moz-origin: 39%;
}
75% {
opacity: 1;
-moz-transform: rotate(900deg);
-moz-animation-timing-function: ease-out;
-moz-origin: 70%;
}
76% {
opacity: 0;
-moz-transform: rotate(900deg);
}
100% {
opacity: 0;
-moz-transform: rotate(900deg);
}
}
#floatingCirclesG {
position: relative;
width: 125px;
height: 125px;
margin: auto;
transform: scale(0.6);
-o-transform: scale(0.6);
-ms-transform: scale(0.6);
-webkit-transform: scale(0.6);
-moz-transform: scale(0.6);
}
.f_circleG {
position: absolute;
background-color: #efefef;
height: 22px;
width: 22px;
border-radius: 12px;
-o-border-radius: 12px;
-ms-border-radius: 12px;
-webkit-border-radius: 12px;
-moz-border-radius: 12px;
animation-name: f_fadeG;
-o-animation-name: f_fadeG;
-ms-animation-name: f_fadeG;
-webkit-animation-name: f_fadeG;
-moz-animation-name: f_fadeG;
animation-duration: 1.2s;
-o-animation-duration: 1.2s;
-ms-animation-duration: 1.2s;
-webkit-animation-duration: 1.2s;
-moz-animation-duration: 1.2s;
animation-iteration-count: infinite;
-o-animation-iteration-count: infinite;
-ms-animation-iteration-count: infinite;
-webkit-animation-iteration-count: infinite;
-moz-animation-iteration-count: infinite;
animation-direction: normal;
-o-animation-direction: normal;
-ms-animation-direction: normal;
-webkit-animation-direction: normal;
-moz-animation-direction: normal;
}
#frotateG_01 {
left: 0;
top: 51px;
animation-delay: 0.45s;
-o-animation-delay: 0.45s;
-ms-animation-delay: 0.45s;
-webkit-animation-delay: 0.45s;
-moz-animation-delay: 0.45s;
}
#frotateG_02 {
left: 15px;
top: 15px;
animation-delay: 0.6s;
-o-animation-delay: 0.6s;
-ms-animation-delay: 0.6s;
-webkit-animation-delay: 0.6s;
-moz-animation-delay: 0.6s;
}
#frotateG_03 {
left: 51px;
top: 0;
animation-delay: 0.75s;
-o-animation-delay: 0.75s;
-ms-animation-delay: 0.75s;
-webkit-animation-delay: 0.75s;
-moz-animation-delay: 0.75s;
}
#frotateG_04 {
right: 15px;
top: 15px;
animation-delay: 0.9s;
-o-animation-delay: 0.9s;
-ms-animation-delay: 0.9s;
-webkit-animation-delay: 0.9s;
-moz-animation-delay: 0.9s;
}
#frotateG_05 {
right: 0;
top: 51px;
animation-delay: 1.05s;
-o-animation-delay: 1.05s;
-ms-animation-delay: 1.05s;
-webkit-animation-delay: 1.05s;
-moz-animation-delay: 1.05s;
}
#frotateG_06 {
right: 15px;
bottom: 15px;
animation-delay: 1.2s;
-o-animation-delay: 1.2s;
-ms-animation-delay: 1.2s;
-webkit-animation-delay: 1.2s;
-moz-animation-delay: 1.2s;
}
#frotateG_07 {
left: 51px;
bottom: 0;
animation-delay: 1.35s;
-o-animation-delay: 1.35s;
-ms-animation-delay: 1.35s;
-webkit-animation-delay: 1.35s;
-moz-animation-delay: 1.35s;
}
#frotateG_08 {
left: 15px;
bottom: 15px;
animation-delay: 1.5s;
-o-animation-delay: 1.5s;
-ms-animation-delay: 1.5s;
-webkit-animation-delay: 1.5s;
-moz-animation-delay: 1.5s;
}
@-o-keyframes f_fadeG {
0% {
background-color: #000;
}
100% {
background-color: #efefef;
}
}
@-ms-keyframes f_fadeG {
0% {
background-color: #000;
}
100% {
background-color: #efefef;
}
}
@-webkit-keyframes f_fadeG {
0% {
background-color: #000;
}
100% {
background-color: #efefef;
}
}
@-moz-keyframes f_fadeG {
0% {
background-color: #000;
}
100% {
background-color: #efefef;
}
}
p {
margin: 10px 0;
}
list-item {
display: block;
}
list-item strong {
display: block;
margin-bottom: 5px;
}
list-item.modified, list-item.modified p {
text-align: right;
}