diff --git a/packages/devextreme/js/__internal/core/format_helper.ts b/packages/devextreme/js/__internal/core/format_helper.ts index 47a59ba385ba..a9a3dd4fc509 100644 --- a/packages/devextreme/js/__internal/core/format_helper.ts +++ b/packages/devextreme/js/__internal/core/format_helper.ts @@ -12,7 +12,7 @@ import { isPlainObject, isString, } from '@js/core/utils/type'; -import { injector as dependencyInjector } from '@ts/core/utils/m_dependency_injector'; +import { injector as dependencyInjector } from '@ts/core/utils/dependency_injector'; import { getGlobalFormatByDataType } from './global_format_config'; diff --git a/packages/devextreme/js/__internal/core/guid.ts b/packages/devextreme/js/__internal/core/guid.ts new file mode 100644 index 000000000000..0f582e1d16c0 --- /dev/null +++ b/packages/devextreme/js/__internal/core/guid.ts @@ -0,0 +1,47 @@ +export class Guid { + private readonly _value: string; + + constructor(value?: string) { + const initialValue = value ? String(value) : ''; + + this._value = this._normalize(initialValue || this._generate()); + } + + public _normalize(value: string): string { + let normalizedValue = value.replace(/[^a-f0-9]/ig, '').toLowerCase(); + + while (normalizedValue.length < 32) { + normalizedValue += '0'; + } + + return [ + normalizedValue.substr(0, 8), + normalizedValue.substr(8, 4), + normalizedValue.substr(12, 4), + normalizedValue.substr(16, 4), + normalizedValue.substr(20, 12), + ].join('-'); + } + + private _generate(): string { + let value = ''; + + for (let i = 0; i < 32; i += 1) { + value += Math.round(Math.random() * 15).toString(16); + } + + return value; + } + + public toString(): string { + return this._value; + } + + public valueOf(): string { + return this._value; + } + + public toJSON(): string { + return this._value; + } +} diff --git a/packages/devextreme/js/__internal/core/localization/core.ts b/packages/devextreme/js/__internal/core/localization/core.ts index 4d247e1dd99a..726a34befea6 100644 --- a/packages/devextreme/js/__internal/core/localization/core.ts +++ b/packages/devextreme/js/__internal/core/localization/core.ts @@ -1,26 +1,35 @@ import parentLocales from '@ts/core/localization/cldr-data/parent_locales'; import getParentLocale from '@ts/core/localization/parentLocale'; -import { injector as dependencyInjector } from '@ts/core/utils/m_dependency_injector'; +import { injector as dependencyInjector } from '@ts/core/utils/dependency_injector'; const DEFAULT_LOCALE = 'en'; +interface LocaleAccessor { + (): string; + (locale: string): void; +} + export default dependencyInjector({ - locale: (() => { + locale: ((): LocaleAccessor => { let currentLocale = DEFAULT_LOCALE; - // eslint-disable-next-line @typescript-eslint/no-invalid-void-type,consistent-return - return (locale?: string): string | void => { + function localeAccessor(): string; + function localeAccessor(locale: string): void; + // eslint-disable-next-line consistent-return + function localeAccessor(locale?: string): string | void { if (!locale) { return currentLocale; } currentLocale = locale; - }; + } + + return localeAccessor; })(), - getValueByClosestLocale( - getter: (locale: string) => string | number | undefined, - ): string | number | undefined { + getValueByClosestLocale( + getter: (locale: string) => TValue | undefined, + ): TValue | undefined { let locale: string = this.locale(); let value = getter(locale); let isRootLocale = false; diff --git a/packages/devextreme/js/__internal/core/localization/currency.ts b/packages/devextreme/js/__internal/core/localization/currency.ts index 643aac6f0aa8..2f33ac20a8bc 100644 --- a/packages/devextreme/js/__internal/core/localization/currency.ts +++ b/packages/devextreme/js/__internal/core/localization/currency.ts @@ -16,10 +16,12 @@ export default { // eslint-disable-next-line @typescript-eslint/no-unsafe-return return this.callBase.apply(this, [value, format, formatConfig]); }, - getCurrencySymbol(): { symbol: string } { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getCurrencySymbol(currency?: string): { symbol: string } { return { symbol: '$' }; }, - getOpenXmlCurrencyFormat(): string { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getOpenXmlCurrencyFormat(currency?: string): string | undefined { return '$#,##0{0}_);\\($#,##0{0}\\)'; }, }; diff --git a/packages/devextreme/js/__internal/core/localization/date.ts b/packages/devextreme/js/__internal/core/localization/date.ts index bf7002575276..9487b2be105c 100644 --- a/packages/devextreme/js/__internal/core/localization/date.ts +++ b/packages/devextreme/js/__internal/core/localization/date.ts @@ -10,7 +10,7 @@ import { getFormatter as getLDMLDateFormatter } from '@ts/core/localization/ldml import { getParser as getLDMLDateParser } from '@ts/core/localization/ldml/date.parser'; import numberLocalization from '@ts/core/localization/number'; import errors from '@ts/core/m_errors'; -import { injector as dependencyInjector } from '@ts/core/utils/m_dependency_injector'; +import { injector as dependencyInjector } from '@ts/core/utils/dependency_injector'; import { each } from '@ts/core/utils/m_iterator'; import { isString } from '@ts/core/utils/m_type'; @@ -85,7 +85,6 @@ const dateLocalization = dependencyInjector({ (presetOverride as string).toLowerCase() ] || presetOverride as string; - // eslint-disable-next-line @typescript-eslint/no-unsafe-return return numberLocalization.convertDigits( getLDMLDateFormatter(pattern, this)(date), ); @@ -114,16 +113,20 @@ const dateLocalization = dependencyInjector({ return result; }, - getMonthNames(format: BaseFormat): string[] { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getMonthNames(format: Format, type?: string): string[] { return defaultDateNames.getMonthNames(format); }, - getDayNames(format: BaseFormat): string[] { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getDayNames(format: Format, type?: string): string[] { return defaultDateNames.getDayNames(format); }, - getQuarterNames(format: BaseFormat): string[] { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getQuarterNames(format: Format, type?: string): string[] { return defaultDateNames.getQuarterNames(format); }, - getPeriodNames(format: BaseFormat): string[] { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getPeriodNames(format?: Format, type?: string): string[] { return defaultDateNames.getPeriodNames(format); }, getTimeSeparator(): string { @@ -175,7 +178,6 @@ const dateLocalization = dependencyInjector({ // eslint-disable-next-line no-param-reassign format = (FORMATS_TO_PATTERN_MAP[(format as string).toLowerCase()] || format) as string; - // eslint-disable-next-line @typescript-eslint/no-unsafe-return return numberLocalization.convertDigits(getLDMLDateFormatter(format, this)(date)); } } @@ -216,7 +218,6 @@ const dateLocalization = dependencyInjector({ // eslint-disable-next-line @typescript-eslint/no-shadow const text: string = that.format(value, format); - // eslint-disable-next-line @typescript-eslint/no-unsafe-return return numberLocalization.convertDigits(text, true); }; try { diff --git a/packages/devextreme/js/__internal/core/localization/default_date_names.ts b/packages/devextreme/js/__internal/core/localization/default_date_names.ts index 622f69033428..4e47a67fe195 100644 --- a/packages/devextreme/js/__internal/core/localization/default_date_names.ts +++ b/packages/devextreme/js/__internal/core/localization/default_date_names.ts @@ -30,7 +30,7 @@ export default { return QUARTERS; }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - getPeriodNames(_format: Format): string[] { + getPeriodNames(_format?: Format): string[] { return PERIODS; }, }; diff --git a/packages/devextreme/js/__internal/core/localization/globalize/currency.ts b/packages/devextreme/js/__internal/core/localization/globalize/currency.ts index 7364f4b98dbe..677462c74845 100644 --- a/packages/devextreme/js/__internal/core/localization/globalize/currency.ts +++ b/packages/devextreme/js/__internal/core/localization/globalize/currency.ts @@ -4,7 +4,7 @@ import '@ts/core/localization/currency'; import 'globalize/currency'; import config from '@js/core/config'; -import type { FormatConfig, NormalizedConfig } from '@ts/core/localization/number'; +import type { FormatConfig, LocalizationFormat, NormalizedConfig } from '@ts/core/localization/number'; import numberLocalization from '@ts/core/localization/number'; import openXmlCurrencyFormat from '@ts/core/localization/open_xml_currency_format'; // eslint-disable-next-line import/no-extraneous-dependencies @@ -79,30 +79,29 @@ if (Globalize?.formatCurrency) { }, format( value: string | number, - format: number | string | FormatConfig | Function | undefined, + format?: LocalizationFormat, ): string | number { if (typeof value !== 'number') { return value; } - // eslint-disable-next-line no-param-reassign - format = this._normalizeFormat(format) as FormatConfig; + const normalizedFormat = this._normalizeFormat(format) as FormatConfig; - if (format) { - if (format.currency === 'default') { - format.currency = config().defaultCurrency; + if (normalizedFormat) { + if (normalizedFormat.currency === 'default') { + normalizedFormat.currency = config().defaultCurrency; } - if (format.type === 'currency') { + if (normalizedFormat.type === 'currency') { // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return this._formatNumber(value, this._parseNumberFormatString('currency'), format); - } if (!format.type && format.currency) { - return getFormatter(format.currency, format)(value); + return this._formatNumber(value, this._parseNumberFormatString('currency'), normalizedFormat); + } if (!normalizedFormat.type && normalizedFormat.currency) { + return getFormatter(normalizedFormat.currency, normalizedFormat)(value); } } // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return this.callBase.apply(this, [value, format]); + return this.callBase.apply(this, [value, normalizedFormat]); }, getCurrencySymbol(currency?: string): { symbol: string } { if (!currency) { @@ -113,7 +112,7 @@ if (Globalize?.formatCurrency) { // eslint-disable-next-line @typescript-eslint/no-unsafe-return return Globalize.cldr.main(`numbers/currencies/${currency}`); }, - getOpenXmlCurrencyFormat(currency: string): string | undefined { + getOpenXmlCurrencyFormat(currency?: string): string | undefined { const currencySymbol = this.getCurrencySymbol(currency).symbol; const accountingFormat = Globalize.cldr.main('numbers/currencyFormats-numberSystem-latn').accounting; diff --git a/packages/devextreme/js/__internal/core/localization/globalize/date.ts b/packages/devextreme/js/__internal/core/localization/globalize/date.ts index 56be41d04666..0ba0261c70bc 100644 --- a/packages/devextreme/js/__internal/core/localization/globalize/date.ts +++ b/packages/devextreme/js/__internal/core/localization/globalize/date.ts @@ -127,7 +127,7 @@ if (Globalize?.formatDate) { return Globalize.locale().main(`dates/calendars/gregorian/${path}`); }, - getPeriodNames(format: Format, type: string): string[] { + getPeriodNames(format?: Format, type?: string): string[] { // eslint-disable-next-line no-param-reassign format = format || 'wide'; // eslint-disable-next-line no-param-reassign @@ -137,7 +137,7 @@ if (Globalize?.formatDate) { return [json.am, json.pm]; }, - getMonthNames(format: Format, type: string): string[] { + getMonthNames(format: Format, type?: string): string[] { const months: Record = Globalize.locale().main(`dates/calendars/gregorian/months/${type === 'format' ? type : 'stand-alone'}/${format || 'wide'}`); // eslint-disable-next-line @typescript-eslint/no-unsafe-return @@ -228,7 +228,7 @@ if (Globalize?.formatDate) { return this.removeRtlMarks(formatter(date)); }, - parse(text: string, format: FormatObject | DateParser | string): Date | null | undefined { + parse(text: string, format?: FormatObject | DateParser | string): Date | null | undefined { if (!text) { return undefined; } diff --git a/packages/devextreme/js/__internal/core/localization/globalize/number.ts b/packages/devextreme/js/__internal/core/localization/globalize/number.ts index 3c4e99be24f1..0326f42c54c8 100644 --- a/packages/devextreme/js/__internal/core/localization/globalize/number.ts +++ b/packages/devextreme/js/__internal/core/localization/globalize/number.ts @@ -99,7 +99,7 @@ if (Globalize?.formatNumber) { format( value: string | number, - format: LocalizationFormat, + format?: LocalizationFormat, ): string { if (typeof value !== 'number') { return value; @@ -120,7 +120,7 @@ if (Globalize?.formatNumber) { parse( text: string, - format: FormatConfig | string, + format?: FormatConfig | string, ): number | null | undefined { if (!text) { return undefined; diff --git a/packages/devextreme/js/__internal/core/localization/intl/date.ts b/packages/devextreme/js/__internal/core/localization/intl/date.ts index b2ae3bf9313b..4964694a7146 100644 --- a/packages/devextreme/js/__internal/core/localization/intl/date.ts +++ b/packages/devextreme/js/__internal/core/localization/intl/date.ts @@ -169,7 +169,7 @@ export default { engine(): string { return 'intl'; }, - getMonthNames(format: Format, type: string): string[] { + getMonthNames(format: Format, type?: string): string[] { // eslint-disable-next-line @typescript-eslint/no-shadow const intlFormats: Record, Intl.DateTimeFormatOptions['month']> = { wide: 'long', @@ -178,14 +178,12 @@ export default { }; const monthFormat = intlFormats[format || 'wide']; - - // eslint-disable-next-line no-param-reassign - type = type === 'format' ? type : 'standalone'; + const nameType = type === 'format' ? type : 'standalone'; return Array.from( { length: 12 }, // eslint-disable-next-line @typescript-eslint/no-unsafe-return - (_, monthIndex): string => monthNameStrategies[type](monthIndex, monthFormat), + (_, monthIndex): string => monthNameStrategies[nameType](monthIndex, monthFormat), ); }, @@ -267,7 +265,7 @@ export default { return getIntlFormatter(format)(date); }, - parse(dateString: string, format: FormatObject | string): Date | null | undefined { + parse(dateString: string, format?: FormatObject | string): Date | null | undefined { // eslint-disable-next-line @typescript-eslint/init-declarations let formatter: DateFormatter | undefined; diff --git a/packages/devextreme/js/__internal/core/localization/intl/number.ts b/packages/devextreme/js/__internal/core/localization/intl/number.ts index 630f0ffc7275..afc160434d02 100644 --- a/packages/devextreme/js/__internal/core/localization/intl/number.ts +++ b/packages/devextreme/js/__internal/core/localization/intl/number.ts @@ -101,7 +101,7 @@ export default { }, format( value: string | number, - format: LocalizationFormat, + format?: LocalizationFormat, ): string { if (typeof value !== 'number') { return value; @@ -149,10 +149,9 @@ export default { }; }, - getCurrencySymbol(currency: string): { symbol: string } { + getCurrencySymbol(currency?: string): { symbol: string } { if (!currency) { - // @ts-expect-error - // eslint-disable-next-line + // eslint-disable-next-line no-param-reassign currency = dxConfig().defaultCurrency; } @@ -161,7 +160,7 @@ export default { symbol: symbolInfo.symbol, }; }, - getOpenXmlCurrencyFormat(currency: string): string | undefined { + getOpenXmlCurrencyFormat(currency?: string): string | undefined { const targetCurrency = currency || dxConfig().defaultCurrency; const currencySymbol: string = this._getCurrencySymbolInfo(targetCurrency).symbol; const closestAccountingFormat: string | undefined = localizationCoreUtils diff --git a/packages/devextreme/js/__internal/core/localization/ldml/date.formatter.ts b/packages/devextreme/js/__internal/core/localization/ldml/date.formatter.ts index b845cfa57270..58fde9bb16b1 100644 --- a/packages/devextreme/js/__internal/core/localization/ldml/date.formatter.ts +++ b/packages/devextreme/js/__internal/core/localization/ldml/date.formatter.ts @@ -101,54 +101,64 @@ const LDML_FORMATTERS = { }, }; -export const getFormatter = ( +export function getFormatter( + format: string, + dateParts: LdlmDateLocalization, +): (date: Date | null) => string; +export function getFormatter( format: string | undefined, dateParts: LdlmDateLocalization, -) => (date: Date | null): Date | string | null => { - // eslint-disable-next-line @typescript-eslint/init-declarations - let charIndex: number; - // eslint-disable-next-line @typescript-eslint/init-declarations - let formatter: typeof LDML_FORMATTERS[keyof typeof LDML_FORMATTERS]; - // eslint-disable-next-line @typescript-eslint/init-declarations - let char: string; - let charCount = 0; - const separator = '\''; - let isEscaping = false; +): (date: Date | null) => Date | string | null; +export function getFormatter( + format: string | undefined, + dateParts: LdlmDateLocalization, +): (date: Date | null) => Date | string | null { + return (date: Date | null): Date | string | null => { // eslint-disable-next-line @typescript-eslint/init-declarations - let isCurrentCharEqualsNext: boolean; - let result = ''; + let charIndex: number; + // eslint-disable-next-line @typescript-eslint/init-declarations + let formatter: typeof LDML_FORMATTERS[keyof typeof LDML_FORMATTERS]; + // eslint-disable-next-line @typescript-eslint/init-declarations + let char: string; + let charCount = 0; + const separator = '\''; + let isEscaping = false; + // eslint-disable-next-line @typescript-eslint/init-declarations + let isCurrentCharEqualsNext: boolean; + let result = ''; - if (!date) { - return null; - } + if (!date) { + return null; + } - if (!format) { - return date; - } + if (!format) { + return date; + } - const useUtc = format.endsWith('Z') || format.endsWith('\'Z\''); + const useUtc = format.endsWith('Z') || format.endsWith('\'Z\''); - for (charIndex = 0; charIndex < format.length; charIndex += 1) { - char = format[charIndex]; - formatter = LDML_FORMATTERS[char]; - isCurrentCharEqualsNext = char === format[charIndex + 1]; - charCount += 1; + for (charIndex = 0; charIndex < format.length; charIndex += 1) { + char = format[charIndex]; + formatter = LDML_FORMATTERS[char]; + isCurrentCharEqualsNext = char === format[charIndex + 1]; + charCount += 1; - if (!isCurrentCharEqualsNext) { - if (formatter && !isEscaping) { - result += formatter(date, charCount, useUtc, dateParts); + if (!isCurrentCharEqualsNext) { + if (formatter && !isEscaping) { + result += formatter(date, charCount, useUtc, dateParts); + } + charCount = 0; } - charCount = 0; - } - if (char === separator && !isCurrentCharEqualsNext) { - isEscaping = !isEscaping; - } else if (isEscaping || !formatter) { - result += char; - } - if (char === separator && isCurrentCharEqualsNext) { - charIndex += 1; + if (char === separator && !isCurrentCharEqualsNext) { + isEscaping = !isEscaping; + } else if (isEscaping || !formatter) { + result += char; + } + if (char === separator && isCurrentCharEqualsNext) { + charIndex += 1; + } } - } - return result; -}; + return result; + }; +} diff --git a/packages/devextreme/js/__internal/core/localization/message.ts b/packages/devextreme/js/__internal/core/localization/message.ts index ce6af1292db3..c0ae6d4317d6 100644 --- a/packages/devextreme/js/__internal/core/localization/message.ts +++ b/packages/devextreme/js/__internal/core/localization/message.ts @@ -1,6 +1,6 @@ import coreLocalization from '@ts/core/localization/core'; import { defaultMessages } from '@ts/core/localization/default_messages'; -import { injector as dependencyInjector } from '@ts/core/utils/m_dependency_injector'; +import { injector as dependencyInjector } from '@ts/core/utils/dependency_injector'; import { extend } from '@ts/core/utils/m_extend'; import { humanize } from '@ts/core/utils/m_inflector'; import { format as stringFormat } from '@ts/core/utils/m_string'; diff --git a/packages/devextreme/js/__internal/core/localization/number.ts b/packages/devextreme/js/__internal/core/localization/number.ts index 1a920b2fe10e..53631e00f316 100644 --- a/packages/devextreme/js/__internal/core/localization/number.ts +++ b/packages/devextreme/js/__internal/core/localization/number.ts @@ -7,8 +7,8 @@ import currencyLocalization from '@ts/core/localization/currency'; import intlNumberLocalization from '@ts/core/localization/intl/number'; import { getFormatter } from '@ts/core/localization/ldml/number'; import { toFixed } from '@ts/core/localization/utils'; +import { injector as dependencyInjector } from '@ts/core/utils/dependency_injector'; import { escapeRegExp } from '@ts/core/utils/m_common'; -import { injector as dependencyInjector } from '@ts/core/utils/m_dependency_injector'; import { each } from '@ts/core/utils/m_iterator'; import { isPlainObject } from '@ts/core/utils/m_type'; @@ -62,8 +62,8 @@ export interface FormatterConfig { unlimitedIntegerDigits?: boolean; } -const numberLocalization = dependencyInjector({ - engine() { +const numberLocalizationBase = { + engine(): string { return 'base'; }, numericFormats: NUMERIC_FORMATS, @@ -267,7 +267,10 @@ const numberLocalization = dependencyInjector({ return this.format(1.2, { type: 'fixedPoint', precision: 1 })[1] as string; }, - convertDigits(value: string | number, toStandard?: boolean): string | number { + convertDigits( + value: TValue, + toStandard?: boolean, + ): TValue { const digits: string = this.format(90, 'decimal'); if (typeof value !== 'string' || digits[1] === '0') { @@ -280,7 +283,7 @@ const numberLocalization = dependencyInjector({ const regExp = new RegExp(`[${fromFirstDigit}-${fromLastDigit}]`, 'g'); // eslint-disable-next-line @stylistic/max-len - return value.replace(regExp, (char) => String.fromCharCode(char.charCodeAt(0) + (toFirstDigit.charCodeAt(0) - fromFirstDigit.charCodeAt(0)))); + return value.replace(regExp, (char) => String.fromCharCode(char.charCodeAt(0) + (toFirstDigit.charCodeAt(0) - fromFirstDigit.charCodeAt(0)))) as TValue; }, getNegativeEtalonRegExp(format: FormatConfig | string): RegExp { @@ -298,7 +301,7 @@ const numberLocalization = dependencyInjector({ return new RegExp(negativeEtalon, 'g'); }, - getSign(text: string, format: FormatConfig | string): 1 | -1 { + getSign(text: string, format?: FormatConfig | string): 1 | -1 { if (!format) { if (text.replace(/[^0-9-]/g, '').startsWith('-')) { return -1; @@ -357,7 +360,7 @@ const numberLocalization = dependencyInjector({ return this._formatNumber(value, numberConfig, format) as string; }, - parse(text: string, format: FormatConfig | string): number | null | undefined { + parse(text: string, format?: FormatConfig | string): number | null | undefined { if (!text) { return undefined; } @@ -437,7 +440,20 @@ const numberLocalization = dependencyInjector({ } return result; }, -}); +}; + +type NumberLocalizationContract = Omit + & typeof currencyLocalization + & { + format: { + (value: number, format?: LocalizationFormat): string; + (value: string | number, format?: LocalizationFormat): string | number; + }; + }; + +const numberLocalization = dependencyInjector( + numberLocalizationBase as NumberLocalizationContract, +); numberLocalization.inject(currencyLocalization); diff --git a/packages/devextreme/js/__internal/core/m_guid.ts b/packages/devextreme/js/__internal/core/m_guid.ts deleted file mode 100644 index 13e922b3d1a0..000000000000 --- a/packages/devextreme/js/__internal/core/m_guid.ts +++ /dev/null @@ -1,47 +0,0 @@ -import Class from '@js/core/class'; - -const Guid = Class.inherit({ - ctor: function (value) { - if (value) { - value = String(value); - } - this._value = this._normalize(value || this._generate()); - }, - - _normalize: function (value) { - value = value.replace(/[^a-f0-9]/ig, '').toLowerCase(); - while (value.length < 32) { - value += '0'; - } - return [ - value.substr(0, 8), - value.substr(8, 4), - value.substr(12, 4), - value.substr(16, 4), - value.substr(20, 12), - ].join('-'); - }, - - _generate: function () { - let value = ''; - for (let i = 0; i < 32; i++) { - value += Math.round(Math.random() * 15).toString(16); - } - return value; - }, - - toString: function () { - return this._value; - }, - - valueOf: function () { - return this._value; - }, - - toJSON: function () { - return this._value; - }, - -}); - -export { Guid }; diff --git a/packages/devextreme/js/__internal/core/utils/dependency_injector.ts b/packages/devextreme/js/__internal/core/utils/dependency_injector.ts new file mode 100644 index 000000000000..39d04c38ee6f --- /dev/null +++ b/packages/devextreme/js/__internal/core/utils/dependency_injector.ts @@ -0,0 +1,115 @@ +import { extend } from '@js/core/utils/extend'; +import { each } from '@js/core/utils/iterator'; +import { isFunction } from '@js/core/utils/type'; + +type InjectionStore = Record; + +type InjectedMethod = (...args: unknown[]) => unknown; + +interface CallBaseHolder { + callBase?: unknown; +} + +export type Injection = { + [K in keyof T]?: T[K] extends (...args: infer TArgs) => infer TResult + ? (this: Injectable & { callBase: T[K] }, ...args: TArgs) => TResult + : T[K]; +} & { + // Invoked once when the injection is applied (and again on every later inject call + // while this injection stays the topmost one that declares a ctor). + ctor?: (this: T) => void; +}; + +export type Injectable = T & { + inject: (injectionObject: Injection) => void; + resetInjection: () => void; +}; + +function isInjectedMethod(value: unknown): value is InjectedMethod { + return isFunction(value); +} + +function wrapOverridden( + base: InjectionStore, + methodName: string, + method: InjectedMethod, +): InjectedMethod { + return function overriddenMethod(this: CallBaseHolder, ...args: unknown[]): unknown { + const prevCallBase = this.callBase; + + this.callBase = base[methodName]; + + try { + return method.apply(this, args); + } finally { + this.callBase = prevCallBase; + } + }; +} + +function injector(object: T): Injectable { + const facade = object as InjectionStore; + const initialFields: InjectionStore = {}; + const baseInstance: InjectionStore = {}; + + each(facade, (key: string): void => { + baseInstance[key] = facade[key]; + }); + + let instance = baseInstance; + + const invokeConstructor = (...args: unknown[]): void => { + const { ctor } = instance; + + if (isInjectedMethod(ctor)) { + ctor.apply(instance, args); + } + }; + + const injectFields = (injectionObject: object, initial?: boolean): void => { + each(injectionObject, (key: string): void => { + if (isFunction(instance[key])) { + if (initial || !facade[key]) { + facade[key] = (...args: unknown[]): unknown => { + const method = instance[key] as InjectedMethod; + + return method.apply(facade, args); + }; + } + } else { + if (initial) { + initialFields[key] = facade[key]; + } + facade[key] = instance[key]; + } + }); + }; + + invokeConstructor(object); + injectFields(facade, true); + + facade.inject = (injectionObject: Injection): void => { + const overriddenInstance: InjectionStore = Object.create(instance); + + each(injectionObject, (key: string, member: unknown): void => { + overriddenInstance[key] = isFunction(instance[key]) && isInjectedMethod(member) + ? wrapOverridden(instance, key, member) + : member; + }); + + instance = overriddenInstance; + + invokeConstructor(); + injectFields(injectionObject); + }; + + facade.resetInjection = (): void => { + extend(facade, initialFields); + instance = baseInstance; + invokeConstructor(); + }; + + return object as Injectable; +} + +export { injector }; diff --git a/packages/devextreme/js/__internal/core/utils/m_data.ts b/packages/devextreme/js/__internal/core/utils/m_data.ts index c95ba7dc3544..bd80b11beaf0 100644 --- a/packages/devextreme/js/__internal/core/utils/m_data.ts +++ b/packages/devextreme/js/__internal/core/utils/m_data.ts @@ -1,5 +1,6 @@ import Class from '@js/core/class'; import errors from '@js/core/errors'; +import Guid from '@js/core/guid'; import { each } from '@js/core/utils/iterator'; import { deepExtendArraySafe } from '@js/core/utils/object'; import { @@ -240,7 +241,7 @@ export const toComparable = function (value, caseSensitive?, options: any = {}) const collatorSensitivity = options?.collatorOptions?.sensitivity; - if (value && value instanceof Class && value.valueOf) { + if (value && (value instanceof Class || value instanceof Guid) && value.valueOf) { value = value.valueOf(); } else if (typeof value === 'string' && (collatorSensitivity === 'base' || collatorSensitivity === 'case')) { const REMOVE_DIACRITICAL_MARKS_REGEXP = /[\u0300-\u036f]/g; diff --git a/packages/devextreme/js/__internal/core/utils/m_dependency_injector.ts b/packages/devextreme/js/__internal/core/utils/m_dependency_injector.ts deleted file mode 100644 index acda838cd10d..000000000000 --- a/packages/devextreme/js/__internal/core/utils/m_dependency_injector.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* eslint-disable prefer-arrow-callback */ -/* eslint-disable func-names */ -import Class from '@js/core/class'; -import { extend } from '@js/core/utils/extend'; -import { each } from '@js/core/utils/iterator'; -import { isFunction } from '@js/core/utils/type'; - -function injector(object) { - const BaseClass = Class.inherit(object); - let InjectedClass = BaseClass; - let instance = new InjectedClass(object); - const initialFields = {}; - - const injectFields = function (injectionObject, initial?) { - each(injectionObject, function (key) { - if (isFunction(instance[key])) { - if (initial || !object[key]) { - object[key] = function () { - return instance[key].apply(object, arguments); - }; - } - } else { - if (initial) { - initialFields[key] = object[key]; - } - object[key] = instance[key]; - } - }); - }; - - injectFields(object, true); - - object.inject = function (injectionObject) { - InjectedClass = InjectedClass.inherit(injectionObject); - instance = new InjectedClass(); - injectFields(injectionObject); - }; - - object.resetInjection = function () { - extend(object, initialFields); - InjectedClass = BaseClass; - instance = new BaseClass(); - }; - - return object; -} - -export { injector }; diff --git a/packages/devextreme/js/__internal/core/utils/m_variable_wrapper.ts b/packages/devextreme/js/__internal/core/utils/m_variable_wrapper.ts index b1c82d0e17e3..c4e2579acff6 100644 --- a/packages/devextreme/js/__internal/core/utils/m_variable_wrapper.ts +++ b/packages/devextreme/js/__internal/core/utils/m_variable_wrapper.ts @@ -1,8 +1,15 @@ -/* eslint-disable object-shorthand */ import { logger } from '@js/core/utils/console'; import dependencyInjector from '@js/core/utils/dependency_injector'; -const variableWrapper = dependencyInjector({ +interface VariableWrapper { + isWrapped: (value: any) => boolean; + isWritableWrapped: (value: any) => boolean; + wrap: (value: any) => any; + unwrap: (value: any) => any; + assign: (variable: any, value: any) => void; +} + +const variableWrapper = dependencyInjector({ isWrapped: function () { return false; }, diff --git a/packages/devextreme/js/__internal/events/core/m_events_engine.ts b/packages/devextreme/js/__internal/events/core/m_events_engine.ts index b11f4ce2a4d4..b6343211d372 100644 --- a/packages/devextreme/js/__internal/events/core/m_events_engine.ts +++ b/packages/devextreme/js/__internal/events/core/m_events_engine.ts @@ -11,6 +11,7 @@ import { isFunction, isObject, isString, isWindow, } from '@js/core/utils/type'; import { getWindow, hasWindow } from '@js/core/utils/window'; +import type { Injection } from '@ts/core/utils/dependency_injector'; import { EMPTY_EVENT_NAME, EVENT_PROPERTIES, @@ -29,6 +30,29 @@ const elementDataMap = new WeakMap(); let guid = 0; let skipEvent; +type EventsEngineMethod = (...args: any[]) => void; + +interface EventFactory { + prototype: any; + (src?: any, config?: any): any; + new (src?: any, config?: any): any; +} + +interface EventsEngine { + on: EventsEngineMethod; + one: EventsEngineMethod; + off: EventsEngineMethod; + trigger: EventsEngineMethod; + triggerHandler: EventsEngineMethod; + Event: EventFactory; + set: (engine: Injection) => void; + subscribeGlobal: EventsEngineMethod; + forcePassiveFalseEventNames: typeof forcePassiveFalseEventNames; + passiveEventHandlersSupported: () => boolean; + elementDataMap: typeof elementDataMap; + detectPassiveEventHandlersSupport: () => boolean; +} + const special = (function () { const specialData = {}; @@ -109,7 +133,7 @@ const eventsEngine = injector({ const handlersController = getHandlersController(element, event.type); handlersController.callHandlers(event, extraParameters); })), -}); +} as EventsEngine); function applyForEach(args, method) { const element = args[0]; @@ -457,7 +481,7 @@ function normalizeEventArguments(callback) { } callback.call(this, src, config); - }; + } as EventFactory; Object.assign(eventsEngine.Event.prototype, { _propagationStopped: false, _immediatePropagationStopped: false, @@ -622,7 +646,7 @@ hookTouchProps(addProperty); const beforeSetStrategy = Callbacks(); const afterSetStrategy = Callbacks(); -eventsEngine.set = function (engine) { +eventsEngine.set = function (engine: Injection) { beforeSetStrategy.fire(); eventsEngine.inject(engine); initEvent(engine.Event); @@ -631,7 +655,7 @@ eventsEngine.set = function (engine) { eventsEngine.subscribeGlobal = function () { applyForEach(arguments, normalizeOnArguments(function () { - const args = arguments; + const args: any = arguments; eventsEngine.on.apply(this, args); diff --git a/packages/devextreme/js/__internal/grids/new/card_view/content_view/content/card/card.test.tsx b/packages/devextreme/js/__internal/grids/new/card_view/content_view/content/card/card.test.tsx index 449eb3fe9e2e..b730348db7ec 100644 --- a/packages/devextreme/js/__internal/grids/new/card_view/content_view/content/card/card.test.tsx +++ b/packages/devextreme/js/__internal/grids/new/card_view/content_view/content/card/card.test.tsx @@ -2,7 +2,7 @@ import { afterEach, beforeEach, describe, expect, it, jest, } from '@jest/globals'; import { compileGetter } from '@js/common/data'; -import { Guid } from '@ts/core/m_guid'; +import { Guid } from '@ts/core/guid'; import { render } from 'inferno'; import { Card } from './card'; diff --git a/packages/devextreme/js/__internal/grids/new/card_view/content_view/content/card/card.tsx b/packages/devextreme/js/__internal/grids/new/card_view/content_view/content/card/card.tsx index 1dbfc85de48a..4baa5294c769 100644 --- a/packages/devextreme/js/__internal/grids/new/card_view/content_view/content/card/card.tsx +++ b/packages/devextreme/js/__internal/grids/new/card_view/content_view/content/card/card.tsx @@ -7,7 +7,7 @@ import { getPublicElement } from '@js/core/element'; import $ from '@js/core/renderer'; import { off, on } from '@js/events/index'; import type * as dxToolbar from '@js/ui/toolbar'; -import { Guid } from '@ts/core/m_guid'; +import { Guid } from '@ts/core/guid'; import { combineClasses } from '@ts/core/utils/combine_classes'; import type { Position } from '@ts/grids/new/grid_core/accessibility/types'; import { getCardDescriptiveLabel, getCardRoleDescription, getCardStateDescription } from '@ts/grids/new/grid_core/accessibility/utils'; @@ -151,8 +151,8 @@ export class Card extends Component { const cardRole = Template ? 'presentation' : 'application'; - const coverId = new Guid(); - const contentId = new Guid(); + const coverId = new Guid().toString(); + const contentId = new Guid().toString(); return ( dateLocalization.format(value, format)) as string; + this._formatPattern = getFormat( + (value) => dateLocalization.format(value, format) as string, + ) as string; } return this._formatPattern; @@ -487,7 +488,7 @@ class DateBoxMask< .split(quantifierRegexp) .map((sourcePart) => (quantifierRegexp.test(sourcePart) ? sourcePart - : numberLocalization.convertDigits(sourcePart, false)) as string) + : numberLocalization.convertDigits(sourcePart, false))) .join(''); this._regExpInfo.regexp = new RegExp(convertedSource, flags); } diff --git a/packages/devextreme/js/__internal/ui/drop_down_button.ts b/packages/devextreme/js/__internal/ui/drop_down_button.ts index 7cdf29ffe916..a8d4374b28c6 100644 --- a/packages/devextreme/js/__internal/ui/drop_down_button.ts +++ b/packages/devextreme/js/__internal/ui/drop_down_button.ts @@ -18,9 +18,9 @@ import ButtonGroup from '@js/ui/button_group'; import type { Item, Properties } from '@js/ui/drop_down_button'; import type { ItemClickEvent } from '@js/ui/list'; import type { PositionAlignment } from '@js/ui/popup'; +import { Guid } from '@ts/core/guid'; import messageLocalization from '@ts/core/localization/message'; import { getPublicElement } from '@ts/core/m_element'; -import { Guid } from '@ts/core/m_guid'; import { FunctionTemplate } from '@ts/core/templates/m_function_template'; import { ensureDefined } from '@ts/core/utils/m_common'; import { isDefined, isObject, isPlainObject } from '@ts/core/utils/m_type'; diff --git a/packages/devextreme/js/__internal/ui/number_box/m_number_box.mask.ts b/packages/devextreme/js/__internal/ui/number_box/m_number_box.mask.ts index 61c30aff7988..551fd0c65649 100644 --- a/packages/devextreme/js/__internal/ui/number_box/m_number_box.mask.ts +++ b/packages/devextreme/js/__internal/ui/number_box/m_number_box.mask.ts @@ -518,7 +518,7 @@ class NumberBoxMask extends NumberBoxBase { parsedValue = Math.abs(this._parsedValue * 0); } - if (isNaN(parsedValue)) { + if (parsedValue === undefined || (typeof parsedValue === 'number' && isNaN(parsedValue))) { return undefined; } @@ -546,7 +546,7 @@ class NumberBoxMask extends NumberBoxBase { const sign = number.getSign(text, format?.formatter || format); const textWithoutStubs = this._removeStubs(text, true); const parsedValue = this._parse(textWithoutStubs, format); - const parsedValueSign = parsedValue < 0 ? -1 : 1; + const parsedValueSign = parsedValue != null && parsedValue < 0 ? -1 : 1; const parsedValueWithSign = isNumeric(parsedValue) && sign !== parsedValueSign ? sign * parsedValue : parsedValue; return parsedValueWithSign; diff --git a/packages/devextreme/js/__internal/ui/slider/m_slider.ts b/packages/devextreme/js/__internal/ui/slider/m_slider.ts index 819c39d69569..8f4ae61ae359 100644 --- a/packages/devextreme/js/__internal/ui/slider/m_slider.ts +++ b/packages/devextreme/js/__internal/ui/slider/m_slider.ts @@ -396,7 +396,7 @@ class Slider< .appendTo(this._$wrapper); } - this._$minLabel.text(numberLocalization.format(min, labelFormat)); + this._$minLabel.text(numberLocalization.format(min!, labelFormat)); if (!this._$maxLabel) { this._$maxLabel = $('
') @@ -404,7 +404,7 @@ class Slider< .appendTo(this._$wrapper); } - this._$maxLabel.text(numberLocalization.format(max, labelFormat)); + this._$maxLabel.text(numberLocalization.format(max!, labelFormat)); // eslint-disable-next-line @typescript-eslint/restrict-plus-operands, @typescript-eslint/no-base-to-string this.$element().addClass(SLIDER_LABEL_POSITION_CLASS_PREFIX + position); diff --git a/packages/devextreme/js/__internal/ui/validation_engine.ts b/packages/devextreme/js/__internal/ui/validation_engine.ts index 02c0b700f9ba..8624a1e47e5a 100644 --- a/packages/devextreme/js/__internal/ui/validation_engine.ts +++ b/packages/devextreme/js/__internal/ui/validation_engine.ts @@ -238,7 +238,9 @@ class NumericRuleValidator extends SyncRuleValidator { return true; } if (rule.useCultureSettings && isString(value)) { - return !isNaN(numberLocalization.parse(value)); + const parsedValue = numberLocalization.parse(value); + + return parsedValue === null || (parsedValue !== undefined && !isNaN(parsedValue)); } return isNumeric(value); } diff --git a/packages/devextreme/js/common/guid.js b/packages/devextreme/js/common/guid.js index 2a2ab6ecb7c7..d45af0848ede 100644 --- a/packages/devextreme/js/common/guid.js +++ b/packages/devextreme/js/common/guid.js @@ -7,5 +7,5 @@ * @publicName ctor(value) * @param1 value:string */ -import { Guid } from '../__internal/core/m_guid'; +import { Guid } from '../__internal/core/guid'; export default Guid; diff --git a/packages/devextreme/js/core/utils/dependency_injector.js b/packages/devextreme/js/core/utils/dependency_injector.js index d74c4cb93691..b371e1d15ba5 100644 --- a/packages/devextreme/js/core/utils/dependency_injector.js +++ b/packages/devextreme/js/core/utils/dependency_injector.js @@ -1,3 +1,3 @@ // deprecated -import { injector } from '../../__internal/core/utils/m_dependency_injector'; +import { injector } from '../../__internal/core/utils/dependency_injector'; export default injector;