Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit 07d5653

Browse files
EisenbergEffectThomasBurleson
authored andcommitted
feat(circular_progress): Add circular progress component
This commit adds a new component, material-circular-progress, which enables the use of both determinate and indeterminate circular progress indicators. Fixes Issue #192 Closes #365
1 parent 5847006 commit 07d5653

10 files changed

+315
-1
lines changed

config/build.config.js

+1
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ module.exports = {
102102
'src/components/whiteframe/whiteframe.js',
103103
'src/components/divider/divider.js',
104104
'src/components/linearProgress/linearProgress.js',
105+
'src/components/circularProgress/circularProgress.js',
105106

106107
// Non-visual Components
107108
'src/components/swipe/swipe.js',
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Circular progress indicators, created with the `<material-circular-progress>` directive.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
$circle-size:48px;
2+
$circle-background:transparent;
3+
$circle-color:$color-blue-500;
4+
$inset-size:36px;
5+
$inset-color:white;
6+
$transition-length:.3s;
7+
$shadow:6px 6px 10px rgba(0, 0, 0, 0.2);
8+
material-circular-progress {
9+
display: block;
10+
width: $circle-size + 6;
11+
height: $circle-size + 6;
12+
background-color: $circle-background;
13+
border-radius: 50%;
14+
padding: 3px;
15+
16+
.wrapper1, .wrapper2 {
17+
width: $circle-size;
18+
height: $circle-size;
19+
position: absolute;
20+
border-radius: 50%;
21+
}
22+
23+
.circle {
24+
.mask, .fill, .shadow {
25+
width: $circle-size;
26+
height: $circle-size;
27+
position: absolute;
28+
border-radius: 50%;
29+
}
30+
31+
.shadow { }
32+
33+
.mask, .fill {
34+
-webkit-backface-visibility: hidden;
35+
transition: -webkit-transform $transition-length;
36+
transition: -ms-transform $transition-length;
37+
transition: transform $transition-length;
38+
}
39+
40+
.mask {
41+
clip: rect(0px, $circle-size, $circle-size, $circle-size/2);
42+
.fill {
43+
clip: rect(0px, $circle-size/2, $circle-size, 0px);
44+
background-color: $circle-color;
45+
}
46+
}
47+
}
48+
49+
.inset {
50+
width: $inset-size;
51+
height: $inset-size;
52+
position: absolute;
53+
margin-left: ($circle-size - $inset-size)/2;
54+
margin-top: ($circle-size - $inset-size)/2;
55+
background-color: $inset-color;
56+
border-radius: 50%;
57+
}
58+
59+
&[mode=indeterminate] {
60+
.wrapper1, .wrapper2 {
61+
-ms-transform-origin: 50% 50%; /* IE 9 */
62+
webkit-transform-origin: 50% 50%; /* Chrome, Safari, Opera */
63+
transform-origin: 50% 50%;
64+
}
65+
66+
.wrapper1{
67+
@include animation(indeterminate_rotate1 3s infinite linear);
68+
}
69+
70+
.wrapper2{
71+
@include animation(indeterminate_rotate2 1.5s infinite linear);
72+
}
73+
74+
.fill, .mask.full{
75+
@include animation(indeterminate_size_fill 1.5s infinite linear);
76+
}
77+
78+
.fill.fix {
79+
@include animation(indeterminate_size_fix 1.5s infinite linear);
80+
}
81+
}
82+
}
83+
84+
@include keyframes(indeterminate_rotate1) {
85+
0%{
86+
@include transform(rotate(0deg));
87+
}
88+
100%{
89+
@include transform(rotate(360deg));
90+
}
91+
}
92+
93+
@include keyframes(indeterminate_rotate2) {
94+
0%{
95+
@include transform(rotate(0deg));
96+
}
97+
70%{
98+
@include transform(rotate(0deg));
99+
}
100+
100%{
101+
@include transform(rotate(360deg));
102+
}
103+
}
104+
105+
@include keyframes(indeterminate_size_fill) {
106+
0%{
107+
@include transform(rotate(5deg));
108+
}
109+
10%{
110+
@include transform(rotate(5deg));
111+
}
112+
50%{
113+
@include transform(rotate(135deg));
114+
}
115+
70%{
116+
@include transform(rotate(135deg));
117+
}
118+
100%{
119+
@include transform(rotate(5deg));
120+
}
121+
}
122+
123+
@include keyframes(indeterminate_size_fix) {
124+
0%{
125+
@include transform(rotate(10deg));
126+
}
127+
10%{
128+
@include transform(rotate(10deg));
129+
}
130+
50%{
131+
@include transform(rotate(270deg));
132+
}
133+
70%{
134+
@include transform(rotate(270deg));
135+
}
136+
100%{
137+
@include transform(rotate(10deg));
138+
}
139+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/**
2+
* @ngdoc module
3+
* @name material.components.circularProgress
4+
* @description Circular Progress module!
5+
*/
6+
angular.module('material.components.circularProgress', [
7+
'material.animations',
8+
'material.services.aria'
9+
])
10+
.directive('materialCircularProgress', [
11+
'$$rAF',
12+
'$materialEffects',
13+
MaterialCircularProgressDirective
14+
]);
15+
16+
/**
17+
* @ngdoc directive
18+
* @name materialCircularProgress
19+
* @module material.components.circularProgress
20+
* @restrict E
21+
*
22+
* @description
23+
* The circular progress directive is used to make loading content in your app as delightful and painless as possible by minimizing the amount of visual change a user sees before they can view and interact with content.
24+
*
25+
* For operations where the percentage of the operation completed can be determined, use a determinate indicator. They give users a quick sense of how long an operation will take.
26+
*
27+
* For operations where the user is asked to wait a moment while something finishes up, and it’s not necessary to expose what's happening behind the scenes and how long it will take, use an indeterminate indicator.
28+
*
29+
* @param {string} mode Select from one of two modes: determinate and indeterminate.
30+
* @param {number=} value In determinate mode, this number represents the percentage of the circular progress. Default: 0
31+
* @param {number=} diameter This specifies the diamter of the circular progress. Default: 48
32+
*
33+
* @usage
34+
* <hljs lang="html">
35+
* <material-circular-progress mode="determinate" value="..."></material-circular-progress>
36+
*
37+
* <material-circular-progress mode="determinate" ng-value="..."></material-circular-progress>
38+
*
39+
* <material-circular-progress mode="determinate" value="..." diameter="100"></material-circular-progress>
40+
*
41+
* <material-circular-progress mode="indeterminate"></material-circular-progress>
42+
* </hljs>
43+
*/
44+
function MaterialCircularProgressDirective($$rAF, $materialEffects) {
45+
var fillRotations = new Array(101),
46+
fixRotations = new Array(101);
47+
48+
for (var i = 0; i < 101; i++) {
49+
var percent = i / 100;
50+
var rotation = Math.floor(percent * 180);
51+
52+
fillRotations[i] = 'rotate(' + rotation.toString() + 'deg)';
53+
fixRotations[i] = 'rotate(' + (rotation * 2).toString() + 'deg)';
54+
}
55+
56+
return {
57+
restrict: 'E',
58+
template:
59+
'<div class="wrapper1"><div class="wrapper2"><div class="circle">' +
60+
'<div class="mask full">' +
61+
'<div class="fill"></div>' +
62+
'</div>' +
63+
'<div class="mask half">' +
64+
'<div class="fill"></div>' +
65+
'<div class="fill fix"></div>' +
66+
'</div>' +
67+
'<div class="shadow"></div>' +
68+
'</div>' +
69+
'<div class="inset"></div></div></div>',
70+
compile: compile
71+
};
72+
73+
function compile(tElement, tAttrs, transclude) {
74+
tElement.attr('aria-valuemin', 0);
75+
tElement.attr('aria-valuemax', 100);
76+
tElement.attr('role', 'progressbar');
77+
78+
return postLink;
79+
}
80+
81+
function postLink(scope, element, attr) {
82+
var circle = element[0],
83+
fill = circle.querySelectorAll('.fill, .mask.full'),
84+
fix = circle.querySelectorAll('.fill.fix'),
85+
i, clamped, fillRotation, fixRotation;
86+
87+
var diameter = attr.diameter || 48;
88+
var scale = diameter/48;
89+
90+
circle.style[$materialEffects.TRANSFORM] = 'scale(' + scale.toString() + ')';
91+
92+
attr.$observe('value', function(value) {
93+
clamped = clamp(value);
94+
fillRotation = fillRotations[clamped];
95+
fixRotation = fixRotations[clamped];
96+
97+
element.attr('aria-valuenow', clamped);
98+
99+
for (i = 0; i < fill.length; i++) {
100+
fill[i].style[$materialEffects.TRANSFORM] = fillRotation;
101+
}
102+
103+
for (i = 0; i < fix.length; i++) {
104+
fix[i].style[$materialEffects.TRANSFORM] = fixRotation;
105+
}
106+
});
107+
}
108+
109+
function clamp(value) {
110+
if (value > 100) {
111+
return 100;
112+
}
113+
114+
if (value < 0) {
115+
return 0;
116+
}
117+
118+
return Math.ceil(value || 0);
119+
}
120+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
describe('materialCircularProgress', function() {
2+
beforeEach(module('material.components.circularProgress'));
3+
4+
it('should update aria-valuenow', inject(function($compile, $rootScope) {
5+
var element = $compile('<div>' +
6+
'<material-circular-progress value="{{progress}}">' +
7+
'</material-circular-progress>' +
8+
'</div>')($rootScope);
9+
10+
$rootScope.$apply(function() {
11+
$rootScope.progress = 50;
12+
});
13+
14+
var progress = element.find('material-circular-progress');
15+
16+
expect(progress.eq(0).attr('aria-valuenow')).toEqual('50');
17+
}));
18+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<div ng-controller="AppCtrl" layout="vertical" layout-padding>
2+
<h3>Determinate</h3>
3+
<material-circular-progress mode="determinate" value="{{determinateValue}}"></material-circular-progress>
4+
5+
<h3>Indeterminate</h3>
6+
<material-circular-progress mode="indeterminate"></material-circular-progress>
7+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
angular.module('circularProgressDemo1', ['ngMaterial'])
2+
.controller('AppCtrl', ['$scope', '$interval',
3+
function($scope, $interval) {
4+
$scope.mode = 'query';
5+
$scope.determinateValue = 30;
6+
7+
$interval(function() {
8+
$scope.determinateValue += 1;
9+
if ($scope.determinateValue > 100) {
10+
$scope.determinateValue = 30;
11+
}
12+
}, 100, 0, true);
13+
}
14+
]);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
body {
2+
padding: 20px;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"module": "material.components.circularProgress",
3+
"name": "Circular Progress",
4+
"demos": {
5+
"demo1": {
6+
"name": "Circular Progress Basic Usage",
7+
"files": ["demo1/*"]
8+
}
9+
}
10+
}

src/main.scss

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,5 @@
3333
"components/list/list",
3434
"components/divider/divider",
3535
"components/whiteframe/whiteframe",
36-
"components/linearProgress/linearProgress";
36+
"components/linearProgress/linearProgress",
37+
"components/circularProgress/circularProgress";

0 commit comments

Comments
 (0)