Uses Chance.js for content generation, CSS counters, and some Angular controls to show and manage the state for each of a series of steps. If there are undone steps, it will select this first undone step the next time it starts. Based on a design from @zherring
Created
July 19, 2017 22:27
-
-
Save Jeet-Metapercept/d7c427132fcc3b1da1f1a006bc0f50b4 to your computer and use it in GitHub Desktop.
Steps-states Indicators
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<body ng-app="app" ng-controller="main" ng-class="{'show-steps': showSteps}"> | |
<main> | |
<button ng-class="{'dismiss': showSteps}" ng-click="showSteps = !showSteps"><i class="fa fa-2x fa-fw fa-{{(showSteps?'times':'life-ring')}}"></i></button> | |
</main> | |
<aside> | |
<header> | |
<ol data-title="Steps of Things" ng-class="{'started': active}"> | |
<li ng-repeat="item in items" ng-click="click($index)" ng-class="{'seen': item.seen, 'done': item.done, 'active': item == active}"><span data-title="Step">{{item.step}}</span></li> | |
</ol> | |
</header> | |
<article> | |
<button ng-click="next()" class="center" ng-if="!active"><i class="fa fa-play-circle fa-fw fa-lg"></i> Ready? Go!</button> | |
<section ng-repeat="item in items" ng-if="item === active"> | |
<!-- <h3>{{item.step}}</h3> --> | |
<ol> | |
<li ng-repeat="step in item.substeps"><span>{{step}}</span></li> | |
</ol> | |
</section> | |
<!-- <pre>{{items | json}}</pre> --> | |
<!-- <pre>{{active | json}}</pre> --> | |
</article> | |
<footer ng-if="active"> | |
<button ng-click="reset()"><i class="fa fa-undo fa-fw fa-lg"></i> Reset</button> | |
<button ng-click="prev()"><i class="fa fa-chevron-circle-left fa-fw fa-lg"></i> Back</button> | |
<button ng-click="next(true)"><i class="fa fa-chevron-circle-right fa-fw fa-lg"></i> Skip</button> | |
<button ng-click="next()"><i class="fa fa-check-circle fa-fw fa-lg"></i> {{(isLast() ? 'Done' : 'Next')}}</button> | |
</footer> | |
</aside> | |
</body> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function(angular) { | |
'use strict'; | |
angular.module('app', []) | |
.controller('main', ['$scope', function($scope) { | |
var randArray = function(max, min) { | |
return Array.apply(null, Array(Math.floor(Math.random() * (max - min)) + min)).map(function() { | |
return chance.sentence(); | |
}); | |
}; | |
$scope.items = [{ | |
step: 'The first thing', | |
substeps: randArray(15, 1) | |
}, { | |
step: 'Number two', | |
substeps: randArray(15, 1) | |
}, { | |
step: 'This is the third one', | |
substeps: randArray(15, 1) | |
}, { | |
step: 'Do the fourth thing', | |
substeps: randArray(15, 1) | |
}, { | |
step: 'The Last - a.k.a. part five', | |
substeps: randArray(15, 1) | |
}]; | |
$scope.showSteps = true; | |
// return the index for first !item.done | |
var _bestIndex = function(field) { | |
return $scope.items.map(function(item, index) { | |
if (!item[field]) { | |
return index; | |
} | |
}).filter(isFinite)[0]; | |
}; | |
$scope.click = function(index) { | |
$scope.active = $scope.items[index]; | |
$scope.active.seen = true; | |
$scope.index = index; | |
}; | |
$scope.reset = function() { | |
angular.forEach($scope.items, function(item) { | |
item.seen = false; | |
item.done = false; | |
item.substeps = randArray(15, 1); | |
}); | |
$scope.index = -1; | |
$scope.active = undefined; | |
console.clear(); | |
}; | |
$scope.isLast = function() { | |
return $scope.index === $scope.items.length - 1; | |
}; | |
$scope.next = function(skip) { | |
var index = $scope.index; | |
if ($scope.items[index] && !skip) { | |
$scope.items[index].done = true; | |
} | |
// determine proper index | |
if (index > $scope.items.length - 1) { | |
index = _bestIndex('done') || 0; | |
} else { | |
index += 1; | |
} | |
// set active & seen | |
$scope.active = $scope.items[index]; | |
$scope.index = index; | |
if ($scope.items[index]) { | |
$scope.items[index].seen = true; | |
} | |
}; | |
$scope.prev = function() { | |
$scope.items[$scope.index].seen = true; | |
$scope.index = $scope.index - 1; | |
$scope.active = $scope.items[$scope.index]; | |
}; | |
$scope.reset(); | |
}]) | |
.directive('ngChance', function() { | |
return function(scope, element, attrs) { | |
console.log(chance[attrs.ngChance]()); | |
element.text(chance[attrs.ngChance]()); | |
} | |
}); | |
})(window.angular) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script src="//cdnjs.cloudflare.com/ajax/libs/chance/1.0.2/chance.min.js"></script> | |
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.2/angular.min.js"></script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// colors | |
$dark: #1d1f21; | |
$light: #fff; | |
$ui: mix($dark, $light); | |
$active: #0ebeff; | |
$pending: #e08438; | |
$success: #9ccf5e; | |
// icons | |
$check: '\f00c'; | |
$ellipsis-h: '\f141'; | |
$clock-o: '\f017'; | |
$circle: '\f111'; | |
// plumbing: step numbers | |
header ol { | |
counter-reset: step; | |
> li { | |
counter-increment: step; | |
&::after { | |
content: counter(step); | |
} | |
> span::before { | |
content: attr(data-title) ' ' counter(step) ' '; | |
} | |
} | |
} | |
// mechanical | |
header ol { | |
margin: 0; | |
padding: 2.5em 1em 1em 1em; | |
list-style: none; | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
position: relative; | |
font-weight: 300; | |
&:not(.started)::after { | |
content: attr(data-title); | |
position: absolute; | |
z-index: 0; | |
bottom: 50%; | |
left: 50%; | |
transform: translate(-50%, -70%); | |
} | |
&::before { | |
content: ''; | |
position: absolute; | |
height: .5em; | |
bottom: 1.5em; | |
left: 2em; | |
right: 2em; | |
transform: translateY(-50%); | |
} | |
> li { | |
flex: 0 0 auto; | |
z-index: 1; | |
width: 2em; | |
height: 2em; | |
padding: 0; | |
margin: 0; | |
display: inline-flex; | |
justify-content: center; | |
align-items: center; | |
border-radius: 2em; | |
border: .3em solid; | |
margin: 0 .15em; | |
cursor: pointer; | |
transition: .3s ease-out; | |
> span { | |
opacity: 0; | |
position: absolute; | |
white-space: nowrap; | |
text-align: center; | |
pointer-events: none; | |
bottom: 65%; | |
left: 0; | |
width: 100%; | |
font-size: 90%; | |
transition: inherit; | |
} | |
&::before { | |
font-family: FontAwesome; | |
line-height: 1; | |
} | |
&.seen:not(.done):not(.active)::before { | |
content: $circle; | |
transform: translate(3em, -1.2em); | |
font-size: 50%; | |
margin: 0 -.425em; | |
} | |
&.done::before { | |
content: $check; | |
} | |
&.done::after { | |
display: none; | |
} | |
&.active::before { | |
content: $ellipsis-h; | |
margin-top: .1em; | |
} | |
&.active.done::before { | |
content: $check; | |
margin-top: 0; | |
} | |
&.active::after { | |
display: none; | |
} | |
&.active > span { | |
opacity: 1; | |
} | |
} | |
} | |
// porcelain | |
header ol { | |
background: $dark; | |
&::before { | |
background: $ui; | |
} | |
li { | |
color: mix($ui, $light); | |
background: mix($ui, $dark); | |
border-color: $ui; | |
&.seen:not(.done):not(.active)::before { | |
color: $pending; | |
} | |
&.done { | |
color: $light; | |
background: $success; | |
border-color: $dark; | |
} | |
&.active { | |
color: $active; | |
background: $light; | |
border-color: inherit; | |
} | |
> span { | |
color: mix($ui, $light); | |
&::before { | |
opacity: .5; | |
} | |
} | |
} | |
} | |
// content | |
section { | |
position: relative; | |
line-height: 1.5; | |
font-size: .725em; | |
ol { | |
margin: 0; | |
padding: 0; | |
list-style: none; | |
counter-reset: step; | |
> li { | |
counter-increment: step; | |
&::before { | |
content: counter(step, lower-alpha); | |
} | |
} | |
} | |
li { | |
padding: 2em; | |
position: relative; | |
display: flex; | |
align-items: baseline; | |
span { | |
flex: 1; | |
padding-left: 1em; | |
} | |
&::before { | |
width: 2em; | |
height: 2em; | |
line-height: 2; | |
text-align: center; | |
background: mix($dark, $ui); | |
border-radius: 1em; | |
border: 1px solid mix($dark, $ui); | |
} | |
+ li { | |
border-top: 1px solid mix($dark, $ui); | |
} | |
} | |
} | |
// utils | |
.center { | |
position: absolute; | |
bottom: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
} | |
// boring | |
html { | |
height: 100%; | |
overflow: hidden; // ew! | |
*, *::before, *::after { | |
box-sizing: border-box; | |
} | |
body { | |
margin: 0; | |
padding: 0; | |
height: 100%; | |
font-size: 2.1vmin; | |
display: flex; | |
overflow-x: hidden; // ew! | |
flex-direction: row; | |
font-family: helvetica, arial, sans-serif; | |
&:not(.show-steps) aside { | |
margin-right: -20em; | |
> * { | |
display: none; | |
} | |
} | |
} | |
main { | |
flex: 1; | |
background: $light; | |
color: $dark; | |
padding: 1em; | |
display: flex; | |
flex-direction: column; | |
align-items: flex-end; | |
button { | |
border-radius: 2em; | |
padding: 0; | |
width: 3em; | |
height: 3em; | |
} | |
} | |
aside { | |
background: mix($light, $dark, 5%); | |
color: $light; | |
width: 20em; | |
margin-right: 0; | |
flex-direction: column; | |
display: flex; | |
transform: translateZ(0); | |
transition: margin-right .3s ease-out; | |
} | |
header { | |
flex: 0 0 auto; | |
} | |
article { | |
position: relative; | |
flex: 1; //0 1 100%; | |
overflow: auto; | |
border-top: 1px solid mix($dark, $ui); | |
border-bottom: 1px solid mix($dark, $ui); | |
} | |
footer { | |
flex: 0 0 auto; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
background: $dark; | |
padding: 1em; | |
z-index: 1; | |
button { | |
line-height: 1.5; | |
} | |
} | |
button { | |
font-size: 70%; | |
text-transform: capitalize; | |
line-height: 0; | |
border: 0; | |
border-radius: .3em; | |
background: $ui; | |
color: rgba($light, .85); | |
padding: 1em; | |
margin: 0 .5em; | |
outline: none; | |
&.dismiss { | |
background: rgba($dark, .2); | |
color: $ui; | |
} | |
&[disabled] { | |
opacity: .5; | |
} | |
&:not(.dismiss):first-child { | |
background: $pending; | |
} | |
&:not(.dismiss):last-child { | |
background: $active; | |
} | |
&:active { | |
background: mix($active, $light); | |
color: $light; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<link href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.1/css/font-awesome.min.css" rel="stylesheet" /> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment