UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

898 lines (747 loc) • 28.9 kB
"use strict"; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var $ = require("../../core/renderer"), window = require("../../core/utils/window").getWindow(), eventsEngine = require("../../events/core/events_engine"), Guid = require("../../core/guid"), registerComponent = require("../../core/component_registrator"), commonUtils = require("../../core/utils/common"), typeUtils = require("../../core/utils/type"), extend = require("../../core/utils/extend").extend, inArray = require("../../core/utils/array").inArray, DropDownEditor = require("./ui.drop_down_editor"), List = require("../list"), errors = require("../widget/ui.errors"), eventUtils = require("../../events/utils"), devices = require("../../core/devices"), DataExpressionMixin = require("../editor/ui.data_expression"), messageLocalization = require("../../localization/message"), themes = require("../themes"), ChildDefaultTemplate = require("../widget/child_default_template"), Deferred = require("../../core/utils/deferred").Deferred; var LIST_ITEM_SELECTOR = ".dx-list-item", LIST_ITEM_DATA_KEY = "dxListItemData", DROPDOWNLIST_POPUP_WRAPPER_CLASS = "dx-dropdownlist-popup-wrapper", SKIP_GESTURE_EVENT_CLASS = "dx-skip-gesture-event", SEARCH_MODES = ["startswith", "contains", "endwith", "notcontains"]; /** * @name dxDropDownList * @publicName dxDropDownList * @inherits DataExpressionMixin, dxDropDownEditor * @module ui/drop_down_editor/ui.drop_down_list * @export default * @hidden */ var DropDownList = DropDownEditor.inherit({ _supportedKeys: function _supportedKeys() { var parent = this.callBase(); return extend({}, parent, { tab: function tab(e) { if (this.option("opened") && this.option("applyValueMode") === "instantly") { this._saveValueChangeEvent(e); var $focusedItem = $(this._list.option("focusedElement")); $focusedItem.length && this._setSelectedElement($focusedItem); } parent.tab.apply(this, arguments); }, space: commonUtils.noop, home: commonUtils.noop, end: commonUtils.noop }); }, _setSelectedElement: function _setSelectedElement($element) { var value = this._valueGetter(this._list._getItemData($element)); this._setValue(value); }, _setValue: function _setValue(value) { this.option("value", value); }, _getDefaultOptions: function _getDefaultOptions() { return extend(this.callBase(), extend(DataExpressionMixin._dataExpressionDefaultOptions(), { /** * @name dxDropDownListOptions.displayValue * @publicName displayValue * @type string * @readonly * @default undefined * @ref */ displayValue: undefined, /** * @name dxDropDownListOptions.searchEnabled * @publicName searchEnabled * @type boolean * @default false */ searchEnabled: false, /** * @name dxDropDownListOptions.searchMode * @publicName searchMode * @type Enums.DropDownSearchMode * @default "contains" */ searchMode: "contains", /** * @name dxDropDownListOptions.searchTimeout * @publicName searchTimeout * @type number * @default 500 */ searchTimeout: 500, /** * @name dxDropDownListOptions.minSearchLength * @publicName minSearchLength * @type number * @default 0 */ minSearchLength: 0, /** * @name dxDropDownListOptions.searchExpr * @publicName searchExpr * @type getter|Array<getter> * @default null */ searchExpr: null, /** * @name dxDropDownListOptions.valueChangeEvent * @publicName valueChangeEvent * @type string * @default "input change keyup" */ valueChangeEvent: "input change keyup", /** * @name dxDropDownListOptions.selectedItem * @publicName selectedItem * @type any * @readonly * @default null * @ref */ selectedItem: null, /** * @name dxDropDownListOptions.noDataText * @publicName noDataText * @type string * @default "No data to display" */ noDataText: messageLocalization.format("dxCollectionWidget-noDataText"), /** * @name dxDropDownListOptions.onSelectionChanged * @publicName onSelectionChanged * @extends Action * @type function(e) * @type_function_param1 e:object * @type_function_param1_field4 selectedItem:object * @action */ onSelectionChanged: null, /** * @name dxDropDownListOptions.onItemClick * @publicName onItemClick * @extends Action * @type function(e) * @type_function_param1 e:object * @type_function_param1_field4 itemData:object * @type_function_param1_field5 itemElement:object * @type_function_param1_field6 itemIndex:number | object * @action */ onItemClick: commonUtils.noop, /** * @name dxDropDownListOptions.showDataBeforeSearch * @publicName showDataBeforeSearch * @type boolean * @default false */ showDataBeforeSearch: false, /** * @name dxDropDownListOptions.grouped * @publicName grouped * @type boolean * @default false */ grouped: false, /** * @name dxDropDownListOptions.groupTemplate * @publicName groupTemplate * @type template|function * @default "group" * @type_function_param1 itemData:object * @type_function_param2 itemIndex:number * @type_function_param3 itemElement:dxElement * @type_function_return string|Node|jQuery */ groupTemplate: "group", popupPosition: { my: "left top", at: "left bottom", offset: { h: 0, v: 0 }, collision: "flip" }, /** * @name dxDropDownListOptions.onValueChanged * @publicName onValueChanged * @extends Action * @type function(e) * @type_function_param1 e:object * @type_function_param1_field4 value:object * @type_function_param1_field5 previousValue:object * @type_function_param1_field6 jQueryEvent:jQuery.Event:deprecated(event) * @type_function_param1_field7 event:event * @action */ /** * @name dxDropDownListOptions.fieldTemplate * @publicName fieldTemplate * @hidden * @inheritdoc */ /** * @name dxDropDownListOptions.fieldRender * @publicName fieldRender * @hidden * @inheritdoc */ /** * @name dxDropDownListOptions.contentTemplate * @publicName contentTemplate * @hidden * @inheritdoc */ /** * @name dxDropDownListOptions.contentRender * @publicName contentRender * @hidden * @inheritdoc */ /** * @name dxDropDownListOptions.applyValueMode * @publicName applyValueMode * @hidden * @inheritdoc */ popupWidthExtension: 0 })); }, _defaultOptionsRules: function _defaultOptionsRules() { return this.callBase().concat([{ device: function device(_device) { return _device.platform === "win" && _device.version && _device.version[0] === 8; }, options: { popupPosition: { offset: { v: -6 } } } }, { device: function device() { return (/android5/.test(themes.current()) ); }, options: { popupWidthExtension: 32 } }, { device: { platform: "ios" }, options: { popupPosition: { offset: { v: -1 } } } }, { device: { platform: "generic" }, options: { buttonsLocation: "bottom center" } }]); }, _setOptionsByReference: function _setOptionsByReference() { this.callBase(); extend(this._optionsByReference, { /** * @name dxDropDownListOptions.value * @publicName value * @ref * @inheritdoc */ value: true, selectedItem: true, displayValue: true }); }, _init: function _init() { this.callBase(); this._initDataExpressions(); this._initActions(); this._setListDataSource(); this._validateSearchMode(); this._clearSelectedItem(); }, _initActions: function _initActions() { this._initContentReadyAction(); this._initSelectionChangedAction(); this._initItemClickAction(); }, _initContentReadyAction: function _initContentReadyAction() { this._contentReadyAction = this._createActionByOption("onContentReady", { excludeValidators: ["disabled", "readOnly"] }); }, _initSelectionChangedAction: function _initSelectionChangedAction() { this._selectionChangedAction = this._createActionByOption("onSelectionChanged", { excludeValidators: ["disabled", "readOnly"] }); }, _initItemClickAction: function _initItemClickAction() { this._itemClickAction = this._createActionByOption("onItemClick"); }, _initTemplates: function _initTemplates() { this.callBase(); this._defaultTemplates["item"] = new ChildDefaultTemplate("item", this); }, _renderField: function _renderField() { this.callBase(); eventsEngine.on(this._input(), "input", this._setFocusPolicy.bind(this)); }, _saveFocusOnWidget: function _saveFocusOnWidget(e) { if (this._list && this._list.initialOption("focusStateEnabled")) { this._focusInput(); } }, _createPopup: function _createPopup() { this.callBase(); this._popup._wrapper().addClass(this._popupWrapperClass()); var $popupContent = this._popup.$content(); eventsEngine.off($popupContent, "mouseup"); eventsEngine.on($popupContent, "mouseup", this._saveFocusOnWidget.bind(this)); }, _popupWrapperClass: function _popupWrapperClass() { return DROPDOWNLIST_POPUP_WRAPPER_CLASS; }, _renderInputValue: function _renderInputValue() { var value = this._getCurrentValue(); return this._loadInputValue(value, this._setSelectedItem.bind(this)).always(this.callBase.bind(this, value)); }, _loadInputValue: function _loadInputValue(value, callback) { return this._loadItem(value).always(callback); }, _loadItem: function _loadItem(value, cache) { var plainItems, selectedItem; if (cache && (typeof value === "undefined" ? "undefined" : _typeof(value)) !== "object") { if (!cache.itemByValue) { cache.itemByValue = {}; plainItems = this._getPlainItems(); plainItems.forEach(function (item) { cache.itemByValue[this._valueGetter(item)] = item; }, this); } selectedItem = cache.itemByValue[value]; } if (!selectedItem) { plainItems = this._getPlainItems(); selectedItem = commonUtils.grep(plainItems, function (item) { return this._isValueEquals(this._valueGetter(item), value); }.bind(this))[0]; } return selectedItem !== undefined ? new Deferred().resolve(selectedItem).promise() : this._loadValue(value); }, _getPlainItems: function _getPlainItems(items) { var plainItems = []; items = items || this.option("items") || []; for (var i = 0; i < items.length; i++) { if (items[i] && items[i].items) { plainItems = plainItems.concat(items[i].items); } else { plainItems.push(items[i]); } } return plainItems; }, _setSelectedItem: function _setSelectedItem(item) { var displayValue = this._displayValue(item); this.option("selectedItem", commonUtils.ensureDefined(item, null)); this.option("displayValue", displayValue); }, _displayValue: function _displayValue(item) { return this._displayGetter(item); }, _refreshSelected: function _refreshSelected() { var cache = {}; this._listItemElements().each(function (_, itemElement) { var $itemElement = $(itemElement); var itemValue = this._valueGetter($itemElement.data(LIST_ITEM_DATA_KEY)); var isItemSelected = this._isSelectedValue(itemValue, cache); if (isItemSelected) { this._list.selectItem($itemElement); } else { this._list.unselectItem($itemElement); } }.bind(this)); }, _popupShownHandler: function _popupShownHandler() { this.callBase(); this._setFocusPolicy(); }, _setFocusPolicy: function _setFocusPolicy() { if (!this.option("focusStateEnabled") || !this._list) { return; } this._list.option("focusedElement", null); }, _isSelectedValue: function _isSelectedValue(value) { return this._isValueEquals(value, this.option("value")); }, _validateSearchMode: function _validateSearchMode() { var searchMode = this.option("searchMode"), normalizedSearchMode = searchMode.toLowerCase(); if (inArray(normalizedSearchMode, SEARCH_MODES) < 0) { throw errors.Error("E1019", searchMode); } }, _clearSelectedItem: function _clearSelectedItem() { this.option("selectedItem", null); }, _processDataSourceChanging: function _processDataSourceChanging() { this._setListDataSource(); this._renderInputValue().fail(function () { if (this._isCustomValueAllowed()) { return; } this._clearSelectedItem(); }.bind(this)); }, _isCustomValueAllowed: function _isCustomValueAllowed() { return this.option("displayCustomValue"); }, reset: function reset() { this.callBase(); this._clearFilter(); this._clearSelectedItem(); }, _listItemElements: function _listItemElements() { return this._$list ? this._$list.find(LIST_ITEM_SELECTOR) : $(); }, _popupConfig: function _popupConfig() { var that = this; return extend(this.callBase(), { templatesRenderAsynchronously: false, width: this.option("width"), onShowing: function onShowing() { that.$element().addClass(SKIP_GESTURE_EVENT_CLASS); }, onHidden: function onHidden() { that.$element().removeClass(SKIP_GESTURE_EVENT_CLASS); }, height: "auto", maxHeight: this._getMaxHeight.bind(this) }); }, _renderPopupContent: function _renderPopupContent() { this._renderList(); }, _attachChildKeyboardEvents: function _attachChildKeyboardEvents() { this._childKeyboardProcessor = this._keyboardProcessor.attachChildProcessor(); this._setListOption("_keyboardProcessor", this._childKeyboardProcessor); }, _fireContentReadyAction: commonUtils.noop, _setAriaTargetForList: function _setAriaTargetForList() { // TODO: make getAriaTarget option this._list._getAriaTarget = this._getAriaTarget.bind(this); this._list.setAria("role", "combobox"); }, _renderList: function _renderList() { this._listId = "dx-" + new Guid()._value; var $list = this._$list = $("<div>").attr("id", this._listId).appendTo(this._popup.$content()); this._list = this._createComponent($list, List, this._listConfig()); this._refreshList(); this._setAriaTargetForList(); this._renderPreventBlur(this._$list); }, _renderPreventBlur: function _renderPreventBlur($target) { var eventName = eventUtils.addNamespace("mousedown", "dxDropDownList"); eventsEngine.off($target, eventName); eventsEngine.on($target, eventName, function (e) { e.preventDefault(); }.bind(this)); }, _renderOpenedState: function _renderOpenedState() { this.callBase(); var opened = this.option("opened") || undefined; this.setAria({ "activedescendant": opened && this._list.getFocusedItemId(), "owns": opened && this._listId }); }, _refreshList: function _refreshList() { if (this._list && this._shouldRefreshDataSource()) { this._setListDataSource(); } }, _shouldRefreshDataSource: function _shouldRefreshDataSource() { var dataSourceProvided = !!this._list.option("dataSource"); return dataSourceProvided !== this._needPassDataSourceToList(); }, _isDesktopDevice: function _isDesktopDevice() { return devices.real().deviceType === "desktop"; }, _getListKeyExpr: function _getListKeyExpr() { var valueExpr = this.option("valueExpr"), isValueExprField = typeUtils.isString(valueExpr) && valueExpr !== "this"; return isValueExprField ? valueExpr : null; }, _listConfig: function _listConfig() { var options = { selectionMode: "single", _templates: this.option("_templates"), templateProvider: this.option("templateProvider"), noDataText: this.option("noDataText"), grouped: this.option("grouped"), onContentReady: this._listContentReadyHandler.bind(this), itemTemplate: this._getTemplateByOption("itemTemplate"), indicateLoading: false, keyExpr: this._getListKeyExpr(), groupTemplate: this.option("groupTemplate"), tabIndex: null, onItemClick: this._listItemClickAction.bind(this), dataSource: this._getDataSource(), _keyboardProcessor: this._childKeyboardProcessor, hoverStateEnabled: this._isDesktopDevice() ? this.option("hoverStateEnabled") : false, focusStateEnabled: this._isDesktopDevice() ? this.option("focusStateEnabled") : false }; return options; }, _getDataSource: function _getDataSource() { return this._needPassDataSourceToList() ? this._dataSource : null; }, _dataSourceOptions: function _dataSourceOptions() { return { paginate: false }; }, _dataSourceFromUrlLoadMode: function _dataSourceFromUrlLoadMode() { return "raw"; }, _listContentReadyHandler: function _listContentReadyHandler() { this._list = this._list || this._$list.dxList("instance"); if (!this.option("deferRendering")) { this._refreshSelected(); } this._dimensionChanged(); this._contentReadyAction(); }, _setListOption: function _setListOption(optionName, value) { this._setWidgetOption("_list", arguments); }, _listItemClickAction: function _listItemClickAction(e) { this._listItemClickHandler(e); this._itemClickAction(e); }, _listItemClickHandler: commonUtils.noop, _setListDataSource: function _setListDataSource() { if (!this._list) { return; } this._setListOption("dataSource", this._getDataSource()); if (!this._needPassDataSourceToList()) { this._setListOption("items", []); } }, _needPassDataSourceToList: function _needPassDataSourceToList() { return this.option("showDataBeforeSearch") || this._isMinSearchLengthExceeded(); }, _isMinSearchLengthExceeded: function _isMinSearchLengthExceeded() { return this._searchValue().toString().length >= this.option("minSearchLength"); }, _searchValue: function _searchValue() { return this._input().val() || ""; }, _getSearchEvent: function _getSearchEvent() { return eventUtils.addNamespace("keyup", this.NAME + "Search"); }, _renderEvents: function _renderEvents() { this.callBase(); if (this._shouldRenderSearchEvent()) { eventsEngine.on(this._input(), this._getSearchEvent(), this._searchHandler.bind(this)); } }, _shouldRenderSearchEvent: function _shouldRenderSearchEvent() { return this.option("searchEnabled"); }, _refreshEvents: function _refreshEvents() { eventsEngine.off(this._input(), this._getSearchEvent()); this.callBase(); }, _searchHandler: function _searchHandler() { if (!this._isMinSearchLengthExceeded()) { this._searchCanceled(); return; } var searchTimeout = this.option("searchTimeout"); if (searchTimeout) { this._clearSearchTimer(); this._searchTimer = setTimeout(this._searchDataSource.bind(this), searchTimeout); } else { this._searchDataSource(); } }, _searchCanceled: function _searchCanceled() { this._clearSearchTimer(); if (this._needPassDataSourceToList()) { this._filterDataSource(null); } this._refreshList(); }, _searchDataSource: function _searchDataSource() { this._filterDataSource(this._searchValue()); }, _filterDataSource: function _filterDataSource(searchValue) { this._clearSearchTimer(); var dataSource = this._dataSource; dataSource.searchExpr(this.option("searchExpr") || this._displayGetterExpr()); dataSource.searchOperation(this.option("searchMode")); dataSource.searchValue(searchValue); return dataSource.load().done(this._dataSourceFiltered.bind(this, searchValue)); }, _clearFilter: function _clearFilter() { var dataSource = this._dataSource; dataSource && dataSource.searchValue() && dataSource.searchValue(null); }, _dataSourceFiltered: function _dataSourceFiltered() { this._refreshList(); this._refreshPopupVisibility(); }, _refreshPopupVisibility: function _refreshPopupVisibility() { if (this.option("readOnly")) { return; } this.option("opened", this._hasItemsToShow()); if (this.option("opened")) { this._dimensionChanged(); } }, _dataSourceChangedHandler: function _dataSourceChangedHandler(newItems) { if (this._dataSource.pageIndex() === 0) { this.option().items = newItems; } else { this.option().items = this.option().items.concat(newItems); } }, _hasItemsToShow: function _hasItemsToShow() { var resultItems = this._dataSource && this._dataSource.items() || []; var resultAmount = resultItems.length; var isMinSearchLengthExceeded = this._needPassDataSourceToList(); return isMinSearchLengthExceeded && resultAmount && this._hasFocusClass(); }, _clearSearchTimer: function _clearSearchTimer() { clearTimeout(this._searchTimer); delete this._searchTimer; }, _popupShowingHandler: function _popupShowingHandler() { this._dimensionChanged(); }, _dimensionChanged: function _dimensionChanged() { this._popup && this._updatePopupDimensions(); }, _updatePopupDimensions: function _updatePopupDimensions() { this._updatePopupWidth(); this._updatePopupHeight(); }, _updatePopupWidth: function _updatePopupWidth() { this._setPopupOption("width", this.$element().outerWidth() + this.option("popupWidthExtension")); }, _needPopupRepaint: function _needPopupRepaint() { if (!this._dataSource) { return false; } var currentPageIndex = this._dataSource.pageIndex(), needRepaint = typeUtils.isDefined(this._pageIndex) && currentPageIndex <= this._pageIndex; this._pageIndex = currentPageIndex; return needRepaint; }, _updatePopupHeight: function _updatePopupHeight() { if (this._needPopupRepaint()) { this._popup.repaint(); } this._list && this._list.updateDimensions(); }, _getMaxHeight: function _getMaxHeight() { var $element = this.$element(), offset = $element.offset(), windowHeight = $(window).height(), maxHeight = Math.max(offset.top, windowHeight - offset.top - $element.outerHeight()); return Math.min(windowHeight * 0.5, maxHeight); }, _clean: function _clean() { if (this._list) { delete this._list; } this.callBase(); }, _dispose: function _dispose() { this._clearSearchTimer(); this.callBase(); }, _setCollectionWidgetOption: function _setCollectionWidgetOption() { this._setListOption.apply(this, arguments); }, _optionChanged: function _optionChanged(args) { this._dataExpressionOptionChanged(args); switch (args.name) { case "hoverStateEnabled": case "focusStateEnabled": this._isDesktopDevice() && this._setListOption(args.name, args.value); this.callBase(args); break; case "items": if (!this.option("dataSource")) { this._processDataSourceChanging(); } break; case "dataSource": this._processDataSourceChanging(); break; case "valueExpr": this._renderValue(); this._setListOption("keyExpr", this._getListKeyExpr()); break; case "displayExpr": this._renderValue(); break; case "searchMode": this._validateSearchMode(); break; case "minSearchLength": this._refreshList(); break; case "searchEnabled": case "showDataBeforeSearch": case "searchExpr": this._invalidate(); break; case "onContentReady": this._initContentReadyAction(); break; case "onSelectionChanged": this._initSelectionChangedAction(); break; case "onItemClick": this._initItemClickAction(); break; case "grouped": case "groupTemplate": case "noDataText": this._setListOption(args.name); break; case "displayValue": this.option("text", args.value); break; case "itemTemplate": case "searchTimeout": case "popupWidthExtension": break; case "selectedItem": this._selectionChangedAction({ selectedItem: args.value }); break; default: this.callBase(args); } } }).include(DataExpressionMixin); registerComponent("dxDropDownList", DropDownList); module.exports = DropDownList;