123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. var __extends = (this && this.__extends) || (function () {
  2. var extendStatics = Object.setPrototypeOf ||
  3. ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
  4. function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  5. return function (d, b) {
  6. extendStatics(d, b);
  7. function __() { this.constructor = d; }
  8. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  9. };
  10. })();
  11. (function (factory) {
  12. if (typeof module === "object" && typeof module.exports === "object") {
  13. var v = factory(require, exports);
  14. if (v !== undefined) module.exports = v;
  15. }
  16. else if (typeof define === "function" && define.amd) {
  17. define(["require", "exports", "@angular/core", "@angular/forms", "../../util/util", "../../config/config", "../../platform/dom-controller", "../../util/form", "../../tap-click/haptic", "../../util/base-input", "../item/item", "../../platform/platform", "../../util/dom", "../../gestures/ui-event-manager"], factory);
  18. }
  19. })(function (require, exports) {
  20. "use strict";
  21. Object.defineProperty(exports, "__esModule", { value: true });
  22. var core_1 = require("@angular/core");
  23. var forms_1 = require("@angular/forms");
  24. var util_1 = require("../../util/util");
  25. var config_1 = require("../../config/config");
  26. var dom_controller_1 = require("../../platform/dom-controller");
  27. var form_1 = require("../../util/form");
  28. var haptic_1 = require("../../tap-click/haptic");
  29. var base_input_1 = require("../../util/base-input");
  30. var item_1 = require("../item/item");
  31. var platform_1 = require("../../platform/platform");
  32. var dom_1 = require("../../util/dom");
  33. var ui_event_manager_1 = require("../../gestures/ui-event-manager");
  34. /**
  35. * @name Range
  36. * @description
  37. * The Range slider lets users select from a range of values by moving
  38. * the slider knob. It can accept dual knobs, but by default one knob
  39. * controls the value of the range.
  40. *
  41. * ### Range Labels
  42. * Labels can be placed on either side of the range by adding the
  43. * `range-left` or `range-right` property to the element. The element
  44. * doesn't have to be an `ion-label`, it can be added to any element
  45. * to place it to the left or right of the range. See [usage](#usage)
  46. * below for examples.
  47. *
  48. *
  49. * ### Minimum and Maximum Values
  50. * Minimum and maximum values can be passed to the range through the `min`
  51. * and `max` properties, respectively. By default, the range sets the `min`
  52. * to `0` and the `max` to `100`.
  53. *
  54. *
  55. * ### Steps and Snaps
  56. * The `step` property specifies the value granularity of the range's value.
  57. * It can be useful to set the `step` when the value isn't in increments of `1`.
  58. * Setting the `step` property will show tick marks on the range for each step.
  59. * The `snaps` property can be set to automatically move the knob to the nearest
  60. * tick mark based on the step property value.
  61. *
  62. *
  63. * ### Dual Knobs
  64. * Setting the `dualKnobs` property to `true` on the range component will
  65. * enable two knobs on the range. If the range has two knobs, the value will
  66. * be an object containing two properties: `lower` and `upper`.
  67. *
  68. *
  69. * @usage
  70. * ```html
  71. * <ion-list>
  72. * <ion-item>
  73. * <ion-range [(ngModel)]="singleValue" color="danger" pin="true"></ion-range>
  74. * </ion-item>
  75. *
  76. * <ion-item>
  77. * <ion-range min="-200" max="200" [(ngModel)]="saturation" color="secondary">
  78. * <ion-label range-left>-200</ion-label>
  79. * <ion-label range-right>200</ion-label>
  80. * </ion-range>
  81. * </ion-item>
  82. *
  83. * <ion-item>
  84. * <ion-range min="20" max="80" step="2" [(ngModel)]="brightness">
  85. * <ion-icon small range-left name="sunny"></ion-icon>
  86. * <ion-icon range-right name="sunny"></ion-icon>
  87. * </ion-range>
  88. * </ion-item>
  89. *
  90. * <ion-item>
  91. * <ion-label>step=100, snaps, {{singleValue4}}</ion-label>
  92. * <ion-range min="1000" max="2000" step="100" snaps="true" color="secondary" [(ngModel)]="singleValue4"></ion-range>
  93. * </ion-item>
  94. *
  95. * <ion-item>
  96. * <ion-label>dual, step=3, snaps, {{dualValue2 | json}}</ion-label>
  97. * <ion-range dualKnobs="true" [(ngModel)]="dualValue2" min="21" max="72" step="3" snaps="true"></ion-range>
  98. * </ion-item>
  99. * </ion-list>
  100. * ```
  101. *
  102. *
  103. * @demo /docs/demos/src/range/
  104. */
  105. var Range = (function (_super) {
  106. __extends(Range, _super);
  107. function Range(form, _haptic, item, config, _plt, elementRef, renderer, _dom, _cd) {
  108. var _this = _super.call(this, config, elementRef, renderer, 'range', 0, form, item, null) || this;
  109. _this._haptic = _haptic;
  110. _this._plt = _plt;
  111. _this._dom = _dom;
  112. _this._cd = _cd;
  113. _this._min = 0;
  114. _this._max = 100;
  115. _this._step = 1;
  116. _this._valA = 0;
  117. _this._valB = 0;
  118. _this._ratioA = 0;
  119. _this._ratioB = 0;
  120. _this._events = new ui_event_manager_1.UIEventManager(_plt);
  121. return _this;
  122. }
  123. Object.defineProperty(Range.prototype, "min", {
  124. /**
  125. * @input {number} Minimum integer value of the range. Defaults to `0`.
  126. */
  127. get: function () {
  128. return this._min;
  129. },
  130. set: function (val) {
  131. val = Math.round(val);
  132. if (!isNaN(val)) {
  133. this._min = val;
  134. this._inputUpdated();
  135. }
  136. },
  137. enumerable: true,
  138. configurable: true
  139. });
  140. Object.defineProperty(Range.prototype, "max", {
  141. /**
  142. * @input {number} Maximum integer value of the range. Defaults to `100`.
  143. */
  144. get: function () {
  145. return this._max;
  146. },
  147. set: function (val) {
  148. val = Math.round(val);
  149. if (!isNaN(val)) {
  150. this._max = val;
  151. this._inputUpdated();
  152. }
  153. },
  154. enumerable: true,
  155. configurable: true
  156. });
  157. Object.defineProperty(Range.prototype, "step", {
  158. /**
  159. * @input {number} Specifies the value granularity. Defaults to `1`.
  160. */
  161. get: function () {
  162. return this._step;
  163. },
  164. set: function (val) {
  165. val = Math.round(val);
  166. if (!isNaN(val) && val > 0) {
  167. this._step = val;
  168. }
  169. },
  170. enumerable: true,
  171. configurable: true
  172. });
  173. Object.defineProperty(Range.prototype, "snaps", {
  174. /**
  175. * @input {boolean} If true, the knob snaps to tick marks evenly spaced based
  176. * on the step property value. Defaults to `false`.
  177. */
  178. get: function () {
  179. return this._snaps;
  180. },
  181. set: function (val) {
  182. this._snaps = util_1.isTrueProperty(val);
  183. },
  184. enumerable: true,
  185. configurable: true
  186. });
  187. Object.defineProperty(Range.prototype, "pin", {
  188. /**
  189. * @input {boolean} If true, a pin with integer value is shown when the knob
  190. * is pressed. Defaults to `false`.
  191. */
  192. get: function () {
  193. return this._pin;
  194. },
  195. set: function (val) {
  196. this._pin = util_1.isTrueProperty(val);
  197. },
  198. enumerable: true,
  199. configurable: true
  200. });
  201. Object.defineProperty(Range.prototype, "debounce", {
  202. /**
  203. * @input {number} How long, in milliseconds, to wait to trigger the
  204. * `ionChange` event after each change in the range value. Default `0`.
  205. */
  206. get: function () {
  207. return this._debouncer.wait;
  208. },
  209. set: function (val) {
  210. this._debouncer.wait = val;
  211. },
  212. enumerable: true,
  213. configurable: true
  214. });
  215. Object.defineProperty(Range.prototype, "dualKnobs", {
  216. /**
  217. * @input {boolean} Show two knobs. Defaults to `false`.
  218. */
  219. get: function () {
  220. return this._dual;
  221. },
  222. set: function (val) {
  223. this._dual = util_1.isTrueProperty(val);
  224. },
  225. enumerable: true,
  226. configurable: true
  227. });
  228. Object.defineProperty(Range.prototype, "ratio", {
  229. /**
  230. * Returns the ratio of the knob's is current location, which is a number
  231. * between `0` and `1`. If two knobs are used, this property represents
  232. * the lower value.
  233. */
  234. get: function () {
  235. if (this._dual) {
  236. return Math.min(this._ratioA, this._ratioB);
  237. }
  238. return this._ratioA;
  239. },
  240. enumerable: true,
  241. configurable: true
  242. });
  243. Object.defineProperty(Range.prototype, "ratioUpper", {
  244. /**
  245. * Returns the ratio of the upper value's is current location, which is
  246. * a number between `0` and `1`. If there is only one knob, then this
  247. * will return `null`.
  248. */
  249. get: function () {
  250. if (this._dual) {
  251. return Math.max(this._ratioA, this._ratioB);
  252. }
  253. return null;
  254. },
  255. enumerable: true,
  256. configurable: true
  257. });
  258. /**
  259. * @hidden
  260. */
  261. Range.prototype.ngAfterContentInit = function () {
  262. this._initialize();
  263. // add touchstart/mousedown listeners
  264. this._events.pointerEvents({
  265. element: this._slider.nativeElement,
  266. pointerDown: this._pointerDown.bind(this),
  267. pointerMove: this._pointerMove.bind(this),
  268. pointerUp: this._pointerUp.bind(this),
  269. zone: true
  270. });
  271. // build all the ticks if there are any to show
  272. this._createTicks();
  273. };
  274. /** @internal */
  275. Range.prototype._pointerDown = function (ev) {
  276. // TODO: we could stop listening for events instead of checking this._disabled.
  277. // since there are a lot of events involved, this solution is
  278. // enough for the moment
  279. if (this._disabled) {
  280. return false;
  281. }
  282. // trigger ionFocus event
  283. this._fireFocus();
  284. // prevent default so scrolling does not happen
  285. ev.preventDefault();
  286. ev.stopPropagation();
  287. // get the start coordinates
  288. var current = dom_1.pointerCoord(ev);
  289. // get the full dimensions of the slider element
  290. var rect = this._rect = this._plt.getElementBoundingClientRect(this._slider.nativeElement);
  291. // figure out which knob they started closer to
  292. var ratio = util_1.clamp(0, (current.x - rect.left) / (rect.width), 1);
  293. this._activeB = this._dual && (Math.abs(ratio - this._ratioA) > Math.abs(ratio - this._ratioB));
  294. // update the active knob's position
  295. this._update(current, rect, true);
  296. // trigger a haptic start
  297. this._haptic.gestureSelectionStart();
  298. // return true so the pointer events
  299. // know everything's still valid
  300. return true;
  301. };
  302. /** @internal */
  303. Range.prototype._pointerMove = function (ev) {
  304. if (this._disabled) {
  305. return;
  306. }
  307. // prevent default so scrolling does not happen
  308. ev.preventDefault();
  309. ev.stopPropagation();
  310. // update the active knob's position
  311. var hasChanged = this._update(dom_1.pointerCoord(ev), this._rect, true);
  312. if (hasChanged && this._snaps) {
  313. // trigger a haptic selection changed event
  314. // if this is a snap range
  315. this._haptic.gestureSelectionChanged();
  316. }
  317. };
  318. /** @internal */
  319. Range.prototype._pointerUp = function (ev) {
  320. if (this._disabled) {
  321. return;
  322. }
  323. // prevent default so scrolling does not happen
  324. ev.preventDefault();
  325. ev.stopPropagation();
  326. // update the active knob's position
  327. this._update(dom_1.pointerCoord(ev), this._rect, false);
  328. // trigger a haptic end
  329. this._haptic.gestureSelectionEnd();
  330. // trigger ionBlur event
  331. this._fireBlur();
  332. };
  333. /** @internal */
  334. Range.prototype._update = function (current, rect, isPressed) {
  335. // figure out where the pointer is currently at
  336. // update the knob being interacted with
  337. var ratio = util_1.clamp(0, (current.x - rect.left) / (rect.width), 1);
  338. var val = this._ratioToValue(ratio);
  339. if (this._snaps) {
  340. // snaps the ratio to the current value
  341. ratio = this._valueToRatio(val);
  342. }
  343. // update which knob is pressed
  344. this._pressed = isPressed;
  345. var valChanged = false;
  346. if (this._activeB) {
  347. // when the pointer down started it was determined
  348. // that knob B was the one they were interacting with
  349. this._pressedB = isPressed;
  350. this._pressedA = false;
  351. this._ratioB = ratio;
  352. valChanged = val === this._valB;
  353. this._valB = val;
  354. }
  355. else {
  356. // interacting with knob A
  357. this._pressedA = isPressed;
  358. this._pressedB = false;
  359. this._ratioA = ratio;
  360. valChanged = val === this._valA;
  361. this._valA = val;
  362. }
  363. this._updateBar();
  364. if (valChanged) {
  365. return false;
  366. }
  367. // value has been updated
  368. var value;
  369. if (this._dual) {
  370. // dual knobs have an lower and upper value
  371. value = {
  372. lower: Math.min(this._valA, this._valB),
  373. upper: Math.max(this._valA, this._valB)
  374. };
  375. (void 0) /* console.debug */;
  376. }
  377. else {
  378. // single knob only has one value
  379. value = this._valA;
  380. (void 0) /* console.debug */;
  381. }
  382. // Update input value
  383. this.value = value;
  384. return true;
  385. };
  386. /** @internal */
  387. Range.prototype._updateBar = function () {
  388. var ratioA = this._ratioA;
  389. var ratioB = this._ratioB;
  390. if (this._dual) {
  391. this._barL = (Math.min(ratioA, ratioB) * 100) + "%";
  392. this._barR = 100 - (Math.max(ratioA, ratioB) * 100) + "%";
  393. }
  394. else {
  395. this._barL = '';
  396. this._barR = 100 - (ratioA * 100) + "%";
  397. }
  398. this._updateTicks();
  399. };
  400. /** @internal */
  401. Range.prototype._createTicks = function () {
  402. var _this = this;
  403. if (this._snaps) {
  404. this._dom.write(function () {
  405. // TODO: Fix to not use RAF
  406. _this._ticks = [];
  407. for (var value = _this._min; value <= _this._max; value += _this._step) {
  408. var ratio = _this._valueToRatio(value);
  409. _this._ticks.push({
  410. ratio: ratio,
  411. left: ratio * 100 + "%",
  412. });
  413. }
  414. _this._updateTicks();
  415. });
  416. }
  417. };
  418. /** @internal */
  419. Range.prototype._updateTicks = function () {
  420. var ticks = this._ticks;
  421. var ratio = this.ratio;
  422. if (this._snaps && ticks) {
  423. if (this._dual) {
  424. var upperRatio = this.ratioUpper;
  425. ticks.forEach(function (t) {
  426. t.active = (t.ratio >= ratio && t.ratio <= upperRatio);
  427. });
  428. }
  429. else {
  430. ticks.forEach(function (t) {
  431. t.active = (t.ratio <= ratio);
  432. });
  433. }
  434. }
  435. };
  436. /** @hidden */
  437. Range.prototype._keyChg = function (isIncrease, isKnobB) {
  438. var step = this._step;
  439. if (isKnobB) {
  440. if (isIncrease) {
  441. this._valB += step;
  442. }
  443. else {
  444. this._valB -= step;
  445. }
  446. this._valB = util_1.clamp(this._min, this._valB, this._max);
  447. this._ratioB = this._valueToRatio(this._valB);
  448. }
  449. else {
  450. if (isIncrease) {
  451. this._valA += step;
  452. }
  453. else {
  454. this._valA -= step;
  455. }
  456. this._valA = util_1.clamp(this._min, this._valA, this._max);
  457. this._ratioA = this._valueToRatio(this._valA);
  458. }
  459. this._updateBar();
  460. };
  461. /** @internal */
  462. Range.prototype._ratioToValue = function (ratio) {
  463. ratio = Math.round(((this._max - this._min) * ratio));
  464. ratio = Math.round(ratio / this._step) * this._step + this._min;
  465. return util_1.clamp(this._min, ratio, this._max);
  466. };
  467. /** @internal */
  468. Range.prototype._valueToRatio = function (value) {
  469. value = Math.round((value - this._min) / this._step) * this._step;
  470. value = value / (this._max - this._min);
  471. return util_1.clamp(0, value, 1);
  472. };
  473. Range.prototype._inputNormalize = function (val) {
  474. if (this._dual) {
  475. return val;
  476. }
  477. else {
  478. val = parseFloat(val);
  479. return isNaN(val) ? undefined : val;
  480. }
  481. };
  482. /**
  483. * @hidden
  484. */
  485. Range.prototype._inputUpdated = function () {
  486. var val = this.value;
  487. if (this._dual) {
  488. this._valA = val.lower;
  489. this._valB = val.upper;
  490. this._ratioA = this._valueToRatio(val.lower);
  491. this._ratioB = this._valueToRatio(val.upper);
  492. }
  493. else {
  494. this._valA = val;
  495. this._ratioA = this._valueToRatio(val);
  496. }
  497. this._updateBar();
  498. this._cd.detectChanges();
  499. };
  500. /**
  501. * @hidden
  502. */
  503. Range.prototype.ngOnDestroy = function () {
  504. _super.prototype.ngOnDestroy.call(this);
  505. this._events.destroy();
  506. };
  507. Range.decorators = [
  508. { type: core_1.Component, args: [{
  509. selector: 'ion-range',
  510. template: '<ng-content select="[range-left]"></ng-content>' +
  511. '<div class="range-slider" #slider>' +
  512. '<div class="range-tick" *ngFor="let t of _ticks" [style.left]="t.left" [class.range-tick-active]="t.active" role="presentation"></div>' +
  513. '<div class="range-bar" role="presentation"></div>' +
  514. '<div class="range-bar range-bar-active" [style.left]="_barL" [style.right]="_barR" #bar role="presentation"></div>' +
  515. '<div class="range-knob-handle" (ionIncrease)="_keyChg(true, false)" (ionDecrease)="_keyChg(false, false)" [ratio]="_ratioA" [val]="_valA" [pin]="_pin" [pressed]="_pressedA" [min]="_min" [max]="_max" [disabled]="_disabled" [labelId]="_labelId"></div>' +
  516. '<div class="range-knob-handle" (ionIncrease)="_keyChg(true, true)" (ionDecrease)="_keyChg(false, true)" [ratio]="_ratioB" [val]="_valB" [pin]="_pin" [pressed]="_pressedB" [min]="_min" [max]="_max" [disabled]="_disabled" [labelId]="_labelId" *ngIf="_dual"></div>' +
  517. '</div>' +
  518. '<ng-content select="[range-right]"></ng-content>',
  519. host: {
  520. '[class.range-disabled]': '_disabled',
  521. '[class.range-pressed]': '_pressed',
  522. '[class.range-has-pin]': '_pin'
  523. },
  524. providers: [{ provide: forms_1.NG_VALUE_ACCESSOR, useExisting: Range, multi: true }],
  525. encapsulation: core_1.ViewEncapsulation.None,
  526. },] },
  527. ];
  528. /** @nocollapse */
  529. Range.ctorParameters = function () { return [
  530. { type: form_1.Form, },
  531. { type: haptic_1.Haptic, },
  532. { type: item_1.Item, decorators: [{ type: core_1.Optional },] },
  533. { type: config_1.Config, },
  534. { type: platform_1.Platform, },
  535. { type: core_1.ElementRef, },
  536. { type: core_1.Renderer, },
  537. { type: dom_controller_1.DomController, },
  538. { type: core_1.ChangeDetectorRef, },
  539. ]; };
  540. Range.propDecorators = {
  541. '_slider': [{ type: core_1.ViewChild, args: ['slider',] },],
  542. 'min': [{ type: core_1.Input },],
  543. 'max': [{ type: core_1.Input },],
  544. 'step': [{ type: core_1.Input },],
  545. 'snaps': [{ type: core_1.Input },],
  546. 'pin': [{ type: core_1.Input },],
  547. 'debounce': [{ type: core_1.Input },],
  548. 'dualKnobs': [{ type: core_1.Input },],
  549. };
  550. return Range;
  551. }(base_input_1.BaseInput));
  552. exports.Range = Range;
  553. });
  554. //# sourceMappingURL=range.js.map