UNPKG

devextreme

Version:

JavaScript/TypeScript Component Suite for Responsive Web Development

313 lines (312 loc) • 10.8 kB
/** * DevExtreme (esm/__internal/ui/drop_down_box.js) * Version: 26.1.4 * Build date: Fri Jul 24 2026 * * Copyright (c) 2012 - 2026 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ import eventsEngine from "../../common/core/events/core/events_engine"; import { normalizeKeyName } from "../../common/core/events/utils/index"; import registerComponent from "../../core/component_registrator"; import devices from "../../core/devices"; import domAdapter from "../../core/dom_adapter"; import { getPublicElement } from "../../core/element"; import $ from "../../core/renderer"; import { Deferred, when } from "../../core/utils/deferred"; import { extend } from "../../core/utils/extend"; import { map } from "../../core/utils/iterator"; import { isDefined, isObject } from "../../core/utils/type"; import DataExpressionMixin from "../../ui/editor/ui.data_expression"; import { grep } from "../core/utils/m_common"; import { tabbable } from "../core/utils/m_selectors"; import DropDownEditor from "../ui/drop_down_editor/drop_down_editor"; import { getElementMaxHeightByWindow } from "../ui/overlay/utils"; const { getActiveElement: getActiveElement } = domAdapter; const DROP_DOWN_BOX_CLASS = "dx-dropdownbox"; const ANONYMOUS_TEMPLATE_NAME = "content"; class DropDownBox extends DropDownEditor { _supportedKeys() { return Object.assign({}, super._supportedKeys(), { tab(e) { const { opened: opened } = this.option(); if (!opened) { return } const $tabbableElements = this._getTabbableElements(); const $focusableElement = e.shiftKey ? $tabbableElements.last() : $tabbableElements.first(); if ($focusableElement) { eventsEngine.trigger($focusableElement, "focus") } e.preventDefault() } }) } _getTabbableElements() { return this._getElements().filter(tabbable) } _getElements() { return $(this.content()).find("*") } _getDefaultOptions() { return Object.assign({}, super._getDefaultOptions(), { acceptCustomValue: false, contentTemplate: "content", openOnFieldClick: true, displayValueFormatter: value => Array.isArray(value) ? value.join(", ") : value, useHiddenSubmitElement: true }) } _getAnonymousTemplateName() { return "content" } _initTemplates() { super._initTemplates() } _initMarkup() { this._initDataExpressions(); this.$element().addClass("dx-dropdownbox"); super._initMarkup() } _setSubmitValue() { const { value: value } = this.option(); const submitValue = this._shouldUseDisplayValue(value) ? this._displayGetter(value) : value; this._getSubmitElement().val(submitValue) } _shouldUseDisplayValue(value) { const { valueExpr: valueExpr } = this.option(); return "this" === valueExpr && isObject(value) } _sortValuesByKeysOrder(orderedKeys, values) { const sortedValues = values.sort((a, b) => orderedKeys.indexOf(a.itemKey) - orderedKeys.indexOf(b.itemKey)); return sortedValues.map(x => x.itemDisplayValue) } _renderInputValue() { let { renderOnly: renderOnly } = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; this._rejectValueLoading(); const values = []; if (!this._dataSource) { super._renderInputValue({ renderOnly: renderOnly, value: values }); return Deferred().resolve() } const rawValue = this._getCurrentValue() ?? []; const keys = Array.isArray(rawValue) ? rawValue : [rawValue]; const itemLoadDeferreds = map(keys, key => { const deferred = Deferred(); this._loadItem(key).always(item => { const displayValue = this._displayGetter(item); const { acceptCustomValue: acceptCustomValue } = this.option(); if (isDefined(displayValue)) { values.push({ itemKey: key, itemDisplayValue: displayValue }) } else if (acceptCustomValue) { values.push({ itemKey: key, itemDisplayValue: key }) } deferred.resolve() }); return deferred }); const callBase = super._renderInputValue.bind(this); return when.apply(this, itemLoadDeferreds).always(() => { const orderedValues = this._sortValuesByKeysOrder(keys, values); this.option("displayValue", orderedValues); callBase({ renderOnly: renderOnly, value: values.length && orderedValues }) }) } _loadItem(value) { const deferred = Deferred(); const selectedItem = grep(this.option("items") || [], item => this._isValueEquals(this._valueGetter(item), value), false)[0]; if (void 0 !== selectedItem) { deferred.resolve(selectedItem) } else { this._loadValue(value).done(item => { deferred.resolve(item) }).fail(args => { if (null !== args && void 0 !== args && args.shouldSkipCallback) { return } if (this.option("acceptCustomValue")) { deferred.resolve(value) } else { deferred.reject() } }) } return deferred } _popupTabHandler(e) { if ("tab" !== normalizeKeyName(e)) { return } const $firstTabbable = this._getTabbableElements().first().get(0); const $lastTabbable = this._getTabbableElements().last().get(0); const $target = e.target; const moveBackward = !!($target === $firstTabbable && e.shiftKey); const moveForward = !!($target === $lastTabbable && !e.shiftKey); if (moveBackward || moveForward) { this.close(); eventsEngine.trigger(this._input(), "focus"); if (moveBackward) { e.preventDefault() } } } _renderPopupContent() { var _this$_popup; const { contentTemplate: optionContentTemplate } = this.option(); if ("content" === optionContentTemplate) { return } const contentTemplate = this._getTemplateByOption("contentTemplate"); if (!(contentTemplate && optionContentTemplate)) { return } const $popupContent = null === (_this$_popup = this._popup) || void 0 === _this$_popup ? void 0 : _this$_popup.$content(); if (!$popupContent) { return } const templateData = { value: this._fieldRenderData(), component: this }; $popupContent.empty(); contentTemplate.render({ container: getPublicElement($popupContent), model: templateData }) } _canShowVirtualKeyboard() { return !!devices.real().mac } _isNestedElementActive() { var _this$_popup2; const activeElement = getActiveElement(); return !!(activeElement && null !== (_this$_popup2 = this._popup) && void 0 !== _this$_popup2 && null !== (_this$_popup2 = _this$_popup2.$content()) && void 0 !== _this$_popup2 && null !== (_this$_popup2 = _this$_popup2.get(0)) && void 0 !== _this$_popup2 && _this$_popup2.contains(activeElement)) } _shouldHideOnParentScroll() { return "desktop" === devices.real().deviceType && this._canShowVirtualKeyboard() && this._isNestedElementActive() } _popupHiddenHandler() { super._popupHiddenHandler(); this._popupPosition = void 0 } _popupPositionedHandler(e) { super._popupPositionedHandler(e); this._popupPosition = e.position } _getDefaultPopupPosition(isRtlEnabled) { const { my: my, at: at } = super._getDefaultPopupPosition(isRtlEnabled); return { my: my, at: at, offset: { v: -1 }, collision: "flipfit" } } _popupConfig() { const { focusStateEnabled: focusStateEnabled } = this.option(); return Object.assign({}, super._popupConfig(), { tabIndex: -1, dragEnabled: false, focusStateEnabled: focusStateEnabled, contentTemplate: "content", hideOnParentScroll: this._shouldHideOnParentScroll.bind(this), position: extend(this.option("popupPosition"), { of: this.$element() }), _ignoreFunctionValueDeprecation: true, maxHeight: () => { var _this$_popupPosition; const popupLocation = null === (_this$_popupPosition = this._popupPosition) || void 0 === _this$_popupPosition ? void 0 : _this$_popupPosition.v.location; return getElementMaxHeightByWindow(this.$element(), popupLocation) } }) } _popupShowingHandler() { super._popupShowingHandler(); this._updatePopupWidth() } _popupShownHandler() { super._popupShownHandler(); const $firstElement = this._getTabbableElements().first(); eventsEngine.trigger($firstElement, "focus") } _setCollectionWidgetOption() {} _shouldLogFieldTemplateDeprecationWarning() { return true } _optionChanged(args) { this._dataExpressionOptionChanged(args); switch (args.name) { case "dataSource": this._renderInputValue(); break; case "displayValue": this.option("text", args.value); break; case "displayExpr": this._renderValue(); break; case "contentTemplate": this._invalidate(); break; default: super._optionChanged(args) } } } DropDownBox.include(DataExpressionMixin); registerComponent("dxDropDownBox", DropDownBox); export default DropDownBox;