ui.collection_widget.base.js 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875
  1. /**
  2. * DevExtreme (ui/collection/ui.collection_widget.base.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 _renderer = require("../../core/renderer");
  11. var _renderer2 = _interopRequireDefault(_renderer);
  12. var _events_engine = require("../../events/core/events_engine");
  13. var _events_engine2 = _interopRequireDefault(_events_engine);
  14. var _common = require("../../core/utils/common");
  15. var _common2 = _interopRequireDefault(_common);
  16. var _dom = require("../../core/utils/dom");
  17. var _dom_adapter = require("../../core/dom_adapter");
  18. var _dom_adapter2 = _interopRequireDefault(_dom_adapter);
  19. var _type = require("../../core/utils/type");
  20. var _deferred = require("../../core/utils/deferred");
  21. var _extend = require("../../core/utils/extend");
  22. var _array = require("../../core/utils/array");
  23. var _iterator = require("../../core/utils/iterator");
  24. var _iterator2 = _interopRequireDefault(_iterator);
  25. var _action = require("../../core/action");
  26. var _action2 = _interopRequireDefault(_action);
  27. var _guid = require("../../core/guid");
  28. var _guid2 = _interopRequireDefault(_guid);
  29. var _ui = require("../widget/ui.widget");
  30. var _ui2 = _interopRequireDefault(_ui);
  31. var _utils = require("../../events/utils");
  32. var _utils2 = _interopRequireDefault(_utils);
  33. var _pointer = require("../../events/pointer");
  34. var _pointer2 = _interopRequireDefault(_pointer);
  35. var _data_helper = require("../../data_helper");
  36. var _data_helper2 = _interopRequireDefault(_data_helper);
  37. var _item = require("./item");
  38. var _item2 = _interopRequireDefault(_item);
  39. var _selectors = require("../widget/selectors");
  40. var _selectors2 = _interopRequireDefault(_selectors);
  41. var _message = require("../../localization/message");
  42. var _message2 = _interopRequireDefault(_message);
  43. var _hold = require("../../events/hold");
  44. var _hold2 = _interopRequireDefault(_hold);
  45. var _data = require("../../core/utils/data");
  46. var _click = require("../../events/click");
  47. var _click2 = _interopRequireDefault(_click);
  48. var _contextmenu = require("../../events/contextmenu");
  49. var _contextmenu2 = _interopRequireDefault(_contextmenu);
  50. var _bindable_template = require("../widget/bindable_template");
  51. var _bindable_template2 = _interopRequireDefault(_bindable_template);
  52. function _interopRequireDefault(obj) {
  53. return obj && obj.__esModule ? obj : {
  54. "default": obj
  55. }
  56. }
  57. var COLLECTION_CLASS = "dx-collection";
  58. var ITEM_CLASS = "dx-item";
  59. var CONTENT_CLASS_POSTFIX = "-content";
  60. var ITEM_CONTENT_PLACEHOLDER_CLASS = "dx-item-content-placeholder";
  61. var ITEM_DATA_KEY = "dxItemData";
  62. var ITEM_INDEX_KEY = "dxItemIndex";
  63. var ITEM_TEMPLATE_ID_PREFIX = "tmpl-";
  64. var ITEMS_SELECTOR = "[data-options*='dxItem']";
  65. var SELECTED_ITEM_CLASS = "dx-item-selected";
  66. var ITEM_RESPONSE_WAIT_CLASS = "dx-item-response-wait";
  67. var EMPTY_COLLECTION = "dx-empty-collection";
  68. var TEMPLATE_WRAPPER_CLASS = "dx-template-wrapper";
  69. var ITEM_PATH_REGEX = /^([^.]+\[\d+\]\.)+([\w.]+)$/;
  70. var FOCUS_UP = "up";
  71. var FOCUS_DOWN = "down";
  72. var FOCUS_LEFT = "left";
  73. var FOCUS_RIGHT = "right";
  74. var FOCUS_PAGE_UP = "pageup";
  75. var FOCUS_PAGE_DOWN = "pagedown";
  76. var FOCUS_LAST = "last";
  77. var FOCUS_FIRST = "first";
  78. var CollectionWidget = _ui2.default.inherit({
  79. _activeStateUnit: "." + ITEM_CLASS,
  80. _supportedKeys: function() {
  81. var enter = function(e) {
  82. var $itemElement = (0, _renderer2.default)(this.option("focusedElement"));
  83. if (!$itemElement.length) {
  84. return
  85. }
  86. this._itemClickHandler((0, _extend.extend)({}, e, {
  87. target: $itemElement,
  88. currentTarget: $itemElement
  89. }))
  90. };
  91. var space = function(e) {
  92. e.preventDefault();
  93. enter.call(this, e)
  94. };
  95. var move = function(location, e) {
  96. e.preventDefault();
  97. e.stopPropagation();
  98. this._moveFocus(location, e)
  99. };
  100. return (0, _extend.extend)(this.callBase(), {
  101. space: space,
  102. enter: enter,
  103. leftArrow: move.bind(this, FOCUS_LEFT),
  104. rightArrow: move.bind(this, FOCUS_RIGHT),
  105. upArrow: move.bind(this, FOCUS_UP),
  106. downArrow: move.bind(this, FOCUS_DOWN),
  107. pageUp: move.bind(this, FOCUS_UP),
  108. pageDown: move.bind(this, FOCUS_DOWN),
  109. home: move.bind(this, FOCUS_FIRST),
  110. end: move.bind(this, FOCUS_LAST)
  111. })
  112. },
  113. _getDefaultOptions: function() {
  114. return (0, _extend.extend)(this.callBase(), {
  115. selectOnFocus: false,
  116. loopItemFocus: true,
  117. items: [],
  118. itemTemplate: "item",
  119. onItemRendered: null,
  120. onItemClick: null,
  121. onItemHold: null,
  122. itemHoldTimeout: 750,
  123. onItemContextMenu: null,
  124. onFocusedItemChanged: null,
  125. noDataText: _message2.default.format("dxCollectionWidget-noDataText"),
  126. dataSource: null,
  127. _itemAttributes: {},
  128. itemTemplateProperty: "template",
  129. focusOnSelectedItem: true,
  130. focusedElement: null,
  131. displayExpr: void 0,
  132. disabledExpr: function(data) {
  133. return data ? data.disabled : void 0
  134. },
  135. visibleExpr: function(data) {
  136. return data ? data.visible : void 0
  137. }
  138. })
  139. },
  140. _getAnonymousTemplateName: function() {
  141. return "item"
  142. },
  143. _init: function() {
  144. this._compileDisplayGetter();
  145. this.callBase();
  146. this._cleanRenderedItems();
  147. this._refreshDataSource()
  148. },
  149. _compileDisplayGetter: function() {
  150. var displayExpr = this.option("displayExpr");
  151. this._displayGetter = displayExpr ? (0, _data.compileGetter)(this.option("displayExpr")) : void 0
  152. },
  153. _initTemplates: function() {
  154. this._initItemsFromMarkup();
  155. this.callBase();
  156. this._initDefaultItemTemplate()
  157. },
  158. _initDefaultItemTemplate: function() {
  159. var fieldsMap = this._getFieldsMap();
  160. this._defaultTemplates.item = new _bindable_template2.default(function($container, data) {
  161. if ((0, _type.isPlainObject)(data)) {
  162. this._prepareDefaultItemTemplate(data, $container)
  163. } else {
  164. if (fieldsMap && (0, _type.isFunction)(fieldsMap.text)) {
  165. data = fieldsMap.text(data)
  166. }
  167. $container.text(String(_common2.default.ensureDefined(data, "")))
  168. }
  169. }.bind(this), this._getBindableFields(), this.option("integrationOptions.watchMethod"), fieldsMap)
  170. },
  171. _getBindableFields: function() {
  172. return ["text", "html"]
  173. },
  174. _getFieldsMap: function() {
  175. if (this._displayGetter) {
  176. return {
  177. text: this._displayGetter
  178. }
  179. }
  180. },
  181. _prepareDefaultItemTemplate: function(data, $container) {
  182. if ((0, _type.isDefined)(data.text)) {
  183. $container.text(data.text)
  184. }
  185. if ((0, _type.isDefined)(data.html)) {
  186. $container.html(data.html)
  187. }
  188. },
  189. _initItemsFromMarkup: function() {
  190. var _this = this;
  191. var $items = this.$element().contents().filter(ITEMS_SELECTOR);
  192. if (!$items.length || this.option("items").length) {
  193. return
  194. }
  195. var items = [].slice.call($items).map(function(item) {
  196. var $item = (0, _renderer2.default)(item);
  197. var result = (0, _dom.getElementOptions)(item).dxItem;
  198. var isTemplateRequired = $item.html().trim() && !result.template;
  199. if (isTemplateRequired) {
  200. result.template = _this._prepareItemTemplate($item)
  201. } else {
  202. $item.remove()
  203. }
  204. return result
  205. });
  206. this.option("items", items)
  207. },
  208. _prepareItemTemplate: function($item) {
  209. var templateId = ITEM_TEMPLATE_ID_PREFIX + new _guid2.default;
  210. var $template = $item.detach().clone().removeAttr("data-options").addClass(TEMPLATE_WRAPPER_CLASS);
  211. this._saveTemplate(templateId, $template);
  212. return templateId
  213. },
  214. _dataSourceOptions: function() {
  215. return {
  216. paginate: false
  217. }
  218. },
  219. _cleanRenderedItems: function() {
  220. this._renderedItemsCount = 0
  221. },
  222. _focusTarget: function() {
  223. return this.$element()
  224. },
  225. _focusInHandler: function(e) {
  226. this.callBase.apply(this, arguments);
  227. if ((0, _array.inArray)(e.target, this._focusTarget()) === -1) {
  228. return
  229. }
  230. var $focusedElement = (0, _renderer2.default)(this.option("focusedElement"));
  231. if ($focusedElement.length) {
  232. this._setFocusedItem($focusedElement)
  233. } else {
  234. var $activeItem = this._getActiveItem();
  235. if ($activeItem.length) {
  236. this.option("focusedElement", (0, _dom.getPublicElement)($activeItem))
  237. }
  238. }
  239. },
  240. _focusOutHandler: function() {
  241. this.callBase.apply(this, arguments);
  242. var $target = (0, _renderer2.default)(this.option("focusedElement"));
  243. this._updateFocusedItemState($target, false)
  244. },
  245. _getActiveItem: function(last) {
  246. var $focusedElement = (0, _renderer2.default)(this.option("focusedElement"));
  247. if ($focusedElement.length) {
  248. return $focusedElement
  249. }
  250. var index = this.option("focusOnSelectedItem") ? this.option("selectedIndex") : 0;
  251. var activeElements = this._getActiveElement();
  252. var lastIndex = activeElements.length - 1;
  253. if (index < 0) {
  254. index = last ? lastIndex : 0
  255. }
  256. return activeElements.eq(index)
  257. },
  258. _moveFocus: function(location) {
  259. var $items = this._getAvailableItems();
  260. var $newTarget;
  261. switch (location) {
  262. case FOCUS_PAGE_UP:
  263. case FOCUS_UP:
  264. $newTarget = this._prevItem($items);
  265. break;
  266. case FOCUS_PAGE_DOWN:
  267. case FOCUS_DOWN:
  268. $newTarget = this._nextItem($items);
  269. break;
  270. case FOCUS_RIGHT:
  271. $newTarget = this.option("rtlEnabled") ? this._prevItem($items) : this._nextItem($items);
  272. break;
  273. case FOCUS_LEFT:
  274. $newTarget = this.option("rtlEnabled") ? this._nextItem($items) : this._prevItem($items);
  275. break;
  276. case FOCUS_FIRST:
  277. $newTarget = $items.first();
  278. break;
  279. case FOCUS_LAST:
  280. $newTarget = $items.last();
  281. break;
  282. default:
  283. return false
  284. }
  285. if (0 !== $newTarget.length) {
  286. this.option("focusedElement", (0, _dom.getPublicElement)($newTarget))
  287. }
  288. },
  289. _getVisibleItems: function($itemElements) {
  290. $itemElements = $itemElements || this._itemElements();
  291. return $itemElements.filter(":visible")
  292. },
  293. _getAvailableItems: function($itemElements) {
  294. return this._getVisibleItems($itemElements).not(".dx-state-disabled")
  295. },
  296. _prevItem: function($items) {
  297. var $target = this._getActiveItem();
  298. var targetIndex = $items.index($target);
  299. var $last = $items.last();
  300. var $item = (0, _renderer2.default)($items[targetIndex - 1]);
  301. var loop = this.option("loopItemFocus");
  302. if (0 === $item.length && loop) {
  303. $item = $last
  304. }
  305. return $item
  306. },
  307. _nextItem: function($items) {
  308. var $target = this._getActiveItem(true);
  309. var targetIndex = $items.index($target);
  310. var $first = $items.first();
  311. var $item = (0, _renderer2.default)($items[targetIndex + 1]);
  312. var loop = this.option("loopItemFocus");
  313. if (0 === $item.length && loop) {
  314. $item = $first
  315. }
  316. return $item
  317. },
  318. _selectFocusedItem: function($target) {
  319. this.selectItem($target)
  320. },
  321. _updateFocusedItemState: function(target, isFocused, needCleanItemId) {
  322. var $target = (0, _renderer2.default)(target);
  323. if ($target.length) {
  324. this._refreshActiveDescendant();
  325. this._refreshItemId($target, needCleanItemId);
  326. this._toggleFocusClass(isFocused, $target)
  327. }
  328. },
  329. _refreshActiveDescendant: function($target) {
  330. this.setAria("activedescendant", (0, _type.isDefined)(this.option("focusedElement")) ? this.getFocusedItemId() : null, $target)
  331. },
  332. _refreshItemId: function($target, needCleanItemId) {
  333. if (!needCleanItemId && this.option("focusedElement")) {
  334. this.setAria("id", this.getFocusedItemId(), $target)
  335. } else {
  336. this.setAria("id", null, $target)
  337. }
  338. },
  339. _setFocusedItem: function($target) {
  340. if (!$target || !$target.length) {
  341. return
  342. }
  343. this._updateFocusedItemState($target, true);
  344. this.onFocusedItemChanged(this.getFocusedItemId());
  345. if (this.option("selectOnFocus")) {
  346. this._selectFocusedItem($target)
  347. }
  348. },
  349. _findItemElementByItem: function(item) {
  350. var result = (0, _renderer2.default)();
  351. var that = this;
  352. this.itemElements().each(function() {
  353. var $item = (0, _renderer2.default)(this);
  354. if ($item.data(that._itemDataKey()) === item) {
  355. result = $item;
  356. return false
  357. }
  358. });
  359. return result
  360. },
  361. _getIndexByItem: function(item) {
  362. return this.option("items").indexOf(item)
  363. },
  364. _itemOptionChanged: function(item, property, value, oldValue) {
  365. var $item = this._findItemElementByItem(item);
  366. if (!$item.length) {
  367. return
  368. }
  369. if (!this.constructor.ItemClass.getInstance($item).setDataField(property, value)) {
  370. this._refreshItem($item, item)
  371. }
  372. var isDisabling = "disabled" === property && value;
  373. if (isDisabling) {
  374. this._resetItemFocus($item)
  375. }
  376. },
  377. _resetItemFocus: function($item) {
  378. if ($item.is(this.option("focusedElement"))) {
  379. this.option("focusedElement", null)
  380. }
  381. },
  382. _refreshItem: function($item) {
  383. var itemData = this._getItemData($item);
  384. var index = $item.data(this._itemIndexKey());
  385. this._renderItem(this._renderedItemsCount + index, itemData, null, $item)
  386. },
  387. _optionChanged: function(args) {
  388. if ("items" === args.name) {
  389. var matches = args.fullName.match(ITEM_PATH_REGEX);
  390. if (matches && matches.length) {
  391. var property = matches[matches.length - 1];
  392. var itemPath = args.fullName.replace("." + property, "");
  393. var item = this.option(itemPath);
  394. this._itemOptionChanged(item, property, args.value, args.previousValue);
  395. return
  396. }
  397. }
  398. switch (args.name) {
  399. case "items":
  400. case "_itemAttributes":
  401. case "itemTemplateProperty":
  402. this._cleanRenderedItems();
  403. this._invalidate();
  404. break;
  405. case "dataSource":
  406. this._refreshDataSource();
  407. this._renderEmptyMessage();
  408. break;
  409. case "noDataText":
  410. this._renderEmptyMessage();
  411. break;
  412. case "itemTemplate":
  413. this._invalidate();
  414. break;
  415. case "onItemRendered":
  416. this._createItemRenderAction();
  417. break;
  418. case "onItemClick":
  419. break;
  420. case "onItemHold":
  421. case "itemHoldTimeout":
  422. this._attachHoldEvent();
  423. break;
  424. case "onItemContextMenu":
  425. this._attachContextMenuEvent();
  426. break;
  427. case "onFocusedItemChanged":
  428. this.onFocusedItemChanged = this._createActionByOption("onFocusedItemChanged");
  429. break;
  430. case "selectOnFocus":
  431. case "loopItemFocus":
  432. case "focusOnSelectedItem":
  433. break;
  434. case "focusedElement":
  435. this._updateFocusedItemState(args.previousValue, false, true);
  436. this._setFocusedItem((0, _renderer2.default)(args.value));
  437. break;
  438. case "displayExpr":
  439. this._compileDisplayGetter();
  440. this._initDefaultItemTemplate();
  441. this._invalidate();
  442. break;
  443. case "visibleExpr":
  444. case "disabledExpr":
  445. this._invalidate();
  446. break;
  447. default:
  448. this.callBase(args)
  449. }
  450. },
  451. _invalidate: function() {
  452. this.option("focusedElement", null);
  453. return this.callBase.apply(this, arguments)
  454. },
  455. _loadNextPage: function() {
  456. var dataSource = this._dataSource;
  457. this._expectNextPageLoading();
  458. dataSource.pageIndex(1 + dataSource.pageIndex());
  459. return dataSource.load()
  460. },
  461. _expectNextPageLoading: function() {
  462. this._startIndexForAppendedItems = 0
  463. },
  464. _expectLastItemLoading: function() {
  465. this._startIndexForAppendedItems = -1
  466. },
  467. _forgetNextPageLoading: function() {
  468. this._startIndexForAppendedItems = null
  469. },
  470. _dataSourceChangedHandler: function(newItems) {
  471. var items = this.option("items");
  472. if (this._initialized && items && this._shouldAppendItems()) {
  473. this._renderedItemsCount = items.length;
  474. if (!this._isLastPage() || this._startIndexForAppendedItems !== -1) {
  475. this.option().items = items.concat(newItems.slice(this._startIndexForAppendedItems))
  476. }
  477. this._forgetNextPageLoading();
  478. this._refreshContent()
  479. } else {
  480. this.option("items", newItems.slice())
  481. }
  482. },
  483. _refreshContent: function() {
  484. this._prepareContent();
  485. this._renderContent()
  486. },
  487. _dataSourceLoadErrorHandler: function() {
  488. this._forgetNextPageLoading();
  489. this.option("items", this.option("items"))
  490. },
  491. _shouldAppendItems: function() {
  492. return null != this._startIndexForAppendedItems && this._allowDynamicItemsAppend()
  493. },
  494. _allowDynamicItemsAppend: function() {
  495. return false
  496. },
  497. _clean: function() {
  498. this._cleanFocusState();
  499. this._cleanItemContainer()
  500. },
  501. _cleanItemContainer: function() {
  502. (0, _renderer2.default)(this._itemContainer()).empty()
  503. },
  504. _dispose: function() {
  505. this.callBase();
  506. clearTimeout(this._itemFocusTimeout)
  507. },
  508. _refresh: function() {
  509. this._cleanRenderedItems();
  510. this.callBase.apply(this, arguments)
  511. },
  512. _itemContainer: function() {
  513. return this.$element()
  514. },
  515. _itemClass: function() {
  516. return ITEM_CLASS
  517. },
  518. _itemContentClass: function() {
  519. return this._itemClass() + CONTENT_CLASS_POSTFIX
  520. },
  521. _selectedItemClass: function() {
  522. return SELECTED_ITEM_CLASS
  523. },
  524. _itemResponseWaitClass: function() {
  525. return ITEM_RESPONSE_WAIT_CLASS
  526. },
  527. _itemSelector: function() {
  528. return "." + this._itemClass()
  529. },
  530. _itemDataKey: function() {
  531. return ITEM_DATA_KEY
  532. },
  533. _itemIndexKey: function() {
  534. return ITEM_INDEX_KEY
  535. },
  536. _itemElements: function() {
  537. return this._itemContainer().find(this._itemSelector())
  538. },
  539. _initMarkup: function() {
  540. this.callBase();
  541. this.onFocusedItemChanged = this._createActionByOption("onFocusedItemChanged");
  542. this.$element().addClass(COLLECTION_CLASS);
  543. this._prepareContent()
  544. },
  545. _prepareContent: _common2.default.deferRenderer(function() {
  546. this._renderContentImpl()
  547. }),
  548. _renderContent: function() {
  549. this._fireContentReadyAction()
  550. },
  551. _render: function() {
  552. this.callBase();
  553. this._attachClickEvent();
  554. this._attachHoldEvent();
  555. this._attachContextMenuEvent()
  556. },
  557. _attachClickEvent: function() {
  558. var itemSelector = this._itemSelector();
  559. var clickEventNamespace = _utils2.default.addNamespace(_click2.default.name, this.NAME);
  560. var pointerDownEventNamespace = _utils2.default.addNamespace(_pointer2.default.down, this.NAME);
  561. var that = this;
  562. var pointerDownAction = new _action2.default(function(args) {
  563. var event = args.event;
  564. that._itemPointerDownHandler(event)
  565. });
  566. _events_engine2.default.off(this._itemContainer(), clickEventNamespace, itemSelector);
  567. _events_engine2.default.off(this._itemContainer(), pointerDownEventNamespace, itemSelector);
  568. _events_engine2.default.on(this._itemContainer(), clickEventNamespace, itemSelector, function(e) {
  569. this._itemClickHandler(e)
  570. }.bind(this));
  571. _events_engine2.default.on(this._itemContainer(), pointerDownEventNamespace, itemSelector, function(e) {
  572. pointerDownAction.execute({
  573. element: (0, _renderer2.default)(e.target),
  574. event: e
  575. })
  576. })
  577. },
  578. _itemClickHandler: function(e, args, config) {
  579. this._itemDXEventHandler(e, "onItemClick", args, config)
  580. },
  581. _itemPointerDownHandler: function(e) {
  582. if (!this.option("focusStateEnabled")) {
  583. return
  584. }
  585. this._itemFocusHandler = function() {
  586. clearTimeout(this._itemFocusTimeout);
  587. this._itemFocusHandler = null;
  588. if (e.isDefaultPrevented()) {
  589. return
  590. }
  591. var $target = (0, _renderer2.default)(e.target);
  592. var $closestItem = $target.closest(this._itemElements());
  593. var $closestFocusable = this._closestFocusable($target);
  594. if ($closestItem.length && $closestFocusable && (0, _array.inArray)($closestFocusable.get(0), this._focusTarget()) !== -1) {
  595. this.option("focusedElement", (0, _dom.getPublicElement)($closestItem))
  596. }
  597. }.bind(this);
  598. this._itemFocusTimeout = setTimeout(this._forcePointerDownFocus.bind(this))
  599. },
  600. _closestFocusable: function($target) {
  601. if ($target.is(_selectors2.default.focusable)) {
  602. return $target
  603. } else {
  604. $target = $target.parent();
  605. while ($target.length && !_dom_adapter2.default.isDocument($target.get(0))) {
  606. if ($target.is(_selectors2.default.focusable)) {
  607. return $target
  608. }
  609. $target = $target.parent()
  610. }
  611. }
  612. },
  613. _forcePointerDownFocus: function() {
  614. this._itemFocusHandler && this._itemFocusHandler()
  615. },
  616. _updateFocusState: function() {
  617. this.callBase.apply(this, arguments);
  618. this._forcePointerDownFocus()
  619. },
  620. _attachHoldEvent: function() {
  621. var $itemContainer = this._itemContainer();
  622. var itemSelector = this._itemSelector();
  623. var eventName = _utils2.default.addNamespace(_hold2.default.name, this.NAME);
  624. _events_engine2.default.off($itemContainer, eventName, itemSelector);
  625. _events_engine2.default.on($itemContainer, eventName, itemSelector, {
  626. timeout: this._getHoldTimeout()
  627. }, this._itemHoldHandler.bind(this))
  628. },
  629. _getHoldTimeout: function() {
  630. return this.option("itemHoldTimeout")
  631. },
  632. _shouldFireHoldEvent: function() {
  633. return this.hasActionSubscription("onItemHold")
  634. },
  635. _itemHoldHandler: function(e) {
  636. if (this._shouldFireHoldEvent()) {
  637. this._itemDXEventHandler(e, "onItemHold")
  638. } else {
  639. e.cancel = true
  640. }
  641. },
  642. _attachContextMenuEvent: function() {
  643. var $itemContainer = this._itemContainer();
  644. var itemSelector = this._itemSelector();
  645. var eventName = _utils2.default.addNamespace(_contextmenu2.default.name, this.NAME);
  646. _events_engine2.default.off($itemContainer, eventName, itemSelector);
  647. _events_engine2.default.on($itemContainer, eventName, itemSelector, this._itemContextMenuHandler.bind(this))
  648. },
  649. _shouldFireContextMenuEvent: function() {
  650. return this.hasActionSubscription("onItemContextMenu")
  651. },
  652. _itemContextMenuHandler: function(e) {
  653. if (this._shouldFireContextMenuEvent()) {
  654. this._itemDXEventHandler(e, "onItemContextMenu")
  655. } else {
  656. e.cancel = true
  657. }
  658. },
  659. _renderContentImpl: function() {
  660. var items = this.option("items") || [];
  661. if (this._renderedItemsCount) {
  662. this._renderItems(items.slice(this._renderedItemsCount))
  663. } else {
  664. this._renderItems(items)
  665. }
  666. },
  667. _renderItems: function(items) {
  668. if (items.length) {
  669. _iterator2.default.each(items, function(index, itemData) {
  670. this._renderItem(this._renderedItemsCount + index, itemData)
  671. }.bind(this))
  672. }
  673. this._renderEmptyMessage()
  674. },
  675. _renderItem: function(index, itemData, $container, $itemToReplace) {
  676. $container = $container || this._itemContainer();
  677. var $itemFrame = this._renderItemFrame(index, itemData, $container, $itemToReplace);
  678. this._setElementData($itemFrame, itemData, index);
  679. $itemFrame.attr(this.option("_itemAttributes"));
  680. this._attachItemClickEvent(itemData, $itemFrame);
  681. var $itemContent = this._getItemContent($itemFrame);
  682. var renderContentPromise = this._renderItemContent({
  683. index: index,
  684. itemData: itemData,
  685. container: (0, _dom.getPublicElement)($itemContent),
  686. contentClass: this._itemContentClass(),
  687. defaultTemplateName: this.option("itemTemplate")
  688. });
  689. var that = this;
  690. (0, _deferred.when)(renderContentPromise).done(function($itemContent) {
  691. that._postprocessRenderItem({
  692. itemElement: $itemFrame,
  693. itemContent: $itemContent,
  694. itemData: itemData,
  695. itemIndex: index
  696. });
  697. that._executeItemRenderAction(index, itemData, (0, _dom.getPublicElement)($itemFrame))
  698. });
  699. return $itemFrame
  700. },
  701. _getItemContent: function($itemFrame) {
  702. var $itemContent = $itemFrame.find("." + ITEM_CONTENT_PLACEHOLDER_CLASS);
  703. $itemContent.removeClass(ITEM_CONTENT_PLACEHOLDER_CLASS);
  704. return $itemContent
  705. },
  706. _attachItemClickEvent: function(itemData, $itemElement) {
  707. if (!itemData || !itemData.onClick) {
  708. return
  709. }
  710. _events_engine2.default.on($itemElement, _click2.default.name, function(e) {
  711. this._itemEventHandlerByHandler($itemElement, itemData.onClick, {
  712. event: e
  713. })
  714. }.bind(this))
  715. },
  716. _renderItemContent: function(args) {
  717. var itemTemplateName = this._getItemTemplateName(args);
  718. var itemTemplate = this._getTemplate(itemTemplateName);
  719. this._addItemContentClasses(args);
  720. var $templateResult = (0, _renderer2.default)(this._createItemByTemplate(itemTemplate, args));
  721. if (!$templateResult.hasClass(TEMPLATE_WRAPPER_CLASS)) {
  722. return args.container
  723. }
  724. return this._renderItemContentByNode(args, $templateResult)
  725. },
  726. _renderItemContentByNode: function(args, $node) {
  727. (0, _renderer2.default)(args.container).replaceWith($node);
  728. args.container = (0, _dom.getPublicElement)($node);
  729. this._addItemContentClasses(args);
  730. return $node
  731. },
  732. _addItemContentClasses: function(args) {
  733. var classes = [ITEM_CLASS + CONTENT_CLASS_POSTFIX, args.contentClass];
  734. (0, _renderer2.default)(args.container).addClass(classes.join(" "))
  735. },
  736. _appendItemToContainer: function($container, $itemFrame, index) {
  737. $itemFrame.appendTo($container)
  738. },
  739. _renderItemFrame: function(index, itemData, $container, $itemToReplace) {
  740. var $itemFrame = (0, _renderer2.default)("<div>");
  741. new this.constructor.ItemClass($itemFrame, this._itemOptions(), itemData || {});
  742. if ($itemToReplace && $itemToReplace.length) {
  743. $itemToReplace.replaceWith($itemFrame)
  744. } else {
  745. this._appendItemToContainer.call(this, $container, $itemFrame, index)
  746. }
  747. return $itemFrame
  748. },
  749. _itemOptions: function() {
  750. var that = this;
  751. return {
  752. watchMethod: function() {
  753. return that.option("integrationOptions.watchMethod")
  754. },
  755. owner: that,
  756. fieldGetter: function(field) {
  757. var expr = that.option(field + "Expr");
  758. var getter = (0, _data.compileGetter)(expr);
  759. return getter
  760. }
  761. }
  762. },
  763. _postprocessRenderItem: _common2.default.noop,
  764. _executeItemRenderAction: function(index, itemData, itemElement) {
  765. this._getItemRenderAction()({
  766. itemElement: itemElement,
  767. itemIndex: index,
  768. itemData: itemData
  769. })
  770. },
  771. _setElementData: function(element, data, index) {
  772. element.addClass([ITEM_CLASS, this._itemClass()].join(" ")).data(this._itemDataKey(), data).data(this._itemIndexKey(), index)
  773. },
  774. _createItemRenderAction: function() {
  775. return this._itemRenderAction = this._createActionByOption("onItemRendered", {
  776. element: this.element(),
  777. excludeValidators: ["disabled", "readOnly"],
  778. category: "rendering"
  779. })
  780. },
  781. _getItemRenderAction: function() {
  782. return this._itemRenderAction || this._createItemRenderAction()
  783. },
  784. _getItemTemplateName: function(args) {
  785. var data = args.itemData;
  786. var templateProperty = args.templateProperty || this.option("itemTemplateProperty");
  787. var template = data && data[templateProperty];
  788. return template || args.defaultTemplateName
  789. },
  790. _createItemByTemplate: function(itemTemplate, renderArgs) {
  791. return itemTemplate.render({
  792. model: renderArgs.itemData,
  793. container: renderArgs.container,
  794. index: renderArgs.index
  795. })
  796. },
  797. _emptyMessageContainer: function() {
  798. return this._itemContainer()
  799. },
  800. _renderEmptyMessage: function(items) {
  801. items = items || this.option("items");
  802. var noDataText = this.option("noDataText");
  803. var hideNoData = !noDataText || items && items.length || this._isDataSourceLoading();
  804. if (hideNoData && this._$noData) {
  805. this._$noData.remove();
  806. this._$noData = null;
  807. this.setAria("label", void 0)
  808. }
  809. if (!hideNoData) {
  810. this._$noData = this._$noData || (0, _renderer2.default)("<div>").addClass("dx-empty-message");
  811. this._$noData.appendTo(this._emptyMessageContainer()).html(noDataText);
  812. this.setAria("label", noDataText)
  813. }
  814. this.$element().toggleClass(EMPTY_COLLECTION, !hideNoData)
  815. },
  816. _itemDXEventHandler: function(dxEvent, handlerOptionName, actionArgs, actionConfig) {
  817. this._itemEventHandler(dxEvent.target, handlerOptionName, (0, _extend.extend)(actionArgs, {
  818. event: dxEvent
  819. }), actionConfig)
  820. },
  821. _itemEventHandler: function(initiator, handlerOptionName, actionArgs, actionConfig) {
  822. var action = this._createActionByOption(handlerOptionName, (0, _extend.extend)({
  823. validatingTargetName: "itemElement"
  824. }, actionConfig));
  825. return this._itemEventHandlerImpl(initiator, action, actionArgs)
  826. },
  827. _itemEventHandlerByHandler: function(initiator, handler, actionArgs, actionConfig) {
  828. var action = this._createAction(handler, (0, _extend.extend)({
  829. validatingTargetName: "itemElement"
  830. }, actionConfig));
  831. return this._itemEventHandlerImpl(initiator, action, actionArgs)
  832. },
  833. _itemEventHandlerImpl: function(initiator, action, actionArgs) {
  834. var $itemElement = this._closestItemElement((0, _renderer2.default)(initiator));
  835. var args = (0, _extend.extend)({}, actionArgs);
  836. return action((0, _extend.extend)(actionArgs, this._extendActionArgs($itemElement), args))
  837. },
  838. _extendActionArgs: function($itemElement) {
  839. return {
  840. itemElement: (0, _dom.getPublicElement)($itemElement),
  841. itemIndex: this._itemElements().index($itemElement),
  842. itemData: this._getItemData($itemElement)
  843. }
  844. },
  845. _closestItemElement: function($element) {
  846. return (0, _renderer2.default)($element).closest(this._itemSelector())
  847. },
  848. _getItemData: function(itemElement) {
  849. return (0, _renderer2.default)(itemElement).data(this._itemDataKey())
  850. },
  851. _getSummaryItemsWidth: function(items, includeMargin) {
  852. var result = 0;
  853. if (items) {
  854. _iterator2.default.each(items, function(_, item) {
  855. result += (0, _renderer2.default)(item).outerWidth(includeMargin || false)
  856. })
  857. }
  858. return result
  859. },
  860. getFocusedItemId: function() {
  861. if (!this._focusedItemId) {
  862. this._focusedItemId = "dx-" + new _guid2.default
  863. }
  864. return this._focusedItemId
  865. },
  866. itemElements: function() {
  867. return this._itemElements()
  868. },
  869. itemsContainer: function() {
  870. return this._itemContainer()
  871. }
  872. }).include(_data_helper2.default);
  873. CollectionWidget.ItemClass = _item2.default;
  874. module.exports = CollectionWidget;