From d09b2394a7d1bbe27e11bd658d2a6a2c675f2366 Mon Sep 17 00:00:00 2001 From: EugeniyKiyashko Date: Wed, 4 Sep 2024 17:41:23 +0400 Subject: [PATCH] Chat: messageBox should be extended from DOMComponent (#27985) --- .vscode/settings.json | 3 +- .../js/__internal/core/widget/component.ts | 248 ++++++++++------ .../__internal/core/widget/dom_component.ts | 228 +++++++++------ .../js/__internal/core/widget/types.ts | 50 ++++ .../js/__internal/core/widget/widget.ts | 266 +++++++++++------- .../devextreme/js/__internal/tsconfig.json | 6 +- .../devextreme/js/__internal/ui/chat/chat.ts | 34 +-- .../js/__internal/ui/chat/chat_avatar.ts | 12 +- .../js/__internal/ui/chat/chat_header.ts | 19 +- .../js/__internal/ui/chat/chat_message_box.ts | 28 +- .../__internal/ui/chat/chat_message_bubble.ts | 12 +- .../__internal/ui/chat/chat_message_group.ts | 11 +- .../__internal/ui/chat/chat_message_list.ts | 14 +- .../__internal/ui/splitter/resize_handle.ts | 13 +- .../__internal/ui/splitter/splitter_item.ts | 2 +- .../devextreme/js/__internal/ui/widget.ts | 4 +- .../devextreme/js/core/dom_component.d.ts | 2 +- .../DevExpress.ui/defaultOptions.tests.js | 3 + packages/devextreme/ts/dx.all.d.ts | 2 +- 19 files changed, 609 insertions(+), 348 deletions(-) create mode 100644 packages/devextreme/js/__internal/core/widget/types.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 1853ec4f35e9..d0a025fb9500 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,5 +4,6 @@ }, "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" - } + }, + "typescript.tsdk": "packages/devextreme/node_modules/typescript/lib" } diff --git a/packages/devextreme/js/__internal/core/widget/component.ts b/packages/devextreme/js/__internal/core/widget/component.ts index b9eaa0f2a6ab..ec6b5587f570 100644 --- a/packages/devextreme/js/__internal/core/widget/component.ts +++ b/packages/devextreme/js/__internal/core/widget/component.ts @@ -1,113 +1,131 @@ -/* eslint-disable max-classes-per-file */ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -/* eslint-disable @typescript-eslint/ban-types */ -/* eslint-disable consistent-return */ -/* eslint-disable no-param-reassign */ -/* eslint-disable @typescript-eslint/init-declarations */ -/* eslint-disable no-plusplus */ -/* eslint-disable @typescript-eslint/prefer-for-of */ -/* eslint-disable @typescript-eslint/no-unused-expressions */ -/* eslint-disable default-case */ -/* eslint-disable @typescript-eslint/prefer-optional-chain */ -/* eslint-disable max-len */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/naming-convention */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/restrict-plus-operands */ -/* eslint-disable @typescript-eslint/explicit-function-return-type */ import Action from '@js/core/action'; import Class from '@js/core/class'; +import type { + ComponentOptions, +} from '@js/core/component'; import Config from '@js/core/config'; +import type { OptionChangedEventInfo } from '@js/core/dom_component'; import errors from '@js/core/errors'; import { EventsStrategy } from '@js/core/events_strategy'; import { Options } from '@js/core/options/index'; +import type { DefaultOptionsRule } from '@js/core/options/utils'; import { convertRulesToOptions } from '@js/core/options/utils'; import { PostponedOperations } from '@js/core/postponed_operations'; +import type { dxElementWrapper } from '@js/core/renderer'; import Callbacks from '@js/core/utils/callbacks'; import { noop } from '@js/core/utils/common'; import { getPathParts } from '@js/core/utils/data'; import { extend } from '@js/core/utils/extend'; import { name as publicComponentName } from '@js/core/utils/public_component'; import { isDefined, isFunction, isPlainObject } from '@js/core/utils/type'; +import type { EventInfo, InitializedEventInfo } from '@js/events'; -const getEventName = (actionName) => actionName.charAt(2).toLowerCase() + actionName.substr(3); +import type { OptionChanged } from './types'; -const isInnerOption = (optionName) => optionName.indexOf('_', 0) === 0; +// eslint-disable-next-line max-len +// eslint-disable-next-line @typescript-eslint/restrict-plus-operands, @typescript-eslint/no-unsafe-return, max-len +const getEventName = (actionName): string => actionName.charAt(2).toLowerCase() + actionName.substr(3); -export class Component extends Class.inherit({}) { - _deprecatedOptions: any; +const isInnerOption = (optionName): boolean => optionName.indexOf('_', 0) === 0; - _options: any; +export interface Properties extends ComponentOptions< +EventInfo, +InitializedEventInfo, +OptionChangedEventInfo +> { + onInitializing?: ((e: Record) => void) | undefined; - _optionsByReference: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + defaultOptionsRules?: DefaultOptionsRule[]; +} + +// eslint-disable-next-line max-len +export class Component< + TComponent extends Component, + TProperties extends Properties, +// eslint-disable-next-line @typescript-eslint/ban-types +> extends (Class.inherit({}) as new() => {}) { + _deprecatedOptions!: Partial; + + _options!: Options; - NAME: any; + _optionsByReference!: Partial; + NAME?: string; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any _eventsStrategy: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any _optionChangedCallbacks: any; - _updateLockCount: any; + _updateLockCount!: number; + // eslint-disable-next-line @typescript-eslint/no-explicit-any _disposingCallbacks: any; - postponedOperations: any; + postponedOperations!: PostponedOperations; - _initialized: any; + _initialized!: boolean; - _optionChangedAction: any; + _optionChangedAction?: (event?: Record) => void; - _disposingAction: any; + _disposingAction?: (event?: Record) => void; - _disposed: any; + _disposed?: boolean; - _initializing: any; + _initializing?: boolean; - _cancelOptionChange: any; + _cancelOptionChange?: string | boolean; - _setDeprecatedOptions() { + _setDeprecatedOptions(): void { this._deprecatedOptions = {}; } - _getDeprecatedOptions() { + _getDeprecatedOptions(): Partial { return this._deprecatedOptions; } - _getDefaultOptions() { + _getDefaultOptions(): TProperties { return { onInitialized: null, onOptionChanged: null, onDisposing: null, defaultOptionsRules: null, - }; + } as unknown as TProperties; } - _defaultOptionsRules(): any[] { + _defaultOptionsRules(): DefaultOptionsRule[] { return []; } - _setOptionsByDevice(rules) { + _setOptionsByDevice(rules: DefaultOptionsRule[] | undefined): void { this._options.applyRules(rules); } - _convertRulesToOptions(rules) { + _convertRulesToOptions(rules: DefaultOptionsRule[]): TProperties { return convertRulesToOptions(rules); } - _isInitialOptionValue(name) { + _isInitialOptionValue(name: string): boolean { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return this._options.isInitial(name); } - _setOptionsByReference() { + _setOptionsByReference(): void { this._optionsByReference = {}; } - _getOptionsByReference() { + _getOptionsByReference(): Partial { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return this._optionsByReference; } - ctor(options: any = {}) { + // eslint-disable-next-line max-len + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-unused-vars + ctor(options: any = {}, extra?: unknown): void { + // eslint-disable-next-line @typescript-eslint/naming-convention const { _optionChangedCallbacks, _disposingCallbacks } = options; this.NAME = publicComponentName(this.constructor); @@ -122,7 +140,7 @@ export class Component extends Class.inherit({}) { this._createOptions(options); } - _createOptions(options) { + _createOptions(options: TProperties): void { this.beginUpdate(); try { @@ -136,7 +154,9 @@ export class Component extends Class.inherit({}) { ); this._options.onChanging( - (name, previousValue, value) => this._initialized && this._optionChanging(name, previousValue, value), + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + (name, previousValue, value) => this._initialized + && this._optionChanging(name, previousValue, value), ); this._options.onDeprecated( (option, info) => this._logDeprecatedOptionWarning(option, info), @@ -148,7 +168,9 @@ export class Component extends Class.inherit({}) { this._options.onEndChange(() => this.endUpdate()); this._options.addRules(this._defaultOptionsRules()); + // eslint-disable-next-line @typescript-eslint/prefer-optional-chain if (options && options.onInitializing) { + // @ts-expect-error options.onInitializing.apply(this, [options]); } @@ -159,11 +181,11 @@ export class Component extends Class.inherit({}) { } } - _initOptions(options) { + _initOptions(options: TProperties): void { this.option(options); } - _init() { + _init(): void { this._createOptionChangedAction(); this.on('disposing', (args) => { @@ -171,25 +193,30 @@ export class Component extends Class.inherit({}) { }); } - _logDeprecatedOptionWarning(option, info) { + _logDeprecatedOptionWarning( + option: string, + info: { since: string; message: string; alias: string }, + ): void { const message = info.message || `Use the '${info.alias}' option instead`; errors.log('W0001', this.NAME, option, info.since, message); } - _logDeprecatedComponentWarning(since, alias) { + _logDeprecatedComponentWarning(since: 'string', alias: 'string'): void { errors.log('W0000', this.NAME, since, `Use the '${alias}' widget instead`); } - _createOptionChangedAction() { + _createOptionChangedAction(): void { this._optionChangedAction = this._createActionByOption('onOptionChanged', { excludeValidators: ['disabled', 'readOnly'] }); } - _createDisposingAction() { + _createDisposingAction(): void { this._disposingAction = this._createActionByOption('onDisposing', { excludeValidators: ['disabled', 'readOnly'] }); } - _optionChanged(args) { - switch (args.name) { + _optionChanged(args: OptionChanged | Record): void { + const { name } = args; + + switch (name) { case 'onDisposing': case 'onInitialized': break; @@ -198,46 +225,51 @@ export class Component extends Class.inherit({}) { break; case 'defaultOptionsRules': break; + default: + break; } } - _dispose() { + _dispose(): void { this._optionChangedCallbacks.empty(); this._createDisposingAction(); - this._disposingAction(); + this._disposingAction?.(); this._eventsStrategy.dispose(); this._options.dispose(); this._disposed = true; } - _lockUpdate() { + _lockUpdate(): void { + // eslint-disable-next-line no-plusplus this._updateLockCount++; } - _unlockUpdate() { + _unlockUpdate(): void { this._updateLockCount = Math.max(this._updateLockCount - 1, 0); } // TODO: remake as getter after ES6 refactor - _isUpdateAllowed() { + _isUpdateAllowed(): boolean { return this._updateLockCount === 0; } // TODO: remake as getter after ES6 refactor - _isInitializingRequired() { + _isInitializingRequired(): boolean { return !this._initializing && !this._initialized; } - isInitialized() { + isInitialized(): boolean { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return this._initialized; } - _commitUpdate() { + _commitUpdate(): void { this.postponedOperations.callPostponedOperations(); + // eslint-disable-next-line @typescript-eslint/no-unused-expressions this._isInitializingRequired() && this._initializeComponent(); } - _initializeComponent() { + _initializeComponent(): void { this._initializing = true; try { @@ -251,27 +283,30 @@ export class Component extends Class.inherit({}) { } } - instance() { - return this; + instance(): TComponent { + return this as unknown as TComponent; } - beginUpdate() { + beginUpdate(): void { this._lockUpdate(); } - endUpdate() { + endUpdate(): void { this._unlockUpdate(); + // eslint-disable-next-line @typescript-eslint/no-unused-expressions this._isUpdateAllowed() && this._commitUpdate(); } - // eslint-disable-next-line @typescript-eslint/no-unused-vars + // eslint-disable-next-line max-len + // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/explicit-function-return-type, @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any _optionChanging(...args: any[]) { } - _notifyOptionChanged(option, value, previousValue) { + _notifyOptionChanged(option: string, value: unknown, previousValue: unknown): void { if (this._initialized) { const optionNames = [option].concat(this._options.getAliasesByName(option)); + // eslint-disable-next-line @typescript-eslint/prefer-for-of, no-plusplus for (let i = 0; i < optionNames.length; i++) { const name = optionNames[i]; const args = { @@ -283,7 +318,7 @@ export class Component extends Class.inherit({}) { if (!isInnerOption(name)) { this._optionChangedCallbacks.fireWith(this, [extend(this._defaultActionArgs(), args)]); - this._optionChangedAction(extend({}, args)); + this._optionChangedAction?.(extend({}, args)); } if (!this._disposed && this._cancelOptionChange !== name) { @@ -293,49 +328,64 @@ export class Component extends Class.inherit({}) { } } - initialOption(name) { + initialOption(name: string): TProperties { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return this._options.initial(name); } - _defaultActionConfig() { + _defaultActionConfig(): { context: TComponent; component: TComponent } { return { - context: this, - component: this, + context: this as unknown as TComponent, + component: this as unknown as TComponent, }; } - _defaultActionArgs() { + _defaultActionArgs(): { component: TComponent; element?: dxElementWrapper; model?: unknown } { return { - component: this, + component: this as unknown as TComponent, }; } - _createAction(actionSource, config) { + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + _createAction(actionSource, config): (e) => void { + // eslint-disable-next-line @typescript-eslint/init-declarations let action; return (e) => { if (!isDefined(e)) { + // eslint-disable-next-line no-param-reassign e = {}; } if (!isPlainObject(e)) { + // eslint-disable-next-line no-param-reassign e = { actionValue: e }; } action = action || new Action(actionSource, extend({}, config, this._defaultActionConfig())); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return action.execute.call(action, extend(e, this._defaultActionArgs())); }; } - _createActionByOption(optionName, config) { + _createActionByOption( + optionName: string, + config: Record, + ): (event?: Record) => void { + // eslint-disable-next-line @typescript-eslint/init-declarations let action; + // eslint-disable-next-line @typescript-eslint/init-declarations let eventName; + // eslint-disable-next-line @typescript-eslint/init-declarations let actionFunc; + // eslint-disable-next-line no-param-reassign config = extend({}, config); + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type const result = (...args) => { if (!eventName) { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing, no-param-reassign config = config || {}; if (typeof optionName !== 'string') { @@ -354,13 +404,17 @@ export class Component extends Class.inherit({}) { actionFunc = this.option(optionName); } - if (!action && !actionFunc && !config.beforeExecute && !config.afterExecute && !this._eventsStrategy.hasEvent(eventName)) { + if (!action && !actionFunc && !config.beforeExecute + && !config.afterExecute && !this._eventsStrategy.hasEvent(eventName)) { return; } if (!action) { const { beforeExecute } = config; - config.beforeExecute = (...props) => { + config.beforeExecute = (...props): void => { + // @ts-expect-error + // eslint-disable-next-line max-len + // eslint-disable-next-line @typescript-eslint/prefer-optional-chain, @typescript-eslint/no-unused-expressions beforeExecute && beforeExecute.apply(this, props); this._eventsStrategy.fireEvent(eventName, props[0].args); }; @@ -370,10 +424,13 @@ export class Component extends Class.inherit({}) { // @ts-expect-error if (Config().wrapActionsBeforeExecute) { const beforeActionExecute = this.option('beforeActionExecute') || noop; + // @ts-expect-error const wrappedAction = beforeActionExecute(this, action, config) || action; + // eslint-disable-next-line consistent-return, @typescript-eslint/no-unsafe-return return wrappedAction.apply(this, args); } + // eslint-disable-next-line consistent-return, @typescript-eslint/no-unsafe-return return action.apply(this, args); }; @@ -381,52 +438,63 @@ export class Component extends Class.inherit({}) { if (Config().wrapActionsBeforeExecute) { return result; } - const onActionCreated = this.option('onActionCreated') || noop; + // @ts-expect-error + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return onActionCreated(this, result, config) || result; } - on(eventName, eventHandler) { + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + on(eventName: string, eventHandler): TComponent { this._eventsStrategy.on(eventName, eventHandler); - return this; + return this as unknown as TComponent; } - off(eventName, eventHandler) { + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + off(eventName: string, eventHandler): TComponent { this._eventsStrategy.off(eventName, eventHandler); - return this; + return this as unknown as TComponent; } - hasActionSubscription(actionName) { + hasActionSubscription(actionName: string): boolean { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return !!this._options.silent(actionName) || this._eventsStrategy.hasEvent(getEventName(actionName)); } - isOptionDeprecated(name) { + isOptionDeprecated(name: string): boolean { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return this._options.isDeprecated(name); } - _setOptionWithoutOptionChange(name, value) { + _setOptionWithoutOptionChange(name: string, value: unknown): void { this._cancelOptionChange = name; this.option(name, value); this._cancelOptionChange = false; } - _getOptionValue(name, context) { + // eslint-disable-next-line max-len + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any + _getOptionValue(name: string, context: any): any { const value = this.option(name); if (isFunction(value)) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return value.bind(context)(); } + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return value; } - option(...args) { + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + option(...args): TProperties { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return this._options.option(...args); } - resetOption(name) { + resetOption(name: string): void { this.beginUpdate(); this._options.reset(name); this.endUpdate(); diff --git a/packages/devextreme/js/__internal/core/widget/dom_component.ts b/packages/devextreme/js/__internal/core/widget/dom_component.ts index 9174e38acea8..be5a88c4f504 100644 --- a/packages/devextreme/js/__internal/core/widget/dom_component.ts +++ b/packages/devextreme/js/__internal/core/widget/dom_component.ts @@ -1,21 +1,9 @@ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable no-plusplus */ -/* eslint-disable consistent-return */ -/* eslint-disable @typescript-eslint/explicit-function-return-type */ -/* eslint-disable new-cap */ -/* eslint-disable no-void */ -/* eslint-disable no-return-assign */ -/* eslint-disable @typescript-eslint/no-shadow */ -/* eslint-disable @typescript-eslint/prefer-optional-chain */ -/* eslint-disable @typescript-eslint/no-unused-expressions */ -/* eslint-disable no-multi-assign */ -/* eslint-disable max-len */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ import config from '@js/core/config'; +import type { DOMComponentOptions } from '@js/core/dom_component'; import { getPublicElement } from '@js/core/element'; import { cleanDataRecursive } from '@js/core/element_data'; import errors from '@js/core/errors'; +import type { dxElementWrapper } from '@js/core/renderer'; import $ from '@js/core/renderer'; import { TemplateManager } from '@js/core/template_manager'; // @ts-expect-error @@ -31,32 +19,56 @@ import { resize as resizeEvent, visibility as visibilityEvents } from '@js/event import license, { peekValidationPerformed } from '@ts/core/license/license_validation'; import { Component } from './component'; +import type { OptionChanged } from './types'; -class DOMComponent extends Component { - static _classCustomRules: any; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export interface Properties extends DOMComponentOptions { + _ignoreFunctionValueDeprecation?: boolean; + integrationOptions?: Record; + + nestedComponentOptions?: (context: TComponent) => void; + + modelByElement?: ($element: dxElementWrapper) => unknown; +} + +class DOMComponent< + TComponent extends Component, + TProperties extends Properties = Properties, +> extends Component { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + static _classCustomRules: any[]; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any private _customClass: any; - private _$element: any; + private _$element!: dxElementWrapper; + // eslint-disable-next-line @typescript-eslint/no-explicit-any private _windowResizeCallBack: any; - private _isHidden: any; + private _isHidden?: boolean; - private _requireRefresh: any; + private _requireRefresh?: boolean; - private _templateManager: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private _templateManager?: any; + // eslint-disable-next-line max-len + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/explicit-module-boundary-types static getInstance(element) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return getInstanceByElement($(element), this); } - static defaultOptions(rule) { + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + static defaultOptions(rule): void { this._classCustomRules = Object.hasOwnProperty.bind(this)('_classCustomRules') && this._classCustomRules ? this._classCustomRules : []; this._classCustomRules.push(rule); } - _getDefaultOptions() { + _getDefaultOptions(): TProperties { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return extend(super._getDefaultOptions(), { width: undefined, @@ -74,8 +86,7 @@ class DOMComponent extends Component { }, this._useTemplates() ? TemplateManager.createDefaultOptions() : {}); } - // @ts-expect-error - ctor(element, options) { + ctor(element: Element, options: TProperties): void { this._customClass = null; this._createElement(element); @@ -90,15 +101,17 @@ class DOMComponent extends Component { } } - _createElement(element) { + _createElement(element: Element): void { this._$element = $(element); } - _getSynchronizableOptionsForCreateComponent() { + _getSynchronizableOptionsForCreateComponent(): (keyof TProperties)[] { + // @ts-expect-error return ['rtlEnabled', 'disabled', 'templatesRenderAsynchronously']; } - _checkFunctionValueDeprecation(optionNames) { + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + _checkFunctionValueDeprecation(optionNames): void { if (!this.option('_ignoreFunctionValueDeprecation')) { optionNames.forEach((optionName) => { if (isFunction(this.option(optionName))) { @@ -109,11 +122,11 @@ class DOMComponent extends Component { } // eslint-disable-next-line @typescript-eslint/no-unused-vars - _visibilityChanged(value: boolean) {} + _visibilityChanged(value: boolean): void {} - _dimensionChanged() {} + _dimensionChanged(): void {} - _init() { + _init(): void { super._init(); this._checkFunctionValueDeprecation([ 'width', 'height', @@ -125,41 +138,46 @@ class DOMComponent extends Component { this._initTemplateManager(); } - _setOptionsByDevice(instanceCustomRules) { + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + _setOptionsByDevice(instanceCustomRules): void { // @ts-expect-error + // eslint-disable-next-line max-len super._setOptionsByDevice([].concat(this.constructor._classCustomRules || [], instanceCustomRules || [])); } - _isInitialOptionValue(name) { + _isInitialOptionValue(name: string): boolean { // @ts-expect-error const isCustomOption = this.constructor._classCustomRules // @ts-expect-error + // eslint-disable-next-line max-len && Object.prototype.hasOwnProperty.call(this._convertRulesToOptions(this.constructor._classCustomRules), name); return !isCustomOption && super._isInitialOptionValue(name); } - _attachWindowResizeCallback() { + _attachWindowResizeCallback(): void { if (this._isDimensionChangeSupported()) { + // eslint-disable-next-line no-multi-assign const windowResizeCallBack = this._windowResizeCallBack = this._dimensionChanged.bind(this); windowResizeCallbacks.add(windowResizeCallBack); } } - _isDimensionChangeSupported() { + _isDimensionChangeSupported(): boolean { return this._dimensionChanged !== DOMComponent.prototype._dimensionChanged; } - _renderComponent() { + _renderComponent(): void { addShadowDomStyles(this.$element()); this._initMarkup(); + // eslint-disable-next-line @typescript-eslint/no-unused-expressions hasWindow() && this._render(); } - _initMarkup() { + _initMarkup(): void { const { rtlEnabled } = this.option() || {}; this._renderElementAttributes(); @@ -168,26 +186,27 @@ class DOMComponent extends Component { this._renderDimensions(); } - _render() { + _render(): void { this._attachVisibilityChangeHandlers(); } - _renderElementAttributes() { + _renderElementAttributes(): void { const { elementAttr } = this.option() || {}; const attributes = extend({}, elementAttr); const classNames = attributes.class; delete attributes.class; - + // @ts-expect-error this.$element() .attr(attributes) + // @ts-expect-error .removeClass(this._customClass) .addClass(classNames); this._customClass = classNames; } - _renderVisibilityChange() { + _renderVisibilityChange(): void { if (this._isDimensionChangeSupported()) { this._attachDimensionChangeHandlers(); } @@ -199,13 +218,14 @@ class DOMComponent extends Component { } } - _renderDimensions() { + _renderDimensions(): void { const $element = this.$element(); const element = $element.get(0); const width = this._getOptionValue('width', element); const height = this._getOptionValue('height', element); if (this._isCssUpdateRequired(element, height, width)) { + // @ts-expect-error $element.css({ width: width === null ? '' : width, height: height === null ? '' : height, @@ -213,11 +233,12 @@ class DOMComponent extends Component { } } - _isCssUpdateRequired(element, height, width) { + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + _isCssUpdateRequired(element, height, width): boolean { return !!(isDefined(width) || isDefined(height) || element.style.width || element.style.height); } - _attachDimensionChangeHandlers() { + _attachDimensionChangeHandlers(): void { const $el = this.$element(); const namespace = `${this.NAME}VisibilityChange`; @@ -225,7 +246,7 @@ class DOMComponent extends Component { resizeEvent.on($el, () => this._dimensionChanged(), { namespace }); } - _attachVisibilityChangeHandlers() { + _attachVisibilityChangeHandlers(): void { if (this._isVisibilityChangeSupported()) { const $el = this.$element(); const namespace = `${this.NAME}VisibilityChange`; @@ -241,13 +262,13 @@ class DOMComponent extends Component { } } - _isVisible() { + _isVisible(): boolean { const $element = this.$element(); return $element.is(':visible'); } - _checkVisibilityChanged(action) { + _checkVisibilityChanged(action: 'shown' | 'hiding'): void { const isVisible = this._isVisible(); if (isVisible) { @@ -261,21 +282,21 @@ class DOMComponent extends Component { } } - _isVisibilityChangeSupported() { + _isVisibilityChangeSupported(): boolean { return this._visibilityChanged !== DOMComponent.prototype._visibilityChanged && hasWindow(); } - _clean() {} + _clean(): void {} // eslint-disable-next-line @typescript-eslint/no-unused-vars - _modelByElement(element) { + _modelByElement(element: dxElementWrapper): unknown | undefined { const { modelByElement } = this.option(); const $element = this.$element(); return modelByElement ? modelByElement($element) : undefined; } - _invalidate() { + _invalidate(): void { if (this._isUpdateAllowed()) { throw errors.Error('E0007'); } @@ -283,69 +304,87 @@ class DOMComponent extends Component { this._requireRefresh = true; } - _refresh() { + _refresh(): void { this._clean(); this._renderComponent(); } - _dispose() { + _dispose(): void { + // eslint-disable-next-line max-len + // eslint-disable-next-line @typescript-eslint/prefer-optional-chain, @typescript-eslint/no-unused-expressions this._templateManager && this._templateManager.dispose(); super._dispose(); this._clean(); this._detachWindowResizeCallback(); } - _detachWindowResizeCallback() { + _detachWindowResizeCallback(): void { if (this._isDimensionChangeSupported()) { windowResizeCallbacks.remove(this._windowResizeCallBack); } } - _toggleRTLDirection(rtl) { + _toggleRTLDirection(rtl: boolean | undefined): void { const $element = this.$element(); $element.toggleClass('dx-rtl', rtl); } - _createComponent(element, component, config = {}) { + _createComponent( + element: string | HTMLElement | dxElementWrapper, + component: string | (new (...args) => TTComponent), + // eslint-disable-next-line @typescript-eslint/no-explicit-any + componentConfiguration: TTComponent extends Component + ? TTProperties + : Record, + ): TTComponent { + const configuration = componentConfiguration ?? {}; + const synchronizableOptions = grep( this._getSynchronizableOptionsForCreateComponent(), - (value) => !(value in config), + (value) => !(value in configuration), ); const { integrationOptions } = this.option(); let { nestedComponentOptions } = this.option(); - nestedComponentOptions = nestedComponentOptions || noop; + nestedComponentOptions = nestedComponentOptions ?? noop; const nestedComponentConfig = extend( { integrationOptions }, nestedComponentOptions(this), ); - synchronizableOptions.forEach((optionName) => nestedComponentConfig[optionName] = this.option(optionName)); + synchronizableOptions.forEach( + // eslint-disable-next-line no-return-assign + (optionName) => nestedComponentConfig[optionName] = this.option(optionName), + ); - this._extendConfig(config, nestedComponentConfig); + this._extendConfig(configuration, nestedComponentConfig); + // eslint-disable-next-line no-void let instance = void 0; if (isString(component)) { - const $element = $(element)[component](config); + const $element = $(element)[component](configuration); instance = $element[component]('instance'); } else if (element) { + // @ts-expect-error instance = component.getInstance(element); if (instance) { // @ts-expect-error - instance.option(config); + instance.option(configuration); } else { - instance = new component(element, config); + // @ts-expect-error + // eslint-disable-next-line new-cap + instance = new component(element, configuration); } } if (instance) { - const optionChangedHandler = ({ name, value }) => { + const optionChangedHandler = ({ name, value }): void => { if (synchronizableOptions.includes(name)) { // @ts-expect-error instance.option(name, value); @@ -357,32 +396,39 @@ class DOMComponent extends Component { instance.on('disposing', () => this.off('optionChanged', optionChangedHandler)); } + // @ts-expect-error return instance; } - _extendConfig(config, extendConfig) { + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + _extendConfig(configuration, extendConfig): void { each(extendConfig, (key, value) => { - !Object.prototype.hasOwnProperty.call(config, key) && (config[key] = value); + // eslint-disable-next-line @typescript-eslint/no-unused-expressions + !Object.prototype.hasOwnProperty.call(configuration, key) && (configuration[key] = value); }); } - _defaultActionConfig() { + _defaultActionConfig(): { context: TComponent; component: TComponent } { const $element = this.$element(); const context = this._modelByElement($element); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return extend(super._defaultActionConfig(), { context }); } - _defaultActionArgs() { + _defaultActionArgs(): { component: TComponent; element?: dxElementWrapper; model?: unknown } { const $element = this.$element(); const model = this._modelByElement($element); const element = this.element(); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return extend(super._defaultActionArgs(), { element, model }); } - _optionChanged(args) { - switch (args.name) { + _optionChanged(args: OptionChanged | Record): void { + const { name } = args; + + switch (name) { case 'width': case 'height': this._renderDimensions(); @@ -402,15 +448,17 @@ class DOMComponent extends Component { } } - _removeAttributes(element) { + _removeAttributes(element: Element): void { const attrs = element.attributes; + // eslint-disable-next-line no-plusplus for (let i = attrs.length - 1; i >= 0; i--) { const attr = attrs[i]; if (attr) { const { name } = attr; + // eslint-disable-next-line @typescript-eslint/prefer-includes if (!name.indexOf('aria-') || name.indexOf('dx-') !== -1 || name === 'role' || name === 'style' || name === 'tabindex') { element.removeAttribute(name); @@ -419,14 +467,14 @@ class DOMComponent extends Component { } } - _removeClasses(element) { + _removeClasses(element: Element): void { element.className = element.className .split(' ') .filter((cssClass) => cssClass.lastIndexOf('dx-', 0) !== 0) .join(' '); } - _updateDOMComponent(renderRequired) { + _updateDOMComponent(renderRequired: boolean): void { if (renderRequired) { this._renderComponent(); } else if (this._requireRefresh) { @@ -435,24 +483,25 @@ class DOMComponent extends Component { } } - endUpdate() { + endUpdate(): void { const renderRequired = this._isInitializingRequired(); super.endUpdate(); + // eslint-disable-next-line @typescript-eslint/no-unused-expressions this._isUpdateAllowed() && this._updateDOMComponent(renderRequired); } - $element() { + $element(): dxElementWrapper { return this._$element; } - element() { + element(): Element { const $element = this.$element(); return getPublicElement($element); } - dispose() { + dispose(): void { const element = this.$element().get(0); cleanDataRecursive(element, true); @@ -461,21 +510,24 @@ class DOMComponent extends Component { this._removeClasses(element); } - resetOption(optionName) { + resetOption(optionName: string): void { super.resetOption(optionName); if (optionName === 'width' || optionName === 'height') { const initialOption = this.initialOption(optionName); + // eslint-disable-next-line @typescript-eslint/no-unused-expressions !isDefined(initialOption) && this.$element().css(optionName, ''); } } - _getAnonymousTemplateName() { + _getAnonymousTemplateName(): void { + // eslint-disable-next-line no-void return void 0; } - _initTemplateManager() { + _initTemplateManager(): undefined { + // eslint-disable-next-line no-void if (this._templateManager || !this._useTemplates()) return void 0; const { integrationOptions = {} } = this.option(); @@ -491,8 +543,11 @@ class DOMComponent extends Component { return undefined; } - _initTemplates() { - const { templates, anonymousTemplateMeta } = this._templateManager.extractTemplates(this.$element()); + _initTemplates(): void { + const { + templates, + anonymousTemplateMeta, + } = this._templateManager.extractTemplates(this.$element()); const anonymousTemplate = this.option(`integrationOptions.templates.${anonymousTemplateMeta.name}`); templates.forEach(({ name, template }) => { @@ -505,15 +560,21 @@ class DOMComponent extends Component { } } + // eslint-disable-next-line max-len + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/explicit-module-boundary-types _getTemplateByOption(optionName) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return this._getTemplate(this.option(optionName)); } + // eslint-disable-next-line max-len + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/explicit-module-boundary-types _getTemplate(templateSource) { const templates = this.option('integrationOptions.templates'); const isAsyncTemplate = this.option('templatesRenderAsynchronously'); const skipTemplates = this.option('integrationOptions.skipTemplates'); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return this._templateManager.getTemplate( templateSource, templates, @@ -525,14 +586,15 @@ class DOMComponent extends Component { ); } - _saveTemplate(name, template) { + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + _saveTemplate(name, template): void { this._setOptionWithoutOptionChange( `integrationOptions.templates.${name}`, this._templateManager._createTemplate(template), ); } - _useTemplates() { + _useTemplates(): boolean { return true; } } diff --git a/packages/devextreme/js/__internal/core/widget/types.ts b/packages/devextreme/js/__internal/core/widget/types.ts new file mode 100644 index 000000000000..72a2f2d4f8a1 --- /dev/null +++ b/packages/devextreme/js/__internal/core/widget/types.ts @@ -0,0 +1,50 @@ +import type { PropertyType } from '@js/core'; + +// TODO: move types to index.d.ts file + +type DotPrefix = T extends '' ? '' : `.${T}`; + +// eslint-disable-next-line spellcheck/spell-checker +type DecrementalCounter = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; + +type IsObject = +0 extends (1 & T) + ? false +// eslint-disable-next-line @typescript-eslint/no-explicit-any + : T extends any[] + ? false + : string extends keyof T + ? false + : T extends object + ? true + : false; + +type DotNestedKeys = +( + IsObject extends true ? + ( + RLIMIT extends 1 ? keyof T : + { + // eslint-disable-next-line spellcheck/spell-checker + [K in Exclude]: `${K}${DotPrefix>}` | K + }[Exclude] + ) : + '' +) extends infer D ? Extract : never; + +export type ComponentPropertyType< +T, TProp extends string, +> = PropertyType extends never ? never : PropertyType | undefined; +interface OptionChangedArgs { + name: TKey extends `${infer TName}.${string}` ? TName : TKey; + fullName: TKey; + previousValue: ComponentPropertyType; + value: ComponentPropertyType; + handled: boolean; +} + +type OptionNames = DotNestedKeys>; + +export type OptionChanged = { + [P in OptionNames]: OptionChangedArgs; +}[OptionNames]; diff --git a/packages/devextreme/js/__internal/core/widget/widget.ts b/packages/devextreme/js/__internal/core/widget/widget.ts index 922258b428cf..7bc01ab41ff0 100644 --- a/packages/devextreme/js/__internal/core/widget/widget.ts +++ b/packages/devextreme/js/__internal/core/widget/widget.ts @@ -1,22 +1,14 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -/* eslint-disable consistent-return */ -/* eslint-disable @typescript-eslint/no-invalid-this */ -/* eslint-disable no-void */ -/* eslint-disable max-len */ -/* eslint-disable @typescript-eslint/prefer-optional-chain */ -/* eslint-disable @typescript-eslint/no-unused-expressions */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable no-param-reassign */ -/* eslint-disable @typescript-eslint/explicit-function-return-type */ import '@js/events/click'; import '@js/events/core/emitter.feedback'; import '@js/events/hover'; import Action from '@js/core/action'; import devices from '@js/core/devices'; +import type { DefaultOptionsRule } from '@js/core/options/utils'; +import type { dxElementWrapper } from '@js/core/renderer'; import $ from '@js/core/renderer'; import { deferRender } from '@js/core/utils/common'; +import type { DeferredObj } from '@js/core/utils/deferred'; import { extend } from '@js/core/utils/extend'; import { each } from '@js/core/utils/iterator'; import { isDefined, isPlainObject } from '@js/core/utils/type'; @@ -25,29 +17,50 @@ import { active, focus, hover, keyboard, } from '@js/events/short'; import { focusable as focusableSelector } from '@js/ui/widget/selectors'; +import type { WidgetOptions } from '@js/ui/widget/ui.widget'; import DOMComponent from './dom_component'; +import type { OptionChanged } from './types'; -function setAttribute(name, value, target) { +const DISABLED_STATE_CLASS = 'dx-state-disabled'; +const FOCUSED_STATE_CLASS = 'dx-state-focused'; +const INVISIBLE_STATE_CLASS = 'dx-state-invisible'; + +function setAttribute(name, value, target): void { + // eslint-disable-next-line no-param-reassign name = name === 'role' || name === 'id' ? name : `aria-${name}`; + // eslint-disable-next-line no-param-reassign value = isDefined(value) ? value.toString() : null; target.attr(name, value); } -class Widget extends DOMComponent { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export interface Properties extends WidgetOptions { + useResizeObserver?: boolean; + onKeyboardHandled?: (event: KeyboardEvent) => void; + isActive?: boolean; + ignoreParentReadOnly?: boolean; + hoveredElement?: dxElementWrapper; +} + +class Widget< + TProperties extends Properties = Properties, +> extends DOMComponent, TProperties> { private readonly _feedbackHideTimeout = 400; private readonly _feedbackShowTimeout = 30; - private _contentReadyAction: any; + private _contentReadyAction?: ((event?: Record) => void) | null; - private readonly _activeStateUnit: any; + private readonly _activeStateUnit!: string; - private _keyboardListenerId: any; + private _keyboardListenerId?: string | null; - private _isReady: any; + private _isReady?: boolean; + // eslint-disable-next-line max-len + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type static getOptionsFromContainer({ name, fullName, value }) { let options = {}; @@ -62,12 +75,14 @@ class Widget extends DOMComponent { return options; } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - _supportedKeys(event?) { + _supportedKeys(): + // eslint-disable-next-line @typescript-eslint/no-invalid-void-type + Record) => void | boolean> { return {}; } - _getDefaultOptions() { + _getDefaultOptions(): TProperties { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return extend(super._getDefaultOptions(), { hoveredElement: null, isActive: false, @@ -99,49 +114,58 @@ class Widget extends DOMComponent { }); } - _defaultOptionsRules() { + _defaultOptionsRules(): DefaultOptionsRule[] { return super._defaultOptionsRules().concat([{ - device() { + device(): boolean { const device = devices.real(); const { platform } = device; const { version } = device; return platform === 'ios' && compareVersions(version, '13.3') <= 0; }, + // @ts-expect-error options: { useResizeObserver: false, }, }]); } - _init() { + _init(): void { super._init(); this._initContentReadyAction(); } - _innerWidgetOptionChanged(innerWidget, args) { + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + _innerWidgetOptionChanged(innerWidget, args): void { const options = Widget.getOptionsFromContainer(args); + // eslint-disable-next-line max-len + // eslint-disable-next-line @typescript-eslint/no-unused-expressions, @typescript-eslint/prefer-optional-chain innerWidget && innerWidget.option(options); this._options.cache(args.name, options); } - _bindInnerWidgetOptions(innerWidget, optionsContainer) { - const syncOptions = () => this._options.silent(optionsContainer, extend({}, innerWidget.option())); + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + _bindInnerWidgetOptions(innerWidget, optionsContainer): void { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + const syncOptions = (): void => this._options.silent( + optionsContainer, + extend({}, innerWidget.option()), + ); syncOptions(); innerWidget.on('optionChanged', syncOptions); } - _getAriaTarget() { + _getAriaTarget(): dxElementWrapper { return this._focusTarget(); } - _initContentReadyAction() { + _initContentReadyAction(): void { this._contentReadyAction = this._createActionByOption('onContentReady', { excludeValidators: ['disabled', 'readOnly'], }); } - _initMarkup() { + _initMarkup(): void { const { disabled, visible } = this.option(); this.$element().addClass('dx-widget'); @@ -149,12 +173,13 @@ class Widget extends DOMComponent { this._toggleDisabledState(disabled); this._toggleVisibility(visible); this._renderHint(); + // eslint-disable-next-line @typescript-eslint/no-unused-expressions this._isFocusable() && this._renderFocusTarget(); super._initMarkup(); } - _render() { + _render(): void { super._render(); this._renderContent(); @@ -164,47 +189,51 @@ class Widget extends DOMComponent { this._toggleIndependentState(); } - _renderHint() { + _renderHint(): void { const { hint } = this.option(); + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing this.$element().attr('title', hint || null); } - _renderContent() { + _renderContent(): void { + // eslint-disable-next-line no-void deferRender(() => (!this._disposed ? this._renderContentImpl() : void 0)) // @ts-expect-error + // eslint-disable-next-line no-void, @typescript-eslint/no-unsafe-return .done(() => (!this._disposed ? this._fireContentReadyAction() : void 0)); } - _renderContentImpl() {} + _renderContentImpl(): void {} - _fireContentReadyAction() { - return deferRender(() => this._contentReadyAction()); + _fireContentReadyAction(): Promise | DeferredObj | void { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return deferRender(() => this._contentReadyAction?.()); } - _dispose() { + _dispose(): void { this._contentReadyAction = null; this._detachKeyboardEvents(); super._dispose(); } - _resetActiveState() { + _resetActiveState(): void { this._toggleActiveState(this._eventBindingTarget(), false); } - _clean() { + _clean(): void { this._cleanFocusState(); this._resetActiveState(); super._clean(); this.$element().empty(); } - _toggleVisibility(visible) { - this.$element().toggleClass('dx-state-invisible', !visible); + _toggleVisibility(visible: boolean | undefined): void { + this.$element().toggleClass(INVISIBLE_STATE_CLASS, !visible); } - _renderFocusState() { + _renderFocusState(): void { this._attachKeyboardEvents(); if (this._isFocusable()) { @@ -214,37 +243,39 @@ class Widget extends DOMComponent { } } - _renderAccessKey() { + _renderAccessKey(): void { const $el = this._focusTarget(); const { accessKey } = this.option(); + // @ts-expect-error $el.attr('accesskey', accessKey); } - _isFocusable() { + _isFocusable(): boolean | undefined { const { focusStateEnabled, disabled } = this.option(); return focusStateEnabled && !disabled; } - _eventBindingTarget() { + _eventBindingTarget(): dxElementWrapper { return this.$element(); } - _focusTarget() { + _focusTarget(): dxElementWrapper { return this._getActiveElement(); } - _isFocusTarget(element) { + _isFocusTarget(element: Element): boolean { const focusTargets = $(this._focusTarget()).toArray(); return focusTargets.includes(element); } - _findActiveTarget($element) { - return $element.find(this._activeStateUnit).not('.dx-state-disabled'); + _findActiveTarget($element: dxElementWrapper): dxElementWrapper { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return $element.find(this._activeStateUnit).not(`.${DISABLED_STATE_CLASS}`); } - _getActiveElement() { + _getActiveElement(): dxElementWrapper { const activeElement = this._eventBindingTarget(); if (this._activeStateUnit) { @@ -254,26 +285,28 @@ class Widget extends DOMComponent { return activeElement; } - _renderFocusTarget() { + _renderFocusTarget(): void { const { tabIndex } = this.option(); + // @ts-expect-error this._focusTarget().attr('tabIndex', tabIndex); } - _keyboardEventBindingTarget() { + _keyboardEventBindingTarget(): dxElementWrapper { return this._eventBindingTarget(); } - _refreshFocusEvent() { + _refreshFocusEvent(): void { this._detachFocusEvents(); this._attachFocusEvents(); } - _focusEventTarget() { + _focusEventTarget(): dxElementWrapper { return this._focusTarget(); } - _focusInHandler(event) { + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + _focusInHandler(event): void { if (!event.isDefaultPrevented()) { this._createActionByOption('onFocusIn', { beforeExecute: () => this._updateFocusState(event, true), @@ -282,7 +315,8 @@ class Widget extends DOMComponent { } } - _focusOutHandler(event) { + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + _focusOutHandler(event): void { if (!event.isDefaultPrevented()) { this._createActionByOption('onFocusOut', { beforeExecute: () => this._updateFocusState(event, false), @@ -291,38 +325,43 @@ class Widget extends DOMComponent { } } - _updateFocusState({ target }, isFocused) { + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + _updateFocusState({ target }, isFocused: boolean): void { if (this._isFocusTarget(target)) { this._toggleFocusClass(isFocused, $(target)); } } - _toggleFocusClass(isFocused, $element?) { + _toggleFocusClass(isFocused: boolean, $element?: dxElementWrapper): void { + // eslint-disable-next-line @typescript-eslint/prefer-optional-chain const $focusTarget = $element && $element.length ? $element : this._focusTarget(); - $focusTarget.toggleClass('dx-state-focused', isFocused); + $focusTarget.toggleClass(FOCUSED_STATE_CLASS, isFocused); } - _hasFocusClass(element?) { - const $focusTarget = $(element || this._focusTarget()); + _hasFocusClass(element?: dxElementWrapper): boolean { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const $focusTarget = $(element ?? this._focusTarget()); - return $focusTarget.hasClass('dx-state-focused'); + return $focusTarget.hasClass(FOCUSED_STATE_CLASS); } - _isFocused() { + _isFocused(): boolean { return this._hasFocusClass(); } + // eslint-disable-next-line @typescript-eslint/no-explicit-any _getKeyboardListeners(): any[] { return []; } - _attachKeyboardEvents() { + _attachKeyboardEvents(): void { this._detachKeyboardEvents(); const { focusStateEnabled, onKeyboardHandled } = this.option(); const hasChildListeners = this._getKeyboardListeners().length; const hasKeyboardEventHandler = !!onKeyboardHandled; + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const shouldAttach = focusStateEnabled || hasChildListeners || hasKeyboardEventHandler; if (shouldAttach) { @@ -334,9 +373,11 @@ class Widget extends DOMComponent { } } - _keyboardHandler(options, onlyChildProcessing?) { + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + _keyboardHandler(options, onlyChildProcessing?: boolean): boolean { if (!onlyChildProcessing) { const { originalEvent, keyName, which } = options; + // @ts-expect-error const keys = this._supportedKeys(originalEvent); const func = keys[keyName] || keys[which]; @@ -353,19 +394,23 @@ class Widget extends DOMComponent { const keyboardListeners = this._getKeyboardListeners(); const { onKeyboardHandled } = this.option(); + // eslint-disable-next-line max-len + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/prefer-optional-chain keyboardListeners.forEach((listener) => listener && listener._keyboardHandler(options)); + // eslint-disable-next-line max-len + // eslint-disable-next-line @typescript-eslint/no-unused-expressions, @typescript-eslint/prefer-optional-chain onKeyboardHandled && onKeyboardHandled(options); return true; } - _refreshFocusState() { + _refreshFocusState(): void { this._cleanFocusState(); this._renderFocusState(); } - _cleanFocusState() { + _cleanFocusState(): void { const $element = this._focusTarget(); $element.removeAttr('tabIndex'); @@ -374,12 +419,12 @@ class Widget extends DOMComponent { this._detachKeyboardEvents(); } - _detachKeyboardEvents() { + _detachKeyboardEvents(): void { keyboard.off(this._keyboardListenerId); this._keyboardListenerId = null; } - _attachHoverEvents() { + _attachHoverEvents(): void { const { hoverStateEnabled } = this.option(); const selector = this._activeStateUnit; const namespace = 'UIFeedback'; @@ -398,7 +443,7 @@ class Widget extends DOMComponent { } } - _attachFeedbackEvents() { + _attachFeedbackEvents(): void { const { activeStateEnabled } = this.option(); const selector = this._activeStateUnit; const namespace = 'UIFeedback'; @@ -424,13 +469,13 @@ class Widget extends DOMComponent { } } - _detachFocusEvents() { + _detachFocusEvents(): void { const $el = this._focusEventTarget(); focus.off($el, { namespace: `${this.NAME}Focus` }); } - _attachFocusEvents() { + _attachFocusEvents(): void { const $el = this._focusEventTarget(); focus.on( @@ -446,55 +491,70 @@ class Widget extends DOMComponent { } // eslint-disable-next-line @typescript-eslint/no-unused-vars - _hoverStartHandler(event) {} - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - _hoverEndHandler(event) {} + _hoverStartHandler(event: unknown): void {} // eslint-disable-next-line @typescript-eslint/no-unused-vars - _toggleActiveState($element, value, event?) { + _hoverEndHandler(event: unknown): void {} + + _toggleActiveState( + $element: dxElementWrapper, + value: boolean, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + event?: Record, + ): void { this.option('isActive', value); $element.toggleClass('dx-state-active', value); } - _updatedHover() { + _updatedHover(): void { const hoveredElement = this._options.silent('hoveredElement'); this._hover(hoveredElement, hoveredElement); } - _findHoverTarget($el) { + _findHoverTarget($el?: dxElementWrapper): dxElementWrapper | undefined { + // eslint-disable-next-line max-len + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/prefer-optional-chain return $el && $el.closest(this._activeStateUnit || this._eventBindingTarget()); } - _hover($el, $previous) { + _hover($el: dxElementWrapper | undefined, $previous: dxElementWrapper | undefined): void { const { hoverStateEnabled, disabled, isActive } = this.option(); + // eslint-disable-next-line no-param-reassign $previous = this._findHoverTarget($previous); + // eslint-disable-next-line max-len + // eslint-disable-next-line @typescript-eslint/no-unused-expressions, @typescript-eslint/prefer-optional-chain $previous && $previous.toggleClass('dx-state-hover', false); if ($el && hoverStateEnabled && !disabled && !isActive) { const newHoveredElement = this._findHoverTarget($el); + // eslint-disable-next-line max-len + // eslint-disable-next-line @typescript-eslint/no-unused-expressions, @typescript-eslint/prefer-optional-chain newHoveredElement && newHoveredElement.toggleClass('dx-state-hover', true); } } - _toggleDisabledState(value) { - this.$element().toggleClass('dx-state-disabled', Boolean(value)); + _toggleDisabledState(value: boolean | undefined): void { + this.$element().toggleClass(DISABLED_STATE_CLASS, Boolean(value)); + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing this.setAria('disabled', value || undefined); } - _toggleIndependentState() { - this.$element().toggleClass('dx-state-independent', this.option('ignoreParentReadOnly')); + _toggleIndependentState(): void { + const { ignoreParentReadOnly } = this.option(); + + this.$element().toggleClass('dx-state-independent', ignoreParentReadOnly); } - _setWidgetOption(widgetName, args) { + _setWidgetOption(widgetName: 'string', args: Record): void { if (!this[widgetName]) { return; } if (isPlainObject(args[0])) { + // @ts-expect-error each(args[0], (option, value) => this._setWidgetOption(widgetName, [option, value])); return; @@ -512,12 +572,12 @@ class Widget extends DOMComponent { this[widgetName].option(widgetOptionMap ? widgetOptionMap(optionName) : optionName, value); } - _optionChanged(args) { + _optionChanged(args: OptionChanged | Record): void { const { name, value, previousValue } = args; switch (name) { case 'disabled': - this._toggleDisabledState(value); + this._toggleDisabledState(value as Properties[typeof name]); this._updatedHover(); this._refreshFocusState(); break; @@ -546,13 +606,13 @@ class Widget extends DOMComponent { this._renderAccessKey(); break; case 'hoveredElement': - this._hover(value, previousValue); + this._hover(value as Properties[typeof name], previousValue as Properties[typeof name]); break; case 'isActive': this._updatedHover(); break; case 'visible': - this._toggleVisibility(value); + this._toggleVisibility(value as Properties[typeof name]); if (this._isVisibilityChangeSupported()) { // TODO hiding works wrong this._checkVisibilityChanged(value ? 'shown' : 'hiding'); @@ -569,18 +629,19 @@ class Widget extends DOMComponent { } } - _isVisible() { + _isVisible(): boolean { const { visible } = this.option(); + // @ts-expect-error return super._isVisible() && visible; } - beginUpdate() { + beginUpdate(): void { this._ready(false); super.beginUpdate(); } - endUpdate() { + endUpdate(): void { super.endUpdate(); if (this._initialized) { @@ -588,15 +649,18 @@ class Widget extends DOMComponent { } } - _ready(value?) { + _ready(value?: boolean): boolean { if (arguments.length === 0) { - return this._isReady; + return !!this._isReady; } - this._isReady = value; + this._isReady = !!value; + + return this._isReady; } - setAria(...args) { + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + setAria(...args): void { if (!isPlainObject(args[0])) { setAttribute(args[0], args[1], args[2] || this._getAriaTarget()); } else { @@ -606,22 +670,24 @@ class Widget extends DOMComponent { } } - isReady() { + isReady(): boolean { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return this._ready(); } - repaint() { + repaint(): void { this._refresh(); } - focus() { + focus(): void { focus.trigger(this._focusTarget()); } - registerKeyHandler(key, handler) { + registerKeyHandler(key: string, handler: () => void): void { const currentKeys = this._supportedKeys(); - this._supportedKeys = () => extend(currentKeys, { [key]: handler }); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, max-len + this._supportedKeys = (): Record boolean> => extend(currentKeys, { [key]: handler }); } } diff --git a/packages/devextreme/js/__internal/tsconfig.json b/packages/devextreme/js/__internal/tsconfig.json index 673aa2eac9b3..5a999bc5f382 100644 --- a/packages/devextreme/js/__internal/tsconfig.json +++ b/packages/devextreme/js/__internal/tsconfig.json @@ -32,12 +32,12 @@ //"useUnknownInCatchVariables": true, //"noImplicitOverride": true, "paths": { - "@js/*": [ - "./*" - ], "@ts/*": [ "./__internal/*" ], + "@js/*": [ + "./*" + ], }, "types": [ "jest" ] }, diff --git a/packages/devextreme/js/__internal/ui/chat/chat.ts b/packages/devextreme/js/__internal/ui/chat/chat.ts index 09aab331aca5..70f211547527 100644 --- a/packages/devextreme/js/__internal/ui/chat/chat.ts +++ b/packages/devextreme/js/__internal/ui/chat/chat.ts @@ -1,15 +1,15 @@ -import type { ComponentFactory } from '@js/core/component_registrator'; import registerComponent from '@js/core/component_registrator'; import Guid from '@js/core/guid'; import type { dxElementWrapper } from '@js/core/renderer'; import $ from '@js/core/renderer'; -import type { Message, MessageSendEvent, Properties } from '@js/ui/chat'; +import type { Message, MessageSendEvent, Properties as ChatProperties } from '@js/ui/chat'; +import type { OptionChanged } from '@ts/core/widget/types'; +import Widget from '@ts/core/widget/widget'; -import Widget from '../widget'; import ChatHeader from './chat_header'; import type { - MessageBoxProperties, MessageSendEvent as MessageBoxMessageSendEvent, + Properties as MessageBoxProperties, } from './chat_message_box'; import MessageBox from './chat_message_box'; import MessageList from './chat_message_list'; @@ -17,9 +17,9 @@ import MessageList from './chat_message_list'; const CHAT_CLASS = 'dx-chat'; const TEXTEDITOR_INPUT_CLASS = 'dx-texteditor-input'; -type Title = string; +type Properties = ChatProperties & { title: string }; -class Chat extends Widget { +class Chat extends Widget { _chatHeader?: ChatHeader; _messageBox!: MessageBox; @@ -28,7 +28,7 @@ class Chat extends Widget { _messageSendAction?: (e: Partial) => void; - _getDefaultOptions(): Properties & { title: Title } { + _getDefaultOptions(): Properties { return { ...super._getDefaultOptions(), activeStateEnabled: true, @@ -67,7 +67,6 @@ class Chat extends Widget { const $header = $('
'); this.element().prepend($header.get(0)); - this._chatHeader = this._createComponent($header, ChatHeader, { title, }); @@ -133,21 +132,21 @@ class Chat extends Widget { return $input; } - _optionChanged(args: Record): void { + _optionChanged(args: OptionChanged): void { const { name, value } = args; switch (name) { case 'activeStateEnabled': case 'focusStateEnabled': case 'hoverStateEnabled': - this._messageBox.option(name, value as Properties[typeof name]); + this._messageBox.option(name, value); break; case 'title': { if (value) { if (this._chatHeader) { - this._chatHeader.option('title', (value as Title)); + this._chatHeader.option('title', value); } else { - this._renderHeader((value as Title)); + this._renderHeader(value); } } else if (this._chatHeader) { this._chatHeader.dispose(); @@ -155,12 +154,15 @@ class Chat extends Widget { } break; } - case 'user': - this._messageList.option('currentUserId', (value as Properties[typeof name])?.id); + case 'user': { + const author = value as Properties[typeof name]; + + this._messageList.option('currentUserId', author?.id); break; + } case 'items': case 'dataSource': - this._messageList.option(name, value as Properties[typeof name]); + this._messageList.option(name, value); break; case 'onMessageSend': this._createMessageSendAction(); @@ -179,6 +181,6 @@ class Chat extends Widget { } } -registerComponent('dxChat', Chat as unknown as ComponentFactory); +registerComponent('dxChat', Chat); export default Chat; diff --git a/packages/devextreme/js/__internal/ui/chat/chat_avatar.ts b/packages/devextreme/js/__internal/ui/chat/chat_avatar.ts index 2c1d6903e19e..bb8deb3eb7bf 100644 --- a/packages/devextreme/js/__internal/ui/chat/chat_avatar.ts +++ b/packages/devextreme/js/__internal/ui/chat/chat_avatar.ts @@ -2,20 +2,20 @@ import type { dxElementWrapper } from '@js/core/renderer'; import $ from '@js/core/renderer'; import { isDefined } from '@js/core/utils/type'; import type { WidgetOptions } from '@js/ui/widget/ui.widget'; - -import Widget from '../widget'; +import type { OptionChanged } from '@ts/core/widget/types'; +import Widget from '@ts/core/widget/widget'; const CHAT_MESSAGE_AVATAR_CLASS = 'dx-chat-message-avatar'; const CHAT_MESSAGE_AVATAR_INITIALS_CLASS = 'dx-chat-message-avatar-initials'; -export interface AvatarOptions extends WidgetOptions { +export interface Properties extends WidgetOptions { name?: string; } -class Avatar extends Widget { +class Avatar extends Widget { _$initials!: dxElementWrapper; - _getDefaultOptions(): AvatarOptions { + _getDefaultOptions(): Properties { return { ...super._getDefaultOptions(), name: '', @@ -51,7 +51,7 @@ class Avatar extends Widget { return ''; } - _optionChanged(args: Record): void { + _optionChanged(args: OptionChanged): void { const { name } = args; switch (name) { diff --git a/packages/devextreme/js/__internal/ui/chat/chat_header.ts b/packages/devextreme/js/__internal/ui/chat/chat_header.ts index b2d2a804b63b..d09c46aecebd 100644 --- a/packages/devextreme/js/__internal/ui/chat/chat_header.ts +++ b/packages/devextreme/js/__internal/ui/chat/chat_header.ts @@ -1,35 +1,33 @@ -import type { Properties } from '@js/core/dom_component'; -import DOMComponent from '@js/core/dom_component'; +import type { Properties as DOMComponentProperties } from '@js/core/dom_component'; import type { dxElementWrapper } from '@js/core/renderer'; import $ from '@js/core/renderer'; +import DOMComponent from '@ts/core/widget/dom_component'; +import type { OptionChanged } from '@ts/core/widget/types'; const CHAT_HEADER_CLASS = 'dx-chat-header'; const CHAT_HEADER_TEXT_CLASS = 'dx-chat-header-text'; -export interface ChatHeaderProperties extends Properties { +export interface Properties extends DOMComponentProperties { title: string; } -class ChatHeader extends DOMComponent { +class ChatHeader extends DOMComponent { private _$text!: dxElementWrapper; - _getDefaultOptions(): ChatHeaderProperties { + _getDefaultOptions(): Properties { return { - // @ts-expect-error ...super._getDefaultOptions(), title: '', - } as ChatHeaderProperties; + } as Properties; } _init(): void { - // @ts-expect-error super._init(); $(this.element()).addClass(CHAT_HEADER_CLASS); } _initMarkup(): void { - // @ts-expect-error super._initMarkup(); this._renderTextElement(); @@ -48,7 +46,7 @@ class ChatHeader extends DOMComponent { this._$text.text(title); } - _optionChanged(args: Record): void { + _optionChanged(args: OptionChanged): void { const { name } = args; switch (name) { @@ -56,7 +54,6 @@ class ChatHeader extends DOMComponent { this._updateText(); break; default: - // @ts-expect-error super._optionChanged(args); } } diff --git a/packages/devextreme/js/__internal/ui/chat/chat_message_box.ts b/packages/devextreme/js/__internal/ui/chat/chat_message_box.ts index c77948f7dc64..51fe0831c54d 100644 --- a/packages/devextreme/js/__internal/ui/chat/chat_message_box.ts +++ b/packages/devextreme/js/__internal/ui/chat/chat_message_box.ts @@ -2,11 +2,12 @@ import $ from '@js/core/renderer'; import type { NativeEventInfo } from '@js/events'; import type { ClickEvent } from '@js/ui/button'; import Button from '@js/ui/button'; -import type { WidgetOptions } from '@js/ui/widget/ui.widget'; +import type { Properties as DOMComponentProperties } from '@ts/core/widget/dom_component'; +import DOMComponent from '@ts/core/widget/dom_component'; +import type { OptionChanged } from '@ts/core/widget/types'; import type dxTextArea from '../../../ui/text_area'; import TextArea from '../m_text_area'; -import Widget from '../widget'; const CHAT_MESSAGE_BOX_CLASS = 'dx-chat-message-box'; const CHAT_MESSAGE_BOX_TEXTAREA_CLASS = 'dx-chat-message-box-text-area'; @@ -16,21 +17,30 @@ export type MessageSendEvent = NativeEventInfo & { text?: string }; -export interface MessageBoxProperties extends WidgetOptions { +export interface Properties extends DOMComponentProperties { onMessageSend?: (e: MessageSendEvent) => void; + + activeStateEnabled?: boolean; + + focusStateEnabled?: boolean; + + hoverStateEnabled?: boolean; } -class MessageBox extends Widget { +class MessageBox extends DOMComponent { _textArea!: dxTextArea; _button!: Button; _messageSendAction?: (e: Partial) => void; - _getDefaultOptions(): MessageBoxProperties { + _getDefaultOptions(): Properties { return { ...super._getDefaultOptions(), onMessageSend: undefined, + activeStateEnabled: true, + focusStateEnabled: true, + hoverStateEnabled: true, }; } @@ -108,17 +118,15 @@ class MessageBox extends Widget { this._textArea?.reset(); } - _optionChanged(args: Record): void { + _optionChanged(args: OptionChanged): void { const { name, value } = args; switch (name) { case 'activeStateEnabled': case 'focusStateEnabled': case 'hoverStateEnabled': { - const options = { [name]: value }; - - this._button.option(options); - this._textArea.option(options); + this._button.option(name, value); + this._textArea.option(name, value); break; } diff --git a/packages/devextreme/js/__internal/ui/chat/chat_message_bubble.ts b/packages/devextreme/js/__internal/ui/chat/chat_message_bubble.ts index bde60390a260..c2c3369daf67 100644 --- a/packages/devextreme/js/__internal/ui/chat/chat_message_bubble.ts +++ b/packages/devextreme/js/__internal/ui/chat/chat_message_bubble.ts @@ -1,16 +1,16 @@ import $ from '@js/core/renderer'; import type { WidgetOptions } from '@js/ui/widget/ui.widget'; - -import Widget from '../widget'; +import type { OptionChanged } from '@ts/core/widget/types'; +import Widget from '@ts/core/widget/widget'; const CHAT_MESSAGE_BUBBLE_CLASS = 'dx-chat-message-bubble'; -export interface MessageBubbleOptions extends WidgetOptions { +export interface Properties extends WidgetOptions { text?: string; } -class MessageBubble extends Widget { - _getDefaultOptions(): MessageBubbleOptions { +class MessageBubble extends Widget { + _getDefaultOptions(): Properties { return { ...super._getDefaultOptions(), text: '', @@ -32,7 +32,7 @@ class MessageBubble extends Widget { $(this.element()).text(text); } - _optionChanged(args: Record): void { + _optionChanged(args: OptionChanged): void { const { name } = args; switch (name) { diff --git a/packages/devextreme/js/__internal/ui/chat/chat_message_group.ts b/packages/devextreme/js/__internal/ui/chat/chat_message_group.ts index 163c592b3f70..9437d365743e 100644 --- a/packages/devextreme/js/__internal/ui/chat/chat_message_group.ts +++ b/packages/devextreme/js/__internal/ui/chat/chat_message_group.ts @@ -4,8 +4,9 @@ import dateSerialization from '@js/core/utils/date_serialization'; import { isDefined } from '@js/core/utils/type'; import type { Message } from '@js/ui/chat'; import type { WidgetOptions } from '@js/ui/widget/ui.widget'; +import type { OptionChanged } from '@ts/core/widget/types'; +import Widget from '@ts/core/widget/widget'; -import Widget from '../widget'; import Avatar from './chat_avatar'; import MessageBubble from './chat_message_bubble'; @@ -19,17 +20,17 @@ const CHAT_MESSAGE_BUBBLE_CONTAINER_CLASS = 'dx-chat-message-bubble-container'; export type MessageGroupAlignment = 'start' | 'end'; -export interface MessageGroupOptions extends WidgetOptions { +export interface Properties extends WidgetOptions { items: Message[]; alignment: MessageGroupAlignment; } -class MessageGroup extends Widget { +class MessageGroup extends Widget { _avatar?: Avatar; _$messageBubbleContainer!: dxElementWrapper; - _getDefaultOptions(): MessageGroupOptions { + _getDefaultOptions(): Properties { return { ...super._getDefaultOptions(), items: [], @@ -142,7 +143,7 @@ class MessageGroup extends Widget { $information.appendTo(this.element()); } - _optionChanged(args: Record): void { + _optionChanged(args: OptionChanged): void { const { name } = args; switch (name) { diff --git a/packages/devextreme/js/__internal/ui/chat/chat_message_list.ts b/packages/devextreme/js/__internal/ui/chat/chat_message_list.ts index dfa9ada27a7f..b67b6b858005 100644 --- a/packages/devextreme/js/__internal/ui/chat/chat_message_list.ts +++ b/packages/devextreme/js/__internal/ui/chat/chat_message_list.ts @@ -1,30 +1,30 @@ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import type { dxElementWrapper } from '@js/core/renderer'; import $ from '@js/core/renderer'; import { hasWindow } from '@js/core/utils/window'; import type { Message } from '@js/ui/chat'; import Scrollable from '@js/ui/scroll_view/ui.scrollable'; import type { WidgetOptions } from '@js/ui/widget/ui.widget'; +import type { OptionChanged } from '@ts/core/widget/types'; +import Widget from '@ts/core/widget/widget'; -import Widget from '../widget'; import type { MessageGroupAlignment } from './chat_message_group'; import MessageGroup from './chat_message_group'; const CHAT_MESSAGE_LIST_CLASS = 'dx-chat-message-list'; -export interface MessageListOptions extends WidgetOptions { +export interface Properties extends WidgetOptions { items: Message[]; currentUserId: number | string | undefined; } -class MessageList extends Widget { +class MessageList extends Widget { _messageGroups?: MessageGroup[]; private _$content!: dxElementWrapper; private _scrollable?: Scrollable; - _getDefaultOptions(): MessageListOptions { + _getDefaultOptions(): Properties { return { ...super._getDefaultOptions(), items: [], @@ -185,7 +185,7 @@ class MessageList extends Widget { } } - _optionChanged(args: Record): void { + _optionChanged(args: OptionChanged): void { const { name, value, previousValue } = args; switch (name) { @@ -193,7 +193,7 @@ class MessageList extends Widget { this._invalidate(); break; case 'items': - this._processItemsUpdating(value as MessageListOptions['items'], previousValue as MessageListOptions['items']); + this._processItemsUpdating(value ?? [], previousValue ?? []); break; default: super._optionChanged(args); diff --git a/packages/devextreme/js/__internal/ui/splitter/resize_handle.ts b/packages/devextreme/js/__internal/ui/splitter/resize_handle.ts index ceaa208093dd..6eec1305cf50 100644 --- a/packages/devextreme/js/__internal/ui/splitter/resize_handle.ts +++ b/packages/devextreme/js/__internal/ui/splitter/resize_handle.ts @@ -13,8 +13,9 @@ import type { ItemCollapsedEvent, ItemExpandedEvent, ResizeEndEvent, ResizeEvent, ResizeStartEvent, } from '@js/ui/splitter'; import type { WidgetOptions } from '@js/ui/widget/ui.widget'; +import type { OptionChanged } from '@ts/core/widget/types'; +import Widget from '@ts/core/widget/widget'; -import Widget from '../widget'; import { COLLAPSE_EVENT, getActionNameByEventName, @@ -74,7 +75,8 @@ class ResizeHandle extends Widget { private DOUBLE_CLICK_EVENT_NAME?: string; - _supportedKeys(): Record void> { + // eslint-disable-next-line @typescript-eslint/no-invalid-void-type + _supportedKeys(): Record void | boolean> { return { ...super._supportedKeys(), ...{ @@ -247,7 +249,6 @@ class ResizeHandle extends Widget { const dimension = isHorizontal ? 'width' : 'height'; const inverseDimension = isHorizontal ? 'height' : 'width'; - // @ts-expect-error ts-error this.option(inverseDimension, null); this.option(dimension, this.getSize()); } @@ -483,7 +484,9 @@ class ResizeHandle extends Widget { } _isHorizontalDirection(): boolean { - return this.option('direction') === RESIZE_DIRECTION.horizontal; + const { direction } = this.option(); + + return direction === RESIZE_DIRECTION.horizontal; } _clean(): void { @@ -493,7 +496,7 @@ class ResizeHandle extends Widget { super._clean(); } - _optionChanged(args: Record): void { + _optionChanged(args: OptionChanged): void { const { name, value } = args; switch (name) { diff --git a/packages/devextreme/js/__internal/ui/splitter/splitter_item.ts b/packages/devextreme/js/__internal/ui/splitter/splitter_item.ts index eece7d54ad74..5c2b588539cf 100644 --- a/packages/devextreme/js/__internal/ui/splitter/splitter_item.ts +++ b/packages/devextreme/js/__internal/ui/splitter/splitter_item.ts @@ -35,7 +35,7 @@ class SplitterItem extends CollectionWidgetItem { this._setIdAttr(id); const config = this._owner._getResizeHandleConfig(id); - + // @ts-expect-error this._resizeHandle = this._owner._createComponent($('
'), ResizeHandle, config); if (this._resizeHandle && this._$element) { diff --git a/packages/devextreme/js/__internal/ui/widget.ts b/packages/devextreme/js/__internal/ui/widget.ts index b6d1f084e8a4..0cda28788c66 100644 --- a/packages/devextreme/js/__internal/ui/widget.ts +++ b/packages/devextreme/js/__internal/ui/widget.ts @@ -61,7 +61,7 @@ declare class ExtendedWidget extends Widget { _getTemplateByOption(optionName: string): unknown; _getSynchronizableOptionsForCreateComponent(): string[]; _defaultOptionsRules(): Record[]; - _optionChanged(args: Record): void; + _setOptionWithoutOptionChange(optionName: string, value: unknown): void; _setOptionsByReference(): void; @@ -80,7 +80,7 @@ declare class ExtendedWidget extends Widget { _createActionByOption(optionName: string, config?: Record); _isInitialOptionValue(name: string): boolean; _setDeprecatedOptions(): void; - + _optionChanged(args: Record): void; _dispose(): void; } diff --git a/packages/devextreme/js/core/dom_component.d.ts b/packages/devextreme/js/core/dom_component.d.ts index 200edafbe96a..b8f221da232f 100644 --- a/packages/devextreme/js/core/dom_component.d.ts +++ b/packages/devextreme/js/core/dom_component.d.ts @@ -18,7 +18,7 @@ import { TemplateManager } from './template_manager'; import { FunctionTemplate } from './templates/function_template'; import { DefaultOptionsRule } from './options'; -type OptionChangedEventInfo = EventInfo & ChangedOptionInfo; +export type OptionChangedEventInfo = EventInfo & ChangedOptionInfo; /* eslint-disable no-underscore-dangle */ diff --git a/packages/devextreme/testing/tests/DevExpress.ui/defaultOptions.tests.js b/packages/devextreme/testing/tests/DevExpress.ui/defaultOptions.tests.js index 66fae23c6e33..51e94773cdff 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui/defaultOptions.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui/defaultOptions.tests.js @@ -1369,6 +1369,9 @@ testComponentDefaults(ChatMessageBox, {}, { onMessageSend: undefined, + activeStateEnabled: true, + focusStateEnabled: true, + hoverStateEnabled: true, } ); diff --git a/packages/devextreme/ts/dx.all.d.ts b/packages/devextreme/ts/dx.all.d.ts index 0c78532bcb80..95381a8cf569 100644 --- a/packages/devextreme/ts/dx.all.d.ts +++ b/packages/devextreme/ts/dx.all.d.ts @@ -842,7 +842,7 @@ declare module DevExpress { /** * @deprecated Attention! This type is for internal purposes only. If you used it previously, please submit a ticket to our {@link https://supportcenter.devexpress.com/ticket/create Support Center}. We will check if there is an alternative solution. */ - type OptionChangedEventInfo = + export type OptionChangedEventInfo = DevExpress.events.EventInfo & DevExpress.events.ChangedOptionInfo; /**