|
9 | 9 |
|
10 | 10 | angular.module('material.components.switch', [
|
11 | 11 | 'material.core',
|
12 |
| - 'material.components.checkbox', |
13 |
| - 'material.components.radioButton' |
| 12 | + 'material.components.checkbox' |
14 | 13 | ])
|
15 | 14 | .directive('mdSwitch', MdSwitch);
|
16 | 15 |
|
@@ -47,30 +46,162 @@ angular.module('material.components.switch', [
|
47 | 46 | *
|
48 | 47 | * </hljs>
|
49 | 48 | */
|
50 |
| -function MdSwitch(mdCheckboxDirective, mdRadioButtonDirective, $mdTheming) { |
| 49 | +function MdSwitch(mdCheckboxDirective, $mdTheming, $mdUtil, $document, $mdConstant, $parse) { |
51 | 50 | var checkboxDirective = mdCheckboxDirective[0];
|
52 |
| - var radioButtonDirective = mdRadioButtonDirective[0]; |
53 | 51 |
|
54 | 52 | return {
|
55 | 53 | restrict: 'E',
|
56 | 54 | transclude: true,
|
57 | 55 | template:
|
58 |
| - '<div class="md-switch-bar"></div>' + |
59 |
| - '<div class="md-switch-thumb">' + |
60 |
| - radioButtonDirective.template + |
| 56 | + '<div class="md-container">' + |
| 57 | + '<div class="md-bar"></div>' + |
| 58 | + '<div class="md-thumb-container">' + |
| 59 | + '<div class="md-thumb" md-ink-ripple md-ink-ripple-checkbox></div>' + |
| 60 | + '</div>'+ |
| 61 | + '</div>' + |
| 62 | + '<div class="md-text" ng-transclude>' + |
61 | 63 | '</div>',
|
62 | 64 | require: '?ngModel',
|
63 | 65 | compile: compile
|
64 | 66 | };
|
65 | 67 |
|
66 | 68 | function compile(element, attr) {
|
67 |
| - var thumb = angular.element(element[0].querySelector('.md-switch-thumb')); |
68 |
| - var checkboxLink = checkboxDirective.compile(thumb, attr); |
| 69 | + var checkboxLink = checkboxDirective.compile(element, attr); |
69 | 70 |
|
70 |
| - return function (scope, element, attr, ngModelCtrl) { |
71 |
| - $mdTheming(element); |
72 |
| - return checkboxLink(scope, thumb, attr, ngModelCtrl); |
| 71 | + return function (scope, element, attr, ngModel) { |
| 72 | + ngModel = ngModel || $mdUtil.fakeNgModel(); |
| 73 | + var disabledGetter = $parse(attr.ngDisabled); |
| 74 | + var thumbContainer = angular.element(element[0].querySelector('.md-thumb-container')); |
| 75 | + var elementWidth; |
| 76 | + |
| 77 | + // Tell the checkbox we don't want a click listener. |
| 78 | + // Our drag listener tells us everything, using more granular events. |
| 79 | + attr.noClick = true; |
| 80 | + checkboxLink(scope, element, attr, ngModel); |
| 81 | + |
| 82 | + setupDrag(element, { |
| 83 | + onDragStart: onDragStart, |
| 84 | + onDrag: onDrag, |
| 85 | + onDragEnd: onDragEnd |
| 86 | + }); |
| 87 | + |
| 88 | + function onDragStart(ev, drag) { |
| 89 | + // Don't go if ng-disabled===true |
| 90 | + if (disabledGetter(scope)) return false; |
| 91 | + elementWidth = thumbContainer.prop('offsetWidth'); |
| 92 | + element.addClass('no-animate'); |
| 93 | + } |
| 94 | + function onDrag(ev, drag) { |
| 95 | + var percent = drag.distance / elementWidth; |
| 96 | + |
| 97 | + var translate = ngModel.$viewValue ? |
| 98 | + 1 - percent : //if checked, start from right |
| 99 | + -percent; // else, start from left |
| 100 | + translate = Math.max(0, Math.min(1, translate)); |
| 101 | + |
| 102 | + thumbContainer.css($mdConstant.CSS.TRANSFORM, 'translate3d(' + (100*translate) + '%,0,0)'); |
| 103 | + drag.translate = translate; |
| 104 | + } |
| 105 | + function onDragEnd(ev, drag) { |
| 106 | + if (disabledGetter(scope)) return false; |
| 107 | + |
| 108 | + element.removeClass('no-animate'); |
| 109 | + thumbContainer.css($mdConstant.CSS.TRANSFORM, ''); |
| 110 | + |
| 111 | + // We changed if there is no distance (this is a click a click), |
| 112 | + // or if the drag distance is >50% of the total. |
| 113 | + var isChanged = Math.abs(drag.distance) < 5 || |
| 114 | + ngModel.$viewValue ? drag.translate < 0.5 : drag.translate > 0.5; |
| 115 | + if (isChanged) { |
| 116 | + scope.$apply(function() { |
| 117 | + ngModel.$setViewValue(!ngModel.$viewValue); |
| 118 | + ngModel.$render(); |
| 119 | + }); |
| 120 | + } |
| 121 | + } |
73 | 122 | };
|
74 | 123 | }
|
| 124 | + |
| 125 | + function setupDrag(element, options) { |
| 126 | + // The state of the current drag |
| 127 | + var drag; |
| 128 | + // Whether the pointer is currently down on this element. |
| 129 | + var pointerIsDown; |
| 130 | + |
| 131 | + var START_EVENTS = 'mousedown touchstart pointerdown'; |
| 132 | + var MOVE_EVENTS = 'mousemove touchmove pointermove'; |
| 133 | + var END_EVENTS = 'mouseup mouseleave touchend touchcancel pointerup pointercancel'; |
| 134 | + |
| 135 | + // TODO implement vertical/horizontal drag if needed |
| 136 | + options = angular.extend({ |
| 137 | + onDragStart: angular.noop, |
| 138 | + onDrag: angular.noop, |
| 139 | + onDragEnd: angular.noop |
| 140 | + }, options); |
| 141 | + |
| 142 | + element.on(START_EVENTS, startDrag); |
| 143 | + |
| 144 | + // Listen to move and end events on document. End events especially could have bubbled up |
| 145 | + // from the child. |
| 146 | + $document.on(MOVE_EVENTS, doDrag) |
| 147 | + .on(END_EVENTS, endDrag); |
| 148 | + |
| 149 | + element.on('$destroy', function() { |
| 150 | + $document.off(MOVE_EVENTS, doDrag) |
| 151 | + .off(END_EVENTS, endDrag); |
| 152 | + }); |
| 153 | + |
| 154 | + function startDrag(ev) { |
| 155 | + if (pointerIsDown) return; |
| 156 | + pointerIsDown = true; |
| 157 | + |
| 158 | + drag = { |
| 159 | + // Restrict this drag to whatever started it: if a mousedown started the drag, |
| 160 | + // don't let anything but mouse events continue it. |
| 161 | + pointerType: ev.type.charAt(0), |
| 162 | + startX: getPosition(ev), |
| 163 | + startTime: $mdUtil.now() |
| 164 | + }; |
| 165 | + // Allow user to cancel by returning false |
| 166 | + if (options.onDragStart(ev, drag) === false) { |
| 167 | + drag = null; |
| 168 | + } |
| 169 | + } |
| 170 | + function doDrag(ev) { |
| 171 | + if (!drag || !isProperEventType(ev)) return; |
| 172 | + |
| 173 | + updateDrag(ev); |
| 174 | + |
| 175 | + // Allow user to cancel by returning false |
| 176 | + if (options.onDrag(ev, drag) === false) { |
| 177 | + endDrag(ev); |
| 178 | + } |
| 179 | + } |
| 180 | + function endDrag(ev) { |
| 181 | + pointerIsDown = false; |
| 182 | + if (!drag || !isProperEventType(ev)) return; |
| 183 | + |
| 184 | + updateDrag(ev); |
| 185 | + options.onDragEnd(ev, drag); |
| 186 | + drag = null; |
| 187 | + } |
| 188 | + |
| 189 | + function updateDrag(ev) { |
| 190 | + var x = getPosition(ev); |
| 191 | + drag.distance = drag.startX - x; |
| 192 | + drag.direction = drag.distance > 0 ? 'left' : (drag.distance < 0 ? 'right' : ''); |
| 193 | + drag.time = drag.startTime - $mdUtil.now(); |
| 194 | + drag.velocity = Math.abs(drag.distance) / drag.time; |
| 195 | + } |
| 196 | + function getPosition(ev) { |
| 197 | + ev = ev.originalEvent || ev; //suport jQuery events |
| 198 | + return (ev.touches ? ev.touches[0] : ev).pageX; |
| 199 | + } |
| 200 | + function isProperEventType(ev) { |
| 201 | + return drag && ev && (ev.type || '').charAt(0) == drag.pointerType; |
| 202 | + } |
| 203 | + } |
| 204 | + |
75 | 205 | }
|
| 206 | + |
76 | 207 | })();
|
0 commit comments