devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
360 lines (301 loc) • 11.4 kB
JavaScript
"use strict";
var DropDownEditor = require("./drop_down_editor/ui.drop_down_editor"),
DataExpressionMixin = require("./editor/ui.data_expression"),
commonUtils = require("../core/utils/common"),
window = require("../core/utils/window").getWindow(),
map = require("../core/utils/iterator").map,
isDefined = require("../core/utils/type").isDefined,
selectors = require("./widget/selectors"),
KeyboardProcessor = require("./widget/ui.keyboard_processor"),
deferredUtils = require("../core/utils/deferred"),
when = deferredUtils.when,
Deferred = deferredUtils.Deferred,
$ = require("../core/renderer"),
eventsEngine = require("../events/core/events_engine"),
grep = require("../core/utils/common").grep,
extend = require("../core/utils/extend").extend,
registerComponent = require("../core/component_registrator");
var DROP_DOWN_BOX_CLASS = "dx-dropdownbox";
/**
* @name dxDropDownBox
* @isEditor
* @publicName dxDropDownBox
* @inherits DataExpressionMixin, dxDropDownEditor
* @module ui/drop_down_box
* @export default
*/
var DropDownBox = DropDownEditor.inherit({
_supportedKeys: function _supportedKeys() {
return extend({}, this.callBase(), {
tab: function tab(e) {
if (!this.option("opened")) {
return;
}
var $tabbableElements = this._getTabbableElements(),
$focusableElement = e.shiftKey ? $tabbableElements.last() : $tabbableElements.first();
$focusableElement && eventsEngine.trigger($focusableElement, "focus");
e.preventDefault();
}
});
},
_getTabbableElements: function _getTabbableElements() {
return this._getElements().filter(selectors.tabbable);
},
_getElements: function _getElements() {
return $(this.content()).find("*");
},
_getDefaultOptions: function _getDefaultOptions() {
return extend(this.callBase(), {
/**
* @name dxDropDownBoxOptions.attr
* @publicName attr
* @inheritdoc
* @hidden
*/
/**
* @name dxDropDownBoxOptions.acceptCustomValue
* @publicName acceptCustomValue
* @type boolean
* @default false
*/
acceptCustomValue: false,
/**
* @name dxDropDownBoxOptions.contentTemplate
* @publicName contentTemplate
* @type template|function
* @default null
* @type_function_param1 templateData:object
* @type_function_param1_field1 component:dxDropDownBox
* @type_function_param1_field2 value:any
* @type_function_param2 contentElement:dxElement
* @type_function_return string|Node|jQuery
*/
contentTemplate: null,
/**
* @name dxDropDownBoxOptions.dropDownOptions
* @publicName dropDownOptions
* @type dxPopupOptions
* @default {}
*/
dropDownOptions: {},
/**
* @name dxDropDownBoxOptions.fieldTemplate
* @publicName fieldTemplate
* @type template|function
* @default null
* @type_function_param1 value:object
* @type_function_param2 fieldElement:dxElement
* @type_function_return string|Node|jQuery
*/
/**
* @name dxDropDownBoxOptions.maxLength
* @publicName maxLength
* @type string|number
* @default null
* @hidden
*/
/**
* @name dxDropDownBoxOptions.onContentReady
* @publicName onContentReady
* @hidden true
* @action
*/
/**
* @name dxDropDownBoxOptions.spellcheck
* @publicName spellcheck
* @type boolean
* @default false
* @hidden
*/
/**
* @name dxDropDownBoxOptions.applyValueMode
* @publicName applyValueMode
* @type string
* @default "instantly"
* @acceptValues 'useButtons'|'instantly'
* @hidden
*/
/**
* @name dxDropDownBoxOptions.itemTemplate
* @publicName itemTemplate
* @type template
* @default "item"
* @inheritdoc
* @hidden
*/
openOnFieldClick: true,
/**
* @name dxDropDownBoxOptions.valueChangeEvent
* @publicName valueChangeEvent
* @type string
* @default "change"
*/
valueFormat: function valueFormat(value) {
return Array.isArray(value) ? value.join(", ") : value;
}
});
},
_initMarkup: function _initMarkup() {
this._initDataExpressions();
this._renderSubmitElement();
this.$element().addClass(DROP_DOWN_BOX_CLASS);
this.callBase();
},
_renderSubmitElement: function _renderSubmitElement() {
this._$submitElement = $("<input>").attr("type", "hidden").appendTo(this.$element());
},
_renderValue: function _renderValue() {
this._setSubmitValue();
this.callBase();
},
_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() {
var callBase = this.callBase.bind(this),
values = [];
if (!this._dataSource) {
callBase(values);
return;
}
var currentValue = this._getCurrentValue(),
keys = commonUtils.ensureDefined(currentValue, []);
keys = Array.isArray(keys) ? keys : [keys];
var itemLoadDeferreds = map(keys, function (key) {
return this._loadItem(key).always(function (item) {
var displayValue = this._displayGetter(item);
if (isDefined(displayValue)) {
values.push(displayValue);
}
}.bind(this));
}.bind(this));
when.apply(this, itemLoadDeferreds).done(function () {
this.option("displayValue", values);
callBase(values.length && values);
}.bind(this)).fail(callBase);
return itemLoadDeferreds;
},
_loadItem: function _loadItem(value) {
var deferred = new Deferred(),
that = this;
var selectedItem = grep(this.option("items") || [], function (item) {
return this._isValueEquals(this._valueGetter(item), value);
}.bind(this))[0];
if (selectedItem !== undefined) {
deferred.resolve(selectedItem);
} else {
this._loadValue(value).done(function (item) {
deferred.resolve(item);
}).fail(function (args) {
if (that.option("acceptCustomValue")) {
deferred.resolve(value);
} else {
deferred.reject();
}
});
}
return deferred.promise();
},
_clearValueHandler: function _clearValueHandler(e) {
e.stopPropagation();
this.reset();
},
_updatePopupWidth: function _updatePopupWidth() {
this._setPopupOption("width", this.$element().outerWidth());
},
_popupElementTabHandler: function _popupElementTabHandler(e) {
if (e.key !== "tab") return;
var $firstTabbable = this._getTabbableElements().first().get(0),
$lastTabbable = this._getTabbableElements().last().get(0),
$target = e.originalEvent.target,
moveBackward = !!($target === $firstTabbable && e.shift),
moveForward = !!($target === $lastTabbable && !e.shift);
if (moveBackward || moveForward) {
this.close();
eventsEngine.trigger(this._input(), "focus");
if (moveBackward) {
e.originalEvent.preventDefault();
}
}
},
_renderPopup: function _renderPopup(e) {
this.callBase();
this._options.dropDownOptions = extend({}, this._popup.option());
this._popup.on("optionChanged", function (e) {
this.option("dropDownOptions" + "." + e.fullName, e.value);
}.bind(this));
if (this.option("focusStateEnabled")) {
this._popup._keyboardProcessor.push(new KeyboardProcessor({
element: this.content(),
handler: this._popupElementTabHandler,
context: this
}));
}
},
_popupConfig: function _popupConfig() {
return extend(this.callBase(), {
width: this.$element().outerWidth(),
height: "auto",
tabIndex: -1,
dragEnabled: false,
focusStateEnabled: this.option("focusStateEnabled"),
maxHeight: this._getMaxHeight.bind(this)
}, this.option("dropDownOptions"));
},
_getMaxHeight: function _getMaxHeight() {
var $element = this.$element(),
offsetTop = $element.offset().top - $(window).scrollTop(),
offsetBottom = $(window).innerHeight() - offsetTop - $element.outerHeight(),
maxHeight = Math.max(offsetTop, offsetBottom) * 0.9;
return maxHeight;
},
_popupShownHandler: function _popupShownHandler() {
this.callBase();
var $firstElement = this._getTabbableElements().first();
eventsEngine.trigger($firstElement, "focus");
},
_popupOptionChanged: function _popupOptionChanged(args) {
var options = {};
if (args.name === args.fullName) {
options = args.value;
} else {
var option = args.fullName.split(".").pop();
options[option] = args.value;
}
this._setPopupOption(options);
if (Object.keys(options).indexOf("width") !== -1 && options["width"] === undefined) {
this._updatePopupWidth();
}
},
_setCollectionWidgetOption: commonUtils.noop,
_optionChanged: function _optionChanged(args) {
this._dataExpressionOptionChanged(args);
switch (args.name) {
case "width":
this.callBase(args);
this._updatePopupWidth();
break;
case "dropDownOptions":
this._popupOptionChanged(args);
break;
case "dataSource":
this._renderInputValue();
break;
case "displayValue":
this.option("text", args.value);
break;
case "displayExpr":
this._renderValue();
break;
default:
this.callBase(args);
}
}
}).include(DataExpressionMixin);
registerComponent("dxDropDownBox", DropDownBox);
module.exports = DropDownBox;