JSFiddle - React, Tailwind, and code Playground

by revolunet

HTML

<script src="http://code.angularjs.org/1.2.2/angular.js"></script>
<script src="http://fonts.googleapis.com/css?family=Roboto"></script>
<body ng-app="demo" ng-controller="demoController">
    <h3>rn-stepper demo (3/4)</h3>
    Model value : {{ score }}<br>
    Min value : <input ng-model="minScore"><br>
    Max value : <input ng-model="maxScore"><br>
    <hr>

    <div min="minScore" prevent-invalid max="maxScore" ng-model="score" rn-stepper></div>
</body>

SCSS

body {
 font-family: 'Roboto', sans-serif;
}


$stepper-height: 40px;
$stepper-value-width: 40px;
$stepper-button-width: 40px;
$stepper-border-width: 1px;
$stepper-button-bg: #4D4DFF;
$stepper-value-bg: #eee;
div[rn-stepper] {
    &.ng-invalid-out-of-bound {
        div {
            color: red;
        }
    }
    border:1px solid #bbb;
    display:inline-block;
    height:$stepper-height + ($stepper-border-width * 2);
    box-sizing:border-box;
    button {
        appearance:none;
        -webkit-appearance:none;
        margin:0;
        border:0;
        width: $stepper-button-width;
        height:$stepper-height;
        box-sizing:border-box;
        background: $stepper-button-bg;
        color: white;
        font-weight:bold;
        font-size:20px;
         outline: none;
        &:active {
            box-shadow: inset 0 2px 2px rgba(0, 0, 0, 0.25);
            background: darken($stepper-button-bg, 5%);
       }
    }
    div {
        vertical-align:top;
        width:$stepper-value-width;
        background:$stepper-value-bg;
        text-align:center;
        display:inline-block;    
        height:$stepper-height;
        line-height:$stepper-height;
        box-sizing:border-box;
        
    }
}

JavaScript

angular.module('demo', [])

.controller('demoController', function($scope) {
    $scope.score = 50;    
    $scope.minScore = 40;
    $scope.maxScore = 45;    
})

.directive('rnStepper', function() {
    return {
        restrict: 'AE',
        require: 'ngModel',
        scope: {
            value: '=ngModel',
            min: '=',
            max: '='
        },
        template: '<button ng-disabled="canDecrement" ng-click="decrement()">-</button>' +
                  '<div>{{ value }}</div>' +
                  '<button ng-disabled="canIncrement" ng-click="increment()">+</button>',
        link: function(scope, iElement, iAttrs, ngModelController) {
            function checkValue(newValue) {
                if (!angular.isDefined(newValue)) newValue = scope.value;
                var minRaised = angular.isDefined(scope.min) && newValue < parseInt(scope.min, 10);
                var maxRaised = angular.isDefined(scope.max) && newValue > parseInt(scope.max, 10);
                var valid = !(minRaised||maxRaised);
                ngModelController.$setValidity('out-of-bound', valid);
                ngModelController.$setViewValue(newValue);
//                scope.value = newValue;
                return valid;
            }
            scope.increment = function() {
                checkValue(scope.value + 1);
            }
            scope.decrement = function() {
                checkValue(scope.value - 1);
            }
            checkValue();
            scope.$watch('min+max', function() {
                checkValue();
            });
        }
    };
});