Skip to content
This repository was archived by the owner on May 29, 2019. It is now read-only.

Commit 20ab01a

Browse files
bekospkozlowski-opensource
authored andcommitted
feat(rating): add support for custom icons per instance
1 parent e55d906 commit 20ab01a

File tree

6 files changed

+135
-15
lines changed

6 files changed

+135
-15
lines changed

src/rating/docs/demo.html

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
<div ng-controller="RatingDemoCtrl">
1+
<div ng-controller="RatingDemoCtrl" class="well well-small">
2+
<h4>Default</h4>
23
<rating value="rate" max="max" readonly="isReadonly" on-hover="hoveringOver(value)" on-leave="overStar = null"></rating>
34
<span class="badge" ng-class="{'badge-warning': percent<30, 'badge-info': percent>=30 && percent<70, 'badge-success': percent>=70}" ng-show="overStar && !isReadonly">{{percent}}%</span>
45

5-
<hr/>
6-
<pre>Rate: <b>{{rate}}</b> - Readonly is: <i>{{isReadonly}}</i> - Hovering over: <b>{{overStar || "none"}}</b></pre>
6+
<pre style="margin:15px 0;">Rate: <b>{{rate}}</b> - Readonly is: <i>{{isReadonly}}</i> - Hovering over: <b>{{overStar || "none"}}</b></pre>
77

8-
<hr/>
98
<button class="btn btn-small btn-danger" ng-click="rate = 0" ng-disabled="isReadonly">Clear</button>
109
<button class="btn btn-small" ng-click="isReadonly = ! isReadonly">Toggle Readonly</button>
10+
<hr />
11+
12+
<h4>Custom icons</h4>
13+
<div ng-init="x = 5"><rating value="x" max="15" state-on="'icon-ok-sign'" state-off="'icon-ok-circle'"></rating> <b>(<i>Rate:</i> {{x}})</b></div>
14+
<div ng-init="y = 2"><rating value="y" rating-states="ratingStates"></rating> <b>(<i>Rate:</i> {{y}})</b></div>
1115
</div>

src/rating/docs/demo.js

+8
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,12 @@ var RatingDemoCtrl = function ($scope) {
77
$scope.overStar = value;
88
$scope.percent = 100 * (value / $scope.max);
99
};
10+
11+
$scope.ratingStates = [
12+
{stateOn: 'icon-ok-sign', stateOff: 'icon-ok-circle'},
13+
{stateOn: 'icon-star', stateOff: 'icon-star-empty'},
14+
{stateOn: 'icon-heart', stateOff: 'icon-ban-circle'},
15+
{stateOn: 'icon-heart'},
16+
{stateOff: 'icon-off'}
17+
];
1018
};

src/rating/docs/readme.md

+35-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,37 @@
11
Rating directive that will take care of visualising a star rating bar.
22

3-
It also provides optional attributes: `max` to vary the number of stars, `readonly` to disable user's interaction, `on-hover` to signal when the user's mouse is over a particular star, and `on-leave` to signal when the mouse leaves the control altogether.
3+
### Settings ###
4+
5+
#### `<rating>` ####
6+
7+
* `value` <i class="icon-eye-open"></i>
8+
:
9+
The current rate.
10+
11+
* `max`
12+
_(Defaults: 5)_ :
13+
Changes the number of icons.
14+
15+
* `readonly`
16+
_(Defaults: false)_ :
17+
Prevent user's interaction.
18+
19+
* `on-hover(value)`
20+
:
21+
An optional expression called when user's mouse is over a particular icon.
22+
23+
* `on-leave()`
24+
:
25+
An optional expression called when user's mouse leaves the control altogether.
26+
27+
* `state-on`
28+
_(Defaults: 'icon-star')_ :
29+
A variable used in default template to specify the class for selected icons.
30+
31+
* `state-off`
32+
_(Defaults: 'icon-star-empty')_ :
33+
A variable used in default template to specify the class for unselected icons.
34+
35+
* `rating-states`
36+
_(Defaults: null)_ :
37+
An array of objects defining properties for all icons. In default template, `stateOn` & `stateOff` property is used to specify the icon's class.

src/rating/rating.js

+32-7
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,47 @@
11
angular.module('ui.bootstrap.rating', [])
22

33
.constant('ratingConfig', {
4-
max: 5
4+
max: 5,
5+
stateOn: 'icon-star',
6+
stateOff: 'icon-star-empty'
57
})
68

79
.controller('RatingController', ['$scope', '$attrs', '$parse', 'ratingConfig', function($scope, $attrs, $parse, ratingConfig) {
810

911
this.maxRange = angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : ratingConfig.max;
12+
this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn;
13+
this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff;
1014

11-
$scope.range = [];
12-
for (var i = 1; i <= this.maxRange; i++) {
13-
$scope.range.push(i);
14-
}
15+
this.createDefaultRange = function(len) {
16+
var defaultStateObject = {
17+
stateOn: this.stateOn,
18+
stateOff: this.stateOff
19+
};
20+
21+
var states = new Array(len);
22+
for (var i = 0; i < len; i++) {
23+
states[i] = defaultStateObject;
24+
}
25+
return states;
26+
};
27+
28+
this.normalizeRange = function(states) {
29+
for (var i = 0, n = states.length; i < n; i++) {
30+
states[i].stateOn = states[i].stateOn || this.stateOn;
31+
states[i].stateOff = states[i].stateOff || this.stateOff;
32+
}
33+
return states;
34+
};
35+
36+
// Get objects used in template
37+
$scope.range = angular.isDefined($attrs.ratingStates) ? this.normalizeRange(angular.copy($scope.$parent.$eval($attrs.ratingStates))): this.createDefaultRange(this.maxRange);
1538

1639
$scope.rate = function(value) {
17-
if ( ! $scope.readonly ) {
18-
$scope.value = value;
40+
if ( $scope.readonly || $scope.value === value) {
41+
return;
1942
}
43+
44+
$scope.value = value;
2045
};
2146

2247
$scope.enter = function(value) {

src/rating/test/rating.spec.js

+51-2
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ describe('rating directive', function () {
1818
return getStars().eq( number - 1 );
1919
}
2020

21-
function getState() {
21+
function getState(classOn, classOff) {
2222
var stars = getStars();
2323
var state = [];
2424
for (var i = 0, n = stars.length; i < n; i++) {
25-
state.push( (stars.eq(i).hasClass('icon-star') && ! stars.eq(i).hasClass('icon-star-empty')) );
25+
state.push( (stars.eq(i).hasClass(classOn || 'icon-star') && ! stars.eq(i).hasClass(classOff || 'icon-star-empty')) );
2626
}
2727
return state;
2828
}
@@ -124,12 +124,57 @@ describe('rating directive', function () {
124124
expect($rootScope.leaving).toHaveBeenCalled();
125125
});
126126

127+
describe('custom states', function() {
128+
beforeEach(inject(function() {
129+
$rootScope.classOn = 'icon-ok-sign';
130+
$rootScope.classOff = 'icon-ok-circle';
131+
element = $compile('<rating value="rate" state-on="classOn" state-off="classOff"></rating>')($rootScope);
132+
$rootScope.$digest();
133+
}));
134+
135+
it('changes the default icons', function() {
136+
expect(getState($rootScope.classOn, $rootScope.classOff)).toEqual([true, true, true, false, false]);
137+
});
138+
});
139+
140+
describe('`rating-states`', function() {
141+
beforeEach(inject(function() {
142+
$rootScope.states = [
143+
{stateOn: 'sign', stateOff: 'circle'},
144+
{stateOn: 'heart', stateOff: 'ban'},
145+
{stateOn: 'heart'},
146+
{stateOff: 'off'}
147+
];
148+
element = $compile('<rating value="rate" rating-states="states"></rating>')($rootScope);
149+
$rootScope.$digest();
150+
}));
151+
152+
it('should define number of icon elements', function () {
153+
expect(getStars().length).toBe($rootScope.states.length);
154+
});
155+
156+
it('handles each icon', function() {
157+
var stars = getStars();
158+
159+
for (var i = 0; i < stars.length; i++) {
160+
var star = stars.eq(i);
161+
var state = $rootScope.states[i];
162+
var isOn = i < $rootScope.rate;
163+
164+
expect(star.hasClass(state.stateOn)).toBe(isOn);
165+
expect(star.hasClass(state.stateOff)).toBe(!isOn);
166+
}
167+
});
168+
});
169+
127170
describe('setting ratingConfig', function() {
128171
var originalConfig = {};
129172
beforeEach(inject(function(ratingConfig) {
130173
$rootScope.rate = 5;
131174
angular.extend(originalConfig, ratingConfig);
132175
ratingConfig.max = 10;
176+
ratingConfig.stateOn = 'on';
177+
ratingConfig.stateOff = 'off';
133178
element = $compile('<rating value="rate"></rating>')($rootScope);
134179
$rootScope.$digest();
135180
}));
@@ -141,5 +186,9 @@ describe('rating directive', function () {
141186
it('should change number of icon elements', function () {
142187
expect(getStars().length).toBe(10);
143188
});
189+
190+
it('should change icon states', function () {
191+
expect(getState('on', 'off')).toEqual([true, true, true, true, true, false, false, false, false, false]);
192+
});
144193
});
145194
});

template/rating/rating.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
<span ng-mouseleave="reset()">
2-
<i ng-repeat="number in range" ng-mouseenter="enter(number)" ng-click="rate(number)" ng-class="{'icon-star': number <= val, 'icon-star-empty': number > val}"></i>
2+
<i ng-repeat="r in range" ng-mouseenter="enter($index + 1)" ng-click="rate($index + 1)" ng-class="$index < val && r.stateOn || r.stateOff"></i>
33
</span>

0 commit comments

Comments
 (0)