devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
397 lines (394 loc) • 14.6 kB
JavaScript
/**
* DevExtreme (cjs/__internal/ui/number_box/m_number_box.base.js)
* Version: 24.2.6
* Build date: Mon Mar 17 2025
*
* Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED
* Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
*/
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _events_engine = _interopRequireDefault(require("../../../common/core/events/core/events_engine"));
var _index = require("../../../common/core/events/utils/index");
var _message = _interopRequireDefault(require("../../../common/core/localization/message"));
var _devices = _interopRequireDefault(require("../../../core/devices"));
var _dom_adapter = _interopRequireDefault(require("../../../core/dom_adapter"));
var _renderer = _interopRequireDefault(require("../../../core/renderer"));
var _browser = _interopRequireDefault(require("../../../core/utils/browser"));
var _common = require("../../../core/utils/common");
var _deferred = require("../../../core/utils/deferred");
var _math = require("../../../core/utils/math");
var _type = require("../../../core/utils/type");
var _m_text_editor = _interopRequireDefault(require("../../ui/text_box/m_text_editor"));
var _m_number_box = _interopRequireDefault(require("./m_number_box.spins"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : {
default: e
}
}
function _extends() {
return _extends = Object.assign ? Object.assign.bind() : function(n) {
for (var e = 1; e < arguments.length; e++) {
var t = arguments[e];
for (var r in t) {
({}).hasOwnProperty.call(t, r) && (n[r] = t[r])
}
}
return n
}, _extends.apply(null, arguments)
}
const math = Math;
const WIDGET_CLASS = "dx-numberbox";
const FIREFOX_CONTROL_KEYS = ["tab", "del", "backspace", "leftArrow", "rightArrow", "home", "end", "enter"];
const FORCE_VALUECHANGE_EVENT_NAMESPACE = "NumberBoxForceValueChange";
class NumberBoxBase extends _m_text_editor.default {
_supportedKeys() {
return _extends({}, super._supportedKeys(), {
upArrow(e) {
if (!(0, _index.isCommandKeyPressed)(e)) {
e.preventDefault();
e.stopPropagation();
this._spinUpChangeHandler(e)
}
},
downArrow(e) {
if (!(0, _index.isCommandKeyPressed)(e)) {
e.preventDefault();
e.stopPropagation();
this._spinDownChangeHandler(e)
}
},
enter() {}
})
}
_getDefaultOptions() {
return _extends({}, super._getDefaultOptions(), {
value: 0,
min: void 0,
max: void 0,
step: 1,
showSpinButtons: false,
useLargeSpinButtons: true,
mode: "text",
invalidValueMessage: _message.default.format("dxNumberBox-invalidValueMessage"),
buttons: void 0
})
}
_useTemplates() {
return false
}
_getDefaultButtons() {
return super._getDefaultButtons().concat([{
name: "spins",
Ctor: _m_number_box.default
}])
}
_isSupportInputMode() {
const version = parseFloat(_browser.default.version);
return _browser.default.chrome && version >= 66 || _browser.default.safari && version >= 12
}
_defaultOptionsRules() {
return super._defaultOptionsRules().concat([{
device: () => _devices.default.real().generic && !_devices.default.isSimulator(),
options: {
useLargeSpinButtons: false
}
}, {
device: function() {
return "desktop" !== _devices.default.real().deviceType && !this._isSupportInputMode()
}.bind(this),
options: {
mode: "number"
}
}])
}
_initMarkup() {
this._renderSubmitElement();
this.$element().addClass(WIDGET_CLASS);
super._initMarkup();
this._toggleTabIndex()
}
_getDefaultAttributes() {
const attributes = super._getDefaultAttributes();
attributes.inputmode = "decimal";
return attributes
}
_renderContentImpl() {
this.option("isValid") && this._validateValue(this.option("value"));
this.setAria("role", "spinbutton")
}
_renderSubmitElement() {
this._$submitElement = (0, _renderer.default)("<input>").attr("type", "hidden").appendTo(this.$element());
this._setSubmitValue(this.option("value"))
}
_setSubmitValue(value) {
this._getSubmitElement().val((0, _common.applyServerDecimalSeparator)(value))
}
_getSubmitElement() {
return this._$submitElement
}
_keyPressHandler(e) {
super._keyPressHandler();
const char = (0, _index.getChar)(e);
const isInputCharValid = /[\d.,eE\-+]/.test(char);
if (!isInputCharValid) {
const keyName = (0, _index.normalizeKeyName)(e);
if ((0, _index.isCommandKeyPressed)(e) || keyName && FIREFOX_CONTROL_KEYS.includes(keyName)) {
return
}
e.preventDefault();
return
}
this._keyPressed = true
}
_onMouseWheel(dxEvent) {
dxEvent.delta > 0 ? this._spinValueChange(1, dxEvent) : this._spinValueChange(-1, dxEvent)
}
_renderValue() {
const inputValue = this._input().val();
const value = this.option("value");
if (!inputValue.length || Number(inputValue) !== value) {
this._forceValueRender();
this._toggleEmptinessEventHandler()
}
const valueText = (0, _type.isDefined)(value) ? null : _message.default.format("dxNumberBox-noDataText");
this.setAria({
valuenow: (0, _common.ensureDefined)(value, ""),
valuetext: valueText
});
this.option("text", this._input().val());
this._updateButtons();
return (0, _deferred.Deferred)().resolve()
}
_forceValueRender() {
const value = this.option("value");
const number = Number(value);
const formattedValue = isNaN(number) ? "" : this._applyDisplayValueFormatter(value);
this._renderDisplayText(formattedValue)
}
_applyDisplayValueFormatter(value) {
const {
displayValueFormatter: displayValueFormatter
} = this.option();
return null === displayValueFormatter || void 0 === displayValueFormatter ? void 0 : displayValueFormatter(value)
}
_renderProps() {
this._input().prop({
min: this.option("min"),
max: this.option("max"),
step: this.option("step")
});
this.setAria({
valuemin: (0, _common.ensureDefined)(this.option("min"), ""),
valuemax: (0, _common.ensureDefined)(this.option("max"), "")
})
}
_spinButtonsPointerDownHandler() {
const $input = this._input();
if (!this.option("useLargeSpinButtons") && _dom_adapter.default.getActiveElement() !== $input[0]) {
_events_engine.default.trigger($input, "focus")
}
}
_spinUpChangeHandler(e) {
if (!this.option("readOnly")) {
this._spinValueChange(1, e.event || e)
}
}
_spinDownChangeHandler(e) {
if (!this.option("readOnly")) {
this._spinValueChange(-1, e.event || e)
}
}
_spinValueChange(sign, dxEvent) {
const step = parseFloat(this.option("step"));
if (0 === step) {
return
}
let value = parseFloat(this._normalizeInputValue()) || 0;
value = this._correctRounding(value, step * sign);
const min = this.option("min");
const max = this.option("max");
if ((0, _type.isDefined)(min)) {
value = Math.max(min, value)
}
if ((0, _type.isDefined)(max)) {
value = Math.min(max, value)
}
this._saveValueChangeEvent(dxEvent);
this.option("value", value)
}
_correctRounding(value, step) {
const regex = /[,.](.*)/;
const isFloatValue = regex.test(value);
const isFloatStep = regex.test(step);
if (isFloatValue || isFloatStep) {
const valueAccuracy = isFloatValue ? regex.exec(value)[0].length : 0;
const stepAccuracy = isFloatStep ? regex.exec(step)[0].length : 0;
const accuracy = math.max(valueAccuracy, stepAccuracy);
value = this._round(value + step, accuracy);
return value
}
return value + step
}
_round(value, precision) {
precision = precision || 0;
const multiplier = 10 ** precision;
value *= multiplier;
value = Math.round(value) / multiplier;
return value
}
_renderValueChangeEvent() {
super._renderValueChangeEvent();
const forceValueChangeEvent = (0, _index.addNamespace)("focusout", "NumberBoxForceValueChange");
_events_engine.default.off(this.element(), forceValueChangeEvent);
_events_engine.default.on(this.element(), forceValueChangeEvent, this._forceRefreshInputValue.bind(this))
}
_forceRefreshInputValue() {
const {
mode: mode
} = this.option();
if ("number" === mode) {
return
}
const $input = this._input();
const formattedValue = this._applyDisplayValueFormatter(this.option("value"));
$input.val(null);
$input.val(formattedValue)
}
_valueChangeEventHandler(e) {
const $input = this._input();
const inputValue = this._normalizeText();
const value = this._parseValue(inputValue);
const valueHasDigits = "." !== inputValue && "-" !== inputValue;
if (this._isValueValid() && !this._validateValue(value)) {
$input.val(this._applyDisplayValueFormatter(value));
return
}
if (valueHasDigits) {
super._valueChangeEventHandler(e, isNaN(value) ? null : value)
}
this._applyValueBoundaries(inputValue, value);
this.validationRequest.fire({
value: value,
editor: this
})
}
_applyValueBoundaries(inputValue, parsedValue) {
const isValueIncomplete = this._isValueIncomplete(inputValue);
const isValueCorrect = this._isValueInRange(inputValue);
if (!isValueIncomplete && !isValueCorrect && null !== parsedValue) {
if (Number(inputValue) !== parsedValue) {
this._input().val(this._applyDisplayValueFormatter(parsedValue))
}
}
}
_replaceCommaWithPoint(value) {
return value.replace(",", ".")
}
_inputIsInvalid() {
const {
mode: mode
} = this.option();
const isNumberMode = "number" === mode;
const validityState = this._input().get(0).validity;
return isNumberMode && (null === validityState || void 0 === validityState ? void 0 : validityState.badInput)
}
_renderDisplayText(text) {
if (this._inputIsInvalid()) {
return
}
super._renderDisplayText(text)
}
_isValueIncomplete(value) {
return /(^-$)|(^-?\d*\.$)|(\d+e-?$)/i.test(value)
}
_isValueInRange(value) {
return (0, _math.inRange)(value, this.option("min"), this.option("max"))
}
_isNumber(value) {
return null !== this._parseValue(value)
}
_validateValue(value) {
const inputValue = this._normalizeText();
const isValueValid = this._isValueValid();
let isValid = true;
const isNumber = this._isNumber(inputValue);
if (isNaN(Number(value))) {
isValid = false
}
if (!value && isValueValid) {
isValid = true
} else if (!isNumber && !isValueValid) {
isValid = false
}
this.option({
isValid: isValid,
validationError: isValid ? null : {
editorSpecific: true,
message: this.option("invalidValueMessage")
}
});
return isValid
}
_normalizeInputValue() {
return this._parseValue(this._normalizeText())
}
_normalizeText() {
const value = this._input().val().trim();
return this._replaceCommaWithPoint(value)
}
_parseValue(value) {
const number = parseFloat(value);
if (isNaN(number)) {
return null
}
return (0, _math.fitIntoRange)(number, this.option("min"), this.option("max"))
}
_clearValue() {
if (this._inputIsInvalid()) {
this._input().val("");
this._validateValue()
}
super._clearValue()
}
clear() {
if (null === this.option("value")) {
this.option("text", "");
if (this._input().length) {
this._renderValue()
}
} else {
this.option("value", null)
}
}
_optionChanged(args) {
switch (args.name) {
case "value":
this._validateValue(args.value);
this._setSubmitValue(args.value);
super._optionChanged(args);
this._resumeValueChangeAction();
break;
case "step":
this._renderProps();
break;
case "min":
case "max":
this._renderProps();
this.option("value", this._parseValue(this.option("value")));
break;
case "showSpinButtons":
case "useLargeSpinButtons":
this._updateButtons(["spins"]);
break;
case "invalidValueMessage":
break;
default:
super._optionChanged(args)
}
}
}
var _default = exports.default = NumberBoxBase;