UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

858 lines (710 loc) • 27 kB
"use strict"; var $ = require("../../core/renderer"), eventsEngine = require("../../events/core/events_engine"), Guid = require("../../core/guid"), registerComponent = require("../../core/component_registrator"), commonUtils = require("../../core/utils/common"), domUtils = require("../../core/utils/dom"), focused = require("../widget/selectors").focused, each = require("../../core/utils/iterator").each, isDefined = require("../../core/utils/type").isDefined, extend = require("../../core/utils/extend").extend, getPublicElement = require("../../core/utils/dom").getPublicElement, errors = require("../widget/ui.errors"), positionUtils = require("../../animation/position"), getDefaultAlignment = require("../../core/utils/position").getDefaultAlignment, messageLocalization = require("../../localization/message"), Button = require("../button"), eventUtils = require("../../events/utils"), TextBox = require("../text_box"), clickEvent = require("../../events/click"), FunctionTemplate = require("../widget/function_template"), Popup = require("../popup"); var DROP_DOWN_EDITOR_CLASS = "dx-dropdowneditor", DROP_DOWN_EDITOR_INPUT_WRAPPER_CLASS = "dx-dropdowneditor-input-wrapper", DROP_DOWN_EDITOR_BUTTON_CLASS = "dx-dropdowneditor-button", DROP_DOWN_EDITOR_BUTTON_ICON = "dx-dropdowneditor-icon", DROP_DOWN_EDITOR_OVERLAY = "dx-dropdowneditor-overlay", DROP_DOWN_EDITOR_OVERLAY_FLIPPED = "dx-dropdowneditor-overlay-flipped", DROP_DOWN_EDITOR_ACTIVE = "dx-dropdowneditor-active", DROP_DOWN_EDITOR_BUTTON_VISIBLE = "dx-dropdowneditor-button-visible", DROP_DOWN_EDITOR_FIELD_CLICKABLE = "dx-dropdowneditor-field-clickable"; /** * @name dxDropDownEditor * @publicName dxDropDownEditor * @inherits dxTextBox * @module ui/drop_down_editor/ui.drop_down_editor * @export default * @hidden */ var DropDownEditor = TextBox.inherit({ _supportedKeys: function _supportedKeys() { var homeEndHandler = function homeEndHandler(e) { if (this.option("opened")) { e.preventDefault(); return true; } return false; }; return extend({}, this.callBase(), { tab: function tab(e) { if (!this.option("opened")) { return; } if (this.option("applyValueMode") === "instantly") { this.close(); return; } var $focusableElement = e.shiftKey ? this._getLastPopupElement() : this._getFirstPopupElement(); $focusableElement && eventsEngine.trigger($focusableElement, "focus"); e.preventDefault(); }, escape: function escape(e) { if (this.option("opened")) { e.preventDefault(); } this.close(); }, upArrow: function upArrow(e) { e.preventDefault(); e.stopPropagation(); if (e.altKey) { this.close(); return false; } return true; }, downArrow: function downArrow(e) { e.preventDefault(); e.stopPropagation(); if (e.altKey) { this._validatedOpening(); return false; } return true; }, enter: function enter(e) { if (this.option("opened")) { e.preventDefault(); this._valueChangeEventHandler(e); } return true; }, home: homeEndHandler, end: homeEndHandler }); }, _getDefaultOptions: function _getDefaultOptions() { return extend(this.callBase(), { /** * @name dxDropDownEditorOptions.value * @publicName value * @type any * @default null */ value: null, /** * @name dxDropDownEditorOptions.onOpened * @publicName onOpened * @extends Action * @action */ onOpened: null, /** * @name dxDropDownEditorOptions.onClosed * @publicName onClosed * @extends Action * @action */ onClosed: null, /** * @name dxDropDownEditorOptions.opened * @publicName opened * @type boolean * @default false * @fires dxDropDownEditorOptions.onOpened * @fires dxDropDownEditorOptions.onClosed */ opened: false, /** * @name dxDropDownEditorOptions.acceptCustomValue * @publicName acceptCustomValue * @type boolean * @default true */ acceptCustomValue: true, /** * @name dxDropDownEditorOptions.applyValueMode * @publicName applyValueMode * @type Enums.EditorApplyValueMode * @default "instantly" */ applyValueMode: "instantly", /** * @name dxDropDownEditorOptions.deferRendering * @publicName deferRendering * @type boolean * @default true */ deferRendering: true, /** * @name dxDropDownEditorOptions.activeStateEnabled * @publicName activeStateEnabled * @type boolean * @default true * @inheritdoc */ activeStateEnabled: true, /** * @name dxDropDownEditorOptions.dropDownButtonTemplate * @publicName dropDownButtonTemplate * @type template|function * @default "dropDownButton" * @type_function_param1 buttonData:object * @type_function_param1_field1 text:string * @type_function_param1_field2 icon:string * @type_function_param2 contentElement:dxElement * @type_function_return string|Node|jQuery */ dropDownButtonTemplate: 'dropDownButton', fieldTemplate: null, contentTemplate: null, openOnFieldClick: false, showDropDownButton: true, popupPosition: this._getDefaultPopupPosition(), onPopupInitialized: null, applyButtonText: messageLocalization.format("OK"), cancelButtonText: messageLocalization.format("Cancel"), buttonsLocation: "default", showPopupTitle: false /** * @name dxDropDownEditorOptions.mask * @publicName mask * @hidden * @inheritdoc */ /** * @name dxDropDownEditorOptions.maskChar * @publicName maskChar * @hidden * @inheritdoc */ /** * @name dxDropDownEditorOptions.maskRules * @publicName maskRules * @hidden * @inheritdoc */ /** * @name dxDropDownEditorOptions.maskInvalidMessage * @publicName maskInvalidMessage * @hidden * @inheritdoc */ /** * @name dxDropDownEditorOptions.useMaskedValue * @publicName useMaskedValue * @hidden * @inheritdoc */ /** * @name dxDropDownEditorOptions.mode * @publicName mode * @hidden * @inheritdoc */ /** * @name dxDropDownEditorOptions.showMaskMode * @publicName showMaskMode * @hidden * @inheritdoc */ }); }, _getDefaultPopupPosition: function _getDefaultPopupPosition() { var position = getDefaultAlignment(this.option("rtlEnabled")); return { offset: { h: 0, v: -1 }, my: position + " top", at: position + " bottom", collision: "flip flip" }; }, _defaultOptionsRules: function _defaultOptionsRules() { return this.callBase().concat([{ device: function device(_device) { var isGeneric = _device.platform === "generic", isWin10 = _device.platform === "win" && _device.version && _device.version[0] === 10; return isGeneric || isWin10; }, options: { popupPosition: { offset: { v: 0 } } } }]); }, _inputWrapper: function _inputWrapper() { return this.$element().find("." + DROP_DOWN_EDITOR_INPUT_WRAPPER_CLASS); }, _init: function _init() { this.callBase(); this._initVisibilityActions(); this._initPopupInitializedAction(); }, _initVisibilityActions: function _initVisibilityActions() { this._openAction = this._createActionByOption("onOpened", { excludeValidators: ["disabled", "readOnly"] }); this._closeAction = this._createActionByOption("onClosed", { excludeValidators: ["disabled", "readOnly"] }); }, _initPopupInitializedAction: function _initPopupInitializedAction() { this._popupInitializedAction = this._createActionByOption("onPopupInitialized", { excludeValidators: ["disabled", "readOnly", "designMode"] }); }, _initMarkup: function _initMarkup() { this.callBase(); this.$element().addClass(DROP_DOWN_EDITOR_CLASS); this.setAria("role", "combobox"); }, _render: function _render() { this.callBase(); this._renderOpenHandler(); this._renderOpenedState(); }, _renderContentImpl: function _renderContentImpl() { if (!this.option("deferRendering")) { this._createPopup(); } }, _renderInput: function _renderInput() { this.callBase(); this.$element().wrapInner($("<div>").addClass(DROP_DOWN_EDITOR_INPUT_WRAPPER_CLASS)); this._$container = this.$element().children().eq(0); this.setAria({ "haspopup": "true", "autocomplete": "list" }); }, _readOnlyPropValue: function _readOnlyPropValue() { return !this.option("acceptCustomValue") || this.callBase(); }, _cleanFocusState: function _cleanFocusState() { this.callBase(); if (this.option("fieldTemplate")) { eventsEngine.off(this._input(), "focusin focusout beforeactivate"); } }, _renderField: function _renderField() { var fieldTemplate = this._getTemplateByOption("fieldTemplate"); if (!(fieldTemplate && this.option("fieldTemplate"))) { return; } this._renderTemplatedField(fieldTemplate, this._fieldRenderData()); }, _renderTemplatedField: function _renderTemplatedField(fieldTemplate, data) { var isFocused = focused(this._input()); this._resetFocus(isFocused); var $container = this._$container; $container.empty(); this._$dropDownButton = null; this._$clearButton = null; fieldTemplate.render({ model: data, container: domUtils.getPublicElement($container) }); if (!this._input().length) { throw errors.Error("E1010"); } this._refreshEvents(); this._refreshValueChangeEvent(); this._renderFocusState(); isFocused && eventsEngine.trigger(this._input(), "focus"); }, _resetFocus: function _resetFocus(isFocused) { this._cleanFocusState(); isFocused && eventsEngine.trigger(this._input(), "focusout"); }, _fieldRenderData: function _fieldRenderData() { return this.option("value"); }, _renderInputAddons: function _renderInputAddons() { this._renderField(); this.callBase(); this._renderDropDownButton(); }, _renderDropDownButton: function _renderDropDownButton() { if (this._$dropDownButton) { this._$dropDownButton.remove(); this._$dropDownButton = null; } var showDropDownButton = this.option("showDropDownButton"); this.$element().toggleClass(DROP_DOWN_EDITOR_BUTTON_VISIBLE, showDropDownButton); if (!showDropDownButton) return; this._$dropDownButton = this._createDropDownButton(); this._attachDropDownButtonClickHandler(); }, _attachDropDownButtonClickHandler: function _attachDropDownButtonClickHandler() { if (this.option("showDropDownButton") && !this.option("openOnFieldClick")) { this._$dropDownButton.dxButton("option", "onClick", this._openHandler.bind(this)); } }, _initTemplates: function _initTemplates() { this.callBase(); this._defaultTemplates['dropDownButton'] = new FunctionTemplate(function (options) { var $icon = $("<div>").addClass(DROP_DOWN_EDITOR_BUTTON_ICON); $(options.container).append($icon); }, this); }, _createDropDownButton: function _createDropDownButton() { var $button = $("<div>").addClass(DROP_DOWN_EDITOR_BUTTON_CLASS).prependTo(this._buttonsContainer()); this._createComponent($button, Button, { focusStateEnabled: false, hoverStateEnabled: false, activeStateEnabled: false, disabled: this.option("readOnly"), useInkRipple: false, template: this._getTemplateByOption("dropDownButtonTemplate") }); $button.removeClass("dx-button"); eventsEngine.on($button, "mousedown", function (e) { e.preventDefault(); }); return $button; }, _renderOpenHandler: function _renderOpenHandler() { var that = this, $inputWrapper = that._inputWrapper(), eventName = eventUtils.addNamespace(clickEvent.name, that.NAME), openOnFieldClick = that.option("openOnFieldClick"); eventsEngine.off($inputWrapper, eventName); eventsEngine.on($inputWrapper, eventName, that._getInputClickHandler(openOnFieldClick)); that.$element().toggleClass(DROP_DOWN_EDITOR_FIELD_CLICKABLE, openOnFieldClick); if (openOnFieldClick) { that._openOnFieldClickAction = that._createAction(that._openHandler.bind(that)); } }, _getInputClickHandler: function _getInputClickHandler(openOnFieldClick) { var that = this; return openOnFieldClick ? function (e) { that._executeOpenAction(e); } : function (e) { that._focusInput(); }; }, _openHandler: function _openHandler() { this._toggleOpenState(); }, _executeOpenAction: function _executeOpenAction(e) { this._openOnFieldClickAction({ event: e }); }, _keyboardEventBindingTarget: function _keyboardEventBindingTarget() { return this._input(); }, _focusInput: function _focusInput() { if (this.option("disabled")) { return false; } if (!focused(this._input())) { eventsEngine.trigger(this._input(), "focus"); } return true; }, _toggleOpenState: function _toggleOpenState(isVisible) { if (!this._focusInput()) { return; } if (!this.option("readOnly")) { isVisible = arguments.length ? isVisible : !this.option("opened"); this.option("opened", isVisible); } }, _renderOpenedState: function _renderOpenedState() { var opened = this.option("opened"); if (opened) { this._createPopup(); } this.$element().toggleClass(DROP_DOWN_EDITOR_ACTIVE, opened); this._setPopupOption("visible", opened); this.setAria({ "expanded": opened, "owns": (opened || undefined) && this._popupContentId }); }, _createPopup: function _createPopup() { if (this._$popup) { return; } this._$popup = $("<div>").addClass(DROP_DOWN_EDITOR_OVERLAY).addClass(this.option("customOverlayCssClass")).appendTo(this.$element()); this._renderPopup(); this._renderPopupContent(); }, _renderPopup: function _renderPopup() { this._popup = this._createComponent(this._$popup, Popup, this._popupConfig()); this._popup.on({ "showing": this._popupShowingHandler.bind(this), "shown": this._popupShownHandler.bind(this), "hiding": this._popupHidingHandler.bind(this), "hidden": this._popupHiddenHandler.bind(this) }); this._popup.option("onContentReady", this._contentReadyHandler.bind(this)); this._contentReadyHandler(); this._popupContentId = "dx-" + new Guid(); this.setAria("id", this._popupContentId, this._popup.$content()); }, _contentReadyHandler: commonUtils.noop, _popupConfig: function _popupConfig() { return { onInitialized: this._popupInitializedHandler(), position: extend(this.option("popupPosition"), { of: this.$element() }), showTitle: this.option("showPopupTitle"), width: "auto", height: "auto", shading: false, closeOnTargetScroll: true, closeOnOutsideClick: this._closeOutsideDropDownHandler.bind(this), animation: { show: { type: "fade", duration: 0, from: 0, to: 1 }, hide: { type: "fade", duration: 400, from: 1, to: 0 } }, deferRendering: false, focusStateEnabled: false, showCloseButton: false, toolbarItems: this._getPopupToolbarItems(), onPositioned: this._popupPositionedHandler.bind(this), fullScreen: false }; }, _popupInitializedHandler: function _popupInitializedHandler() { if (!this.option("onPopupInitialized")) { return; } return function (e) { this._popupInitializedAction({ popup: e.component }); }.bind(this); }, _popupPositionedHandler: function _popupPositionedHandler(e) { e.position && this._popup.overlayContent().toggleClass(DROP_DOWN_EDITOR_OVERLAY_FLIPPED, e.position.v.flip); }, _popupShowingHandler: commonUtils.noop, _popupHidingHandler: function _popupHidingHandler() { this.option("opened", false); }, _popupShownHandler: function _popupShownHandler() { this._openAction(); if (this._$validationMessage) { this._$validationMessage.dxOverlay("option", "position", this._getValidationMessagePosition()); } }, _popupHiddenHandler: function _popupHiddenHandler() { this._closeAction(); if (this._$validationMessage) { this._$validationMessage.dxOverlay("option", "position", this._getValidationMessagePosition()); } }, _getValidationMessagePosition: function _getValidationMessagePosition() { var positionRequest = "below"; if (this._popup && this._popup.option("visible")) { var myTop = positionUtils.setup(this.$element()).top, popupTop = positionUtils.setup(this._popup.$content()).top; positionRequest = myTop + this.option("popupPosition").offset.v > popupTop ? "below" : "above"; } return this.callBase(positionRequest); }, _renderPopupContent: function _renderPopupContent() { var contentTemplate = this._getTemplateByOption("contentTemplate"); if (!(contentTemplate && this.option("contentTemplate"))) { return; } var $popupContent = this._popup.$content(), templateData = { value: this._fieldRenderData(), component: this }; $popupContent.empty(); contentTemplate.render({ container: domUtils.getPublicElement($popupContent), model: templateData }); }, _closeOutsideDropDownHandler: function _closeOutsideDropDownHandler(e) { var $target = $(e.target); var isInputClicked = !!$target.closest(this.$element()).length; var isDropDownButtonClicked = !!$target.closest(this._$dropDownButton).length; var isOutsideClick = !isInputClicked && !isDropDownButtonClicked; return isOutsideClick; }, _clean: function _clean() { delete this._$dropDownButton; delete this._openOnFieldClickAction; if (this._$popup) { this._$popup.remove(); delete this._$popup; delete this._popup; } this.callBase(); }, _setPopupOption: function _setPopupOption(optionName, value) { this._setWidgetOption("_popup", arguments); }, _validatedOpening: function _validatedOpening() { if (!this.option("readOnly")) { this._toggleOpenState(true); } }, _getPopupToolbarItems: function _getPopupToolbarItems() { return this.option("applyValueMode") === "useButtons" ? this._popupToolbarItemsConfig() : []; }, _getFirstPopupElement: function _getFirstPopupElement() { return this._popup._wrapper().find(".dx-popup-done.dx-button"); }, _getLastPopupElement: function _getLastPopupElement() { return this._popup._wrapper().find(".dx-popup-cancel.dx-button"); }, _popupElementTabHandler: function _popupElementTabHandler(e) { var $element = $(e.currentTarget); if (e.shiftKey && $element.is(this._getFirstPopupElement()) || !e.shiftKey && $element.is(this._getLastPopupElement())) { eventsEngine.trigger(this._input(), "focus"); e.preventDefault(); } }, _popupElementEscHandler: function _popupElementEscHandler() { eventsEngine.trigger(this._input(), "focus"); this.close(); }, _popupButtonInitializedHandler: function _popupButtonInitializedHandler(e) { e.component.registerKeyHandler("tab", this._popupElementTabHandler.bind(this)); e.component.registerKeyHandler("escape", this._popupElementEscHandler.bind(this)); }, _popupToolbarItemsConfig: function _popupToolbarItemsConfig() { var buttonsConfig = [{ shortcut: "done", options: { onClick: this._applyButtonHandler.bind(this), text: this.option("applyButtonText"), onInitialized: this._popupButtonInitializedHandler.bind(this) } }, { shortcut: "cancel", options: { onClick: this._cancelButtonHandler.bind(this), text: this.option("cancelButtonText"), onInitialized: this._popupButtonInitializedHandler.bind(this) } }]; return this._applyButtonsLocation(buttonsConfig); }, _applyButtonsLocation: function _applyButtonsLocation(buttonsConfig) { var buttonsLocation = this.option("buttonsLocation"), resultConfig = buttonsConfig; if (buttonsLocation !== "default") { var position = commonUtils.splitPair(buttonsLocation); each(resultConfig, function (_, element) { extend(element, { toolbar: position[0], location: position[1] }); }); } return resultConfig; }, _applyButtonHandler: function _applyButtonHandler() { this.close(); this.option("focusStateEnabled") && this.focus(); }, _cancelButtonHandler: function _cancelButtonHandler() { this.close(); this.option("focusStateEnabled") && this.focus(); }, _toggleReadOnlyState: function _toggleReadOnlyState() { this.callBase(); this._$dropDownButton && this._$dropDownButton.dxButton("option", "disabled", this.option("readOnly")); }, _optionChanged: function _optionChanged(args) { switch (args.name) { case "opened": this._renderOpenedState(); break; case "onOpened": case "onClosed": this._initVisibilityActions(); break; case "onPopupInitialized": this._initPopupInitializedAction(); break; case "fieldTemplate": if (isDefined(args.value)) { this._renderInputAddons(); } else { this._invalidate(); } break; case "showDropDownButton": case "contentTemplate": case "acceptCustomValue": case "openOnFieldClick": this._invalidate(); break; case "dropDownButtonTemplate": this._renderDropDownButton(); break; case "popupPosition": case "deferRendering": break; case "applyValueMode": case "applyButtonText": case "cancelButtonText": case "buttonsLocation": this._setPopupOption("toolbarItems", this._getPopupToolbarItems()); break; case "showPopupTitle": this._setPopupOption("showTitle", args.value); break; default: this.callBase(args); } }, /** * @name dxDropDownEditorMethods.open * @publicName open() */ open: function open() { this.option("opened", true); }, /** * @name dxDropDownEditorMethods.close * @publicName close() */ close: function close() { this.option("opened", false); }, /** * @name dxDropDownEditorMethods.reset * @publicName reset() */ reset: function reset() { this.option("value", null); this._input().val(""); }, /** * @name dxDropDownEditorMethods.field * @publicName field() * @return dxElement */ field: function field() { return getPublicElement(this._input()); }, /** * @name dxDropDownEditorMethods.content * @publicName content() * @return dxElement */ content: function content() { return this._popup ? this._popup.content() : null; } }); registerComponent("dxDropDownEditor", DropDownEditor); module.exports = DropDownEditor;