ui.date_view_roller.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. /**
  2. * DevExtreme (ui/date_box/ui.date_view_roller.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 registerComponent = require("../../core/component_registrator");
  13. var extend = require("../../core/utils/extend").extend;
  14. var each = require("../../core/utils/iterator").each;
  15. var eventUtils = require("../../events/utils");
  16. var clickEvent = require("../../events/click");
  17. var Scrollable = require("../scroll_view/ui.scrollable");
  18. var fx = require("../../animation/fx");
  19. var translator = require("../../animation/translator");
  20. var DATEVIEW_ROLLER_CLASS = "dx-dateviewroller";
  21. var DATEVIEW_ROLLER_ACTIVE_CLASS = "dx-state-active";
  22. var DATEVIEW_ROLLER_CURRENT_CLASS = "dx-dateviewroller-current";
  23. var DATEVIEW_ROLLER_ITEM_CLASS = "dx-dateview-item";
  24. var DATEVIEW_ROLLER_ITEM_SELECTED_CLASS = "dx-dateview-item-selected";
  25. var DATEVIEW_ROLLER_ITEM_SELECTED_FRAME_CLASS = "dx-dateview-item-selected-frame";
  26. var DATEVIEW_ROLLER_ITEM_SELECTED_BORDER_CLASS = "dx-dateview-item-selected-border";
  27. var DateViewRoller = Scrollable.inherit({
  28. _getDefaultOptions: function() {
  29. return extend(this.callBase(), {
  30. showScrollbar: false,
  31. useNative: false,
  32. selectedIndex: 0,
  33. bounceEnabled: false,
  34. items: [],
  35. showOnClick: false,
  36. onClick: null,
  37. onSelectedIndexChanged: null
  38. })
  39. },
  40. _defaultOptionsRules: function() {
  41. return this.callBase().concat([{
  42. device: function(_device) {
  43. return "win" === _device.platform && _device.version && 8 === _device.version[0]
  44. },
  45. options: {
  46. showOnClick: true
  47. }
  48. }, {
  49. device: {
  50. platform: "generic"
  51. },
  52. options: {
  53. scrollByContent: true
  54. }
  55. }])
  56. },
  57. _init: function() {
  58. this.callBase();
  59. this._renderSelectedItemFrame()
  60. },
  61. _render: function() {
  62. this.callBase();
  63. this.$element().addClass(DATEVIEW_ROLLER_CLASS);
  64. this._renderContainerClick();
  65. this._renderItems();
  66. this._renderSelectedValue();
  67. this._renderItemsClick();
  68. this._wrapAction("_endAction", this._endActionHandler.bind(this));
  69. this._renderSelectedIndexChanged()
  70. },
  71. _renderSelectedIndexChanged: function() {
  72. this._selectedIndexChanged = this._createActionByOption("onSelectedIndexChanged")
  73. },
  74. _renderContainerClick: function() {
  75. if (!this.option("showOnClick")) {
  76. return
  77. }
  78. var eventName = eventUtils.addNamespace(clickEvent.name, this.NAME);
  79. var clickAction = this._createActionByOption("onClick");
  80. eventsEngine.off(this._$container, eventName);
  81. eventsEngine.on(this._$container, eventName, function(e) {
  82. clickAction({
  83. event: e
  84. })
  85. })
  86. },
  87. _wrapAction: function(actionName, callback) {
  88. var strategy = this._strategy;
  89. var originalAction = strategy[actionName];
  90. strategy[actionName] = function() {
  91. callback.apply(this, arguments);
  92. return originalAction.apply(this, arguments)
  93. }
  94. },
  95. _renderItems: function() {
  96. var items = this.option("items") || [];
  97. var $items = $();
  98. this._$content.empty();
  99. items.forEach(function(item) {
  100. $items = $items.add($("<div>").addClass(DATEVIEW_ROLLER_ITEM_CLASS).append(item))
  101. });
  102. this._$content.append($items);
  103. this._$items = $items;
  104. this.update()
  105. },
  106. _renderSelectedItemFrame: function() {
  107. $("<div>").addClass(DATEVIEW_ROLLER_ITEM_SELECTED_FRAME_CLASS).append($("<div>").addClass(DATEVIEW_ROLLER_ITEM_SELECTED_BORDER_CLASS)).appendTo(this._$container)
  108. },
  109. _renderSelectedValue: function(selectedIndex) {
  110. var index = this._fitIndex(selectedIndex || this.option("selectedIndex"));
  111. this._moveTo({
  112. top: this._getItemPosition(index)
  113. });
  114. this._renderActiveStateItem()
  115. },
  116. _fitIndex: function(index) {
  117. var items = this.option("items") || [];
  118. var itemCount = items.length;
  119. if (index >= itemCount) {
  120. return itemCount - 1
  121. }
  122. if (index < 0) {
  123. return 0
  124. }
  125. return index
  126. },
  127. _getItemPosition: function(index) {
  128. return Math.round(this._itemHeight() * index)
  129. },
  130. _renderItemsClick: function() {
  131. var itemSelector = this._getItemSelector();
  132. var eventName = eventUtils.addNamespace(clickEvent.name, this.NAME);
  133. eventsEngine.off(this.$element(), eventName, itemSelector);
  134. eventsEngine.on(this.$element(), eventName, itemSelector, this._itemClickHandler.bind(this))
  135. },
  136. _getItemSelector: function() {
  137. return "." + DATEVIEW_ROLLER_ITEM_CLASS
  138. },
  139. _itemClickHandler: function(e) {
  140. this.option("selectedIndex", this._itemElementIndex(e.currentTarget))
  141. },
  142. _itemElementIndex: function(itemElement) {
  143. return this._itemElements().index(itemElement)
  144. },
  145. _itemElements: function() {
  146. return this.$element().find(this._getItemSelector())
  147. },
  148. _renderActiveStateItem: function() {
  149. var selectedIndex = this.option("selectedIndex");
  150. each(this._$items, function(index) {
  151. $(this).toggleClass(DATEVIEW_ROLLER_ITEM_SELECTED_CLASS, selectedIndex === index)
  152. })
  153. },
  154. _moveTo: function(targetLocation) {
  155. targetLocation = this._normalizeLocation(targetLocation);
  156. var location = this._location();
  157. var delta = {
  158. x: -(location.left - targetLocation.left),
  159. y: -(location.top - targetLocation.top)
  160. };
  161. if (this._isVisible() && (delta.x || delta.y)) {
  162. this._strategy._prepareDirections(true);
  163. if (this._animation) {
  164. var that = this;
  165. fx.stop(this._$content);
  166. fx.animate(this._$content, {
  167. duration: 200,
  168. type: "slide",
  169. to: {
  170. top: Math.floor(delta.y)
  171. },
  172. complete: function() {
  173. translator.resetPosition(that._$content);
  174. that._strategy.handleMove({
  175. delta: delta
  176. })
  177. }
  178. });
  179. delete this._animation
  180. } else {
  181. this._strategy.handleMove({
  182. delta: delta
  183. })
  184. }
  185. }
  186. },
  187. _validate: function(e) {
  188. return this._strategy.validate(e)
  189. },
  190. _endActionHandler: function() {
  191. var currentSelectedIndex = this.option("selectedIndex");
  192. var ratio = -this._location().top / this._itemHeight();
  193. var newSelectedIndex = Math.round(ratio);
  194. this._animation = true;
  195. if (newSelectedIndex === currentSelectedIndex) {
  196. this._renderSelectedValue(newSelectedIndex)
  197. } else {
  198. this.option("selectedIndex", newSelectedIndex)
  199. }
  200. },
  201. _itemHeight: function() {
  202. var $item = this._$items.first();
  203. return $item.get(0) && $item.get(0).getBoundingClientRect().height || 0
  204. },
  205. _toggleActive: function(state) {
  206. this.$element().toggleClass(DATEVIEW_ROLLER_ACTIVE_CLASS, state)
  207. },
  208. _isVisible: function() {
  209. return this._$container.is(":visible")
  210. },
  211. _fireSelectedIndexChanged: function(value, previousValue) {
  212. this._selectedIndexChanged({
  213. value: value,
  214. previousValue: previousValue,
  215. event: void 0
  216. })
  217. },
  218. _visibilityChanged: function(visible) {
  219. this.callBase(visible);
  220. if (visible) {
  221. this._renderSelectedValue(this.option("selectedIndex"))
  222. }
  223. this.toggleActiveState(false)
  224. },
  225. toggleActiveState: function(state) {
  226. this.$element().toggleClass(DATEVIEW_ROLLER_CURRENT_CLASS, state)
  227. },
  228. _refreshSelectedIndex: function() {
  229. var selectedIndex = this.option("selectedIndex");
  230. var fitIndex = this._fitIndex(selectedIndex);
  231. fitIndex === selectedIndex ? this._renderActiveStateItem() : this.option("selectedIndex", fitIndex)
  232. },
  233. _optionChanged: function(args) {
  234. switch (args.name) {
  235. case "selectedIndex":
  236. this._fireSelectedIndexChanged(args.value, args.previousValue);
  237. this._renderSelectedValue(args.value);
  238. break;
  239. case "items":
  240. this._renderItems();
  241. this._refreshSelectedIndex();
  242. break;
  243. case "onClick":
  244. case "showOnClick":
  245. this._renderContainerClick();
  246. break;
  247. case "onSelectedIndexChanged":
  248. this._renderSelectedIndexChanged();
  249. break;
  250. default:
  251. this.callBase(args)
  252. }
  253. }
  254. });
  255. registerComponent("dxDateViewRoller", DateViewRoller);
  256. module.exports = DateViewRoller;