UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

660 lines (659 loc) • 25.3 kB
/** * DevExtreme (esm/ui/date_box/ui.date_box.base.js) * Version: 21.1.4 * Build date: Mon Jun 21 2021 * * Copyright (c) 2012 - 2021 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ import { getWindow, hasWindow } from "../../core/utils/window"; var window = getWindow(); import registerComponent from "../../core/component_registrator"; import { isDate as isDateType, isString, isNumeric } from "../../core/utils/type"; import { createTextElementHiddenCopy } from "../../core/utils/dom"; import { each } from "../../core/utils/iterator"; import { compare as compareVersions } from "../../core/utils/version"; import { extend } from "../../core/utils/extend"; import { inputType } from "../../core/utils/support"; import devices from "../../core/devices"; import config from "../../core/config"; import dateUtils from "../../core/utils/date"; import uiDateUtils from "./ui.date_utils"; import dateSerialization from "../../core/utils/date_serialization"; import DropDownEditor from "../drop_down_editor/ui.drop_down_editor"; import dateLocalization from "../../localization/date"; import messageLocalization from "../../localization/message"; import Calendar from "./ui.date_box.strategy.calendar"; import DateView from "./ui.date_box.strategy.date_view"; import Native from "./ui.date_box.strategy.native"; import CalendarWithTime from "./ui.date_box.strategy.calendar_with_time"; import List from "./ui.date_box.strategy.list"; var DATEBOX_CLASS = "dx-datebox"; var DX_AUTO_WIDTH_CLASS = "dx-auto-width"; var DX_INVALID_BADGE_CLASS = "dx-show-invalid-badge"; var DX_CLEAR_BUTTON_CLASS = "dx-clear-button-area"; var DATEBOX_WRAPPER_CLASS = "dx-datebox-wrapper"; var PICKER_TYPE = { calendar: "calendar", rollers: "rollers", list: "list", native: "native" }; var TYPE = { date: "date", datetime: "datetime", time: "time" }; var STRATEGY_NAME = { calendar: "Calendar", dateView: "DateView", native: "Native", calendarWithTime: "CalendarWithTime", list: "List" }; var STRATEGY_CLASSES = { Calendar: Calendar, DateView: DateView, Native: Native, CalendarWithTime: CalendarWithTime, List: List }; var DateBox = DropDownEditor.inherit({ _supportedKeys: function() { return extend(this.callBase(), this._strategy.supportedKeys()) }, _renderButtonContainers: function() { this.callBase.apply(this, arguments); this._strategy.customizeButtons() }, _getDefaultOptions: function() { return extend(this.callBase(), { type: "date", showAnalogClock: true, value: null, dateSerializationFormat: void 0, min: void 0, max: void 0, displayFormat: null, interval: 30, disabledDates: null, pickerType: PICKER_TYPE.calendar, invalidDateMessage: messageLocalization.format("dxDateBox-validation-datetime"), dateOutOfRangeMessage: messageLocalization.format("validation-range"), applyButtonText: messageLocalization.format("OK"), adaptivityEnabled: false, calendarOptions: {}, useHiddenSubmitElement: true }) }, _defaultOptionsRules: function() { return this.callBase().concat([{ device: { platform: "ios" }, options: { "dropDownOptions.showTitle": true } }, { device: { platform: "android" }, options: { buttonsLocation: "bottom after" } }, { device: function() { var realDevice = devices.real(); var platform = realDevice.platform; return "ios" === platform || "android" === platform }, options: { pickerType: PICKER_TYPE.native } }, { device: function(currentDevice) { var realDevice = devices.real(); var platform = realDevice.platform; var version = realDevice.version; return "generic" === platform && "desktop" !== currentDevice.deviceType || "android" === platform && compareVersions(version, [4, 4]) < 0 }, options: { pickerType: PICKER_TYPE.rollers } }, { device: { platform: "generic", deviceType: "desktop" }, options: { buttonsLocation: "bottom after" } }]) }, _initOptions: function(options) { this._userOptions = extend({}, options); this.callBase(options); this._updatePickerOptions() }, _updatePickerOptions: function() { var pickerType = this.option("pickerType"); var type = this.option("type"); if (pickerType === PICKER_TYPE.list && (type === TYPE.datetime || type === TYPE.date)) { pickerType = PICKER_TYPE.calendar } if (type === TYPE.time && pickerType === PICKER_TYPE.calendar) { pickerType = PICKER_TYPE.list } this.option("showDropDownButton", "generic" !== devices.real().platform || pickerType !== PICKER_TYPE.native); this._pickerType = pickerType }, _init: function() { this._initStrategy(); this.option(extend({}, this._strategy.getDefaultOptions(), this._userOptions)); delete this._userOptions; this.callBase() }, _toLowerCaseFirstLetter: function(string) { return string.charAt(0).toLowerCase() + string.substr(1) }, _initStrategy: function() { var strategyName = this._getStrategyName(this._getFormatType()); var strategy = STRATEGY_CLASSES[strategyName]; if (!(this._strategy && this._strategy.NAME === strategyName)) { this._strategy = new strategy(this) } }, _getFormatType: function() { var currentType = this.option("type"); var isTime = /h|m|s/g.test(currentType); var isDate = /d|M|Y/g.test(currentType); var type = ""; if (isDate) { type += TYPE.date } if (isTime) { type += TYPE.time } return type }, _getStrategyName: function(type) { var pickerType = this._pickerType; if (pickerType === PICKER_TYPE.rollers) { return STRATEGY_NAME.dateView } else if (pickerType === PICKER_TYPE.native) { return STRATEGY_NAME.native } if (type === TYPE.date) { return STRATEGY_NAME.calendar } if (type === TYPE.datetime) { return STRATEGY_NAME.calendarWithTime } return STRATEGY_NAME.list }, _initMarkup: function() { this.$element().addClass(DATEBOX_CLASS); this.callBase(); this._refreshFormatClass(); this._refreshPickerTypeClass(); this._strategy.renderInputMinMax(this._input()) }, _render: function() { this.callBase(); this._formatValidationIcon() }, _renderDimensions: function() { this.callBase(); this.$element().toggleClass(DX_AUTO_WIDTH_CLASS, !this.option("width")); this._dimensionChanged() }, _dimensionChanged: function() { this.callBase(arguments); if (this._popup) { var _this$_strategy$_upda, _this$_strategy; null === (_this$_strategy$_upda = (_this$_strategy = this._strategy)._updatePopupHeight) || void 0 === _this$_strategy$_upda ? void 0 : _this$_strategy$_upda.call(_this$_strategy) } }, _refreshFormatClass: function() { var $element = this.$element(); each(TYPE, (function(_, item) { $element.removeClass(DATEBOX_CLASS + "-" + item) })); $element.addClass(DATEBOX_CLASS + "-" + this.option("type")) }, _refreshPickerTypeClass: function() { var $element = this.$element(); each(PICKER_TYPE, (function(_, item) { $element.removeClass(DATEBOX_CLASS + "-" + item) })); $element.addClass(DATEBOX_CLASS + "-" + this._pickerType) }, _formatValidationIcon: function() { if (!hasWindow()) { return } var inputElement = this._input().get(0); var isRtlEnabled = this.option("rtlEnabled"); var clearButtonWidth = this._getClearButtonWidth(); var longestElementDimensions = this._getLongestElementDimensions(); var curWidth = parseFloat(window.getComputedStyle(inputElement).width) - clearButtonWidth; var shouldHideValidationIcon = longestElementDimensions.width > curWidth; var style = inputElement.style; this.$element().toggleClass(DX_INVALID_BADGE_CLASS, !shouldHideValidationIcon); if (shouldHideValidationIcon) { if (void 0 === this._storedPadding) { this._storedPadding = isRtlEnabled ? longestElementDimensions.leftPadding : longestElementDimensions.rightPadding } isRtlEnabled ? style.paddingLeft = 0 : style.paddingRight = 0 } else { isRtlEnabled ? style.paddingLeft = this._storedPadding + "px" : style.paddingRight = this._storedPadding + "px" } }, _getClearButtonWidth: function() { var clearButtonWidth = 0; if (this._isClearButtonVisible() && "" === this._input().val()) { var clearButtonElement = this.$element().find("." + DX_CLEAR_BUTTON_CLASS).get(0); clearButtonWidth = parseFloat(window.getComputedStyle(clearButtonElement).width) } return clearButtonWidth }, _getLongestElementDimensions: function() { var format = this._strategy.getDisplayFormat(this.option("displayFormat")); var longestValue = dateLocalization.format(uiDateUtils.getLongestDate(format, dateLocalization.getMonthNames(), dateLocalization.getDayNames()), format); var $input = this._input(); var inputElement = $input.get(0); var $longestValueElement = createTextElementHiddenCopy($input, longestValue); var isPaddingStored = void 0 !== this._storedPadding; var storedPadding = !isPaddingStored ? 0 : this._storedPadding; $longestValueElement.appendTo(this.$element()); var elementWidth = parseFloat(window.getComputedStyle($longestValueElement.get(0)).width); var rightPadding = parseFloat(window.getComputedStyle(inputElement).paddingRight); var leftPadding = parseFloat(window.getComputedStyle(inputElement).paddingLeft); var necessaryWidth = elementWidth + leftPadding + rightPadding + storedPadding; $longestValueElement.remove(); return { width: necessaryWidth, leftPadding: leftPadding, rightPadding: rightPadding } }, _getKeyboardListeners() { return this.callBase().concat([this._strategy && this._strategy.getKeyboardListener()]) }, _renderPopup: function() { this.callBase(); this._popup.$wrapper().addClass(DATEBOX_WRAPPER_CLASS); this._renderPopupWrapper() }, _popupConfig: function() { var popupConfig = this.callBase(); return extend(this._strategy.popupConfig(popupConfig), { title: this._getPopupTitle(), dragEnabled: false }) }, _renderPopupWrapper: function() { if (!this._popup) { return } var $element = this.$element(); var classPostfixes = extend({}, TYPE, PICKER_TYPE); each(classPostfixes, function(_, item) { $element.removeClass(DATEBOX_WRAPPER_CLASS + "-" + item) }.bind(this)); this._popup.$wrapper().addClass(DATEBOX_WRAPPER_CLASS + "-" + this.option("type")).addClass(DATEBOX_WRAPPER_CLASS + "-" + this._pickerType) }, _renderPopupContent: function() { this.callBase(); this._strategy.renderPopupContent() }, _getFirstPopupElement: function() { return this._strategy.getFirstPopupElement() || this.callBase() }, _getLastPopupElement: function() { return this._strategy.getLastPopupElement() || this.callBase() }, _popupShowingHandler: function() { this.callBase(); this._strategy.popupShowingHandler() }, _popupShownHandler: function() { this.callBase(); this._strategy.renderOpenedState() }, _popupHiddenHandler: function() { this.callBase(); this._strategy.renderOpenedState(); this._strategy.popupHiddenHandler() }, _visibilityChanged: function(visible) { if (visible) { this._formatValidationIcon() } }, _clearValueHandler: function(e) { this.option("text", ""); this.callBase(e) }, _readOnlyPropValue: function() { if (this._pickerType === PICKER_TYPE.rollers) { return true } var platform = devices.real().platform; var isCustomValueDisabled = this._isNativeType() && ("ios" === platform || "android" === platform); if (isCustomValueDisabled) { return this.option("readOnly") } return this.callBase() }, _isClearButtonVisible: function() { return this.callBase() && !this._isNativeType() }, _renderValue: function() { var value = this.dateOption("value"); this.option("text", this._getDisplayedText(value)); this._strategy.renderValue(); return this.callBase() }, _setSubmitValue: function() { var value = this.dateOption("value"); var dateSerializationFormat = this.option("dateSerializationFormat"); var submitFormat = uiDateUtils.SUBMIT_FORMATS_MAP[this.option("type")]; var submitValue = dateSerializationFormat ? dateSerialization.serializeDate(value, dateSerializationFormat) : uiDateUtils.toStandardDateFormat(value, submitFormat); this._getSubmitElement().val(submitValue) }, _getDisplayedText: function(value) { var mode = this.option("mode"); var displayedText; if ("text" === mode) { var displayFormat = this._strategy.getDisplayFormat(this.option("displayFormat")); displayedText = dateLocalization.format(value, displayFormat) } else { var format = this._getFormatByMode(mode); if (format) { displayedText = dateLocalization.format(value, format) } else { displayedText = uiDateUtils.toStandardDateFormat(value, mode) } } return displayedText }, _getFormatByMode: function(mode) { return inputType(mode) ? null : uiDateUtils.FORMATS_MAP[mode] }, _valueChangeEventHandler: function(e) { var text = this.option("text"); var currentValue = this.dateOption("value"); if (text === this._getDisplayedText(currentValue)) { this._applyInternalValidation(currentValue); return } var parsedDate = this._getParsedDate(text); var value = null !== currentValue && void 0 !== currentValue ? currentValue : this._getDateByDefault(); var type = this.option("type"); var newValue = uiDateUtils.mergeDates(value, parsedDate, type); var date = parsedDate && "time" === type ? newValue : parsedDate; if (this._applyInternalValidation(date).isValid) { var displayedText = this._getDisplayedText(newValue); if (value && newValue && value.getTime() === newValue.getTime() && displayedText !== text) { this._renderValue() } else { this.dateValue(newValue, e) } } }, _getDateByDefault: function() { return this._strategy.useCurrentDateByDefault() && this._strategy.getDefaultDate() }, _getParsedDate: function(text) { var displayFormat = this._strategy.getDisplayFormat(this.option("displayFormat")); var parsedText = this._strategy.getParsedText(text, displayFormat); return null !== parsedText && void 0 !== parsedText ? parsedText : void 0 }, _applyInternalValidation(value) { var text = this.option("text"); var hasText = !!text && null !== value; var isDate = !!value && isDateType(value) && !isNaN(value.getTime()); var isDateInRange = isDate && dateUtils.dateInRange(value, this.dateOption("min"), this.dateOption("max"), this.option("type")); var isValid = !hasText && !value || isDateInRange; var validationMessage = ""; if (!isDate) { validationMessage = this.option("invalidDateMessage") } else if (!isDateInRange) { validationMessage = this.option("dateOutOfRangeMessage") } this.option({ isValid: isValid, validationError: isValid ? null : { editorSpecific: true, message: validationMessage } }); return { isValid: isValid, isDate: isDate } }, _applyCustomValidation: function(value) { this.validationRequest.fire({ editor: this, value: this._serializeDate(value) }) }, _isValueChanged: function(newValue) { var oldValue = this.dateOption("value"); var oldTime = oldValue && oldValue.getTime(); var newTime = newValue && newValue.getTime(); return oldTime !== newTime }, _isTextChanged: function(newValue) { var oldText = this.option("text"); var newText = newValue && this._getDisplayedText(newValue) || ""; return oldText !== newText }, _renderProps: function() { this.callBase(); this._input().attr("autocomplete", "off") }, _renderOpenedState: function() { if (!this._isNativeType()) { this.callBase() } if (this._strategy.isAdaptivityChanged()) { this._refreshStrategy() } }, _getPopupTitle: function() { var placeholder = this.option("placeholder"); if (placeholder) { return placeholder } var type = this.option("type"); if (type === TYPE.time) { return messageLocalization.format("dxDateBox-simulatedDataPickerTitleTime") } if (type === TYPE.date || type === TYPE.datetime) { return messageLocalization.format("dxDateBox-simulatedDataPickerTitleDate") } return "" }, _renderPlaceholder: function() { this._popup && this._popup.option("title", this._getPopupTitle()); this.callBase() }, _refreshStrategy: function() { this._strategy.dispose(); this._initStrategy(); this.option(this._strategy.getDefaultOptions()); this._refresh() }, _applyButtonHandler: function(e) { var value = this._strategy.getValue(); var { isValid: isValid, isDate: isDate } = this._applyInternalValidation(value); if (isValid) { this.dateValue(value, e.event) } else if (isDate) { var displayedText = this._getDisplayedText(value); this.option("text", displayedText); this._renderDisplayText(displayedText) } this.callBase() }, _dispose: function() { var _this$_strategy2; this.callBase(); null === (_this$_strategy2 = this._strategy) || void 0 === _this$_strategy2 ? void 0 : _this$_strategy2.dispose() }, _isNativeType: function() { return this._pickerType === PICKER_TYPE.native }, _optionChanged: function(args) { switch (args.name) { case "showClearButton": case "buttons": this.callBase.apply(this, arguments); this._formatValidationIcon(); break; case "pickerType": this._updatePickerOptions({ pickerType: args.value }); this._refreshStrategy(); this._refreshPickerTypeClass(); this._invalidate(); break; case "type": this._updatePickerOptions({ format: args.value }); this._refreshStrategy(); this._refreshFormatClass(); this._renderPopupWrapper(); this._formatValidationIcon(); this._updateValue(); break; case "placeholder": this._renderPlaceholder(); break; case "min": case "max": var isValid = this.option("isValid"); this._applyInternalValidation(this.dateOption("value")); if (!isValid) { this._applyCustomValidation(this.dateOption("value")) } this._invalidate(); break; case "dateSerializationFormat": case "interval": case "disabledDates": case "calendarOptions": this._invalidate(); break; case "displayFormat": this.option("text", this._getDisplayedText(this.dateOption("value"))); this._renderInputValue(); break; case "text": this._strategy.textChangedHandler(args.value); this.callBase.apply(this, arguments); break; case "isValid": this.callBase.apply(this, arguments); this._formatValidationIcon(); break; case "showDropDownButton": this._formatValidationIcon(); this.callBase.apply(this, arguments); break; case "readOnly": this.callBase.apply(this, arguments); this._formatValidationIcon(); break; case "invalidDateMessage": case "dateOutOfRangeMessage": case "adaptivityEnabled": case "showAnalogClock": break; default: this.callBase.apply(this, arguments) } }, _getSerializationFormat: function() { var value = this.option("value"); if (this.option("dateSerializationFormat") && config().forceIsoDateParsing) { return this.option("dateSerializationFormat") } if (isNumeric(value)) { return "number" } if (!isString(value)) { return } return dateSerialization.getDateSerializationFormat(value) }, _updateValue: function(value) { this.callBase(); this._applyInternalValidation(null !== value && void 0 !== value ? value : this.dateOption("value")) }, dateValue: function(value, dxEvent) { var isValueChanged = this._isValueChanged(value); if (isValueChanged && dxEvent) { this._saveValueChangeEvent(dxEvent) } if (!isValueChanged) { if (this._isTextChanged(value)) { this._updateValue(value) } else if ("" === this.option("text")) { this._applyCustomValidation(value) } } return this.dateOption("value", value) }, dateOption: function(optionName, value) { if (1 === arguments.length) { return dateSerialization.deserializeDate(this.option(optionName)) } this.option(optionName, this._serializeDate(value)) }, _serializeDate: function(date) { var serializationFormat = this._getSerializationFormat(); return dateSerialization.serializeDate(date, serializationFormat) }, _clearValue: function() { var value = this.option("value"); this.callBase(); if (null === value) { this._applyCustomValidation(null) } }, reset: function() { var value = this.option("value"); this.callBase(); if (null === value) { this._applyInternalValidation(null) } } }); registerComponent("dxDateBox", DateBox); export default DateBox;