devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
870 lines (712 loc) • 27.5 kB
JavaScript
"use strict";
var $ = require("../core/renderer"),
eventsEngine = require("../events/core/events_engine"),
commonUtils = require("../core/utils/common"),
typeUtils = require("../core/utils/type"),
isDefined = typeUtils.isDefined,
isPromise = typeUtils.isPromise,
extend = require("../core/utils/extend").extend,
inArray = require("../core/utils/array").inArray,
each = require("../core/utils/iterator").each,
deferredUtils = require("../core/utils/deferred"),
getPublicElement = require("../core/utils/dom").getPublicElement,
Deferred = deferredUtils.Deferred,
errors = require("../core/errors"),
inkRipple = require("./widget/utils.ink_ripple"),
messageLocalization = require("../localization/message"),
registerComponent = require("../core/component_registrator"),
eventUtils = require("../events/utils"),
dataQuery = require("../data/query"),
DropDownList = require("./drop_down_editor/ui.drop_down_list"),
themes = require("./themes"),
clickEvent = require("../events/click");
var DISABLED_STATE_SELECTOR = ".dx-state-disabled",
SELECTBOX_CLASS = "dx-selectbox",
SELECTBOX_POPUP_CLASS = "dx-selectbox-popup",
SELECTBOX_CONTAINER_CLASS = "dx-selectbox-container",
SELECTBOX_POPUP_WRAPPER_CLASS = "dx-selectbox-popup-wrapper";
/**
* @name dxSelectbox
* @isEditor
* @publicName dxSelectBox
* @inherits dxDropDownList
* @module ui/select_box
* @export default
*/
var SelectBox = DropDownList.inherit({
_supportedKeys: function _supportedKeys() {
var that = this,
parent = this.callBase(),
clearSelectBox = function clearSelectBox(e) {
var isEditable = this._isEditable();
if (!isEditable) {
if (this.option("showClearButton")) {
e.preventDefault();
this.reset();
}
} else if (this._valueSubstituted()) {
this._preventFiltering = true;
}
this._preventSubstitution = true;
};
var searchIfNeeded = function searchIfNeeded() {
if (that.option("searchEnabled") && that._valueSubstituted()) {
that._searchHandler();
}
};
return extend({}, parent, {
tab: function tab() {
if (this.option("opened") && this.option("applyValueMode") === "instantly") {
this._cleanInputSelection();
}
if (this._wasSearch()) {
this._clearFilter();
}
parent.tab.apply(this, arguments);
},
upArrow: function upArrow() {
if (parent.upArrow.apply(this, arguments)) {
if (!this.option("opened")) {
this._setNextValue(-1);
}
return true;
}
},
downArrow: function downArrow() {
if (parent.downArrow.apply(this, arguments)) {
if (!this.option("opened")) {
this._setNextValue(1);
}
return true;
}
},
leftArrow: function leftArrow() {
searchIfNeeded();
parent.leftArrow.apply(this, arguments);
},
rightArrow: function rightArrow() {
searchIfNeeded();
parent.rightArrow.apply(this, arguments);
},
home: function home() {
searchIfNeeded();
parent.home.apply(this, arguments);
},
end: function end() {
searchIfNeeded();
parent.end.apply(this, arguments);
},
escape: function escape() {
parent.escape.apply(this, arguments);
if (!this._isEditable() && this._list) {
this._focusListElement(null);
this._updateField(this.option("selectedItem"));
}
},
enter: function enter(e) {
if (this._input().val() === "" && this.option("value") && this.option("allowClearing")) {
this.option({
selectedItem: null,
value: null
});
this.close();
} else {
if (this.option("acceptCustomValue")) {
e.preventDefault();
return this.option("opened");
}
if (parent.enter.apply(this, arguments)) {
return this.option("opened");
}
}
},
backspace: clearSelectBox,
del: clearSelectBox
});
},
_getDefaultOptions: function _getDefaultOptions() {
return extend(this.callBase(), {
/**
* @name dxSelectBoxOptions.placeholder
* @publicName placeholder
* @type string
* @default "Select"
*/
placeholder: messageLocalization.format("Select"),
/**
* @name dxSelectBoxOptions.fieldTemplate
* @publicName fieldTemplate
* @type template|function
* @default null
* @type_function_param1 selectedItem:object
* @type_function_param2 fieldElement:dxElement
* @type_function_return string|Node|jQuery
*/
fieldTemplate: null,
/**
* @name dxSelectBoxOptions.valueChangeEvent
* @publicName valueChangeEvent
* @type string
* @default "change"
*/
valueChangeEvent: "change",
/**
* @name dxSelectBoxOptions.acceptCustomValue
* @publicName acceptCustomValue
* @type boolean
* @default false
*/
acceptCustomValue: false,
/**
* @name dxSelectBoxOptions.onCustomItemCreating
* @publicName onCustomItemCreating
* @extends Action
* @type function(e)
* @type_function_param1 e:object
* @type_function_param1_field4 text:string
* @type_function_param1_field5 customItem:string|object|Promise<any>
* @action
* @default function(e) { if(!e.customItem) { e.customItem = e.text; } }
*/
onCustomItemCreating: function onCustomItemCreating(e) {
if (!isDefined(e.customItem)) {
e.customItem = e.text;
}
},
/**
* @name dxSelectBoxOptions.showSelectionControls
* @publicName showSelectionControls
* @type boolean
* @default false
*/
showSelectionControls: false,
/**
* @name dxSelectBoxOptions.autocompletionEnabled
* @publicName autocompletionEnabled
* @type boolean
* @default true
* @hidden
*/
autocompletionEnabled: true,
/**
* @name dxSelectBoxOptions.allowClearing
* @publicName allowClearing
* @type boolean
* @default true
* @hidden
*/
allowClearing: true,
tooltipEnabled: false,
openOnFieldClick: true,
showDropDownButton: true,
displayCustomValue: false,
_isAdaptablePopupPosition: false,
useInkRipple: false
});
},
_defaultOptionsRules: function _defaultOptionsRules() {
return this.callBase().concat([{
device: function device() {
return (/win8/.test(themes.current())
);
},
options: {
_isAdaptablePopupPosition: true,
popupPosition: {
at: "left top",
offset: { h: 0, v: 0 }
}
}
}, {
device: function device() {
return (/android5/.test(themes.current())
);
},
options: {
_isAdaptablePopupPosition: true,
popupPosition: {
offset: {
h: -16,
v: -8
}
},
useInkRipple: true
}
}]);
},
_init: function _init() {
this.callBase();
this._initCustomItemCreatingAction();
},
_initMarkup: function _initMarkup() {
this._renderSubmitElement();
this.$element().addClass(SELECTBOX_CLASS);
this._renderTooltip();
this.option("useInkRipple") && this._renderInkRipple();
this.callBase();
this._$container.addClass(SELECTBOX_CONTAINER_CLASS);
},
_renderSubmitElement: function _renderSubmitElement() {
this._$submitElement = $("<input>").attr("type", "hidden").appendTo(this.$element());
},
_renderInkRipple: function _renderInkRipple() {
this._inkRipple = inkRipple.render();
},
_toggleActiveState: function _toggleActiveState($element, value, e) {
this.callBase.apply(this, arguments);
if (!this._inkRipple || this._isEditable()) {
return;
}
var config = {
element: this._inputWrapper(),
event: e
};
if (value) {
this._inkRipple.showWave(config);
} else {
this._inkRipple.hideWave(config);
}
},
_createPopup: function _createPopup() {
this.callBase();
this._popup.$element().addClass(SELECTBOX_POPUP_CLASS);
},
_popupWrapperClass: function _popupWrapperClass() {
return this.callBase() + " " + SELECTBOX_POPUP_WRAPPER_CLASS;
},
_renderOpenedState: function _renderOpenedState() {
this.callBase();
if (this.option("opened")) {
this._scrollToSelectedItem();
this._focusSelectedElement();
}
},
_focusSelectedElement: function _focusSelectedElement() {
var searchValue = this._searchValue();
if (!searchValue) {
this._focusListElement(null);
return;
}
var $listItems = this._list._itemElements(),
index = inArray(this.option("selectedItem"), this.option("items")),
focusedElement = index >= 0 && !this._isCustomItemSelected() ? $listItems.eq(index) : null;
this._focusListElement(focusedElement);
},
_renderFocusedElement: function _renderFocusedElement() {
if (!this._list) {
return;
}
var searchValue = this._searchValue();
if (!searchValue || this.option("acceptCustomValue")) {
this._focusListElement(null);
return;
}
var $listItems = this._list._itemElements(),
focusedElement = $listItems.not(DISABLED_STATE_SELECTOR).eq(0);
this._focusListElement(focusedElement);
},
_focusListElement: function _focusListElement(element) {
this._preventInputValueRender = true;
this._list.option("focusedElement", getPublicElement(element));
delete this._preventInputValueRender;
},
_scrollToSelectedItem: function _scrollToSelectedItem() {
this._list.scrollToItem(this._list.option("selectedItem"));
},
_listContentReadyHandler: function _listContentReadyHandler() {
this.callBase();
var isPaginate = this._dataSource && this._dataSource.paginate();
if (isPaginate && this._needPopupRepaint()) {
return;
}
this._scrollToSelectedItem();
},
_renderValue: function _renderValue() {
this._renderInputValue();
this._setSubmitValue();
},
_setSubmitValue: function _setSubmitValue() {
var value = this.option("value"),
submitValue = this.option("valueExpr") === "this" ? this._displayGetter(value) : value;
this._$submitElement.val(submitValue);
},
_getSubmitElement: function _getSubmitElement() {
return this._$submitElement;
},
_renderInputValue: function _renderInputValue() {
return this.callBase().always(function () {
this._renderInputValueAsync();
}.bind(this));
},
_renderInputValueAsync: function _renderInputValueAsync() {
this._renderTooltip();
this._renderInputValueImpl();
this._refreshSelected();
},
_renderInputValueImpl: function _renderInputValueImpl() {
this._renderInputAddons();
},
_fitIntoRange: function _fitIntoRange(value, start, end) {
if (value > end) return start;
if (value < start) return end;
return value;
},
_setNextValue: function _setNextValue(step) {
var dataSourceIsLoaded = this._dataSource.isLoaded() ? new Deferred().resolve() : this._dataSource.load();
dataSourceIsLoaded.done(function () {
var item = this._calcNextItem(step),
value = this._valueGetter(item);
this._setValue(value);
}.bind(this));
},
_calcNextItem: function _calcNextItem(step) {
var items = this._items();
var nextIndex = this._fitIntoRange(this._getSelectedIndex() + step, 0, items.length - 1);
return items[nextIndex];
},
_items: function _items() {
var items = this._list ? this.option("items") : this._dataSource.items();
var availableItems = new dataQuery(items).filter("disabled", "<>", true).toArray();
return availableItems;
},
_getSelectedIndex: function _getSelectedIndex() {
var items = this._items();
var selectedItem = this.option("selectedItem");
var result = -1;
each(items, function (index, item) {
if (this._isValueEquals(item, selectedItem)) {
result = index;
return false;
}
}.bind(this));
return result;
},
_setSelectedItem: function _setSelectedItem(item) {
var isUnknownItem = !this._isCustomValueAllowed() && item === undefined;
this.callBase(isUnknownItem ? null : item);
},
_isCustomValueAllowed: function _isCustomValueAllowed() {
return this.option("acceptCustomValue") || this.callBase();
},
_displayValue: function _displayValue(item) {
item = !isDefined(item) && this._isCustomValueAllowed() ? this.option("value") : item;
return this.callBase(item);
},
_listConfig: function _listConfig() {
var result = extend(this.callBase(), {
pageLoadMode: "scrollBottom",
onSelectionChanged: this._getSelectionChangeHandler(),
selectedItem: this.option("selectedItem"),
onFocusedItemChanged: this._listFocusedItemChangeHandler.bind(this)
});
if (this.option("showSelectionControls")) {
extend(result, {
showSelectionControls: true,
selectionByClick: true
});
}
return result;
},
_listFocusedItemChangeHandler: function _listFocusedItemChangeHandler(e) {
if (this._preventInputValueRender) {
return;
}
var list = e.component,
focusedElement = $(list.option("focusedElement")),
focusedItem = list._getItemData(focusedElement);
this._updateField(focusedItem);
},
_updateField: function _updateField(item) {
var fieldTemplate = this._getTemplateByOption("fieldTemplate");
if (!(fieldTemplate && this.option("fieldTemplate"))) {
this._renderDisplayText(this._displayGetter(item));
return;
}
this._renderInputAddons();
},
_getSelectionChangeHandler: function _getSelectionChangeHandler() {
return this.option("showSelectionControls") ? this._selectionChangeHandler.bind(this) : commonUtils.noop;
},
_selectionChangeHandler: function _selectionChangeHandler(e) {
each(e.addedItems || [], function (_, addedItem) {
this._setValue(this._valueGetter(addedItem));
}.bind(this));
},
_getActualSearchValue: function _getActualSearchValue() {
return this._dataSource.searchValue();
},
_toggleOpenState: function _toggleOpenState(isVisible) {
if (this.option("disabled")) {
return;
}
isVisible = arguments.length ? isVisible : !this.option("opened");
if (!isVisible) {
this._restoreInputText();
}
if (this._wasSearch() && isVisible) {
this._wasSearch(false);
if (this.option("showDataBeforeSearch") || this.option("minSearchLength") === 0) {
if (this._searchTimer) return;
var searchValue = this._getActualSearchValue();
searchValue && this._wasSearch(true);
this._filterDataSource(searchValue || null);
} else {
this._setListOption("items", []);
}
}
this.callBase(isVisible);
},
_renderTooltip: function _renderTooltip() {
if (this.option("tooltipEnabled")) {
this.$element().attr("title", this.option("displayValue"));
}
},
_renderDimensions: function _renderDimensions() {
this.callBase();
this._setPopupOption("width");
},
_restoreInputText: function _restoreInputText() {
this._loadItemDeferred && this._loadItemDeferred.always(function () {
if (this.option("acceptCustomValue")) {
return;
}
if (this.option("searchEnabled")) {
if (!this._searchValue() && this.option("allowClearing")) {
this._clearTextValue();
return;
}
}
var oldSelectedItem = this.option("selectedItem");
if (this._displayGetter(oldSelectedItem) === this._searchValue()) {
return;
}
this._renderInputValue().always(function (selectedItem) {
var newSelectedItem = commonUtils.ensureDefined(selectedItem, oldSelectedItem);
this._setSelectedItem(newSelectedItem);
this._updateField(newSelectedItem);
this._clearFilter();
}.bind(this));
}.bind(this));
},
_focusOutHandler: function _focusOutHandler(e) {
this.callBase(e);
this._restoreInputText();
},
_clearTextValue: function _clearTextValue() {
this.option("value", null);
},
_renderValueChangeEvent: function _renderValueChangeEvent() {
if (this._isEditable()) {
this.callBase();
}
},
_isEditable: function _isEditable() {
return this.option("acceptCustomValue") || this.option("searchEnabled");
},
_fieldRenderData: function _fieldRenderData() {
var $listFocused = this._list && this.option("opened") && $(this._list.option("focusedElement"));
if ($listFocused && $listFocused.length) {
return this._list._getItemData($listFocused);
}
return this.option("selectedItem");
},
_readOnlyPropValue: function _readOnlyPropValue() {
return !this._isEditable() || this.option("readOnly");
},
_isSelectedValue: function _isSelectedValue(value) {
return this._isValueEquals(value, this.option("value"));
},
_shouldCloseOnItemClick: function _shouldCloseOnItemClick() {
return !(this.option("showSelectionControls") && this.option("selectionMode") !== "single");
},
_listItemClickHandler: function _listItemClickHandler(e) {
var previousValue = this._getCurrentValue();
this._saveValueChangeEvent(e.event);
if (this._wasSearch()) {
this._clearFilter();
}
this._completeSelection(this._valueGetter(e.itemData));
if (this._shouldCloseOnItemClick()) {
this.option("opened", false);
}
if (this.option("searchEnabled") && previousValue === this._valueGetter(e.itemData)) {
this._updateField(e.itemData);
}
},
_completeSelection: function _completeSelection(value) {
this._setValue(value);
},
_clearValueHandler: function _clearValueHandler() {
this.reset();
},
_loadItem: function _loadItem(value, cache) {
var that = this,
deferred = new Deferred();
this.callBase(value, cache).done(function (item) {
deferred.resolve(item);
}.bind(this)).fail(function () {
var selectedItem = that.option("selectedItem");
if (that.option("acceptCustomValue") && value === that._valueGetter(selectedItem)) {
deferred.resolve(selectedItem);
} else {
deferred.reject();
}
}.bind(this));
return deferred.promise();
},
_loadInputValue: function _loadInputValue(value, callback) {
this._loadItemDeferred = this._loadItem(value).always(callback);
return this._loadItemDeferred;
},
_isCustomItemSelected: function _isCustomItemSelected() {
var selectedItem = this.option("selectedItem"),
searchValue = this._searchValue(),
selectedItemText = this._displayGetter(selectedItem);
return !selectedItemText || searchValue !== selectedItemText.toString();
},
_valueChangeEventHandler: function _valueChangeEventHandler() {
if (this.option("acceptCustomValue") && this._isCustomItemSelected()) {
this._customItemAddedHandler();
}
},
_initCustomItemCreatingAction: function _initCustomItemCreatingAction() {
this._customItemCreatingAction = this._createActionByOption("onCustomItemCreating");
},
_createCustomItem: function _createCustomItem(text) {
var params = {
text: text
},
actionResult = this._customItemCreatingAction(params),
item = commonUtils.ensureDefined(actionResult, params.customItem);
if (isDefined(actionResult)) {
errors.log("W0015", "onCustomItemCreating", "customItem");
}
return item;
},
_customItemAddedHandler: function _customItemAddedHandler() {
var searchValue = this._searchValue(),
item = this._createCustomItem(searchValue);
if (item === undefined) {
this._renderValue();
throw errors.Error("E0121");
}
if (isPromise(item)) {
deferredUtils.fromPromise(item).done(this._setCustomItem.bind(this)).fail(this._setCustomItem.bind(this, null));
} else {
this._setCustomItem(item);
}
},
_setCustomItem: function _setCustomItem(item) {
if (this._disposed) {
return;
}
item = item || null;
this.option("selectedItem", item);
this._setValue(this._valueGetter(item));
this._renderDisplayText(this._displayGetter(item));
if (item === null && this._wasSearch()) {
this._filterDataSource(null);
}
},
_createClearButton: function _createClearButton() {
var eventName = eventUtils.addNamespace(clickEvent.name, this.NAME);
var $clearButton = this.callBase();
eventsEngine.on($clearButton, eventName, function () {
return false;
});
return $clearButton;
},
_wasSearch: function _wasSearch(value) {
if (!arguments.length) {
return this._wasSearchValue;
}
this._wasSearchValue = value;
},
_searchHandler: function _searchHandler(e) {
if (e && this._isControlKey(e.key) || this._preventFiltering) {
delete this._preventFiltering;
return;
}
if (this._needPassDataSourceToList()) {
this._wasSearch(true);
}
this.callBase(e);
},
_dataSourceFiltered: function _dataSourceFiltered(searchValue) {
this.callBase();
if (searchValue !== null) {
this._renderInputSubstitution();
this._renderFocusedElement();
}
},
_valueSubstituted: function _valueSubstituted() {
var input = this._input().get(0),
isAllSelected = input.selectionStart === 0 && input.selectionEnd === this._searchValue().length,
inputHasSelection = input.selectionStart !== input.selectionEnd;
return this._wasSearch() && inputHasSelection && !isAllSelected;
},
_shouldSubstitutionBeRendered: function _shouldSubstitutionBeRendered() {
return this.option("autocompletionEnabled") && !this._preventSubstitution && this.option("searchEnabled") && !this.option("acceptCustomValue") && this.option("searchMode") === "startswith";
},
_renderInputSubstitution: function _renderInputSubstitution() {
if (!this._shouldSubstitutionBeRendered()) {
delete this._preventSubstitution;
return;
}
var item = this._list && this._getPlainItems(this._list.option("items"))[0];
if (!item) {
return;
}
var $input = this._input(),
valueLength = $input.val().length;
if (valueLength === 0) {
return;
}
var inputElement = $input.get(0),
displayValue = this._displayGetter(item);
inputElement.value = displayValue;
inputElement.selectionStart = valueLength;
inputElement.selectionEnd = displayValue.length;
},
_cleanInputSelection: function _cleanInputSelection() {
var inputElement = this._input().get(0),
endPosition = inputElement.value.length;
inputElement.selectionStart = endPosition;
inputElement.selectionEnd = endPosition;
},
_dispose: function _dispose() {
this._renderInputValueAsync = commonUtils.noop;
delete this._loadItemDeferred;
this.callBase();
},
_optionChanged: function _optionChanged(args) {
switch (args.name) {
case "_isAdaptablePopupPosition":
case "autocompletionEnabled":
break;
case "onCustomItemCreating":
this._initCustomItemCreatingAction();
break;
case "tooltipEnabled":
this._renderTooltip();
break;
case "displayCustomValue":
case "acceptCustomValue":
case "showSelectionControls":
case "useInkRipple":
this._invalidate();
break;
case "selectedItem":
if (args.previousValue !== args.value) {
this.callBase(args);
}
break;
case "allowClearing":
break;
default:
this.callBase(args);
}
}
});
registerComponent("dxSelectBox", SelectBox);
module.exports = SelectBox;