range_slider.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. /**
  2. * DevExtreme (ui/range_slider.js)
  3. * Version: 19.1.16
  4. * Build date: Tue Oct 18 2022
  5. *
  6. * Copyright (c) 2012 - 2022 Developer Express Inc. ALL RIGHTS RESERVED
  7. * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
  8. */
  9. "use strict";
  10. var $ = require("../core/renderer");
  11. var eventsEngine = require("../events/core/events_engine");
  12. var Slider = require("./slider");
  13. var SliderHandle = require("./slider/ui.slider_handle");
  14. var registerComponent = require("../core/component_registrator");
  15. var extend = require("../core/utils/extend").extend;
  16. var applyServerDecimalSeparator = require("../core/utils/common").applyServerDecimalSeparator;
  17. var eventUtils = require("../events/utils");
  18. var messageLocalization = require("../localization/message");
  19. var RANGE_SLIDER_CLASS = "dx-rangeslider";
  20. var RANGE_SLIDER_START_HANDLE_CLASS = RANGE_SLIDER_CLASS + "-start-handle";
  21. var RANGE_SLIDER_END_HANDLE_CLASS = RANGE_SLIDER_CLASS + "-end-handle";
  22. var RangeSlider = Slider.inherit({
  23. _supportedKeys: function() {
  24. var isRTL = this.option("rtlEnabled");
  25. var that = this;
  26. var _changeHandle = function(e, capturedHandle) {
  27. if (that.option("start") === that.option("end")) {
  28. that._capturedHandle = capturedHandle;
  29. e.target = that._capturedHandle;
  30. eventsEngine.trigger(that._capturedHandle, "focus")
  31. }
  32. };
  33. var _setHandleValue = function(e, step, sign) {
  34. var isStart = $(e.target).hasClass(RANGE_SLIDER_START_HANDLE_CLASS);
  35. var valueOption = isStart ? "start" : "end";
  36. var val = that.option(valueOption);
  37. step = that._valueStep(step);
  38. val += sign * (isRTL ? -step : step);
  39. that.option(valueOption, val)
  40. };
  41. var moveHandleRight = function(e, step) {
  42. _changeHandle(e, isRTL ? that._$handleStart : that._$handleEnd);
  43. _setHandleValue(e, step, 1)
  44. };
  45. var moveHandleLeft = function(e, step) {
  46. _changeHandle(e, isRTL ? that._$handleEnd : that._$handleStart);
  47. _setHandleValue(e, step, -1)
  48. };
  49. return extend(this.callBase(), {
  50. leftArrow: function(e) {
  51. e.preventDefault();
  52. e.stopPropagation();
  53. moveHandleLeft(e, this.option("step"))
  54. },
  55. rightArrow: function(e) {
  56. e.preventDefault();
  57. e.stopPropagation();
  58. moveHandleRight(e, this.option("step"))
  59. },
  60. pageUp: function(e) {
  61. e.preventDefault();
  62. e.stopPropagation();
  63. moveHandleRight(e, this.option("step") * this.option("keyStep"))
  64. },
  65. pageDown: function(e) {
  66. e.preventDefault();
  67. e.stopPropagation();
  68. moveHandleLeft(e, this.option("step") * this.option("keyStep"))
  69. },
  70. home: function(e) {
  71. e.preventDefault();
  72. e.stopPropagation();
  73. var isStart = $(e.target).hasClass(RANGE_SLIDER_START_HANDLE_CLASS);
  74. var valueOption = isStart ? "start" : "end";
  75. var startOption = isStart ? "min" : "start";
  76. var val = this.option(startOption);
  77. this.option(valueOption, val)
  78. },
  79. end: function(e) {
  80. e.preventDefault();
  81. e.stopPropagation();
  82. var isStart = $(e.target).hasClass(RANGE_SLIDER_START_HANDLE_CLASS);
  83. var valueOption = isStart ? "start" : "end";
  84. var endOption = isStart ? "end" : "max";
  85. var val = this.option(endOption);
  86. this.option(valueOption, val)
  87. }
  88. })
  89. },
  90. _getDefaultOptions: function() {
  91. return extend(this.callBase(), {
  92. start: 40,
  93. end: 60,
  94. value: [40, 60],
  95. startName: "",
  96. endName: ""
  97. })
  98. },
  99. _renderSubmitElement: function() {
  100. var $element = this.$element();
  101. this._$submitStartElement = $("<input>").attr("type", "hidden").attr("name", this.option("startName")).appendTo($element);
  102. this._$submitEndElement = $("<input>").attr("type", "hidden").attr("name", this.option("endName")).appendTo($element)
  103. },
  104. _initOptions: function(options) {
  105. this.callBase(options);
  106. var initialValue = this.initialOption("value");
  107. var value = this.option("value");
  108. if (value[0] === initialValue[0] && value[1] === initialValue[1]) {
  109. this.option("value", [this.option("start"), this.option("end")])
  110. } else {
  111. this.option({
  112. start: value[0],
  113. end: value[1]
  114. })
  115. }
  116. },
  117. _initMarkup: function() {
  118. this.$element().addClass(RANGE_SLIDER_CLASS);
  119. this.callBase()
  120. },
  121. _renderContentImpl: function() {
  122. this._callHandlerMethod("repaint");
  123. this.callBase()
  124. },
  125. _renderHandle: function() {
  126. this._$handleStart = this._renderHandleImpl(this.option("start"), this._$handleStart).addClass(RANGE_SLIDER_START_HANDLE_CLASS);
  127. this._$handleEnd = this._renderHandleImpl(this.option("end"), this._$handleEnd).addClass(RANGE_SLIDER_END_HANDLE_CLASS);
  128. this._updateHandleAriaLabels()
  129. },
  130. _startHandler: function(args) {
  131. var e = args.event;
  132. var $range = this._$range;
  133. var rangeWidth = $range.width();
  134. var eventOffsetX = eventUtils.eventData(e).x - this._$bar.offset().left;
  135. var startHandleX = $range.position().left;
  136. var endHandleX = $range.position().left + rangeWidth;
  137. var rtlEnabled = this.option("rtlEnabled");
  138. var startHandleIsClosest = (rtlEnabled ? -1 : 1) * ((startHandleX + endHandleX) / 2 - eventOffsetX) > 0;
  139. this._capturedHandle = startHandleIsClosest ? this._$handleStart : this._$handleEnd;
  140. this.callBase(args)
  141. },
  142. _updateHandleAriaLabels: function() {
  143. this.setAria("label", messageLocalization.getFormatter("dxRangeSlider-ariaFrom")(this.option("dxRangeSlider-ariaFrom")), this._$handleStart);
  144. this.setAria("label", messageLocalization.getFormatter("dxRangeSlider-ariaTill")(this.option("dxRangeSlider-ariaTill")), this._$handleEnd)
  145. },
  146. _activeHandle: function() {
  147. return this._capturedHandle
  148. },
  149. _updateHandlePosition: function(e) {
  150. var rtlEnabled = this.option("rtlEnabled");
  151. var offsetDirection = rtlEnabled ? -1 : 1;
  152. var max = this.option("max");
  153. var min = this.option("min");
  154. var newRatio = this._startOffset + offsetDirection * e.event.offset / this._swipePixelRatio();
  155. newRatio = newRatio.toPrecision(12);
  156. var newValue = newRatio * (max - min) + min;
  157. this._updateSelectedRangePosition(newRatio, newRatio);
  158. SliderHandle.getInstance(this._activeHandle()).fitTooltipPosition;
  159. this._changeValueOnSwipe(newRatio);
  160. var startValue = this.option("start");
  161. var endValue = this.option("end");
  162. var $nextHandle;
  163. if (startValue === endValue) {
  164. if (newValue < startValue) {
  165. $nextHandle = this._$handleStart
  166. } else {
  167. $nextHandle = this._$handleEnd
  168. }
  169. eventsEngine.trigger($nextHandle, "focus");
  170. if ($nextHandle && $nextHandle !== this._capturedHandle) {
  171. this._updateSelectedRangePosition((startValue - min) / (max - min), (endValue - min) / (max - min));
  172. this._toggleActiveState(this._activeHandle(), false);
  173. this._toggleActiveState($nextHandle, true);
  174. this._capturedHandle = $nextHandle
  175. }
  176. this._updateSelectedRangePosition(newRatio, newRatio);
  177. this._changeValueOnSwipe(newRatio)
  178. }
  179. },
  180. _updateSelectedRangePosition: function(leftRatio, rightRatio) {
  181. var rtlEnabled = this.option("rtlEnabled");
  182. var moveRight = this._capturedHandle === this._$handleStart && rtlEnabled || this._capturedHandle === this._$handleEnd && !rtlEnabled;
  183. var prop = moveRight ? "right" : "left";
  184. if (rtlEnabled ^ moveRight) {
  185. this._$range.css(prop, 100 - 100 * rightRatio + "%")
  186. } else {
  187. this._$range.css(prop, 100 * leftRatio + "%")
  188. }
  189. },
  190. _setValueOnSwipe: function(value) {
  191. var option = this._capturedHandle === this._$handleStart ? "start" : "end";
  192. var start = this.option("start");
  193. var end = this.option("end");
  194. var max = this.option("max");
  195. var min = this.option("min");
  196. start = Math.min(Math.max(start, min), max);
  197. end = Math.min(Math.max(end, min), max);
  198. if ("start" === option) {
  199. start = value > end ? end : value
  200. } else {
  201. end = value < start ? start : value
  202. }
  203. this.option("value", [start, end])
  204. },
  205. _renderValue: function() {
  206. var valStart = this.option("start");
  207. var valEnd = this.option("end");
  208. var min = this.option("min");
  209. var max = this.option("max");
  210. var rtlEnabled = this.option("rtlEnabled");
  211. valStart = Math.max(min, Math.min(valStart, max));
  212. valEnd = Math.max(valStart, Math.min(valEnd, max));
  213. this._setOptionSilent("start", valStart);
  214. this._setOptionSilent("end", valEnd);
  215. this._setOptionSilent("value", [valStart, valEnd]);
  216. this._$submitStartElement.val(applyServerDecimalSeparator(valStart));
  217. this._$submitEndElement.val(applyServerDecimalSeparator(valEnd));
  218. var ratio1 = max === min ? 0 : (valStart - min) / (max - min);
  219. var ratio2 = max === min ? 0 : (valEnd - min) / (max - min);
  220. var startOffset = parseFloat((100 * ratio1).toPrecision(12)) + "%";
  221. var endOffset = parseFloat((100 * (1 - ratio2)).toPrecision(12)) + "%";
  222. !this._needPreventAnimation && this._setRangeStyles({
  223. right: rtlEnabled ? startOffset : endOffset,
  224. left: rtlEnabled ? endOffset : startOffset
  225. });
  226. SliderHandle.getInstance(this._$handleStart).option("value", valStart);
  227. SliderHandle.getInstance(this._$handleEnd).option("value", valEnd)
  228. },
  229. _callHandlerMethod: function(name, args) {
  230. SliderHandle.getInstance(this._$handleStart)[name](args);
  231. SliderHandle.getInstance(this._$handleEnd)[name](args)
  232. },
  233. _setValueOption: function() {
  234. var start = this.option("start");
  235. var end = this.option("end");
  236. this.option("value", [start, end])
  237. },
  238. _optionChanged: function(args) {
  239. switch (args.name) {
  240. case "value":
  241. if (args.value[0] === args.previousValue[0] && args.value[1] === args.previousValue[1]) {
  242. break
  243. }
  244. this._setOptionSilent("start", args.value[0]);
  245. this._setOptionSilent("end", args.value[1]);
  246. this._renderValue();
  247. var start = this.option("start");
  248. var end = this.option("end");
  249. this._createActionByOption("onValueChanged", {
  250. excludeValidators: ["disabled", "readOnly"]
  251. })({
  252. start: start,
  253. end: end,
  254. value: [start, end],
  255. event: this._valueChangeEventInstance
  256. });
  257. this.validationRequest.fire({
  258. value: [start, end],
  259. editor: this
  260. });
  261. this._saveValueChangeEvent(void 0);
  262. break;
  263. case "start":
  264. case "end":
  265. this._setValueOption();
  266. break;
  267. case "startName":
  268. this._$submitStartElement.attr("name", args.value);
  269. break;
  270. case "endName":
  271. this._$submitEndElement.attr("name", args.value);
  272. break;
  273. case "name":
  274. break;
  275. default:
  276. this.callBase(args)
  277. }
  278. }
  279. });
  280. registerComponent("dxRangeSlider", RangeSlider);
  281. module.exports = RangeSlider;
  282. module.exports.default = module.exports;