From 8ec293c854c90b2b9cf575033c7c1c0a5b8168cf Mon Sep 17 00:00:00 2001 From: Raushen Date: Fri, 14 Aug 2026 16:16:44 +0300 Subject: [PATCH 1/2] Rename files: remove m_ prefix --- .../ui/number_box/{m_number_box.base.ts => number_box.base.ts} | 0 .../ui/number_box/{m_number_box.caret.ts => number_box.caret.ts} | 0 .../ui/number_box/{m_number_box.mask.ts => number_box.mask.ts} | 0 .../ui/number_box/{m_number_box.spin.ts => number_box.spin.ts} | 0 .../ui/number_box/{m_number_box.spins.ts => number_box.spins.ts} | 0 .../__internal/ui/number_box/{m_number_box.ts => number_box.ts} | 0 .../js/__internal/ui/number_box/{m_utils.ts => utils.ts} | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename packages/devextreme/js/__internal/ui/number_box/{m_number_box.base.ts => number_box.base.ts} (100%) rename packages/devextreme/js/__internal/ui/number_box/{m_number_box.caret.ts => number_box.caret.ts} (100%) rename packages/devextreme/js/__internal/ui/number_box/{m_number_box.mask.ts => number_box.mask.ts} (100%) rename packages/devextreme/js/__internal/ui/number_box/{m_number_box.spin.ts => number_box.spin.ts} (100%) rename packages/devextreme/js/__internal/ui/number_box/{m_number_box.spins.ts => number_box.spins.ts} (100%) rename packages/devextreme/js/__internal/ui/number_box/{m_number_box.ts => number_box.ts} (100%) rename packages/devextreme/js/__internal/ui/number_box/{m_utils.ts => utils.ts} (100%) diff --git a/packages/devextreme/js/__internal/ui/number_box/m_number_box.base.ts b/packages/devextreme/js/__internal/ui/number_box/number_box.base.ts similarity index 100% rename from packages/devextreme/js/__internal/ui/number_box/m_number_box.base.ts rename to packages/devextreme/js/__internal/ui/number_box/number_box.base.ts diff --git a/packages/devextreme/js/__internal/ui/number_box/m_number_box.caret.ts b/packages/devextreme/js/__internal/ui/number_box/number_box.caret.ts similarity index 100% rename from packages/devextreme/js/__internal/ui/number_box/m_number_box.caret.ts rename to packages/devextreme/js/__internal/ui/number_box/number_box.caret.ts diff --git a/packages/devextreme/js/__internal/ui/number_box/m_number_box.mask.ts b/packages/devextreme/js/__internal/ui/number_box/number_box.mask.ts similarity index 100% rename from packages/devextreme/js/__internal/ui/number_box/m_number_box.mask.ts rename to packages/devextreme/js/__internal/ui/number_box/number_box.mask.ts diff --git a/packages/devextreme/js/__internal/ui/number_box/m_number_box.spin.ts b/packages/devextreme/js/__internal/ui/number_box/number_box.spin.ts similarity index 100% rename from packages/devextreme/js/__internal/ui/number_box/m_number_box.spin.ts rename to packages/devextreme/js/__internal/ui/number_box/number_box.spin.ts diff --git a/packages/devextreme/js/__internal/ui/number_box/m_number_box.spins.ts b/packages/devextreme/js/__internal/ui/number_box/number_box.spins.ts similarity index 100% rename from packages/devextreme/js/__internal/ui/number_box/m_number_box.spins.ts rename to packages/devextreme/js/__internal/ui/number_box/number_box.spins.ts diff --git a/packages/devextreme/js/__internal/ui/number_box/m_number_box.ts b/packages/devextreme/js/__internal/ui/number_box/number_box.ts similarity index 100% rename from packages/devextreme/js/__internal/ui/number_box/m_number_box.ts rename to packages/devextreme/js/__internal/ui/number_box/number_box.ts diff --git a/packages/devextreme/js/__internal/ui/number_box/m_utils.ts b/packages/devextreme/js/__internal/ui/number_box/utils.ts similarity index 100% rename from packages/devextreme/js/__internal/ui/number_box/m_utils.ts rename to packages/devextreme/js/__internal/ui/number_box/utils.ts From 31d3fa947bf9528af66f058c9cddfc84be5c5966 Mon Sep 17 00:00:00 2001 From: Raushen Date: Mon, 17 Aug 2026 11:39:53 +0300 Subject: [PATCH 2/2] NumberBox refactoring --- .../js/__internal/ui/color_box/color_view.ts | 4 +- .../js/__internal/ui/date_box/time_view.ts | 4 +- .../ui/number_box/number_box.base.ts | 259 ++++++------ .../ui/number_box/number_box.caret.ts | 179 +++++---- .../ui/number_box/number_box.mask.ts | 374 ++++++++++-------- .../ui/number_box/number_box.spin.ts | 10 +- .../ui/number_box/number_box.spins.ts | 79 ++-- .../js/__internal/ui/number_box/number_box.ts | 2 +- .../js/__internal/ui/number_box/utils.ts | 40 +- .../ui/text_box/text_editor.mask.ts | 2 +- packages/devextreme/js/ui/number_box.js | 2 +- .../numberBoxParts/common.tests.js | 2 +- .../numberBoxParts/mask.caret.tests.js | 2 +- .../toolbar.kbn.tests.js | 2 +- 14 files changed, 530 insertions(+), 431 deletions(-) diff --git a/packages/devextreme/js/__internal/ui/color_box/color_view.ts b/packages/devextreme/js/__internal/ui/color_box/color_view.ts index 70a01dd14860..e4d95ad5d4aa 100644 --- a/packages/devextreme/js/__internal/ui/color_box/color_view.ts +++ b/packages/devextreme/js/__internal/ui/color_box/color_view.ts @@ -18,8 +18,8 @@ import { isCommandKeyPressed } from '@ts/events/utils/index'; import Draggable from '@ts/m_draggable'; import type { EditorProperties, ValueChangedEvent } from '@ts/ui/editor/editor'; import Editor from '@ts/ui/editor/editor'; -import NumberBox from '@ts/ui/number_box/m_number_box'; -import { WIDGET_CLASS as NUMBERBOX_CLASS } from '@ts/ui/number_box/m_number_box.base'; +import NumberBox from '@ts/ui/number_box/number_box'; +import { WIDGET_CLASS as NUMBERBOX_CLASS } from '@ts/ui/number_box/number_box.base'; import TextBox from '@ts/ui/text_box/text_box'; const COLOR_VIEW_CLASS = 'dx-colorview'; diff --git a/packages/devextreme/js/__internal/ui/date_box/time_view.ts b/packages/devextreme/js/__internal/ui/date_box/time_view.ts index e74bbd8cf357..6380e139882b 100644 --- a/packages/devextreme/js/__internal/ui/date_box/time_view.ts +++ b/packages/devextreme/js/__internal/ui/date_box/time_view.ts @@ -9,8 +9,8 @@ import Box from '@ts/ui/box'; import dateUtils from '@ts/ui/date_box/date_utils'; import type { EditorProperties } from '@ts/ui/editor/editor'; import Editor from '@ts/ui/editor/editor'; -import NumberBox from '@ts/ui/number_box/m_number_box'; -import type { NumberBoxMaskProperties } from '@ts/ui/number_box/m_number_box.mask'; +import NumberBox from '@ts/ui/number_box/number_box'; +import type { NumberBoxMaskProperties } from '@ts/ui/number_box/number_box.mask'; import SelectBox from '@ts/ui/select_box'; const TIMEVIEW_CLASS = 'dx-timeview'; diff --git a/packages/devextreme/js/__internal/ui/number_box/number_box.base.ts b/packages/devextreme/js/__internal/ui/number_box/number_box.base.ts index 3c6848500548..fe3c8bc1a911 100644 --- a/packages/devextreme/js/__internal/ui/number_box/number_box.base.ts +++ b/packages/devextreme/js/__internal/ui/number_box/number_box.base.ts @@ -13,25 +13,41 @@ import browser from '@js/core/utils/browser'; import { // @ts-expect-error ts-error applyServerDecimalSeparator, - ensureDefined, } from '@js/core/utils/common'; +import type { DeferredObj } from '@js/core/utils/deferred'; import { Deferred } from '@js/core/utils/deferred'; import { fitIntoRange, inRange } from '@js/core/utils/math'; import { isDefined } from '@js/core/utils/type'; +import type { DxEvent, InteractionEvent } from '@js/events'; +import type { Format } from '@js/localization'; import { getGlobalFormatByDataType } from '@ts/core/global_format_config'; +import type { OptionChanged } from '@ts/core/widget/types'; +import type { SupportedKeys } from '@ts/core/widget/widget'; +import type { DxMouseWheelEvent } from '@ts/ui/scroll_view/types'; import TextEditor from '@ts/ui/text_box/text_editor'; import type { TextEditorBaseProperties } from '../text_box/text_editor.base'; import type { TextEditorButtonInfo } from '../text_box/texteditor_button_collection/index'; -import SpinButtons from './m_number_box.spins'; - -const math = Math; +import type { SpinChangeEvent } from './number_box.spin'; +import SpinButtons from './number_box.spins'; export const WIDGET_CLASS = 'dx-numberbox'; const FIREFOX_CONTROL_KEYS = ['tab', 'del', 'backspace', 'leftArrow', 'rightArrow', 'home', 'end', 'enter']; const FORCE_VALUECHANGE_EVENT_NAMESPACE = 'NumberBoxForceValueChange'; +export type NumberBoxValue = number | null | undefined; + +export type SpinValueChangeEvent = DxEvent | DxMouseWheelEvent; + +export type KeyPressEvent = DxEvent & { + originalEvent: InputEvent & KeyboardEvent; +}; + +const getSpinEvent = ( + e: SpinChangeEvent | DxEvent, +): SpinValueChangeEvent => ('event' in e ? e.event : e); + export interface NumberBoxBaseProperties extends TextEditorBaseProperties { min?: number; max?: number; @@ -39,6 +55,9 @@ export interface NumberBoxBaseProperties extends TextEditorBaseProperties { showSpinButtons?: boolean; useLargeSpinButtons?: boolean; invalidValueMessage?: string; + format?: Format; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + displayValueFormatter?: ((value: any) => string); } class NumberBoxBase< @@ -48,24 +67,24 @@ class NumberBoxBase< _$submitElement!: dxElementWrapper; - _supportedKeys(): Record void> { + _supportedKeys(): SupportedKeys { return { ...super._supportedKeys(), - upArrow(e): void { + upArrow: (e): void => { if (!isCommandKeyPressed(e)) { e.preventDefault(); e.stopPropagation(); this._spinUpChangeHandler(e); } }, - downArrow(e): void { + downArrow: (e): void => { if (!isCommandKeyPressed(e)) { e.preventDefault(); e.stopPropagation(); this._spinDownChangeHandler(e); } }, - enter(): void {}, + enter: (): void => {}, }; } @@ -80,12 +99,10 @@ class NumberBoxBase< useLargeSpinButtons: true, mode: 'text', invalidValueMessage: messageLocalization.format('dxNumberBox-invalidValueMessage'), - // eslint-disable-next-line no-void - buttons: void 0, + buttons: undefined, }; } - // eslint-disable-next-line class-methods-use-this _useTemplates(): boolean { return false; } @@ -95,32 +112,27 @@ class NumberBoxBase< return super._getDefaultButtons().concat([{ name: 'spins', Ctor: SpinButtons }]); } - _isSupportInputMode() { - // @ts-expect-error ts-error - const version = parseFloat(browser.version); + _isSupportInputMode(): boolean { + const version = parseFloat(browser.version ?? ''); - return ( - browser.chrome && version >= 66 - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - || browser.safari && version >= 12 - ); + const isSupportedChrome = !!browser.chrome && version >= 66; + const isSupportedSafari = !!browser.safari && version >= 12; + + return isSupportedChrome || isSupportedSafari; } _defaultOptionsRules(): DefaultOptionsRule[] { // @ts-expect-error ts-error return super._defaultOptionsRules().concat([ { - device() { - return devices.real().generic && !devices.isSimulator(); - }, + device: (): boolean => !!devices.real().generic && !devices.isSimulator(), options: { useLargeSpinButtons: false, }, }, { - device: function () { - return devices.real().deviceType !== 'desktop' && !this._isSupportInputMode(); - }.bind(this), + device: (): boolean => devices.real().deviceType !== 'desktop' + && !this._isSupportInputMode(), options: { mode: 'number', }, @@ -136,7 +148,7 @@ class NumberBoxBase< this._toggleTabIndex(); } - _getDefaultAttributes() { + _getDefaultAttributes(): ReturnType['_getDefaultAttributes']> { const attributes = super._getDefaultAttributes(); // eslint-disable-next-line spellcheck/spell-checker attributes.inputmode = 'decimal'; @@ -144,18 +156,25 @@ class NumberBoxBase< } _renderContentImpl(): void { - this.option('isValid') && this._validateValue(this.option('value')); + const { isValid, value } = this.option(); + + if (isValid) { + this._validateValue(value); + } + this.setAria('role', 'spinbutton'); } _renderSubmitElement(): void { + const { value } = this.option(); + this._$submitElement = $('') .attr('type', 'hidden') .appendTo(this.$element()); - this._setSubmitValue(this.option('value')); + this._setSubmitValue(value); } - _setSubmitValue(value) { + _setSubmitValue(value: NumberBoxValue): void { this._getSubmitElement().val(applyServerDecimalSeparator(value)); } @@ -163,7 +182,7 @@ class NumberBoxBase< return this._$submitElement; } - _keyPressHandler(e) { + _keyPressHandler(e: KeyPressEvent): void { super._keyPressHandler(); const char = getChar(e); @@ -173,7 +192,7 @@ class NumberBoxBase< if (!isInputCharValid) { const keyName = normalizeKeyName(e); // NOTE: Additional check for Firefox control keys - if (isCommandKeyPressed(e) || keyName && FIREFOX_CONTROL_KEYS.includes(keyName)) { + if (isCommandKeyPressed(e) || (keyName && FIREFOX_CONTROL_KEYS.includes(keyName))) { return; } @@ -188,14 +207,14 @@ class NumberBoxBase< return true; } - _onMouseWheel(dxEvent): void { - dxEvent.delta > 0 ? this._spinValueChange(1, dxEvent) : this._spinValueChange(-1, dxEvent); + _onMouseWheel(e: DxMouseWheelEvent): void { + this._spinValueChange(e.delta > 0 ? 1 : -1, e); } - _renderValue() { + _renderValue(): DeferredObj { const inputValue = this._input().val(); - const value = this.option('value'); - // @ts-expect-error ts-error + const { value } = this.option(); + if (!inputValue.length || Number(inputValue) !== value) { this._forceValueRender(); this._toggleEmptinessEventHandler(); @@ -204,9 +223,8 @@ class NumberBoxBase< const valueText = isDefined(value) ? null : messageLocalization.format('dxNumberBox-noDataText'); this.setAria({ - // @ts-expect-error ts-error // eslint-disable-next-line spellcheck/spell-checker - valuenow: ensureDefined(value, ''), + valuenow: value ?? '', // eslint-disable-next-line spellcheck/spell-checker valuetext: valueText, }); @@ -218,17 +236,18 @@ class NumberBoxBase< } _forceValueRender(): void { - const value = this.option('value'); - const number = Number(value); - const formattedValue = isNaN(number) + const { value } = this.option(); + const formattedValue = isNaN(Number(value)) ? '' : this._applyDisplayValueFormatter(value); this._renderDisplayText(formattedValue); } - _applyDisplayValueFormatter(value): string | undefined { - if (!this.option('format')) { + _applyDisplayValueFormatter(value: NumberBoxValue): string | undefined { + const { format, displayValueFormatter } = this.option(); + + if (!format) { const globalNumberFormat = getGlobalFormatByDataType('number'); if (globalNumberFormat) { @@ -239,70 +258,66 @@ class NumberBoxBase< } } - const { displayValueFormatter } = this.option(); - return displayValueFormatter?.(value); } _renderProps(): void { + const { min, max, step } = this.option(); + // @ts-expect-error ts-error - this._input().prop({ - min: this.option('min'), - max: this.option('max'), - step: this.option('step'), - }); + this._input().prop({ min, max, step }); this.setAria({ - // @ts-expect-error ts-error // eslint-disable-next-line spellcheck/spell-checker - valuemin: ensureDefined(this.option('min'), ''), - // @ts-expect-error ts-error + valuemin: min ?? '', // eslint-disable-next-line spellcheck/spell-checker - valuemax: ensureDefined(this.option('max'), ''), + valuemax: max ?? '', }); } _spinButtonsPointerDownHandler(): void { + const { useLargeSpinButtons } = this.option(); const $input = this._input(); - if (!this.option('useLargeSpinButtons') && domAdapter.getActiveElement() !== $input[0]) { + + if (!useLargeSpinButtons && domAdapter.getActiveElement() !== $input[0]) { // @ts-expect-error ts-error eventsEngine.trigger($input, 'focus'); } } - _spinUpChangeHandler(e): void { - if (!this.option('readOnly')) { - this._spinValueChange(1, e.event || e); + _spinUpChangeHandler(e: SpinChangeEvent | DxEvent): void { + const { readOnly } = this.option(); + + if (!readOnly) { + this._spinValueChange(1, getSpinEvent(e)); } } - _spinDownChangeHandler(e): void { - if (!this.option('readOnly')) { - this._spinValueChange(-1, e.event || e); + _spinDownChangeHandler(e: SpinChangeEvent | DxEvent): void { + const { readOnly } = this.option(); + + if (!readOnly) { + this._spinValueChange(-1, getSpinEvent(e)); } } - _spinValueChange(sign, dxEvent): void { - // @ts-expect-error ts-error - const step = parseFloat(this.option('step')); + _spinValueChange(sign: number, dxEvent?: SpinValueChangeEvent): void { + const { step: stepOption, min, max } = this.option(); + const step = parseFloat(String(stepOption)); + if (step === 0) { return; } - // @ts-expect-error ts-error - let value = parseFloat(this._normalizeInputValue()) || 0; - value = this._correctRounding(value, step * sign); + let value = parseFloat(String(this._normalizeInputValue())) || 0; - const min = this.option('min'); - const max = this.option('max'); + value = this._correctRounding(value, step * sign); if (isDefined(min)) { - // @ts-expect-error ts-error value = Math.max(min, value); } if (isDefined(max)) { - // @ts-expect-error ts-error value = Math.min(max, value); } @@ -310,35 +325,28 @@ class NumberBoxBase< this.option('value', value); } - _correctRounding(value, step) { + _correctRounding(value: number, step: number): number { const regex = /[,.](.*)/; - const isFloatValue = regex.test(value); - const isFloatStep = regex.test(step); + const valueText = String(value); + const stepText = String(step); + const isFloatValue = regex.test(valueText); + const isFloatStep = regex.test(stepText); if (isFloatValue || isFloatStep) { - // @ts-expect-error - const valueAccuracy = isFloatValue ? regex.exec(value)[0].length : 0; - // @ts-expect-error - const stepAccuracy = isFloatStep ? regex.exec(step)[0].length : 0; - const accuracy = math.max(valueAccuracy, stepAccuracy); + const valueAccuracy = isFloatValue ? regex.exec(valueText)?.[0].length ?? 0 : 0; + const stepAccuracy = isFloatStep ? regex.exec(stepText)?.[0].length ?? 0 : 0; + const accuracy = Math.max(valueAccuracy, stepAccuracy); - value = this._round(value + step, accuracy); - - return value; + return this._round(value + step, accuracy); } return value + step; } - _round(value, precision) { - precision = precision || 0; - + _round(value: number, precision = 0): number { const multiplier = 10 ** precision; - value *= multiplier; - value = Math.round(value) / multiplier; - - return value; + return Math.round(value * multiplier) / multiplier; } _renderValueChangeEvent(): void { @@ -349,20 +357,21 @@ class NumberBoxBase< eventsEngine.on(this.element(), forceValueChangeEvent, this._forceRefreshInputValue.bind(this)); } - _forceRefreshInputValue() { - const { mode } = this.option(); + _forceRefreshInputValue(): void { + const { mode, value } = this.option(); + if (mode === 'number') { return; } const $input = this._input(); - const formattedValue = this._applyDisplayValueFormatter(this.option('value')); + const formattedValue = this._applyDisplayValueFormatter(value); // @ts-expect-error ts-error $input.val(null); $input.val(formattedValue); } - _valueChangeEventHandler(e) { + _valueChangeEventHandler(e: DxEvent): void { const $input = this._input(); const inputValue = this._normalizeText(); const value = this._parseValue(inputValue); @@ -374,8 +383,7 @@ class NumberBoxBase< } if (valueHasDigits) { - // @ts-expect-error ts-error - super._valueChangeEventHandler(e, isNaN(value) ? null : value); + super._valueChangeEventHandler(e, isNaN(Number(value)) ? null : value); } this._applyValueBoundaries(inputValue, value); @@ -386,7 +394,7 @@ class NumberBoxBase< }); } - _applyValueBoundaries(inputValue, parsedValue) { + _applyValueBoundaries(inputValue: string, parsedValue: NumberBoxValue): void { const isValueIncomplete = this._isValueIncomplete(inputValue); const isValueCorrect = this._isValueInRange(inputValue); @@ -397,20 +405,19 @@ class NumberBoxBase< } } - _replaceCommaWithPoint(value): string { + _replaceCommaWithPoint(value: string): string { return value.replace(',', '.'); } _inputIsInvalid(): boolean { const { mode } = this.option(); const isNumberMode = mode === 'number'; - // @ts-expect-error ts-error - const validityState = this._input().get(0).validity; + const input = this._input().get(0) as HTMLInputElement | undefined; - return isNumberMode && validityState?.badInput; + return isNumberMode && !!input?.validity.badInput; } - _renderDisplayText(text) { + _renderDisplayText(text: string | undefined): void { if (this._inputIsInvalid()) { return; } @@ -418,20 +425,23 @@ class NumberBoxBase< super._renderDisplayText(text); } - _isValueIncomplete(value) { + _isValueIncomplete(value: string): boolean { const incompleteRegex = /(^-$)|(^-?\d*\.$)|(\d+e-?$)/i; return incompleteRegex.test(value); } - _isValueInRange(value) { - return inRange(value, this.option('min'), this.option('max')); + _isValueInRange(value: string | NumberBoxValue): boolean { + const { min, max } = this.option(); + + return inRange(value, min, max); } - _isNumber(value) { + _isNumber(value: string): boolean { return this._parseValue(value) !== null; } - _validateValue(value?) { + _validateValue(value?: NumberBoxValue): boolean { + const { invalidValueMessage } = this.option(); const inputValue = this._normalizeText(); const isValueValid = this._isValueValid(); let isValid = true; @@ -451,31 +461,32 @@ class NumberBoxBase< isValid, validationError: isValid ? null : { editorSpecific: true, - message: this.option('invalidValueMessage'), + message: invalidValueMessage, }, }); return isValid; } - _normalizeInputValue(): number | null { + _normalizeInputValue(): NumberBoxValue { return this._parseValue(this._normalizeText()); } - _normalizeText() { + _normalizeText(): string { const value = this._input().val().trim(); return this._replaceCommaWithPoint(value); } - _parseValue(value): number | null { - const number = parseFloat(value); + _parseValue(value?: string | NumberBoxValue): NumberBoxValue { + const { min, max } = this.option(); + const parsedValue = parseFloat(String(value ?? '')); - if (isNaN(number)) { + if (isNaN(parsedValue)) { return null; } - return fitIntoRange(number, this.option('min'), this.option('max')); + return fitIntoRange(parsedValue, min, max); } _clearValue(): void { @@ -487,7 +498,9 @@ class NumberBoxBase< } clear(): void { - if (this.option('value') === null) { + const { value } = this.option(); + + if (value === null) { this.option('text', ''); if (this._input().length) { this._renderValue(); @@ -497,22 +510,28 @@ class NumberBoxBase< } } - _optionChanged(args) { + _optionChanged(args: OptionChanged): void { switch (args.name) { - case 'value': - this._validateValue(args.value); - this._setSubmitValue(args.value); + case 'value': { + const value = args.value as NumberBoxValue; + + this._validateValue(value); + this._setSubmitValue(value); super._optionChanged(args); this._resumeValueChangeAction(); break; + } case 'step': this._renderProps(); break; case 'min': - case 'max': + case 'max': { + const { value } = this.option(); + this._renderProps(); - this.option('value', this._parseValue(this.option('value'))); + this.option('value', this._parseValue(value)); break; + } case 'showSpinButtons': case 'useLargeSpinButtons': this._updateButtons(['spins']); diff --git a/packages/devextreme/js/__internal/ui/number_box/number_box.caret.ts b/packages/devextreme/js/__internal/ui/number_box/number_box.caret.ts index 0ba137034deb..9149183be2bf 100644 --- a/packages/devextreme/js/__internal/ui/number_box/number_box.caret.ts +++ b/packages/devextreme/js/__internal/ui/number_box/number_box.caret.ts @@ -1,50 +1,35 @@ import number from '@js/common/core/localization/number'; import { escapeRegExp } from '@js/core/utils/common'; import { fitIntoRange } from '@js/core/utils/math'; +import { isString } from '@js/core/utils/type'; +import type { Format } from '@js/localization'; +import type { CaretRange } from '@ts/ui/text_box/utils.caret'; -import { getNthOccurrence, getRealSeparatorIndex, splitByIndex } from './m_utils'; +import { getNthOccurrence, getRealSeparatorIndex, splitByIndex } from './utils'; -export const getCaretBoundaries = function (text, format) { - if (typeof format === 'string') { - const signParts = format.split(';'); - const sign = number.getSign(text, format); - - signParts[1] = signParts[1] || `-${signParts[0]}`; - format = signParts[sign < 0 ? 1 : 0]; - - const mockEscapedStubs = (str) => str.replace(/'([^']*)'/g, (str) => str.split('').map(() => ' ').join('').substr(2)); - - format = mockEscapedStubs(format); +export interface CaretBoundaries { + start: number; + end: number; +} - // @ts-expect-error - const prefixStubLength = /^[^#0.,]*/.exec(format)[0].length; - // @ts-expect-error - const postfixStubLength = /[^#0.,]*$/.exec(format)[0].length; +export type CaretPosition = number | CaretRange | undefined; - return { - start: prefixStubLength, - end: text.length - postfixStubLength, - }; - } - return { start: 0, end: text.length }; -}; +const mockEscapedStubs = (format: string): string => format.replace( + /'([^']*)'/g, + (stub) => stub.split('').map(() => ' ').join('').substr(2), +); -// eslint-disable-next-line @typescript-eslint/naming-convention -const _getDigitCountBeforeIndex = function (index, text) { - const decimalSeparator = number.getDecimalSeparator(); +const getDigitCountBeforeIndex = (index: number, text: string): number => { + const decimalSeparator: string = number.getDecimalSeparator(); const regExp = new RegExp(`[^0-9${escapeRegExp(decimalSeparator)}]`, 'g'); const textBeforePosition = text.slice(0, index); return textBeforePosition.replace(regExp, '').length; }; -// eslint-disable-next-line @typescript-eslint/naming-convention -const _reverseText = function (text) { - return text.split('').reverse().join(''); -}; +const reverseText = (text: string): string => text.split('').reverse().join(''); -// eslint-disable-next-line @typescript-eslint/naming-convention -const _getDigitPositionByIndex = function (digitIndex, text) { +const getDigitPositionByIndex = (digitIndex: number, text: string): number => { if (!digitIndex) { return -1; } @@ -59,82 +44,118 @@ const _getDigitPositionByIndex = function (digitIndex, text) { if (counter >= digitIndex) { return index; } - counter++; + counter += 1; result = regExp.exec(text); } - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - return index === null ? text.length : index; + return index ?? text.length; }; -// eslint-disable-next-line @typescript-eslint/naming-convention -const _trimNonNumericCharsFromEnd = function (text) { - return text.replace(/[^0-9e]+$/, ''); -}; +const trimNonNumericCharsFromEnd = (text: string): string => text.replace(/[^0-9e]+$/, ''); + +const isSeparatorBasedString = (text: string): boolean => text.length === 1 + && !!text.match(/^[,.][0-9]*$/g); + +export const getCaretBoundaries = (text: string, format: Format): CaretBoundaries => { + if (typeof format === 'string') { + const signParts = format.split(';'); + const sign = number.getSign(text, format); + + signParts[1] = signParts[1] || `-${signParts[0]}`; -export const getCaretWithOffset = function (caret, offset) { - if (caret.start === undefined) { - caret = { start: caret, end: caret }; + const signFormat = mockEscapedStubs(signParts[sign < 0 ? 1 : 0]); + + const prefixStubLength = /^[^#0.,]*/.exec(signFormat)?.[0].length ?? 0; + const postfixStubLength = /[^#0.,]*$/.exec(signFormat)?.[0].length ?? 0; + + return { + start: prefixStubLength, + end: text.length - postfixStubLength, + }; } + return { start: 0, end: text.length }; +}; + +export const getCaretWithOffset = ( + caret: CaretPosition, + offset: number, +): CaretBoundaries => { + const range = typeof caret === 'number' ? { start: caret, end: caret } : caret; return { - start: caret.start + offset, - end: caret.end + offset, + start: (range?.start ?? 0) + offset, + end: (range?.end ?? 0) + offset, }; }; -export const getCaretAfterFormat = function (text, formatted, caret, format) { - caret = getCaretWithOffset(caret, 0); +export const getCaretInBoundaries = ( + caret: CaretPosition, + text: string, + format: Format, +): CaretBoundaries => { + const normalizedCaret = getCaretWithOffset(caret, 0); + const boundaries = getCaretBoundaries(text, format); - const point = number.getDecimalSeparator(); - const isSeparatorBasedText = isSeparatorBasedString(text); - const realSeparatorOccurrenceIndex = getRealSeparatorIndex(format).occurrence; - const pointPosition = isSeparatorBasedText ? 0 : getNthOccurrence(text, point, realSeparatorOccurrenceIndex); + return { + start: fitIntoRange(normalizedCaret.start, boundaries.start, boundaries.end), + end: fitIntoRange(normalizedCaret.end, boundaries.start, boundaries.end), + }; +}; + +export const getCaretAfterFormat = ( + text: string, + formatted: string, + caret: CaretPosition, + format: Format, +): CaretBoundaries => { + const normalizedCaret = getCaretWithOffset(caret, 0); + + const point: string = number.getDecimalSeparator(); + const formatPattern = isString(format) ? format : ''; + const realSeparatorOccurrenceIndex = getRealSeparatorIndex(formatPattern).occurrence; + const pointPosition = isSeparatorBasedString(text) + ? 0 + : getNthOccurrence(text, point, realSeparatorOccurrenceIndex); const newPointPosition = getNthOccurrence(formatted, point, realSeparatorOccurrenceIndex); const textParts = splitByIndex(text, pointPosition); const formattedParts = splitByIndex(formatted, newPointPosition); - const isCaretOnFloat = pointPosition !== -1 && caret.start > pointPosition; + const isCaretOnFloat = pointPosition !== -1 && normalizedCaret.start > pointPosition; if (isCaretOnFloat) { - const relativeIndex = caret.start - pointPosition - 1; - const digitsBefore = _getDigitCountBeforeIndex(relativeIndex, textParts[1]); - const newPosition = formattedParts[1] ? newPointPosition + 1 + _getDigitPositionByIndex(digitsBefore, formattedParts[1]) + 1 : formatted.length; + const relativeIndex = normalizedCaret.start - pointPosition - 1; + const digitsBefore = getDigitCountBeforeIndex(relativeIndex, textParts[1]); + const newPosition = formattedParts[1] + ? newPointPosition + 1 + getDigitPositionByIndex(digitsBefore, formattedParts[1]) + 1 + : formatted.length; return getCaretInBoundaries(newPosition, formatted, format); } - const formattedIntPart = _trimNonNumericCharsFromEnd(formattedParts[0]); - const positionFromEnd = textParts[0].length - caret.start; - const digitsFromEnd = _getDigitCountBeforeIndex(positionFromEnd, _reverseText(textParts[0])); - const newPositionFromEnd = _getDigitPositionByIndex(digitsFromEnd, _reverseText(formattedIntPart)); + + const formattedIntPart = trimNonNumericCharsFromEnd(formattedParts[0]); + const positionFromEnd = textParts[0].length - normalizedCaret.start; + const digitsFromEnd = getDigitCountBeforeIndex(positionFromEnd, reverseText(textParts[0])); + const newPositionFromEnd = getDigitPositionByIndex(digitsFromEnd, reverseText(formattedIntPart)); const newPositionFromBegin = formattedIntPart.length - (newPositionFromEnd + 1); return getCaretInBoundaries(newPositionFromBegin, formatted, format); }; -function isSeparatorBasedString(text) { - return text.length === 1 && !!text.match(/^[,.][0-9]*$/g); -} - -export const isCaretInBoundaries = function (caret, text, format) { - caret = getCaretWithOffset(caret, 0); +export const isCaretInBoundaries = ( + caret: CaretPosition, + text: string, + format: Format, +): boolean => { + const normalizedCaret = getCaretWithOffset(caret, 0); + const boundaries = getCaretInBoundaries(normalizedCaret, text, format); - const boundaries = getCaretInBoundaries(caret, text, format); - return caret.start >= boundaries.start && caret.end <= boundaries.end; + return normalizedCaret.start >= boundaries.start && normalizedCaret.end <= boundaries.end; }; -export function getCaretInBoundaries(caret, text, format) { - caret = getCaretWithOffset(caret, 0); - - const boundaries = getCaretBoundaries(text, format); - const adjustedCaret = { - start: fitIntoRange(caret.start, boundaries.start, boundaries.end), - end: fitIntoRange(caret.end, boundaries.start, boundaries.end), - }; - - return adjustedCaret; -} - -export const getCaretOffset = function (previousText, newText, format) { +export const getCaretOffset = ( + previousText: string, + newText: string, + format: Format, +): number => { const previousBoundaries = getCaretBoundaries(previousText, format); const newBoundaries = getCaretBoundaries(newText, format); diff --git a/packages/devextreme/js/__internal/ui/number_box/number_box.mask.ts b/packages/devextreme/js/__internal/ui/number_box/number_box.mask.ts index 61c30aff7988..592a68d817d5 100644 --- a/packages/devextreme/js/__internal/ui/number_box/number_box.mask.ts +++ b/packages/devextreme/js/__internal/ui/number_box/number_box.mask.ts @@ -6,25 +6,32 @@ import { import { getFormat as getLDMLFormat } from '@js/common/core/localization/ldml/number'; import number from '@js/common/core/localization/number'; import devices from '@js/core/devices'; -import { ensureDefined, escapeRegExp } from '@js/core/utils/common'; +import { escapeRegExp } from '@js/core/utils/common'; +import type { DeferredObj } from '@js/core/utils/deferred'; import { fitIntoRange, inRange } from '@js/core/utils/math'; import { isDefined, isFunction, isNumeric, isPlainObject, isString, } from '@js/core/utils/type'; +import type { DxEvent } from '@js/events'; import type { Format, FormatObject } from '@js/localization'; import type { Properties } from '@js/ui/number_box'; import { getGlobalFormatByDataType } from '@ts/core/global_format_config'; +import type { OptionChanged } from '@ts/core/widget/types'; +import type { SupportedKeys } from '@ts/core/widget/widget'; +import type { KeyboardKeyDownEvent } from '@ts/events/core/m_keyboard_processor'; +import type { CaretRange } from '@ts/ui/text_box/utils.caret'; -import NumberBoxBase from './m_number_box.base'; +import type { KeyPressEvent, NumberBoxBaseProperties, NumberBoxValue } from './number_box.base'; +import NumberBoxBase from './number_box.base'; import { getCaretAfterFormat, getCaretBoundaries, getCaretInBoundaries, getCaretOffset, getCaretWithOffset, isCaretInBoundaries, -} from './m_number_box.caret'; +} from './number_box.caret'; import { adjustPercentValue, getNthOccurrence, getRealSeparatorIndex, splitByIndex, -} from './m_utils'; +} from './utils'; const NUMBER_FORMATTER_NAMESPACE = 'dxNumberFormatter'; const MOVE_FORWARD = 1 as const; @@ -44,12 +51,18 @@ const asFormatObject = (format: Format | undefined): FormatObject | undefined => isPlainObject(format) ? format as FormatObject : undefined ); +const isNegativeValue = (value: NumberBoxValue): boolean => { + const parsedValue = Number(value); + + return parsedValue < 0 || 1 / parsedValue === -Infinity; +}; + export interface NumberBoxMaskProperties extends Omit { useMaskBehavior?: boolean; - displayValueFormatter?: ((value: any) => string); + displayValueFormatter?: NumberBoxBaseProperties['displayValueFormatter']; } class NumberBoxMask extends NumberBoxBase { @@ -67,7 +80,7 @@ class NumberBoxMask extends NumberBoxBase { _isValuePasted?: boolean; - _currentFormat?: any; + _currentFormat?: Format; _getDefaultOptions(): NumberBoxMaskProperties { return { @@ -78,27 +91,25 @@ class NumberBoxMask extends NumberBoxBase { }; } - _isDeleteKey(key): boolean { + _isDeleteKey(key: string | null | undefined): boolean { return key === 'del'; } - _supportedKeys() { + _supportedKeys(): SupportedKeys { if (!this._useMaskBehavior()) { return super._supportedKeys(); } - const that = this; - return { ...super._supportedKeys(), - minus: that._revertSign.bind(that), - del: that._removeHandler.bind(that), - backspace: that._removeHandler.bind(that), - leftArrow: that._arrowHandler.bind(that, MOVE_BACKWARD), - rightArrow: that._arrowHandler.bind(that, MOVE_FORWARD), - home: that._boundaryKeyHandler.bind(that, MOVE_FORWARD), - enter: that._updateFormattedValue.bind(that), - end: that._boundaryKeyHandler.bind(that, MOVE_BACKWARD), + minus: (e): void => this._revertSign(e), + del: (e): void => this._removeHandler(e), + backspace: (e): void => this._removeHandler(e), + leftArrow: (e): void => this._arrowHandler(MOVE_BACKWARD, e), + rightArrow: (e): void => this._arrowHandler(MOVE_FORWARD, e), + home: (e): void => this._boundaryKeyHandler(MOVE_FORWARD, e), + enter: (): void => this._updateFormattedValue(), + end: (e): void => this._boundaryKeyHandler(MOVE_BACKWARD, e), }; } @@ -110,15 +121,15 @@ class NumberBoxMask extends NumberBoxBase { : getGlobalFormatByDataType('number'); } - _getTextSeparatorIndex(text) { - const decimalSeparator = number.getDecimalSeparator(); + _getTextSeparatorIndex(text: string): number { + const decimalSeparator: string = number.getDecimalSeparator(); const formatPattern = this._getFormatPattern(); const patternString = isString(formatPattern) ? formatPattern : ''; const realSeparatorOccurrenceIndex = getRealSeparatorIndex(patternString).occurrence; return getNthOccurrence(text, decimalSeparator, realSeparatorOccurrenceIndex); } - _focusInHandler(e): void { + _focusInHandler(e: DxEvent): void { if (!this._preventNestedFocusEvent(e)) { this.clearCaretTimeout(); this._caretTimeout = setTimeout(() => { @@ -141,7 +152,7 @@ class NumberBoxMask extends NumberBoxBase { super._focusInHandler(e); } - _focusOutHandler(e): void { + _focusOutHandler(e: DxEvent): void { const shouldHandleEvent = !this._preventNestedFocusEvent(e); if (shouldHandleEvent) { @@ -158,15 +169,15 @@ class NumberBoxMask extends NumberBoxBase { } } - _hasValueBeenChanged(inputValue) { + _hasValueBeenChanged(inputValue: string): boolean { const format = this._getFormatPattern(); - const value = this.option('value'); + const { value } = this.option(); const formatted = this._format(value, format) || ''; return formatted !== inputValue; } - _updateFormattedValue() { + _updateFormattedValue(): void { const inputValue = this._getInputVal(); if (this._hasValueBeenChanged(inputValue)) { @@ -184,19 +195,20 @@ class NumberBoxMask extends NumberBoxBase { } } - _arrowHandler(step, e) { + _arrowHandler(step: CaretMoveDirection, e: DxEvent): void { if (!this._useMaskBehavior()) { return; } const text = this._getInputVal(); const format = this._getFormatPattern(); - let nextCaret = getCaretWithOffset(this._caret(), step); + const nextCaret = getCaretWithOffset(this._caret(), step); if (!isCaretInBoundaries(nextCaret, text, format)) { - nextCaret = step === MOVE_FORWARD ? nextCaret.end : nextCaret.start; + const nextCaretPosition = step === MOVE_FORWARD ? nextCaret.end : nextCaret.start; + e.preventDefault(); - this._caret(getCaretInBoundaries(nextCaret, text, format)); + this._caret(getCaretInBoundaries(nextCaretPosition, text, format)); this._scrollInputTo(step === MOVE_FORWARD ? 'end' : 'start'); } } @@ -225,7 +237,7 @@ class NumberBoxMask extends NumberBoxBase { this._caret(newCaret); } - _boundaryKeyHandler(direction: CaretMoveDirection, e: KeyboardEvent): void { + _boundaryKeyHandler(direction: CaretMoveDirection, e: DxEvent): void { if (!this._useMaskBehavior() || e.shiftKey) { return; } @@ -235,19 +247,21 @@ class NumberBoxMask extends NumberBoxBase { this._scrollInputTo(direction === MOVE_FORWARD ? 'start' : 'end'); } - _shouldMoveCaret(text, caret) { - const decimalSeparator = number.getDecimalSeparator(); - const isDecimalSeparatorNext = text.charAt(caret.end) === decimalSeparator; - const moveToFloat = (this._lastKey === decimalSeparator || this._lastKey === '.' || this._lastKey === ',') && isDecimalSeparatorNext; + _shouldMoveCaret(text: string, caret: CaretRange | undefined): boolean { + const decimalSeparator: string = number.getDecimalSeparator(); + const isDecimalSeparatorNext = text.charAt(caret?.end ?? 0) === decimalSeparator; + const isSeparatorKey = this._lastKey === decimalSeparator || this._lastKey === '.' || this._lastKey === ','; - return moveToFloat; + return isSeparatorKey && isDecimalSeparatorNext; } - _getInputVal() { - return number.convertDigits(this._input().val(), true); + _getInputVal(): string { + const inputValue: string = number.convertDigits(this._input().val(), true); + + return inputValue; } - _keyboardHandler(e): boolean { + _keyboardHandler(e: KeyboardKeyDownEvent): boolean { this.clearCaretTimeout(); this._lastKey = number.convertDigits(getChar(e), true); @@ -260,11 +274,11 @@ class NumberBoxMask extends NumberBoxBase { const normalizedText = this._getInputVal(); const caret = this._caret(); - let enteredChar; + let enteredChar = this._lastKey; if (this._lastKeyName === MINUS_KEY) { enteredChar = ''; - } else { - enteredChar = e.which === NUMPAD_DOT_KEY_CODE ? number.getDecimalSeparator() : this._lastKey; + } else if (e.which === NUMPAD_DOT_KEY_CODE) { + enteredChar = number.getDecimalSeparator(); } const newValue = this._tryParse(normalizedText, caret, enteredChar); @@ -284,13 +298,13 @@ class NumberBoxMask extends NumberBoxBase { return super._keyboardHandler(e); } - _keyPressHandler(e): void { + _keyPressHandler(e: KeyPressEvent): void { if (!this._useMaskBehavior()) { super._keyPressHandler(e); } } - _removeHandler(e) { + _removeHandler(e: DxEvent): void { const caret = this._caret(); const text = this._getInputVal(); @@ -305,14 +319,15 @@ class NumberBoxMask extends NumberBoxBase { if (start === end) { const caretPosition = start; - const canDelete = isBackspaceKey - && caretPosition > 0 - || isDeleteKey - && caretPosition < text.length; + const canDelete = (isBackspaceKey && caretPosition > 0) + || (isDeleteKey && caretPosition < text.length); if (canDelete) { - isDeleteKey && end++; - isBackspaceKey && start--; + if (isDeleteKey) { + end += 1; + } else { + start -= 1; + } } else { e.preventDefault(); return; @@ -323,12 +338,11 @@ class NumberBoxMask extends NumberBoxBase { if (this._isStub(char)) { this._moveCaret(isDeleteKey ? 1 : -1); - // @ts-expect-error ts-error - if (this._parsedValue < 0 || 1 / this._parsedValue === -Infinity) { + if (isNegativeValue(this._parsedValue)) { this._revertSign(e); this._setTextByParsedValue(); - // @ts-expect-error ts-error - const shouldTriggerInputEvent = this.option('valueChangeEvent').split(' ').includes('input'); + const { valueChangeEvent } = this.option(); + const shouldTriggerInputEvent = valueChangeEvent?.split(' ').includes('input'); if (shouldTriggerInputEvent) { // @ts-expect-error ts-error eventsEngine.trigger(this._input(), 'input'); @@ -338,7 +352,7 @@ class NumberBoxMask extends NumberBoxBase { return; } - const decimalSeparator = number.getDecimalSeparator(); + const decimalSeparator: string = number.getDecimalSeparator(); if (char === decimalSeparator) { const decimalSeparatorIndex = text.indexOf(decimalSeparator); if (this._isNonStubAfter(decimalSeparatorIndex + 1)) { @@ -353,8 +367,7 @@ class NumberBoxMask extends NumberBoxBase { const noDigits = editedText.search(/[0-9]/) < 0; if (noDigits && this._isValueInRange(0)) { - // @ts-expect-error ts-error - this._parsedValue = this._parsedValue < 0 || 1 / this._parsedValue === -Infinity ? -0 : 0; + this._parsedValue = isNegativeValue(this._parsedValue) ? -0 : 0; return; } } @@ -367,48 +380,49 @@ class NumberBoxMask extends NumberBoxBase { } } - _isPercentFormat() { + _isPercentFormat(): boolean { const format = this._getFormatPattern(); - const noEscapedFormat = format.replace(/'[^']+'/g, ''); + const formatPattern = isString(format) ? format : ''; + const noEscapedFormat = formatPattern.replace(/'[^']+'/g, ''); - return noEscapedFormat.indexOf('%') !== -1; + return noEscapedFormat.includes('%'); } - _parse(text, format) { + _parse(text: string, format: Format): NumberBoxValue { const formatOption = this._getEffectiveFormatOption(); const customParser = asFormatObject(formatOption)?.parser; const isCustomParser = isFunction(customParser); const parser = isCustomParser ? customParser : number.parse; let integerPartStartIndex = 0; - if (!isCustomParser) { + if (!isCustomParser && isString(format)) { const formatPointIndex = getRealSeparatorIndex(format).index; const textPointIndex = this._getTextSeparatorIndex(text); const formatIntegerPartLength = formatPointIndex !== -1 ? formatPointIndex : format.length; const textIntegerPartLength = textPointIndex !== -1 ? textPointIndex : text.length; - if (textIntegerPartLength > formatIntegerPartLength && format.indexOf('#') === -1) { + if (textIntegerPartLength > formatIntegerPartLength && !format.includes('#')) { integerPartStartIndex = textIntegerPartLength - formatIntegerPartLength; } } - text = text.substr(integerPartStartIndex); + const parsedValue: NumberBoxValue = parser(text.substr(integerPartStartIndex), format); - return parser(text, format); + return parsedValue; } - _format(value, format) { + _format(value: NumberBoxValue, format: Format): string | undefined { const formatOption = this._getEffectiveFormatOption(); const customFormatter = asFormatObject(formatOption)?.formatter ?? formatOption; const formatter = isFunction(customFormatter) ? customFormatter : number.format; - const formattedValue = value === null ? '' : formatter(value, format); + const formattedValue: string | undefined = value === null ? '' : formatter(value, format); return formattedValue; } - _getFormatPattern() { + _getFormatPattern(): Format { if (!this._currentFormat) { this._updateFormat(); } @@ -426,28 +440,30 @@ class NumberBoxMask extends NumberBoxBase { this._currentFormat = shouldUseFormatAsIs ? format - : getLDMLFormat((value) => { + : getLDMLFormat((value: number): string => { const text = this._format(value, format); - return number.convertDigits(text, true); + const convertedText: string = number.convertDigits(text, true); + + return convertedText; }); } - _getFormatForSign(text) { + _getFormatForSign(text: string): string { const format = this._getFormatPattern(); if (isString(format)) { const signParts = format.split(';'); - const sign = number.getSign(text, format); + const sign: number = number.getSign(text, format); signParts[1] = signParts[1] || `-${signParts[0]}`; return sign < 0 ? signParts[1] : signParts[0]; } - const sign = number.getSign(text); + const sign: number = number.getSign(text); return sign < 0 ? '-' : ''; } - _removeStubs(text, excludeComma?): string { + _removeStubs(text: string, excludeComma?: boolean): string { const format = this._getFormatForSign(text); - const thousandsSeparator = number.getThousandsSeparator(); + const thousandsSeparator: string = number.getThousandsSeparator(); const stubs = this._getStubs(format); let result = text; @@ -465,42 +481,49 @@ class NumberBoxMask extends NumberBoxBase { return result; } - _getStubs(format) { - const regExpResult = /[^']([#0.,]+)/g.exec(format); - const pattern = regExpResult && regExpResult[0].trim(); + _getStubs(format: string): string[] { + const patternMatch = /[^']([#0.,]+)/g.exec(format); + const pattern = patternMatch?.[0].trim(); + const stubs = isDefined(pattern) ? format.split(pattern) : [format]; - return format - .split(pattern) - .map((stub) => stub.replace(/'/g, '')); + return stubs.map((stub) => stub.replace(/'/g, '')); } - _truncateToPrecision(value, maxPrecision) { + _truncateToPrecision(value: NumberBoxValue, maxPrecision: number): NumberBoxValue { if (isDefined(value)) { const strValue = value.toString(); const decimalSeparatorIndex = strValue.indexOf('.'); if (strValue && decimalSeparatorIndex > -1) { - const parsedValue = parseFloat(strValue.substr(0, decimalSeparatorIndex + maxPrecision + 1)); + const truncatedValue = strValue.substr(0, decimalSeparatorIndex + maxPrecision + 1); + const parsedValue = parseFloat(truncatedValue); + return isNaN(parsedValue) ? value : parsedValue; } } return value; } - _tryParse(text, selection, char?) { - const isTextSelected = selection.start !== selection.end; - const isWholeTextSelected = isTextSelected && selection.start === 0 && selection.end === text.length; - const decimalSeparator = number.getDecimalSeparator(); + _tryParse( + text: string, + selection: CaretRange | undefined, + char?: string | null, + ): NumberBoxValue { + const { start = 0, end = 0 } = selection ?? {}; + const isTextSelected = start !== end; + const isWholeTextSelected = isTextSelected && start === 0 && end === text.length; + const decimalSeparator: string = number.getDecimalSeparator(); if (isWholeTextSelected && char === decimalSeparator) { return 0; } - const editedText = this._replaceSelectedText(text, selection, char); + const editedText = this._replaceSelectedText(text, { start, end }, char ?? undefined); const format = this._getFormatPattern(); + const hasCustomParser = isFunction(asFormatObject(format)?.parser); let parsedValue = this._getParsedValue(editedText, format); - const maxPrecision = !format.parser && this._getPrecisionLimits(editedText).max; + const maxPrecision = hasCustomParser ? undefined : this._getPrecisionLimits(editedText).max; const isValueChanged = parsedValue !== this._parsedValue; const isDecimalPointRestricted = char === decimalSeparator && maxPrecision === 0; @@ -514,19 +537,18 @@ class NumberBoxMask extends NumberBoxBase { } if (this._removeStubs(editedText) === '') { - // @ts-expect-error ts-error - parsedValue = Math.abs(this._parsedValue * 0); + parsedValue = Math.abs(Number(this._parsedValue) * 0); } - if (isNaN(parsedValue)) { + if (isNaN(Number(parsedValue))) { return undefined; } const value = parsedValue === null ? this._parsedValue : parsedValue; parsedValue = maxPrecision ? this._truncateToPrecision(value, maxPrecision) : parsedValue; - if (!format.parser && this._isPercentFormat()) { - const interval = this._getIntervalFromPrecision(maxPrecision); + if (!hasCustomParser && this._isPercentFormat()) { + const interval = this._getIntervalFromPrecision(maxPrecision ?? 0); return adjustPercentValue(parsedValue, interval); } @@ -534,7 +556,7 @@ class NumberBoxMask extends NumberBoxBase { return parsedValue; } - _getIntervalFromPrecision(precision) { + _getIntervalFromPrecision(precision: number): number { if (precision < 1) { return 1; } @@ -542,23 +564,24 @@ class NumberBoxMask extends NumberBoxBase { return 10 ** -precision; } - _getParsedValue(text, format) { - const sign = number.getSign(text, format?.formatter || format); + _getParsedValue(text: string, format: Format): NumberBoxValue { + const signFormat = asFormatObject(format)?.formatter ?? format; + const sign: number = number.getSign(text, signFormat); const textWithoutStubs = this._removeStubs(text, true); const parsedValue = this._parse(textWithoutStubs, format); - const parsedValueSign = parsedValue < 0 ? -1 : 1; - const parsedValueWithSign = isNumeric(parsedValue) && sign !== parsedValueSign ? sign * parsedValue : parsedValue; + const parsedValueSign = Number(parsedValue) < 0 ? -1 : 1; + const shouldRevertSign = isNumeric(parsedValue) && sign !== parsedValueSign; - return parsedValueWithSign; + return shouldRevertSign ? sign * parsedValue : parsedValue; } - _isValueIncomplete(text) { + _isValueIncomplete(text: string): boolean { if (!this._useMaskBehavior()) { return super._isValueIncomplete(text); } const caret = this._caret(); - const point = number.getDecimalSeparator(); + const point: string = number.getDecimalSeparator(); const pointIndex = this._getTextSeparatorIndex(text); const isCaretOnFloat = pointIndex >= 0 && pointIndex < (caret?.start ?? 0); const textParts = this._removeStubs(text, true).split(point); @@ -568,30 +591,34 @@ class NumberBoxMask extends NumberBoxBase { } const floatLength = textParts[1].length; - const format = this._getFormatPattern(); - const isCustomParser = !!format.parser; - const precision = !isCustomParser && this._getPrecisionLimits(this._getFormatPattern()); - const isPrecisionInRange = isCustomParser - ? true - // @ts-expect-error ts-error - : inRange(floatLength, precision.min, precision.max); const endsWithZero = textParts[1].charAt(floatLength - 1) === '0'; + const isFloatPartComplete = endsWithZero || !floatLength; + + const format = this._getFormatPattern(); + + if (isFunction(asFormatObject(format)?.parser)) { + return isFloatPartComplete; + } - return isPrecisionInRange && (endsWithZero || !floatLength); + const precision = this._getPrecisionLimits(isString(format) ? format : ''); + + return inRange(floatLength, precision.min, precision.max) && isFloatPartComplete; } - _isValueInRange(value) { - // @ts-expect-error ts-error - const min = ensureDefined(this.option('min'), -Infinity); - // @ts-expect-error ts-error - const max = ensureDefined(this.option('max'), Infinity); + _isValueInRange(value: string | NumberBoxValue): boolean { + const { min, max } = this.option(); - return inRange(value, min, max); + return inRange(value, min ?? -Infinity, max ?? Infinity); } - _setInputText(text): void { - const normalizedText = number.convertDigits(text, true); - const newCaret = getCaretAfterFormat(this._getInputVal(), normalizedText, this._caret(), this._getFormatPattern()); + _setInputText(text: string): void { + const normalizedText: string = number.convertDigits(text, true); + const newCaret = getCaretAfterFormat( + this._getInputVal(), + normalizedText, + this._caret(), + this._getFormatPattern(), + ); this._input().val(text); this._toggleEmptinessEventHandler(); @@ -620,24 +647,29 @@ class NumberBoxMask extends NumberBoxBase { } } - _isChar(str) { + _isChar(str: string | null | undefined): boolean { return isString(str) && str.length === 1; } - _moveCaret(offset?): void { + _moveCaret(offset?: number): void { if (!offset) { return; } const newCaret = getCaretWithOffset(this._caret(), offset); - const adjustedCaret = getCaretInBoundaries(newCaret, this._getInputVal(), this._getFormatPattern()); + const adjustedCaret = getCaretInBoundaries( + newCaret, + this._getInputVal(), + this._getFormatPattern(), + ); this._caret(adjustedCaret); } - _shouldHandleKey(e): boolean { + _shouldHandleKey(e: KeyboardEvent): boolean { const keyName = normalizeKeyName(e); - const isSpecialChar = isCommandKeyPressed(e) || e.altKey || e.shiftKey || !this._isChar(keyName); + const isSpecialChar = isCommandKeyPressed(e) || e.altKey || e.shiftKey + || !this._isChar(keyName); const isMinusKey = keyName === MINUS_KEY; const useMaskBehavior = this._useMaskBehavior(); @@ -649,7 +681,7 @@ class NumberBoxMask extends NumberBoxBase { this._renderFormatter(); } - _renderFormatter() { + _renderFormatter(): void { this._clearCache(); this._detachFormatterEvents(); @@ -658,11 +690,11 @@ class NumberBoxMask extends NumberBoxBase { } } - _detachFormatterEvents() { + _detachFormatterEvents(): void { eventsEngine.off(this._input(), `.${NUMBER_FORMATTER_NAMESPACE}`); } - _isInputFromPaste(e) { + _isInputFromPaste(e: DxEvent): boolean | undefined { const inputType = e.originalEvent?.inputType; if (isDefined(inputType)) { @@ -674,16 +706,24 @@ class NumberBoxMask extends NumberBoxBase { _attachFormatterEvents(): void { const $input = this._input(); - eventsEngine.on($input, addNamespace(INPUT_EVENT, NUMBER_FORMATTER_NAMESPACE), (e) => { - this._formatValue(e); - this._isValuePasted = false; - }); + eventsEngine.on( + $input, + addNamespace(INPUT_EVENT, NUMBER_FORMATTER_NAMESPACE), + (e: DxEvent) => { + this._formatValue(e); + this._isValuePasted = false; + }, + ); eventsEngine.on($input, addNamespace('dxclick', NUMBER_FORMATTER_NAMESPACE), () => { if (!this._caretTimeout) { this._caretTimeout = setTimeout(() => { this._caretTimeout = undefined; - this._caret(getCaretInBoundaries(this._caret(), this._getInputVal(), this._getFormatPattern())); + this._caret(getCaretInBoundaries( + this._caret(), + this._getInputVal(), + this._getFormatPattern(), + )); }, CARET_TIMEOUT_DURATION); } }); @@ -698,34 +738,35 @@ class NumberBoxMask extends NumberBoxBase { this._caretTimeout = undefined; } - _forceRefreshInputValue() { + _forceRefreshInputValue(): void { if (!this._useMaskBehavior()) { - return super._forceRefreshInputValue(); + super._forceRefreshInputValue(); } } - _isNonStubAfter(index) { + _isNonStubAfter(index: number): boolean { const text = this._getInputVal().slice(index); - return text && !this._isStub(text, true); + + return !!text && !this._isStub(text, true); } - _isStub(str, isString?) { + _isStub(str: string | null | undefined, allowMultipleChars?: boolean): boolean { const escapedDecimalSeparator = escapeRegExp(number.getDecimalSeparator()); const regExpString = `^[^0-9${escapedDecimalSeparator}]+$`; const stubRegExp = new RegExp(regExpString, 'g'); - return stubRegExp.test(str) && (isString || this._isChar(str)); + return stubRegExp.test(str ?? '') && (!!allowMultipleChars || this._isChar(str)); } - _parseValue(text?): number | null { + _parseValue(text?: string | NumberBoxValue): NumberBoxValue { if (!this._useMaskBehavior()) { return super._parseValue(text); } - // @ts-expect-error ts-error + return this._parsedValue; } - _getPrecisionLimits(text): { min: number; max: number } { + _getPrecisionLimits(text: string): { min: number; max: number } { const currentFormat = this._getFormatForSign(text); const realSeparatorIndex = getRealSeparatorIndex(currentFormat).index; const floatPart = (splitByIndex(currentFormat, realSeparatorIndex)[1] || '').replace(/[^#0]/g, ''); @@ -735,7 +776,7 @@ class NumberBoxMask extends NumberBoxBase { return { min: minPrecision, max: maxPrecision }; } - _revertSign(e) { + _revertSign(e: DxEvent): void { if (!this._useMaskBehavior()) { return; } @@ -753,8 +794,12 @@ class NumberBoxMask extends NumberBoxBase { this._applyRevertedSign(e, caret); } - _applyRevertedSign(e, caret, preserveSelectedText?): void { - const newValue = -1 * ensureDefined(this._parsedValue, null); + _applyRevertedSign( + e: DxEvent, + caret: CaretRange | undefined, + preserveSelectedText?: boolean, + ): void { + const newValue = -1 * (this._parsedValue ?? 0); if (this._isValueInRange(newValue) || newValue === 0) { this._parsedValue = newValue; @@ -768,23 +813,22 @@ class NumberBoxMask extends NumberBoxBase { const currentText = this._getInputVal(); const offset = getCaretOffset(previousText, currentText, format); - - caret = getCaretWithOffset(caret, offset); - - const caretInBoundaries = getCaretInBoundaries(caret, currentText, format); + const caretWithOffset = getCaretWithOffset(caret, offset); + const caretInBoundaries = getCaretInBoundaries(caretWithOffset, currentText, format); this._caret(caretInBoundaries); } } } - _removeMinusFromText(text, caret) { - const isMinusPressed = this._lastKeyName === MINUS_KEY && text.charAt(caret.start - 1) === MINUS; + _removeMinusFromText(text: string, caret: CaretRange | undefined): string { + const caretStart = caret?.start ?? 0; + const isMinusPressed = this._lastKeyName === MINUS_KEY + && text.charAt(caretStart - 1) === MINUS; - return isMinusPressed ? this._replaceSelectedText(text, { - start: caret.start - 1, - end: caret.start, - }, '') : text; + return isMinusPressed + ? this._replaceSelectedText(text, { start: caretStart - 1, end: caretStart }, '') + : text; } _setTextByParsedValue(): void { @@ -795,7 +839,7 @@ class NumberBoxMask extends NumberBoxBase { this._setInputText(formatted); } - _formatValue(e): void { + _formatValue(e: DxEvent): void { let normalizedText = this._getInputVal(); const caret = this._caret(); const textWithoutMinus = this._removeMinusFromText(normalizedText, caret); @@ -812,7 +856,8 @@ class NumberBoxMask extends NumberBoxBase { return; } - const textWasChanged = number.convertDigits(this._formattedValue, true) !== normalizedText; + const formattedValue: string = number.convertDigits(this._formattedValue, true); + const textWasChanged = formattedValue !== normalizedText; if (textWasChanged) { const value = this._tryParse(normalizedText, caret, ''); @@ -825,18 +870,18 @@ class NumberBoxMask extends NumberBoxBase { this._setTextByParsedValue(); } - _renderDisplayText(): void { + _renderDisplayText(text?: string): void { if (this._useMaskBehavior()) { this._toggleEmptinessEventHandler(); } else { - // @ts-expect-error ts-error - super._renderDisplayText.apply(this, arguments); + super._renderDisplayText(text); } } - _renderValue() { + _renderValue(): DeferredObj { if (this._useMaskBehavior()) { const { value } = this.option(); + this._parsedValue = value; this._setTextByParsedValue(); } @@ -862,12 +907,15 @@ class NumberBoxMask extends NumberBoxBase { return; } - this._parsedValue = fitIntoRange(parsedValue, this.option('min'), this.option('max')); + const { min, max } = this.option(); + + this._parsedValue = fitIntoRange(parsedValue, min, max); } - _valueChangeEventHandler(e): void { + _valueChangeEventHandler(e: DxEvent): void { if (!this._useMaskBehavior()) { - return super._valueChangeEventHandler(e); + super._valueChangeEventHandler(e); + return; } const caret = this._caret(); @@ -885,7 +933,7 @@ class NumberBoxMask extends NumberBoxBase { } } - _optionChanged(args): void { + _optionChanged(args: OptionChanged): void { switch (args.name) { case 'format': case 'useMaskBehavior': diff --git a/packages/devextreme/js/__internal/ui/number_box/number_box.spin.ts b/packages/devextreme/js/__internal/ui/number_box/number_box.spin.ts index d739bae81ba5..2ec292543b26 100644 --- a/packages/devextreme/js/__internal/ui/number_box/number_box.spin.ts +++ b/packages/devextreme/js/__internal/ui/number_box/number_box.spin.ts @@ -8,6 +8,7 @@ import type { dxElementWrapper } from '@js/core/renderer'; import $ from '@js/core/renderer'; import type { DeferredObj } from '@js/core/utils/deferred'; import { Deferred } from '@js/core/utils/deferred'; +import type { DxEvent, PointerInteractionEvent } from '@js/events/events.types'; import type { OptionChanged } from '@ts/core/widget/types'; import type { WidgetProperties } from '@ts/core/widget/widget'; import Widget from '@ts/core/widget/widget'; @@ -21,10 +22,14 @@ const NUMBER_BOX = 'dxNumberBox'; const POINTERUP_EVENT_NAME = addNamespace(pointerEvents.up, NUMBER_BOX); const POINTERCANCEL_EVENT_NAME = addNamespace(pointerEvents.cancel, NUMBER_BOX); +export interface SpinChangeEvent { + event: DxEvent; +} + export interface SpinButtonProperties extends WidgetProperties { direction?: string; - onChange?: () => void; + onChange?: ((e: SpinChangeEvent) => void) | null; } class SpinButton extends Widget { @@ -40,7 +45,6 @@ class SpinButton extends Widget { return { ...super._getDefaultOptions(), direction: 'up', - // @ts-expect-error ts-error onChange: null, activeStateEnabled: true, hoverStateEnabled: true, @@ -73,7 +77,7 @@ class SpinButton extends Widget { this._spinChangeHandler = this._createActionByOption('onChange'); } - _spinDownHandler(e): void { + _spinDownHandler(e: DxEvent): void { e.preventDefault(); this._clearTimer(); diff --git a/packages/devextreme/js/__internal/ui/number_box/number_box.spins.ts b/packages/devextreme/js/__internal/ui/number_box/number_box.spins.ts index 75560e162804..2473522f9b05 100644 --- a/packages/devextreme/js/__internal/ui/number_box/number_box.spins.ts +++ b/packages/devextreme/js/__internal/ui/number_box/number_box.spins.ts @@ -3,16 +3,25 @@ import pointer from '@js/common/core/events/pointer'; import { addNamespace } from '@js/common/core/events/utils/index'; import type { dxElementWrapper } from '@js/core/renderer'; import $ from '@js/core/renderer'; -import { extend } from '@js/core/utils/extend'; +import type { DxEvent, PointerInteractionEvent } from '@js/events/events.types'; import TextEditorButton from '@ts/ui/text_box/texteditor_button_collection/button'; -import SpinButton from './m_number_box.spin'; +import type NumberBoxBase from './number_box.base'; +import type { SpinChangeEvent } from './number_box.spin'; +import SpinButton from './number_box.spin'; const SPIN_CLASS = 'dx-numberbox-spin'; const SPIN_CONTAINER_CLASS = 'dx-numberbox-spin-container'; const SPIN_TOUCH_FRIENDLY_CLASS = 'dx-numberbox-spin-touch-friendly'; -export default class SpinButtons extends TextEditorButton { +interface SpinButtonsOptions { + visible: boolean; + disabled: boolean | undefined; +} + +export default class SpinButtons extends TextEditorButton { + declare instance?: dxElementWrapper | null; + _attachEvents( instance: dxElementWrapper, $spinContainer: dxElementWrapper, @@ -27,8 +36,7 @@ export default class SpinButtons extends TextEditorButton { const $spinContainerChildren = $spinContainer.children(); const pointerDownAction = editor._createAction( - // @ts-expect-error Private API - (e) => { this.editor?._spinButtonsPointerDownHandler(e); }, + () => { this.editor?._spinButtonsPointerDownHandler(); }, ); eventsEngine.off($spinContainer, eventName); @@ -36,19 +44,17 @@ export default class SpinButtons extends TextEditorButton { eventsEngine.on( $spinContainer, eventName, - (e) => pointerDownAction({ event: e }), + (e: DxEvent) => pointerDownAction({ event: e }), ); - SpinButton.getInstance($spinContainerChildren.eq(0)).option( + SpinButton.getInstance($spinContainerChildren.eq(0)).option( 'onChange', - // @ts-expect-error Private API - (e) => { this.editor?._spinUpChangeHandler(e); }, + (e: SpinChangeEvent) => { this.editor?._spinUpChangeHandler(e); }, ); - SpinButton.getInstance($spinContainerChildren.eq(1)).option( + SpinButton.getInstance($spinContainerChildren.eq(1)).option( 'onChange', - // @ts-expect-error Private API - (e) => { this.editor?._spinDownChangeHandler(e); }, + (e: SpinChangeEvent) => { this.editor?._spinDownChangeHandler(e); }, ); } @@ -65,8 +71,8 @@ export default class SpinButtons extends TextEditorButton { this._addToContainer($spinContainer); - editor?._createComponent($spinUp, SpinButton, extend({ direction: 'up' }, options)); - editor?._createComponent($spinDown, SpinButton, extend({ direction: 'down' }, options)); + editor?._createComponent($spinUp, SpinButton, { direction: 'up', ...options }); + editor?._createComponent($spinDown, SpinButton, { direction: 'down', ...options }); this._legacyRender(editor?.$element(), this._isTouchFriendly(), options.visible); @@ -76,11 +82,11 @@ export default class SpinButtons extends TextEditorButton { }; } - _getOptions() { + _getOptions(): SpinButtonsOptions { const { editor } = this; const visible = this._isVisible(); - const disabled = editor?.option('disabled'); + const { disabled } = editor?.option() ?? {}; return { visible, @@ -88,45 +94,46 @@ export default class SpinButtons extends TextEditorButton { }; } - // @ts-expect-error - _isVisible() { + _isVisible(): boolean { const { editor } = this; + const { showSpinButtons } = editor?.option() ?? {}; - return super._isVisible() && editor?.option('showSpinButtons'); + return super._isVisible() && !!showSpinButtons; } - _isTouchFriendly() { + _isTouchFriendly(): boolean { const { editor } = this; + const { showSpinButtons, useLargeSpinButtons } = editor?.option() ?? {}; - return editor?.option('showSpinButtons') && editor?.option('useLargeSpinButtons'); + return !!showSpinButtons && !!useLargeSpinButtons; } // TODO: get rid of it - _legacyRender($editor, isTouchFriendly, isVisible) { - $editor.toggleClass(SPIN_TOUCH_FRIENDLY_CLASS, isTouchFriendly); - $editor.toggleClass(SPIN_CLASS, isVisible); + _legacyRender( + $editor?: dxElementWrapper, + isTouchFriendly?: boolean, + isVisible?: boolean, + ): void { + $editor?.toggleClass(SPIN_TOUCH_FRIENDLY_CLASS, isTouchFriendly); + $editor?.toggleClass(SPIN_CLASS, isVisible); } - // @ts-expect-error - update(): void { + update(): boolean { const shouldUpdate = super.update(); + const { editor, instance } = this; - if (shouldUpdate) { - const { editor, instance } = this; - - const $editor = editor?.$element(); - const isVisible = this._isVisible(); - const isTouchFriendly = this._isTouchFriendly(); - // @ts-expect-error + if (shouldUpdate && instance) { const $spinButtons = instance.children(); - const spinUp = SpinButton.getInstance($spinButtons.eq(0)); - const spinDown = SpinButton.getInstance($spinButtons.eq(1)); + const spinUp = SpinButton.getInstance($spinButtons.eq(0)); + const spinDown = SpinButton.getInstance($spinButtons.eq(1)); const options = this._getOptions(); spinUp.option(options); spinDown.option(options); - this._legacyRender($editor, isTouchFriendly, isVisible); + this._legacyRender(editor?.$element(), this._isTouchFriendly(), options.visible); } + + return shouldUpdate; } } diff --git a/packages/devextreme/js/__internal/ui/number_box/number_box.ts b/packages/devextreme/js/__internal/ui/number_box/number_box.ts index 9b862fa02055..1b484d08e202 100644 --- a/packages/devextreme/js/__internal/ui/number_box/number_box.ts +++ b/packages/devextreme/js/__internal/ui/number_box/number_box.ts @@ -1,6 +1,6 @@ import registerComponent from '@js/core/component_registrator'; -import NumberBoxMask from './m_number_box.mask'; +import NumberBoxMask from './number_box.mask'; // STYLE numberBox diff --git a/packages/devextreme/js/__internal/ui/number_box/utils.ts b/packages/devextreme/js/__internal/ui/number_box/utils.ts index 388587c56f7f..8925885d4577 100644 --- a/packages/devextreme/js/__internal/ui/number_box/utils.ts +++ b/packages/devextreme/js/__internal/ui/number_box/utils.ts @@ -1,15 +1,20 @@ -import { adjust, roundFloatPart } from '@js/core/utils/math'; +import { adjust } from '@js/core/utils/math'; -const getRealSeparatorIndex = function (str) { +export interface SeparatorPosition { + occurrence: number; + index: number; +} + +const getRealSeparatorIndex = (str: string): SeparatorPosition => { let quoteBalance = 0; let separatorCount = 0; - for (let i = 0; i < str.length; ++i) { + for (let i = 0; i < str.length; i += 1) { if (str[i] === '\'') { - quoteBalance++; + quoteBalance += 1; } if (str[i] === '.') { - ++separatorCount; + separatorCount += 1; if (quoteBalance % 2 === 0) { return { occurrence: separatorCount, @@ -22,17 +27,17 @@ const getRealSeparatorIndex = function (str) { return { occurrence: 1, index: -1 }; }; -const getNthOccurrence = function (str, c, n) { - let i = -1; +const getNthOccurrence = (str: string, char: string, occurrence: number): number => { + let index = -1; - while (n-- && i++ < str.length) { - i = str.indexOf(c, i); + for (let remaining = occurrence; remaining > 0; remaining -= 1) { + index = str.indexOf(char, index + 1); } - return i; + return index; }; -const splitByIndex = function (str, index) { +const splitByIndex = (str: string, index: number): string[] => { if (index === -1) { return [str]; } @@ -40,7 +45,10 @@ const splitByIndex = function (str, index) { return [str.slice(0, index), str.slice(index + 1)]; }; -const adjustPercentValue = function (rawValue, interval) { +const adjustPercentValue = ( + rawValue: number | null | undefined, + interval: number, +): number | null | undefined => { if (!rawValue) { return rawValue; } @@ -48,17 +56,9 @@ const adjustPercentValue = function (rawValue, interval) { return adjust(rawValue / 100, interval / 100); }; -const roundFloatPartPercentValue = function (rawValue: number, precision: number) { - if (!rawValue) { - return rawValue; - } - return roundFloatPart(rawValue / 100, precision); -}; - export { adjustPercentValue, getNthOccurrence, getRealSeparatorIndex, - roundFloatPartPercentValue, splitByIndex, }; diff --git a/packages/devextreme/js/__internal/ui/text_box/text_editor.mask.ts b/packages/devextreme/js/__internal/ui/text_box/text_editor.mask.ts index 46d0e038133a..4a18e79436ff 100644 --- a/packages/devextreme/js/__internal/ui/text_box/text_editor.mask.ts +++ b/packages/devextreme/js/__internal/ui/text_box/text_editor.mask.ts @@ -340,7 +340,7 @@ class TextEditorMask< _replaceSelectedText( text: string, selection: CaretRange, - char: string, + char: string | undefined, ): string { if (char === undefined) { return text; diff --git a/packages/devextreme/js/ui/number_box.js b/packages/devextreme/js/ui/number_box.js index 5ea396acda04..a682bbaa8a92 100644 --- a/packages/devextreme/js/ui/number_box.js +++ b/packages/devextreme/js/ui/number_box.js @@ -1,4 +1,4 @@ -import NumberBox from '../__internal/ui/number_box/m_number_box'; +import NumberBox from '../__internal/ui/number_box/number_box'; export default NumberBox; diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/numberBoxParts/common.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/numberBoxParts/common.tests.js index 9e82f2737153..63d935a73456 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/numberBoxParts/common.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/numberBoxParts/common.tests.js @@ -1,5 +1,5 @@ import $ from 'jquery'; -import SpinButton from '__internal/ui/number_box/m_number_box.spin'; +import SpinButton from '__internal/ui/number_box/number_box.spin'; import config from 'core/config'; import eventsEngine from 'common/core/events/core/events_engine'; import keyboardMock from '../../../helpers/keyboardMock.js'; diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/numberBoxParts/mask.caret.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/numberBoxParts/mask.caret.tests.js index 546b5dd3d923..b070db9ed5b1 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/numberBoxParts/mask.caret.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/numberBoxParts/mask.caret.tests.js @@ -4,7 +4,7 @@ import { getCaretInBoundaries, isCaretInBoundaries, getCaretAfterFormat, -} from '__internal/ui/number_box/m_number_box.caret'; +} from '__internal/ui/number_box/number_box.caret'; QUnit.module('format caret', () => { const customFormat = { diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets/toolbar.kbn.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets/toolbar.kbn.tests.js index 564c6efcfa6b..d5fec4952418 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets/toolbar.kbn.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets/toolbar.kbn.tests.js @@ -29,7 +29,7 @@ import { DROP_DOWN_BUTTON_CLASS } from '__internal/ui/drop_down_button'; import { TEXTEDITOR_CLASS, TEXTEDITOR_INPUT_CLASS } from '__internal/ui/text_box/text_editor.base'; import { TEXTBOX_CLASS } from '__internal/ui/text_box/text_box'; -import { WIDGET_CLASS as NUMBERBOX_CLASS } from '__internal/ui/number_box/m_number_box.base'; +import { WIDGET_CLASS as NUMBERBOX_CLASS } from '__internal/ui/number_box/number_box.base'; import { SELECTBOX_CLASS } from '__internal/ui/select_box'; import { SWITCH_CLASS } from '__internal/ui/switch';