diff --git a/packages/devextreme/js/__internal/core/widget/component.ts b/packages/devextreme/js/__internal/core/widget/component.ts index ac0cf9abd18f..a1429c119639 100644 --- a/packages/devextreme/js/__internal/core/widget/component.ts +++ b/packages/devextreme/js/__internal/core/widget/component.ts @@ -401,7 +401,7 @@ export class Component< } _createActionByOption( - optionName: keyof TProperties, + optionName: keyof TProperties | (string & {}), config?: ActionConfig, ): (event?: unknown) => void { // eslint-disable-next-line @typescript-eslint/init-declarations diff --git a/packages/devextreme/js/__internal/core/widget/dom_component.ts b/packages/devextreme/js/__internal/core/widget/dom_component.ts index 72c3779dd090..aad400b2b280 100644 --- a/packages/devextreme/js/__internal/core/widget/dom_component.ts +++ b/packages/devextreme/js/__internal/core/widget/dom_component.ts @@ -104,7 +104,7 @@ class DOMComponent< this._$element = $(element); } - _getSynchronizableOptionsForCreateComponent(): (keyof TProperties)[] { + _getSynchronizableOptionsForCreateComponent(): string[] { return ['rtlEnabled', 'disabled', 'templatesRenderAsynchronously']; } @@ -344,7 +344,7 @@ class DOMComponent< const configuration = componentConfiguration ?? {}; const synchronizableOptions = this._getSynchronizableOptionsForCreateComponent() - .filter((value) => !(value in configuration)); + .filter((value) => !(value in configuration)) as (keyof TProperties)[]; const { integrationOptions } = this.option(); let { nestedComponentOptions } = this.option(); diff --git a/packages/devextreme/js/__internal/core/widget/types.ts b/packages/devextreme/js/__internal/core/widget/types.ts index 5ea5d35fc262..0157c95f7980 100644 --- a/packages/devextreme/js/__internal/core/widget/types.ts +++ b/packages/devextreme/js/__internal/core/widget/types.ts @@ -33,13 +33,14 @@ type DotNestedKeys = ( export type ComponentPropertyType< T, TProp extends string, > = PropertyType extends never ? never : PropertyType | undefined; -interface OptionChangedArgs { +// eslint-disable-next-line @typescript-eslint/consistent-type-definitions +type OptionChangedArgs = { name: TKey extends `${infer TName}.${string}` ? TName : TKey; fullName: TKey; previousValue: ComponentPropertyType; value: ComponentPropertyType; handled: boolean; -} +}; type OptionNames = DotNestedKeys>; diff --git a/packages/devextreme/js/__internal/m_sortable.ts b/packages/devextreme/js/__internal/m_sortable.ts index 2d5846e5a3f0..e72432715d3d 100644 --- a/packages/devextreme/js/__internal/m_sortable.ts +++ b/packages/devextreme/js/__internal/m_sortable.ts @@ -667,7 +667,6 @@ class Sortable extends Draggable { case 'onAdd': case 'onRemove': case 'onReorder': - // @ts-expect-error ts-error this[`_${name}Action`] = this._createActionByOption(name); break; case 'fromIndex': diff --git a/packages/devextreme/js/__internal/ui/collection/collection_widget.base.ts b/packages/devextreme/js/__internal/ui/collection/collection_widget.base.ts index 5e7b3b98d00d..e62521ce413d 100644 --- a/packages/devextreme/js/__internal/ui/collection/collection_widget.base.ts +++ b/packages/devextreme/js/__internal/ui/collection/collection_widget.base.ts @@ -1491,7 +1491,7 @@ class CollectionWidget< actionArgs: ActionArgs, actionConfig?: ActionConfig, ): void { - const action = this._createActionByOption(handlerOptionName, { + const action = this._createActionByOption(String(handlerOptionName), { validatingTargetName: 'itemElement', ...actionConfig, }); diff --git a/packages/devextreme/js/__internal/ui/date_box/date_box.strategy.calendar.ts b/packages/devextreme/js/__internal/ui/date_box/date_box.strategy.calendar.ts index 5d81ccf95d18..91b1a3984ecb 100644 --- a/packages/devextreme/js/__internal/ui/date_box/date_box.strategy.calendar.ts +++ b/packages/devextreme/js/__internal/ui/date_box/date_box.strategy.calendar.ts @@ -43,8 +43,8 @@ class CalendarStrategy< }; } - supportedKeys(): Record boolean | undefined> { - const homeEndHandler = (e: KeyboardEvent): boolean | undefined => { + supportedKeys(): Record) => boolean | undefined> { + const homeEndHandler = (e: DxEvent): boolean | undefined => { if (this.dateBox.option('opened')) { e.preventDefault(); return true; diff --git a/packages/devextreme/js/__internal/ui/date_box/date_box.strategy.list.ts b/packages/devextreme/js/__internal/ui/date_box/date_box.strategy.list.ts index a7f135acb18d..23df16cec110 100644 --- a/packages/devextreme/js/__internal/ui/date_box/date_box.strategy.list.ts +++ b/packages/devextreme/js/__internal/ui/date_box/date_box.strategy.list.ts @@ -42,7 +42,7 @@ class ListStrategy extends DateBoxStrategy { return this._widget as List; } - supportedKeys(): Record void> { + supportedKeys(): Record) => void> { return { space: noop, home: noop, diff --git a/packages/devextreme/js/__internal/ui/date_box/date_box.strategy.ts b/packages/devextreme/js/__internal/ui/date_box/date_box.strategy.ts index c7526aaac620..a5f76caa1849 100644 --- a/packages/devextreme/js/__internal/ui/date_box/date_box.strategy.ts +++ b/packages/devextreme/js/__internal/ui/date_box/date_box.strategy.ts @@ -62,7 +62,7 @@ class DateBoxStrategy { return displayFormat || 'shortdate'; } - supportedKeys(): Record void> { + supportedKeys(): Record) => void> { return {}; } diff --git a/packages/devextreme/js/__internal/ui/date_box/date_box.ts b/packages/devextreme/js/__internal/ui/date_box/date_box.ts index 5f6840200a9e..31660616b126 100644 --- a/packages/devextreme/js/__internal/ui/date_box/date_box.ts +++ b/packages/devextreme/js/__internal/ui/date_box/date_box.ts @@ -1,6 +1,8 @@ import registerComponent from '@js/core/component_registrator'; import DateBoxMask from '@ts/ui/date_box/date_box.mask'; +// STYLE dateBox + registerComponent('dxDateBox', DateBoxMask); export default DateBoxMask; diff --git a/packages/devextreme/js/__internal/ui/date_range_box/date_range.utils.ts b/packages/devextreme/js/__internal/ui/date_range_box/date_range.utils.ts new file mode 100644 index 000000000000..5821041f40f5 --- /dev/null +++ b/packages/devextreme/js/__internal/ui/date_range_box/date_range.utils.ts @@ -0,0 +1,45 @@ +import type { DateLike } from '@js/common'; +import dateUtils from '@js/core/utils/date'; +import dateSerialization from '@js/core/utils/date_serialization'; + +export const getDeserializedDate = ( + value: DateLike | undefined, +): Date => dateSerialization.deserializeDate(value) as Date; + +export const isSameDates = ( + date1: DateLike | undefined, + date2: DateLike | undefined, +): boolean => { + if (!date1 && !date2) { + return true; + } + + return dateUtils.sameDate(getDeserializedDate(date1), getDeserializedDate(date2)); +}; + +export const isSameDateArrays = ( + value: (DateLike | undefined)[], + previousValue: (DateLike | undefined)[], +): boolean => { + const [startDate, endDate] = value; + const [previousStartDate, previousEndDate] = previousValue; + + return isSameDates(startDate, previousStartDate) && isSameDates(endDate, previousEndDate); +}; + +export const sortDatesArray = ( + value: (DateLike | undefined)[], +): (DateLike | undefined)[] => { + const [startDate, endDate] = value; + + if (startDate && endDate && getDeserializedDate(startDate) > getDeserializedDate(endDate)) { + return [endDate, startDate]; + } + + return value; +}; + +export const monthDifference = ( + date1: Date, + date2: Date, +): number => (date2.getFullYear() - date1.getFullYear()) * 12 - date1.getMonth() + date2.getMonth(); diff --git a/packages/devextreme/js/__internal/ui/date_range_box/m_date_range_box.ts b/packages/devextreme/js/__internal/ui/date_range_box/date_range_box.ts similarity index 76% rename from packages/devextreme/js/__internal/ui/date_range_box/m_date_range_box.ts rename to packages/devextreme/js/__internal/ui/date_range_box/date_range_box.ts index b1940dd493d0..000cac1686d3 100644 --- a/packages/devextreme/js/__internal/ui/date_range_box/m_date_range_box.ts +++ b/packages/devextreme/js/__internal/ui/date_range_box/date_range_box.ts @@ -1,4 +1,4 @@ -import type { Position } from '@js/common'; +import type { DateLike, Position } from '@js/common'; import eventsEngine from '@js/common/core/events/core/events_engine'; import { addNamespace } from '@js/common/core/events/utils/index'; import messageLocalization from '@js/common/core/localization/message'; @@ -7,26 +7,31 @@ import config from '@js/core/config'; import devices from '@js/core/devices'; import domAdapter from '@js/core/dom_adapter'; import type { DxElement } from '@js/core/element'; +import type { DefaultOptionsRule } from '@js/core/options/utils'; import type { dxElementWrapper } from '@js/core/renderer'; import $ from '@js/core/renderer'; import { FunctionTemplate } from '@js/core/templates/function_template'; -import { extend } from '@js/core/utils/extend'; import { getImageContainer } from '@js/core/utils/icon'; import { camelize } from '@js/core/utils/inflector'; -import { each } from '@js/core/utils/iterator'; +import type { DxEvent } from '@js/events'; +import type dxButton from '@js/ui/button'; +import type { DatePickerType, DisabledDate } from '@js/ui/date_box'; import type { Properties } from '@js/ui/date_range_box'; -import Editor from '@js/ui/editor/editor'; import { current, isFluent, isMaterial } from '@js/ui/themes'; +import type { OptionChanged } from '@ts/core/widget/types'; +import { + getDeserializedDate, isSameDateArrays, isSameDates, sortDatesArray, +} from '@ts/ui/date_range_box/date_range.utils'; +import type { MultiselectDateBoxProperties } from '@ts/ui/date_range_box/multiselect_date_box'; +import MultiselectDateBox from '@ts/ui/date_range_box/multiselect_date_box'; import DropDownButton from '@ts/ui/drop_down_editor/drop_down_button'; +import Editor from '@ts/ui/editor/editor'; +import type TextEditorBase from '@ts/ui/text_box/text_editor.base'; import ClearButton from '@ts/ui/text_box/text_editor.clear'; +import { isButtonInstance } from '@ts/ui/text_box/texteditor_button_collection/button'; +import type { TextEditorButtonInfo } from '@ts/ui/text_box/texteditor_button_collection/index'; import TextEditorButtonCollection from '@ts/ui/text_box/texteditor_button_collection/index'; -import { - getDeserializedDate, isSameDateArrays, isSameDates, sortDatesArray, -} from './m_date_range.utils'; -import type { MultiselectDateBoxProperties } from './m_multiselect_date_box'; -import MultiselectDateBox from './m_multiselect_date_box'; - const DATERANGEBOX_CLASS = 'dx-daterangebox'; const TEXTEDITOR_LABEL_STATIC_CLASS = 'dx-texteditor-with-label'; const TEXTEDITOR_LABEL_OUTSIDE_CLASS = 'dx-texteditor-label-outside'; @@ -54,10 +59,31 @@ const EVENTS_LIST = [ 'EnterKey', ]; -class DateRangeBox extends Editor { - private _openAction?: any; +interface ValidationErrorEntry { + message?: string; +} - private _closeAction?: any; +export interface DateRangeBoxProperties extends Omit { + value: (DateLike | undefined)[]; + + pickerType?: DatePickerType; + + disabledDates?: Date[] | ((data: DisabledDate) => boolean) | null; + + useHiddenSubmitElement?: boolean; + + _internalValidationErrors: ValidationErrorEntry[]; + + currentSelection: 'startDate' | 'endDate'; +} + +class DateRangeBox extends Editor { + // Temporary solution. Move to component level + public NAME!: string; + + private _openAction!: (e?: Record) => void; + + private _closeAction!: (e?: Record) => void; private _startDateBox!: MultiselectDateBox; @@ -71,17 +97,17 @@ class DateRangeBox extends Editor { private _popupContentId?: string; - private _buttonCollection: any; + private _buttonCollection!: TextEditorButtonCollection; private _shouldSkipIsValidChange?: boolean; - public _$beforeButtonsContainer?: dxElementWrapper; + public _$beforeButtonsContainer?: dxElementWrapper | null; - public _$afterButtonsContainer?: dxElementWrapper; + public _$afterButtonsContainer?: dxElementWrapper | null; - _getDefaultOptions(): Properties { - // @ts-expect-error - return extend(super._getDefaultOptions(), { + _getDefaultOptions(): DateRangeBoxProperties { + return { + ...super._getDefaultOptions(), acceptCustomValue: true, activeStateEnabled: true, applyButtonText: messageLocalization.format('OK'), @@ -150,14 +176,13 @@ class DateRangeBox extends Editor { valueChangeEvent: 'change', _internalValidationErrors: [], currentSelection: 'startDate', - }); + }; } - _defaultOptionsRules() { - // @ts-expect-error + _defaultOptionsRules(): DefaultOptionsRule[] { return super._defaultOptionsRules().concat([ { - device() { + device(): boolean { const themeName = current(); return isMaterial(themeName); }, @@ -167,7 +192,7 @@ class DateRangeBox extends Editor { }, }, { - device() { + device(): boolean { const themeName = current(); return isFluent(themeName); }, @@ -176,7 +201,7 @@ class DateRangeBox extends Editor { }, }, { - device() { + device(): boolean { const realDevice = devices.real(); const { platform } = realDevice; return platform === 'ios' || platform === 'android'; @@ -188,12 +213,10 @@ class DateRangeBox extends Editor { ]); } - _initOptions(options): void { - // @ts-expect-error + _initOptions(options: DateRangeBoxProperties): void { super._initOptions(options); - // @ts-expect-error - const { value: initialValue } = this.initialOption(); + const initialValue = this.initialOption('value') as unknown as DateLike[]; let { value, startDate, endDate } = this.option(); if (value[0] && value[1] && getDeserializedDate(value[0]) > getDeserializedDate(value[1])) { @@ -217,7 +240,6 @@ class DateRangeBox extends Editor { } _createOpenAction(): void { - // @ts-expect-error this._openAction = this._createActionByOption('onOpened', { excludeValidators: ['disabled', 'readOnly'], }); @@ -231,7 +253,6 @@ class DateRangeBox extends Editor { } _createCloseAction(): void { - // @ts-expect-error this._closeAction = this._createActionByOption('onClosed', { excludeValidators: ['disabled', 'readOnly'], }); @@ -244,14 +265,16 @@ class DateRangeBox extends Editor { this._closeAction(); } - _createEventAction(eventName): void { - // @ts-expect-error - this[`_${camelize(eventName)}Action`] = this._createActionByOption(`on${eventName}`, { - excludeValidators: ['readOnly'], - }); + _createEventAction(eventName: string): void { + this[`_${camelize(eventName)}Action`] = this._createActionByOption( + `on${eventName}` as keyof DateRangeBoxProperties, + { + excludeValidators: ['readOnly'], + }, + ); } - _raiseAction(eventName, event): void { + _raiseAction(eventName: string, event: DxEvent | undefined): void { const action = this[`_${camelize(eventName)}Action`]; if (!action) { this._createEventAction(eventName); @@ -259,27 +282,27 @@ class DateRangeBox extends Editor { this[`_${camelize(eventName)}Action`]({ event }); } - _initTemplates() { + _initTemplates(): void { this._templateManager.addDefaultTemplates({ - // @ts-expect-error + // @ts-expect-error should be fixed in FunctionTemplate definition dropDownButton: new FunctionTemplate((options) => { const $icon = $('
').addClass(DROP_DOWN_EDITOR_BUTTON_ICON); $(options.container).append($icon); }), }); - // @ts-expect-error super._initTemplates(); } - _getDefaultButtons() { + _getDefaultButtons(): TextEditorButtonInfo[] { return [ { name: 'clear', Ctor: ClearButton }, + // @ts-expect-error DropDownButton narrows the editor type to DropDownEditor { name: 'dropDown', Ctor: DropDownButton }, ]; } - _initMarkup() { + _initMarkup(): void { $(this.element()) .addClass(DATERANGEBOX_CLASS) .addClass(TEXTEDITOR_CLASS) @@ -289,7 +312,6 @@ class DateRangeBox extends Editor { this._toggleEditorLabelClass(); this._toggleReadOnlyState(); - // @ts-expect-error this._renderStylingMode(); this._renderEndDateBox(); @@ -300,14 +322,12 @@ class DateRangeBox extends Editor { this._renderEmptinessEvent(); this._renderButtonsContainer(); - // @ts-expect-error super._initMarkup(); $(this.element()).removeClass(INVALID_BADGE_CLASS); } _renderEmptinessEvent(): void { - // @ts-expect-error const eventName = addNamespace('input blur', this.NAME); eventsEngine.off(this._focusTarget(), eventName); @@ -322,8 +342,9 @@ class DateRangeBox extends Editor { } _attachKeyboardEvents(): void { - if (!this.option('readOnly')) { - // @ts-expect-error + const { readOnly } = this.option(); + + if (!readOnly) { super._attachKeyboardEvents(); } } @@ -368,21 +389,28 @@ class DateRangeBox extends Editor { .addClass(START_DATEBOX_CLASS) .prependTo(this.$element()); - // @ts-expect-error - this._startDateBox = this._createComponent(this._$startDateBox, MultiselectDateBox, this._getStartDateBoxConfig()); + this._startDateBox = this._createComponent( + this._$startDateBox, + MultiselectDateBox, + this._getStartDateBoxConfig(), + ); this._startDateBox.NAME = '_StartDateBox'; } - _renderEndDateBox() { + _renderEndDateBox(): void { this._$endDateBox = $('
') .addClass(END_DATEBOX_CLASS) .appendTo(this.$element()); - // @ts-expect-error - this._endDateBox = this._createComponent(this._$endDateBox, MultiselectDateBox, this._getEndDateBoxConfig()); + + this._endDateBox = this._createComponent( + this._$endDateBox, + MultiselectDateBox, + this._getEndDateBoxConfig(), + ); this._endDateBox.NAME = '_EndDateBox'; } - _renderSeparator() { + _renderSeparator(): void { const $icon = getImageContainer(SEPARATOR_ICON_NAME); this._$separator = $('
') .addClass(DATERANGEBOX_SEPARATOR_CLASS) @@ -394,7 +422,6 @@ class DateRangeBox extends Editor { } _renderPreventBlurOnSeparatorClick(): void { - // @ts-expect-error const eventName = addNamespace('mousedown', this.NAME); eventsEngine.off(this._$separator, eventName); @@ -408,8 +435,10 @@ class DateRangeBox extends Editor { } _renderButtonsContainer(): void { - // @ts-expect-error - this._buttonCollection = new TextEditorButtonCollection(this, this._getDefaultButtons()); + this._buttonCollection = new TextEditorButtonCollection( + this as unknown as TextEditorBase, + this._getDefaultButtons(), + ); this._$beforeButtonsContainer = undefined; this._$afterButtonsContainer = undefined; @@ -442,7 +471,6 @@ class DateRangeBox extends Editor { const { opened } = this.option(); if (!opened) { - // @ts-expect-error this.getStartDateBox()._focusInput(); } @@ -451,15 +479,17 @@ class DateRangeBox extends Editor { } } - _clearValueHandler(e) { + _clearValueHandler(e: DxEvent): void { e.stopPropagation(); - // @ts-expect-error this._saveValueChangeEvent(e); this.clear(); - !this._isStartDateActiveElement() && this.focus(); - // @ts-expect-error + if (!this._isStartDateActiveElement()) { + this.focus(); + } + + // @ts-expect-error the trigger method is not declared on EventsEngineType eventsEngine.trigger($(this.startDateField()), 'input'); } @@ -467,46 +497,57 @@ class DateRangeBox extends Editor { return this.option('showClearButton') && !this.option('readOnly'); } - _focusInHandler(event): void { + _focusInHandler(event: DxEvent): void { if (this._shouldSkipFocusEvent(event)) { return; } - // @ts-expect-error + super._focusInHandler(event); } - _focusOutHandler(event): void { + _focusOutHandler(event: DxEvent): void { if (this._shouldSkipFocusEvent(event)) { return; } - // @ts-expect-error + super._focusOutHandler(event); } - _shouldSkipFocusEvent(event) { - const { target, relatedTarget } = event; + _shouldSkipFocusEvent(event: DxEvent): boolean { + const target = event.target as Element | null; + const relatedTarget = event.relatedTarget as Element | null; + return ($(target).is($(this.startDateField())) && $(relatedTarget).is($(this.endDateField()))) || ($(target).is($(this.endDateField())) && $(relatedTarget).is($(this.startDateField()))); } - _getPickerType() { - // @ts-expect-error + _getPickerType(): DatePickerType { const { pickerType } = this.option(); - return ['calendar', 'native'].includes(pickerType) ? pickerType : 'calendar'; + + return pickerType && ['calendar', 'native'].includes(pickerType) ? pickerType : 'calendar'; } - _getRestErrors(allErrors, partialErrors) { - return allErrors.filter((error) => !partialErrors.some((prevError) => error.message === prevError.message)); + _getRestErrors( + allErrors: ValidationErrorEntry[], + partialErrors: ValidationErrorEntry[], + ): ValidationErrorEntry[] { + return allErrors.filter( + (error) => !partialErrors.some((prevError) => error.message === prevError.message), + ); } - _syncValidationErrors(optionName, newPartialErrors, previousPartialErrors): void { - newPartialErrors ||= []; - previousPartialErrors ||= []; + _syncValidationErrors( + optionName: 'validationErrors' | '_internalValidationErrors', + newPartialErrors: ValidationErrorEntry[] | null | undefined, + previousPartialErrors: ValidationErrorEntry[] | null | undefined, + ): void { + const newErrors = newPartialErrors ?? []; + const previousErrors = previousPartialErrors ?? []; - const allErrors = this.option(optionName) || []; - const otherErrors = this._getRestErrors(allErrors, previousPartialErrors); + const allErrors = (this.option(optionName) as unknown as ValidationErrorEntry[] | null) ?? []; + const otherErrors = this._getRestErrors(allErrors, previousErrors); - this.option(optionName, [...otherErrors, ...newPartialErrors]); + this.option(optionName, [...otherErrors, ...newErrors]); } _getDateBoxConfig(): MultiselectDateBoxProperties { @@ -539,32 +580,31 @@ class DateRangeBox extends Editor { validationMessageMode: options.validationMessageMode, validationMessagePosition: options.validationMessagePosition, valueChangeEvent: options.valueChangeEvent, - // @ts-expect-error + // @ts-expect-error the event types of dateBox and dateRangeBox are incompatible onKeyDown: options.onKeyDown, - // @ts-expect-error + // @ts-expect-error the event types of dateBox and dateRangeBox are incompatible onKeyUp: options.onKeyUp, - // @ts-expect-error + // @ts-expect-error the event types of dateBox and dateRangeBox are incompatible onChange: options.onChange, - // @ts-expect-error + // @ts-expect-error the event types of dateBox and dateRangeBox are incompatible onInput: options.onInput, - // @ts-expect-error + // @ts-expect-error the event types of dateBox and dateRangeBox are incompatible onCut: options.onCut, - // @ts-expect-error + // @ts-expect-error the event types of dateBox and dateRangeBox are incompatible onCopy: options.onCopy, - // @ts-expect-error + // @ts-expect-error the event types of dateBox and dateRangeBox are incompatible onPaste: options.onPaste, - // @ts-expect-error + // @ts-expect-error the event types of dateBox and dateRangeBox are incompatible onEnterKey: options.onEnterKey, _dateRangeBoxInstance: this, _showValidationMessage: false, }; - each(EVENTS_LIST, (_, eventName) => { + EVENTS_LIST.forEach((eventName) => { const optionName = `on${eventName}`; - // @ts-expect-error - if (this.hasActionSubscription(optionName)) { - dateBoxConfig[optionName] = (e) => { + if (this.hasActionSubscription(optionName as keyof DateRangeBoxProperties)) { + dateBoxConfig[optionName] = (e: { event?: DxEvent }): void => { this._raiseAction(eventName, e.event); }; } @@ -573,13 +613,16 @@ class DateRangeBox extends Editor { return dateBoxConfig; } - _hideOnOutsideClickHandler({ target }): boolean { + _hideOnOutsideClickHandler({ target }: DxEvent): boolean { // TODO: extract this common code part with ddeditor to avoid duplication const $target = $(target); const dropDownButton = this.getButton('dropDown'); - const $dropDownButton = dropDownButton?.$element(); + const $dropDownButton = isButtonInstance(dropDownButton) + ? dropDownButton.$element() + : dropDownButton; const isInputClicked = !!$target.closest($(this.element())).length; - const isDropDownButtonClicked = !!$target.closest($dropDownButton).length; + const isDropDownButtonClicked = !!$dropDownButton + && !!$target.closest($dropDownButton).length; const isOutsideClick = !isInputClicked && !isDropDownButtonClicked; return isOutsideClick; @@ -595,31 +638,32 @@ class DateRangeBox extends Editor { cancelButtonText: options.cancelButtonText, dateOutOfRangeMessage: options.startDateOutOfRangeMessage, deferRendering: options.deferRendering, - // @ts-expect-error + // @ts-expect-error disabledDates is not declared in the dropDownOptions type disabledDates: options.dropDownOptions?.disabledDates, dropDownOptions: { showTitle: false, title: '', hideOnOutsideClick: (e) => this._hideOnOutsideClickHandler(e), hideOnParentScroll: false, - // @ts-expect-error + // @ts-expect-error preventScrollEvents is not declared in the dropDownOptions type preventScrollEvents: false, ...options.dropDownOptions, }, invalidDateMessage: options.invalidStartDateMessage, - onValueChanged: ({ value, event }) => { - const newValue = [value, this.option('value')[1]]; + onValueChanged: ({ value, event }): void => { + const { value: currentValue } = this.option(); + const newValue = [value, currentValue[1]]; this.updateValue(newValue, event); }, opened: options.opened, - onOpened: () => { + onOpened: (): void => { this._raiseOpenAction(); }, - onClosed: () => { + onClosed: (): void => { this._raiseCloseAction(); }, - onOptionChanged: (args) => { + onOptionChanged: (args): void => { const { name, value, previousValue } = args; if (name === 'text') { this.option('startDateText', value); @@ -631,7 +675,7 @@ class DateRangeBox extends Editor { todayButtonText: options.todayButtonText, showClearButton: false, showDropDownButton: false, - value: this.option('value')[0], + value: options.value[0], label: options.startDateLabel, placeholder: options.startDatePlaceholder, inputAttr: options.startDateInputAttr, @@ -647,12 +691,13 @@ class DateRangeBox extends Editor { ...this._getDateBoxConfig(), invalidDateMessage: options.invalidEndDateMessage, dateOutOfRangeMessage: options.endDateOutOfRangeMessage, - onValueChanged: ({ value, event }) => { - const newValue = [this.option('value')[0], value]; + onValueChanged: ({ value, event }): void => { + const { value: currentValue } = this.option(); + const newValue = [currentValue[0], value]; this.updateValue(newValue, event); }, - onOptionChanged: (args) => { + onOptionChanged: (args): void => { const { name, value, previousValue } = args; if (name === 'text') { this.option('endDateText', value); @@ -664,7 +709,7 @@ class DateRangeBox extends Editor { opened: options.opened, showClearButton: false, showDropDownButton: false, - value: this.option('value')[1], + value: options.value[1], label: options.endDateLabel, placeholder: options.endDatePlaceholder, deferRendering: true, @@ -674,28 +719,27 @@ class DateRangeBox extends Editor { } _getValidationMessagePosition(): Position | undefined { - const { validationMessagePosition } = this.option(); + const { validationMessagePosition, opened } = this.option(); if (validationMessagePosition === 'auto') { - return this.option('opened') ? 'top' : 'bottom'; + return opened ? 'top' : 'bottom'; } return validationMessagePosition; } - _getSerializedDates([startDate, endDate]) { + _getSerializedDates([startDate, endDate]: (DateLike | undefined)[]): DateLike[] { return [ - // @ts-expect-error this.getStartDateBox()._serializeDate(getDeserializedDate(startDate)), - // @ts-expect-error this.getStartDateBox()._serializeDate(getDeserializedDate(endDate)), ]; } - updateValue(newValue, event): void { - if (!isSameDateArrays(newValue, this.option('value'))) { + updateValue(newValue: (DateLike | undefined)[], event?: unknown): void { + const { value } = this.option(); + + if (!isSameDateArrays(newValue, value)) { if (event) { - // @ts-expect-error this._saveValueChangeEvent(event); } @@ -703,12 +747,12 @@ class DateRangeBox extends Editor { } } - _updateDateBoxesValue(newValue): void { + _updateDateBoxesValue(newValue: DateLike[]): void { const startDateBox = this.getStartDateBox(); const endDateBox = this.getEndDateBox(); const [newStartDate, newEndDate] = newValue; - const oldStartDate = startDateBox.option('value'); - const oldEndDate = endDateBox.option('value'); + const { value: oldStartDate } = startDateBox.option(); + const { value: oldEndDate } = endDateBox.option(); if (!isSameDates(newStartDate, oldStartDate)) { startDateBox.option('value', newStartDate); @@ -723,7 +767,7 @@ class DateRangeBox extends Editor { const $startDateInput = $(this.field()[0]); const { accessKey } = this.option(); - // @ts-expect-error + // @ts-expect-error the attr method should support undefined values $startDateInput.attr('accesskey', accessKey); } @@ -731,8 +775,8 @@ class DateRangeBox extends Editor { return $(this.element()).find(`.${TEXTEDITOR_INPUT_CLASS}`); } - _focusEventTarget(): HTMLElement { - return this.element(); + _focusEventTarget(): dxElementWrapper { + return this.$element(); } _focusClassTarget(): dxElementWrapper { @@ -740,7 +784,6 @@ class DateRangeBox extends Editor { } _toggleFocusClass(isFocused: boolean): void { - // @ts-expect-error super._toggleFocusClass(isFocused, this._focusClassTarget()); } @@ -756,7 +799,7 @@ class DateRangeBox extends Editor { return this._isActiveElement(this.endDateField()); } - _isActiveElement(input): boolean { + _isActiveElement(input: HTMLElement): boolean { return $(input).is($(domAdapter.getActiveElement(input))); } @@ -778,9 +821,7 @@ class DateRangeBox extends Editor { const ariaOwns = opened ? this._popupContentIdentifier() : undefined; - // @ts-expect-error this.setAria(arias); - // @ts-expect-error this.setAria('owns', ariaOwns, this.$element()); } @@ -792,8 +833,7 @@ class DateRangeBox extends Editor { this._$afterButtonsContainer = undefined; } - _applyCustomValidation(value): void { - // @ts-expect-error + _applyCustomValidation(value: (DateLike | undefined)[]): void { this.validationRequest.fire({ editor: this, value, @@ -807,11 +847,10 @@ class DateRangeBox extends Editor { this._$endDateBox?.remove(); this._$separator?.remove(); - // @ts-expect-error super._clean(); } - _optionChanged(args) { + _optionChanged(args: OptionChanged): void { const { name, fullName, value, previousValue, } = args; @@ -830,7 +869,6 @@ class DateRangeBox extends Editor { this.getEndDateBox().option(name, value); break; case 'rtlEnabled': - // @ts-expect-error super._optionChanged(args); break; case 'labelMode': @@ -849,7 +887,6 @@ class DateRangeBox extends Editor { case 'opened': this._toggleDropDownEditorActiveClass(); this.getStartDateBox().option(name, value); - // @ts-expect-error this.getEndDateBox()._setOptionWithoutOptionChange(name, value); break; case 'buttons': @@ -869,7 +906,6 @@ class DateRangeBox extends Editor { case 'height': this.getStartDateBox().option(name, value); this.getEndDateBox().option(name, value); - // @ts-expect-error super._optionChanged(args); break; case 'dropDownButtonTemplate': @@ -879,10 +915,11 @@ class DateRangeBox extends Editor { case 'showClearButton': this._updateButtons(['clear']); break; - case 'endDate': - // @ts-expect-error - this.updateValue([this.option('value')[0], value]); + case 'endDate': { + const { value: currentValue } = this.option(); + this.updateValue([currentValue[0], value]); break; + } case 'startDateLabel': this._toggleEditorLabelClass(); this.getStartDateBox().option('label', value); @@ -916,14 +953,12 @@ class DateRangeBox extends Editor { case 'activeStateEnabled': case 'focusStateEnabled': case 'hoverStateEnabled': - // @ts-expect-error super._optionChanged(args); this.getStartDateBox().option(name, value); this.getEndDateBox().option(name, value); break; case 'onValueChanged': - // @ts-expect-error this._createValueChangeAction(); break; case 'onOpened': @@ -945,7 +980,6 @@ class DateRangeBox extends Editor { case 'readOnly': this._updateButtons(); - // @ts-expect-error super._optionChanged(args); this.getStartDateBox().option(name, value); @@ -954,7 +988,6 @@ class DateRangeBox extends Editor { case 'disabled': this._updateButtons(); - // @ts-expect-error super._optionChanged(args); this.getStartDateBox().option(name, value); @@ -962,12 +995,12 @@ class DateRangeBox extends Editor { break; case 'disableOutOfRangeSelection': break; - case 'startDate': - // @ts-expect-error - this.updateValue([value, this.option('value')[1]]); + case 'startDate': { + const { value: currentValue } = this.option(); + this.updateValue([value, currentValue[1]]); break; + } case 'stylingMode': - // @ts-expect-error this._renderStylingMode(); this.getStartDateBox().option(name, value); @@ -991,13 +1024,12 @@ class DateRangeBox extends Editor { break; case 'validationMessagePosition': this.getStartDateBox().option(name, value); - // @ts-expect-error super._optionChanged(args); break; case '_internalValidationErrors': { this._syncValidationErrors('validationErrors', value, previousValue); - const validationErrors = this.option('validationErrors'); + const { validationErrors } = this.option(); this.option('isValid', !validationErrors?.length); break; } @@ -1005,11 +1037,10 @@ class DateRangeBox extends Editor { this.getStartDateBox().option(name, value); this.getEndDateBox().option(name, value); - // @ts-expect-error - const isValid = value && !this.option('_internalValidationErrors').length; + const { _internalValidationErrors: internalValidationErrors } = this.option(); + const isValid = value && !internalValidationErrors.length; if (this._shouldSkipIsValidChange || isValid === value) { - // @ts-expect-error super._optionChanged(args); return; } @@ -1020,42 +1051,34 @@ class DateRangeBox extends Editor { break; } case 'validationErrors': { - const internalValidationErrors = this.option('_internalValidationErrors') || []; - const allErrors = value || []; + const { _internalValidationErrors: internalValidationErrors = [] } = this.option(); + const allErrors = value ?? []; const externalErrors = this._getRestErrors(allErrors, internalValidationErrors); - // @ts-expect-error const errors = [...externalErrors, ...internalValidationErrors]; const newValue = errors.length ? errors : null; - // @ts-expect-error + this._options.silent('validationErrors', newValue); - // @ts-expect-error super._optionChanged({ ...args, value: newValue }); break; } case 'value': { - const newValue = sortDatesArray(value); + const newValue = sortDatesArray(value as DateLike[]); - if (!isSameDateArrays(newValue, previousValue)) { - // @ts-expect-error - const isDirty = !isSameDateArrays(newValue, this._initialValue); + if (!isSameDateArrays(newValue, previousValue as DateLike[])) { + const isDirty = !isSameDateArrays(newValue, this._initialValue as DateLike[]); this.option('isDirty', isDirty); - // @ts-expect-error this._setOptionWithoutOptionChange('value', newValue); - // @ts-expect-error this._setOptionWithoutOptionChange('startDate', newValue[0]); - // @ts-expect-error this._setOptionWithoutOptionChange('endDate', newValue[1]); this._applyCustomValidation(newValue); - this._updateDateBoxesValue(newValue); + this._updateDateBoxesValue(newValue as DateLike[]); this.getStartDateBox().getStrategy().renderValue(); this._toggleEmptinessState(); - // @ts-expect-error this._raiseValueChangeAction(newValue, previousValue); - // @ts-expect-error this._saveValueChangeEvent(undefined); } @@ -1064,7 +1087,6 @@ class DateRangeBox extends Editor { case 'currentSelection': break; default: - // @ts-expect-error super._optionChanged(args); } } @@ -1077,7 +1099,7 @@ class DateRangeBox extends Editor { return this._endDateBox; } - getButton(name) { + getButton(name: string): dxElementWrapper | dxButton | null | undefined { return this._buttonCollection.getButton(name); } @@ -1090,7 +1112,7 @@ class DateRangeBox extends Editor { } content(): HTMLElement { - return this.getStartDateBox().content(); + return this.getStartDateBox().content() as HTMLElement; } field(): [HTMLElement, HTMLElement] { @@ -1098,11 +1120,11 @@ class DateRangeBox extends Editor { } startDateField(): HTMLElement { - return this.getStartDateBox().field(); + return this.getStartDateBox().field() as HTMLElement; } endDateField(): DxElement { - return this.getEndDateBox().field(); + return this.getEndDateBox().field() as HTMLElement; } focus(): void { @@ -1117,9 +1139,7 @@ class DateRangeBox extends Editor { startDateBox.reset(); endDateBox.reset(); - // @ts-expect-error startDateBox._updateInternalValidationState(true); - // @ts-expect-error endDateBox._updateInternalValidationState(true); } @@ -1131,7 +1151,6 @@ class DateRangeBox extends Editor { } } -// @ts-expect-error registerComponent('dxDateRangeBox', DateRangeBox); export default DateRangeBox; diff --git a/packages/devextreme/js/__internal/ui/date_range_box/m_date_range.utils.ts b/packages/devextreme/js/__internal/ui/date_range_box/m_date_range.utils.ts deleted file mode 100644 index 827393c9ff67..000000000000 --- a/packages/devextreme/js/__internal/ui/date_range_box/m_date_range.utils.ts +++ /dev/null @@ -1,29 +0,0 @@ -import dateUtils from '@js/core/utils/date'; -import dateSerialization from '@js/core/utils/date_serialization'; - -export const getDeserializedDate = (value) => dateSerialization.deserializeDate(value); - -export const isSameDates = (date1, date2): boolean => { - if (!date1 && !date2) { - return true; - } - - return dateUtils.sameDate(getDeserializedDate(date1), getDeserializedDate(date2)); -}; - -export const isSameDateArrays = (value, previousValue): boolean => { - const [startDate, endDate] = value; - const [previousStartDate, previousEndDate] = previousValue; - - return isSameDates(startDate, previousStartDate) && isSameDates(endDate, previousEndDate); -}; - -export const sortDatesArray = (value) => { - const [startDate, endDate] = value; - if (startDate && endDate && getDeserializedDate(startDate) > getDeserializedDate(endDate)) { - return [endDate, startDate]; - } - return value; -}; - -export const monthDifference = (date1, date2): number => (date2.getFullYear() - date1.getFullYear()) * 12 - date1.getMonth() + date2.getMonth(); diff --git a/packages/devextreme/js/__internal/ui/date_range_box/m_multiselect_date_box.ts b/packages/devextreme/js/__internal/ui/date_range_box/multiselect_date_box.ts similarity index 72% rename from packages/devextreme/js/__internal/ui/date_range_box/m_multiselect_date_box.ts rename to packages/devextreme/js/__internal/ui/date_range_box/multiselect_date_box.ts index 13bb92c2aba2..85f93f8e56e3 100644 --- a/packages/devextreme/js/__internal/ui/date_range_box/m_multiselect_date_box.ts +++ b/packages/devextreme/js/__internal/ui/date_range_box/multiselect_date_box.ts @@ -1,49 +1,35 @@ -// eslint-disable-next-line max-classes-per-file import eventsEngine from '@js/common/core/events/core/events_engine'; import { addNamespace } from '@js/common/core/events/utils/index'; import $ from '@js/core/renderer'; import { getWidth } from '@js/core/utils/size'; -import type { DateBoxBase, Properties } from '@js/ui/date_box'; -import DateBox from '@js/ui/date_box'; -import type { DateLike } from '@js/ui/date_range_box_types'; -import type Popup from '@js/ui/popup'; - -import { getDeserializedDate, monthDifference } from './m_date_range.utils'; -import type DateRangeBox from './m_date_range_box'; -import RangeCalendarStrategy from './strategy/m_rangeCalendar'; +import type { DxEvent, InteractionEvent } from '@js/events'; +import type { OptionChanged } from '@ts/core/widget/types'; +import DateBox from '@ts/ui/date_box/date_box'; +import type { DateBoxBaseProperties } from '@ts/ui/date_box/date_box.base'; +import type { DateBoxMaskProperties } from '@ts/ui/date_box/date_box.mask'; +import { getDeserializedDate, monthDifference } from '@ts/ui/date_range_box/date_range.utils'; +import type DateRangeBox from '@ts/ui/date_range_box/date_range_box'; +import RangeCalendarStrategy from '@ts/ui/date_range_box/strategy/rangeCalendar'; const START_DATEBOX_CLASS = 'dx-start-datebox'; -export interface MultiselectDateBoxProperties extends Properties { - _dateRangeBoxInstance: DateRangeBox; - _showValidationMessage?: boolean; -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -declare class ExtendedDateBox extends DateBoxBase { - reset(value?: DateLike): void; +export interface MultiselectDateBoxProperties extends DateBoxMaskProperties { + _dateRangeBoxInstance?: DateRangeBox; } -const TypedDateBox: typeof ExtendedDateBox = DateBox as any; - -class MultiselectDateBox extends TypedDateBox { +class MultiselectDateBox extends DateBox { // Temporary solution. Move to component level public NAME!: string; private _skipIsValidOptionChange?: boolean; - private _strategy!: RangeCalendarStrategy; - - private readonly _popup?: Popup; - - private readonly _label: any; + _strategy!: RangeCalendarStrategy; _initStrategy(): void { this._strategy = new RangeCalendarStrategy(this); } _initMarkup(): void { - // @ts-expect-error super._initMarkup(); this._renderInputClickEvent(); @@ -51,48 +37,49 @@ class MultiselectDateBox extends TypedDateBox { _renderInputClickEvent(): void { const clickEventName = addNamespace('dxclick', this.NAME); - // @ts-expect-error + eventsEngine.off(this._input(), clickEventName); - // @ts-expect-error - eventsEngine.on(this._input(), clickEventName, (e) => { + eventsEngine.on(this._input(), clickEventName, (e: DxEvent) => { this._processValueChange(e); }); } - _applyButtonHandler({ event }): void { + _applyButtonHandler({ event }: { event: InteractionEvent }): void { const strategy = this.getStrategy(); const value = strategy.getValue(); strategy.getDateRangeBox().updateValue(value, event); this.close(); - this.option('focusStateEnabled') && this.focus(); + + const { focusStateEnabled } = this.option(); + + if (focusStateEnabled) { + this.focus(); + } } - _openHandler(e): void { + _openHandler(e?: unknown): void { if (this.getStrategy().getDateRangeBox().option('opened')) { return; } - // @ts-expect-error + // @ts-expect-error the base method has no arguments super._openHandler(e); } - _renderOpenedState() { + _renderOpenedState(): void { const { opened } = this.option(); this._getDateRangeBox().option('opened', opened); if (this._isStartDateBox()) { if (opened) { - // @ts-expect-error this._createPopup(); } - // @ts-expect-error this._getDateRangeBox()._popupContentIdentifier(this._getControlsAria()); - // @ts-expect-error this._setPopupOption('visible', opened); this._getDateRangeBox()._setAriaAttributes(); @@ -108,56 +95,49 @@ class MultiselectDateBox extends TypedDateBox { } _renderPopup(): void { - // @ts-expect-error super._renderPopup(); if (this._isStartDateBox()) { - // @ts-expect-error this._getDateRangeBox()._bindInnerWidgetOptions(this._popup, 'dropDownOptions'); } } _popupShownHandler(): void { - // @ts-expect-error super._popupShownHandler(); - // @ts-expect-error + this._getDateRangeBox()._validationMessage?.option('positionSide', this._getValidationMessagePositionSide()); } _popupHiddenHandler(): void { - // @ts-expect-error super._popupHiddenHandler(); - // @ts-expect-error this._getDateRangeBox()._validationMessage?.option('positionSide', this._getValidationMessagePositionSide()); } - _focusInHandler(e) { - // @ts-expect-error + _focusInHandler(e: DxEvent): void { super._focusInHandler(e); this._processValueChange(e); } - _popupTabHandler(e): void { + _popupTabHandler(e: DxEvent): void { const $element = $(e.target); - // @ts-expect-error if (e.shiftKey && $element.is(this._getFirstPopupElement())) { this._getDateRangeBox().getEndDateBox().focus(); e.preventDefault(); } - // @ts-expect-error if (!e.shiftKey && $element.is(this._getLastPopupElement())) { this._getDateRangeBox().getStartDateBox().focus(); e.preventDefault(); } } - _processValueChange(e): void { + _processValueChange(e: DxEvent): void { const { target } = e; const dateRangeBox = this._getDateRangeBox(); const [startDateInput, endDateInput] = dateRangeBox.field(); + if ($(target).is($(startDateInput))) { dateRangeBox.option('currentSelection', 'startDate'); } @@ -190,8 +170,7 @@ class MultiselectDateBox extends TypedDateBox { if ($(target).is($(endDateInput))) { if (endDate) { if (startDate && monthDifference(startDate, endDate) > 1) { - // @ts-expect-error - calendar.option('currentDate', calendar._getDateByOffset(null, endDate)); + calendar.option('currentDate', calendar._getDateByOffset(0, endDate)); calendar.option('currentDate', calendar._getDateByOffset(-1, endDate)); } @@ -210,11 +189,10 @@ class MultiselectDateBox extends TypedDateBox { _invalidate(): void { super._invalidate(); - // @ts-expect-error this._refreshStrategy(); } - _updateInternalValidationState(isValid, validationMessage): void { + _updateInternalValidationState(isValid: boolean, validationMessage?: string): void { this.option({ isValid, validationError: isValid ? null : { @@ -223,16 +201,15 @@ class MultiselectDateBox extends TypedDateBox { }); } - _recallInternalValidation(value): void { - // @ts-expect-error - this._applyInternalValidation(value); + _recallInternalValidation(value: unknown): void { + this._applyInternalValidation(value as Date); } - _isTargetOutOfComponent(target) { + _isTargetOutOfComponent(target: EventTarget | null): boolean { const $dateRangeBox = $(this._getDateRangeBox().element()); + // @ts-expect-error Should be fixed on core/renderer level const isTargetOutOfDateRangeBox = $(target).closest($dateRangeBox).length === 0; - // @ts-expect-error return super._isTargetOutOfComponent(target) && isTargetOutOfDateRangeBox; } @@ -245,17 +222,15 @@ class MultiselectDateBox extends TypedDateBox { return; } - // @ts-expect-error super._updateLabelWidth(); } - _optionChanged(args): void { + _optionChanged(args: OptionChanged): void { switch (args.name) { case 'isValid': { - const isValid = this._getDateRangeBox().option('isValid'); + const { isValid } = this._getDateRangeBox().option(); if (this._skipIsValidOptionChange || isValid === args.value) { - // @ts-expect-error super._optionChanged(args); return; } @@ -266,7 +241,6 @@ class MultiselectDateBox extends TypedDateBox { break; } default: - // @ts-expect-error super._optionChanged(args); break; } diff --git a/packages/devextreme/js/__internal/ui/date_range_box/strategy/m_rangeCalendar.ts b/packages/devextreme/js/__internal/ui/date_range_box/strategy/rangeCalendar.ts similarity index 64% rename from packages/devextreme/js/__internal/ui/date_range_box/strategy/m_rangeCalendar.ts rename to packages/devextreme/js/__internal/ui/date_range_box/strategy/rangeCalendar.ts index 9dfd3084950d..73f1abf6e98a 100644 --- a/packages/devextreme/js/__internal/ui/date_range_box/strategy/m_rangeCalendar.ts +++ b/packages/devextreme/js/__internal/ui/date_range_box/strategy/rangeCalendar.ts @@ -1,17 +1,25 @@ +import type { DateLike } from '@js/common'; import eventsEngine from '@js/common/core/events/core/events_engine'; import { extend } from '@js/core/utils/extend'; import { isFunction } from '@js/core/utils/type'; -import type DateBox from '@js/ui/date_box'; +import type { DxEvent } from '@js/events'; +import type { ValueChangedEvent } from '@js/ui/calendar'; import type Calendar from '@ts/ui/calendar/calendar'; +import CalendarStrategy from '@ts/ui/date_box/date_box.strategy.calendar'; +import { getDeserializedDate, isSameDateArrays, isSameDates } from '@ts/ui/date_range_box/date_range.utils'; +import type DateRangeBox from '@ts/ui/date_range_box/date_range_box'; +import type { MultiselectDateBoxProperties } from '@ts/ui/date_range_box/multiselect_date_box'; +import type MultiselectDateBox from '@ts/ui/date_range_box/multiselect_date_box'; +import type { PopupProperties } from '@ts/ui/popup/popup'; +import type Popup from '@ts/ui/popup/popup'; + +interface RangeValueChangedEvent extends Omit { + value: (DateLike | undefined)[]; + previousValue: (DateLike | undefined)[]; +} -import CalendarStrategy from '../../date_box/date_box.strategy.calendar'; -import { getDeserializedDate, isSameDateArrays, isSameDates } from '../m_date_range.utils'; -import type DateRangeBox from '../m_date_range_box'; -import type MultiselectDateBox from '../m_multiselect_date_box'; - -class RangeCalendarStrategy extends CalendarStrategy { - // @ts-expect-error should be refactored after the dateBox refactoring - dateBox: DateBox; +class RangeCalendarStrategy extends CalendarStrategy<(DateLike | undefined)[], DateRangeBox> { + dateBox!: MultiselectDateBox; private readonly dateRangeBox: DateRangeBox; @@ -22,16 +30,17 @@ class RangeCalendarStrategy extends CalendarStrategy { public _widget!: Calendar; constructor(dateBox: MultiselectDateBox) { - // @ts-expect-error should be refactored after the dateBox refactoring super(dateBox); this.dateBox = dateBox; - this.dateRangeBox = dateBox.option('_dateRangeBoxInstance'); + + const multiselectDateBoxOptions = dateBox.option() as MultiselectDateBoxProperties; + this.dateRangeBox = multiselectDateBoxOptions._dateRangeBoxInstance as DateRangeBox; } - popupConfig(popupConfig) { + popupConfig(popupConfig: PopupProperties): PopupProperties { return extend(true, super.popupConfig(popupConfig), { position: { of: this.getDateRangeBox().$element() }, - }); + }) as PopupProperties; } popupShowingHandler(): void { @@ -39,44 +48,39 @@ class RangeCalendarStrategy extends CalendarStrategy { this._dateSelectedCounter = 0; } - _getPopup() { - // @ts-expect-error + _getPopup(): Popup { return super._getPopup() || this.getDateRangeBox().getStartDateBox()._popup; } - supportedKeys(): Record boolean | undefined> { + supportedKeys(): Record) => boolean | undefined> { const dateRangeBox = this.getDateRangeBox(); return { ...super.supportedKeys(), - rightArrow: () => { + rightArrow: (): boolean | undefined => { if (dateRangeBox.option('opened')) { return true; } return undefined; }, - leftArrow: () => { + leftArrow: (): boolean | undefined => { if (dateRangeBox.option('opened')) { return true; } return undefined; }, - enter: (e) => { + enter: (e: DxEvent): boolean | undefined => { if (dateRangeBox.option('opened')) { - // @ts-expect-error const dateBoxValue = this.dateBox.getDateOption('value'); - // @ts-expect-error this.dateBox._valueChangeEventHandler(e); - // @ts-expect-error const newDateBoxValue = this.dateBox.getDateOption('value'); const dateBoxValueChanged = !isSameDates(dateBoxValue, newDateBoxValue); if (dateBoxValueChanged) { dateRangeBox.getStartDateBox().getStrategy().getWidget().option('value', dateRangeBox.option('value')); } else { - // @ts-expect-error dateRangeBox.getStartDateBox().getStrategy().getWidget()._enterKeyHandler(e); } @@ -85,7 +89,7 @@ class RangeCalendarStrategy extends CalendarStrategy { return undefined; }, - tab: (e) => { + tab: (e: DxEvent): boolean | undefined => { if (!dateRangeBox.option('opened')) { return undefined; } @@ -104,14 +108,13 @@ class RangeCalendarStrategy extends CalendarStrategy { } const $focusableElement = e.shiftKey - // @ts-expect-error ? dateRangeBox.getStartDateBox()._getLastPopupElement() - // @ts-expect-error : dateRangeBox.getStartDateBox()._getFirstPopupElement(); if ($focusableElement) { - // @ts-expect-error + // @ts-expect-error the trigger method is not declared on EventsEngineType eventsEngine.trigger($focusableElement, 'focus'); + // @ts-expect-error the select method is not declared on dxElementWrapper $focusableElement.select(); } @@ -121,8 +124,7 @@ class RangeCalendarStrategy extends CalendarStrategy { }; } - _getWidgetOptions() { - // @ts-expect-error + _getWidgetOptions(): Record { const { disabledDates: disabledDatesValue, value, multiView } = this.dateRangeBox.option(); const disabledDates = isFunction(disabledDatesValue) @@ -136,16 +138,15 @@ class RangeCalendarStrategy extends CalendarStrategy { viewsCount: multiView ? 2 : 1, allowChangeSelectionOrder: true, currentSelection: this.getCurrentSelection(), - }); + }) as Record; } - _refreshActiveDescendant(e): void { - // @ts-expect-error + _refreshActiveDescendant(e: DxEvent & { actionValue: string }): void { this.getDateRangeBox().setAria('activedescendant', e.actionValue); } - _injectComponent(func) { - return (params) => func(extend(params, { component: this.getDateRangeBox() })); + _getInjectedComponent(): DateRangeBox { + return this.getDateRangeBox(); } getKeyboardListener(): Calendar { @@ -156,9 +157,10 @@ class RangeCalendarStrategy extends CalendarStrategy { : this.getWidget(); } - getValue(): Date { + getValue(): (DateLike | undefined)[] { const { value } = this.getWidget().option(); - return value as Date; + + return value as (DateLike | undefined)[]; } _updateValue(): void { @@ -173,11 +175,27 @@ class RangeCalendarStrategy extends CalendarStrategy { } _isInstantlyMode(): boolean { - return this.getDateRangeBox().option('applyValueMode') === 'instantly'; + const { applyValueMode } = this.getDateRangeBox().option(); + + return applyValueMode === 'instantly'; + } + + _getDateSelectedCounter( + currentSelection: 'startDate' | 'endDate' | undefined, + value: RangeValueChangedEvent['value'], + ): number { + if (currentSelection === 'startDate') { + return 0; + } + + if (!value[0]) { + return -1; + } + + return 1; } - // @ts-expect-error should be refactored after the dateBox refactoring - _valueChangedHandler({ value, previousValue, event }) { + _valueChangedHandler({ value, previousValue, event }: RangeValueChangedEvent): void { if (isSameDateArrays(value, previousValue) && !this.getWidget()._valueSelected) { this._shouldPreventFocusChange = false; return; @@ -189,17 +207,15 @@ class RangeCalendarStrategy extends CalendarStrategy { if (this._isInstantlyMode()) { if (!dateRangeBox.option('disableOutOfRangeSelection')) { - if (this._getCalendarCurrentSelection() === 'startDate') { - this._dateSelectedCounter = 0; - } else { - this._dateSelectedCounter = 1; - - if (!value[0]) { - this._dateSelectedCounter = -1; - } else if (getDeserializedDate(value[0]) > getDeserializedDate(value[1])) { - dateRangeBox.updateValue([value[0], null], event); - return; - } + const currentSelection = this._getCalendarCurrentSelection(); + + this._dateSelectedCounter = this._getDateSelectedCounter(currentSelection, value); + + if (currentSelection !== 'startDate' + && value[0] + && getDeserializedDate(value[0]) > getDeserializedDate(value[1])) { + dateRangeBox.updateValue([value[0], null], event); + return; } } @@ -230,16 +246,19 @@ class RangeCalendarStrategy extends CalendarStrategy { : this.getDateRangeBox().getStartDateBox(); targetDateBox.focus(); - // @ts-expect-error + // @ts-expect-error the trigger method should support HTMLElement eventsEngine.trigger(targetDateBox.field(), 'dxclick'); } - getCurrentSelection() { - return this.getDateRangeBox().option('currentSelection'); + getCurrentSelection(): 'startDate' | 'endDate' { + const { currentSelection } = this.getDateRangeBox().option(); + + return currentSelection; } - _getCalendarCurrentSelection() { + _getCalendarCurrentSelection(): 'startDate' | 'endDate' | undefined { const { currentSelection } = this.getWidget().option(); + return currentSelection; } @@ -250,17 +269,6 @@ class RangeCalendarStrategy extends CalendarStrategy { return true; } - dateBoxValue() { - const { dateBox } = this; - - if (arguments.length) { - // @ts-expect-error - return dateBox.setDateOption.apply(dateBox, arguments); - } - // @ts-expect-error - return dateBox.getDateOption.apply(dateBox, ['value']); - } - _cellClickHandler(): void { } setActiveStartDateBox(): void { diff --git a/packages/devextreme/js/__internal/ui/diagram/ui.diagram.floating_panel.ts b/packages/devextreme/js/__internal/ui/diagram/ui.diagram.floating_panel.ts index 2611f0c1c87a..602373c89f3e 100644 --- a/packages/devextreme/js/__internal/ui/diagram/ui.diagram.floating_panel.ts +++ b/packages/devextreme/js/__internal/ui/diagram/ui.diagram.floating_panel.ts @@ -232,14 +232,12 @@ class DiagramFloatingPanel extends DiagramPanel { _createOnVisibilityChangingAction(): void { this._onVisibilityChangingAction = this._createActionByOption( - // @ts-expect-error ts-error 'onVisibilityChanging', ); } _createOnVisibilityChangedAction(): void { this._onVisibilityChangedAction = this._createActionByOption( - // @ts-expect-error ts-error 'onVisibilityChanged', ); } diff --git a/packages/devextreme/js/__internal/ui/diagram/ui.diagram.properties_panel.ts b/packages/devextreme/js/__internal/ui/diagram/ui.diagram.properties_panel.ts index a4fb4a0ff52f..2835232df01b 100644 --- a/packages/devextreme/js/__internal/ui/diagram/ui.diagram.properties_panel.ts +++ b/packages/devextreme/js/__internal/ui/diagram/ui.diagram.properties_panel.ts @@ -248,13 +248,11 @@ class DiagramPropertiesPanel extends DiagramFloatingPanel { } _createOnCreateToolbar(): void { - // @ts-expect-error ts-error this._onCreateToolbarAction = this._createActionByOption('onCreateToolbar'); } _createOnSelectedGroupChanged(): void { this._onSelectedGroupChangedAction = this._createActionByOption( - // @ts-expect-error ts-error 'onSelectedGroupChanged', ); } diff --git a/packages/devextreme/js/__internal/ui/diagram/ui.diagram.toolbar.ts b/packages/devextreme/js/__internal/ui/diagram/ui.diagram.toolbar.ts index 7a83562515b8..5898f33d941d 100644 --- a/packages/devextreme/js/__internal/ui/diagram/ui.diagram.toolbar.ts +++ b/packages/devextreme/js/__internal/ui/diagram/ui.diagram.toolbar.ts @@ -711,12 +711,10 @@ class DiagramToolbar extends DiagramPanel { } _createOnInternalCommand(): void { - // @ts-expect-error ts-error this._onInternalCommandAction = this._createActionByOption('onInternalCommand'); } _createOnCustomCommand(): void { - // @ts-expect-error ts-error this._onCustomCommandAction = this._createActionByOption('onCustomCommand'); } @@ -780,7 +778,6 @@ class DiagramToolbar extends DiagramPanel { _createOnSubMenuVisibilityChangingAction(): void { this._onSubMenuVisibilityChangingAction = this._createActionByOption( - // @ts-expect-error ts-error 'onSubMenuVisibilityChanging', ); } diff --git a/packages/devextreme/js/__internal/ui/diagram/ui.diagram.toolbox.ts b/packages/devextreme/js/__internal/ui/diagram/ui.diagram.toolbox.ts index 4ee2223404ba..9746def34ca3 100644 --- a/packages/devextreme/js/__internal/ui/diagram/ui.diagram.toolbox.ts +++ b/packages/devextreme/js/__internal/ui/diagram/ui.diagram.toolbox.ts @@ -413,13 +413,11 @@ class DiagramToolbox extends DiagramFloatingPanel { _createOnShapeCategoryRenderedAction(): void { this._onShapeCategoryRenderedAction = this._createActionByOption( - // @ts-expect-error ts-error 'onShapeCategoryRendered', ); } _createOnFilterChangedAction(): void { - // @ts-expect-error ts-error this._onFilterChangedAction = this._createActionByOption('onFilterChanged'); } diff --git a/packages/devextreme/js/__internal/ui/drop_down_editor/drop_down_list.ts b/packages/devextreme/js/__internal/ui/drop_down_editor/drop_down_list.ts index b963c0e0ae63..4840b38450c7 100644 --- a/packages/devextreme/js/__internal/ui/drop_down_editor/drop_down_list.ts +++ b/packages/devextreme/js/__internal/ui/drop_down_editor/drop_down_list.ts @@ -215,7 +215,6 @@ class DropDownList< } _initContentReadyAction(): void { - // @ts-expect-error _contentReadyAction not typed on base class this._contentReadyAction = this._createActionByOption('onContentReady', { excludeValidators: ['disabled', 'readOnly'], }); diff --git a/packages/devextreme/js/__internal/ui/editor/editor.ts b/packages/devextreme/js/__internal/ui/editor/editor.ts index d51319178a2f..c35b8b266907 100644 --- a/packages/devextreme/js/__internal/ui/editor/editor.ts +++ b/packages/devextreme/js/__internal/ui/editor/editor.ts @@ -1,4 +1,4 @@ -import type { Position } from '@js/common'; +import type { Mode, Position } from '@js/common'; import type { NativeEventInfo } from '@js/common/core/events'; import EventsEngine from '@js/common/core/events/core/events_engine'; import { addNamespace, normalizeKeyName } from '@js/common/core/events/utils/index'; @@ -17,11 +17,10 @@ import type { } from '@js/ui/editor/editor'; import ValidationEngine from '@js/ui/validation_engine'; import ValidationMessage from '@js/ui/validation_message'; +import domUtils from '@ts/core/utils/m_dom'; import type { OptionChanged } from '@ts/core/widget/types'; import Widget from '@ts/core/widget/widget'; -import domUtils from '../../core/utils/m_dom'; - const INVALID_MESSAGE_AUTO = 'dx-invalid-message-auto'; const READONLY_STATE_CLASS = 'dx-state-readonly'; const INVALID_CLASS = 'dx-invalid'; @@ -56,9 +55,15 @@ export interface EditorProperties< _onMarkupRendered?: () => void; } +// NOTE: editors that resolve the position themselves (dropDownEditor, dateRangeBox) +// also accept the 'auto' mode, so the constraint is wider than EditorProperties itself. +export type EditorPropertiesConstraint = Omit< +// eslint-disable-next-line @typescript-eslint/no-explicit-any + EditorProperties, 'validationMessagePosition' +> & { validationMessagePosition?: Position | Mode }; + class Editor< - // eslint-disable-next-line @typescript-eslint/no-explicit-any - TProperties extends EditorProperties = EditorProperties, + TProperties extends EditorPropertiesConstraint = EditorProperties, > extends Widget { _initialValue: unknown; @@ -341,7 +346,7 @@ class Editor< } } - _getValidationMessagePosition(): Position | undefined { + _getValidationMessagePosition(): Position | Mode | undefined { const { validationMessagePosition } = this.option(); return validationMessagePosition; } diff --git a/packages/devextreme/js/__internal/ui/overlay/overlay.ts b/packages/devextreme/js/__internal/ui/overlay/overlay.ts index 8770c4d94899..578cc154a807 100644 --- a/packages/devextreme/js/__internal/ui/overlay/overlay.ts +++ b/packages/devextreme/js/__internal/ui/overlay/overlay.ts @@ -506,7 +506,6 @@ class Overlay< const overlayStack = this._overlayStack(); const innerOverlayElement = $closestInnerOverlay.get(0); - // @ts-expect-error this and Overlay have no overlap const thisIndex = overlayStack.indexOf(this); for (let i = 0; i < overlayStack.length; i += 1) { @@ -918,7 +917,6 @@ class Overlay< _updateZIndexStackPosition(pushToStack: boolean): void { const overlayStack = this._overlayStack(); - // @ts-expect-error this and Overlay have no overlap const index = overlayStack.indexOf(this); const isInStack = index !== -1; const { zIndex } = this.option(); @@ -934,7 +932,6 @@ class Overlay< if (!isInStack) { this._zIndex = zIndex ?? zIndexPool.create(this._zIndexInitValue()); - // @ts-expect-error this and Overlay have no overlap overlayStack.push(this); } diff --git a/packages/devextreme/js/__internal/ui/scroll_view/scrollable.native.ts b/packages/devextreme/js/__internal/ui/scroll_view/scrollable.native.ts index ef6f571371d4..5fcb6fe45112 100644 --- a/packages/devextreme/js/__internal/ui/scroll_view/scrollable.native.ts +++ b/packages/devextreme/js/__internal/ui/scroll_view/scrollable.native.ts @@ -47,7 +47,7 @@ class NativeStrategy< _hideScrollbarTimeout?: ReturnType | number; _createActionByOption!: ( - optionName: keyof TProperties, + optionName: string, config?: ActionConfig, ) => (event?: ScrollEventArgs) => void; diff --git a/packages/devextreme/js/__internal/ui/scroll_view/scrollable.simulated.ts b/packages/devextreme/js/__internal/ui/scroll_view/scrollable.simulated.ts index 52236c88c24d..1399aacceb82 100644 --- a/packages/devextreme/js/__internal/ui/scroll_view/scrollable.simulated.ts +++ b/packages/devextreme/js/__internal/ui/scroll_view/scrollable.simulated.ts @@ -736,7 +736,7 @@ export class SimulatedStrategy< _scrollOffset?: ScrollOffset; _createActionByOption!: ( - optionName: keyof TProperties, + optionName: string, config?: ActionConfig, ) => (event?: Record) => void; @@ -1092,7 +1092,7 @@ export class SimulatedStrategy< } _createActionHandler(optionName: keyof TProperties): () => void { - const actionHandler = this._createActionByOption(optionName); + const actionHandler = this._createActionByOption(String(optionName)); return (...args: unknown[]) => { try { diff --git a/packages/devextreme/js/__internal/ui/text_box/text_editor.base.ts b/packages/devextreme/js/__internal/ui/text_box/text_editor.base.ts index 92d031764f28..47e8cb175f6a 100644 --- a/packages/devextreme/js/__internal/ui/text_box/text_editor.base.ts +++ b/packages/devextreme/js/__internal/ui/text_box/text_editor.base.ts @@ -733,7 +733,7 @@ class TextEditorBase< const hasActionSubscription = this.hasActionSubscription(actionName as keyof TProperties); if (hasActionSubscription) { - const action = this._createActionByOption(actionName as keyof TProperties, { + const action = this._createActionByOption(actionName, { excludeValidators: ['readOnly'], }); diff --git a/packages/devextreme/js/__internal/ui/toolbar/toolbar.base.ts b/packages/devextreme/js/__internal/ui/toolbar/toolbar.base.ts index c4ae4ebb7185..85512b3f3934 100644 --- a/packages/devextreme/js/__internal/ui/toolbar/toolbar.base.ts +++ b/packages/devextreme/js/__internal/ui/toolbar/toolbar.base.ts @@ -96,7 +96,7 @@ class ToolbarBase< _pendingFocusTarget?: FocusRestoreTarget; - _getSynchronizableOptionsForCreateComponent(): (keyof TProperties)[] { + _getSynchronizableOptionsForCreateComponent(): string[] { return super._getSynchronizableOptionsForCreateComponent().filter((item) => item !== 'disabled'); } diff --git a/packages/devextreme/js/ui/date_box.js b/packages/devextreme/js/ui/date_box.js index bf6c42cbde53..2814a02a13d6 100644 --- a/packages/devextreme/js/ui/date_box.js +++ b/packages/devextreme/js/ui/date_box.js @@ -1,8 +1,6 @@ import DateBox from '../__internal/ui/date_box/date_box'; export default DateBox; -// STYLE dateBox - /** * @name dxDateBoxOptions.fieldAddons * @hidden diff --git a/packages/devextreme/js/ui/date_range_box.js b/packages/devextreme/js/ui/date_range_box.js index 8d4a39093ef4..713a66797195 100644 --- a/packages/devextreme/js/ui/date_range_box.js +++ b/packages/devextreme/js/ui/date_range_box.js @@ -1,4 +1,4 @@ -import DateRangeBox from '../__internal/ui/date_range_box/m_date_range_box'; +import DateRangeBox from '../__internal/ui/date_range_box/date_range_box'; export default DateRangeBox; // STYLE dateRangeBox