diff --git a/packages/devextreme/js/__internal/m_draggable.ts b/packages/devextreme/js/__internal/m_draggable.ts index 7c414fbca8f6..33ec45d78136 100644 --- a/packages/devextreme/js/__internal/m_draggable.ts +++ b/packages/devextreme/js/__internal/m_draggable.ts @@ -13,16 +13,13 @@ import { import pointerEvents from '@js/common/core/events/pointer'; import { addNamespace, needSkipEvent } from '@js/common/core/events/utils/index'; import registerComponent from '@js/core/component_registrator'; -import domAdapter from '@js/core/dom_adapter'; +import type { DxElement } from '@js/core/element'; import { getPublicElement } from '@js/core/element'; import type { dxElementWrapper } from '@js/core/renderer'; import $ from '@js/core/renderer'; import { EmptyTemplate } from '@js/core/templates/empty_template'; -// @ts-expect-error -import { splitPair } from '@js/core/utils/common'; import type { DeferredObj } from '@js/core/utils/deferred'; -// @ts-expect-error -import { Deferred, fromPromise, when } from '@js/core/utils/deferred'; +import { Deferred, when } from '@js/core/utils/deferred'; import { extend } from '@js/core/utils/extend'; import { dasherize } from '@js/core/utils/inflector'; import { getBoundingRect } from '@js/core/utils/position'; @@ -34,15 +31,70 @@ import { quadToObject } from '@js/core/utils/string'; import { isFunction, isNumeric, isObject } from '@js/core/utils/type'; import { value as viewPort } from '@js/core/utils/view_port'; import { getWindow } from '@js/core/utils/window'; -import type { PointerInteractionEvent } from '@js/events/events.types'; -import type { Properties } from '@js/ui/draggable'; +import type { DraggableBaseOptions, Properties } from '@js/ui/draggable'; +import { domAdapter } from '@ts/core/dom_adapter'; +import { splitPair } from '@ts/core/utils/m_common'; +import { fromPromise } from '@ts/core/utils/m_deferred'; +import type { DefaultActionArgs } from '@ts/core/widget/component'; import DOMComponent from '@ts/core/widget/dom_component'; +import type { OptionChanged } from '@ts/core/widget/types'; import Animator from './ui/scroll_view/animator'; type BoundOffset = number | string | { h?: number; v?: number }; +type DragHandler = ((e: never) => void) | undefined; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export interface DraggableBaseProperties extends Omit, 'boundary' | 'onDisposing' | 'onInitialized' | 'onOptionChanged'> { + scrollSensitivity: number; + + scrollSpeed: number; + + allowMoveByClick?: boolean; + + boundOffset?: BoundOffset | (() => BoundOffset); + + boundary?: DraggableBaseOptions['boundary'] | dxElementWrapper; + + component?: unknown; + + contentTemplate?: string | null; + + clone?: boolean; + + dragTemplate?: Properties['dragTemplate']; + + filter?: string; + + immediate?: boolean; + + itemData?: unknown; + + onCancelByEsc?: boolean; + + onDragCancel?: DragHandler; + + onDragEnd?: DragHandler; + + onDragEnter?: DragHandler; + + onDragLeave?: DragHandler; + + onDragMove?: DragHandler; + + onDragStart?: DragHandler; + + onDraggableElementShown?: DragHandler; + + onDrop?: DragHandler; +} + export interface DraggableProperties extends Omit { + scrollSensitivity: number; + + scrollSpeed: number; + allowMoveByClick?: boolean; boundOffset?: BoundOffset | (() => BoundOffset); @@ -52,6 +104,24 @@ export interface DraggableProperties extends Omit void; + + onDragEnter?: (e: DragEventArgs) => void; + + onDragLeave?: (e: DragEventArgs) => void; + + onDraggableElementShown?: (e: DragElementShownArgs) => void; + + onDrop?: (e: DragEventArgs) => void; } const window = getWindow(); @@ -68,15 +138,23 @@ const KEYDOWN_EVENT_NAME = addNamespace(KEYDOWN_EVENT, DRAGGABLE); const CLONE_CLASS = 'clone'; -let targetDraggable; -let sourceDraggable; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type AnyDraggable = Draggable; + +let activeTargetDraggable: AnyDraggable | null = null; +let activeSourceDraggable: AnyDraggable | null = null; const ANONYMOUS_TEMPLATE_NAME = 'content'; -const getMousePosition = (event) => ({ - // @ts-expect-error +interface MousePosition { + x: number; + y: number; +} + +const getMousePosition = (event: { pageX: number; pageY: number }): MousePosition => ({ + // @ts-expect-error scrollLeft is declared to return the wrapper x: event.pageX - $(window).scrollLeft(), - // @ts-expect-error + // @ts-expect-error scrollTop is declared to return the wrapper y: event.pageY - $(window).scrollTop(), }); @@ -94,33 +172,102 @@ interface DragEventOffset { y: number; } -type DragEvent = Cancelable & PointerInteractionEvent & { - _cancelPreventDefault?: boolean; +export type DragEvent = Cancelable & { + type: string; + target: Element; + pageX: number; + pageY: number; + key?: string; + originalEvent?: { target?: Element }; offset?: DragEventOffset; + maxLeftOffset?: number; + maxRightOffset?: number; + maxTopOffset?: number; + maxBottomOffset?: number; + _cancelPreventDefault?: boolean; +}; + +export type DragEventArgs = Cancelable & { + event: DragEvent; + itemData: unknown; + itemElement: unknown; + fromComponent: unknown; + toComponent: unknown; + fromData: unknown; + toData: unknown; }; +export type DragStartArgs = Cancelable & { + event: DragEvent; + itemData: unknown; + itemElement: dxElementWrapper; + fromData: unknown; +}; + +export type DragElementShownArgs = DragStartArgs & { dragElement: dxElementWrapper }; + +type CursorOffset = DraggableBaseOptions['cursorOffset']; + +type ElementOffsetOptions = DragStartArgs & { + dragElement: Element | undefined; + initialOffset?: Offset | false; +}; + +type CursorOffsetCallback = (options: ElementOffsetOptions) => CursorOffset; + +interface BoundOffsetQuad { + left: number; + top: number; + right: number; + bottom: number; +} + +export interface DragTemplateArgs { + container: DxElement; + model: { + itemData: unknown; + itemElement: DxElement; + fromIndex?: number; + }; +} + +type ActionFn = (args?: object) => void; + +type ScrollOrientation = 'vertical' | 'horizontal'; + +interface ScrollableInstance { + scrollOffset: () => Record; + scrollTo: (position: Record) => void; +} + +interface ScrollHelperOwner { + option: () => { scrollSensitivity: number; scrollSpeed: number }; + _dragMoveEvent?: DragEvent; + dragMoveHandler: (e: DragEvent) => void; +} + class ScrollHelper { private _preventScroll: boolean; - private readonly _component: any; + private readonly _component: ScrollHelperOwner; - private readonly _scrollValue: string; + private readonly _scrollValue: 'scrollTop' | 'scrollLeft'; - private readonly _overFlowAttr: string; + private readonly _overFlowAttr: 'overflowY' | 'overflowX'; - private readonly _sizeAttr: string; + private readonly _sizeAttr: 'height' | 'width'; - private readonly _scrollSizeProp: string; + private readonly _scrollSizeProp: 'scrollHeight' | 'scrollWidth'; - private readonly _clientSizeProp: string; + private readonly _clientSizeProp: 'clientHeight' | 'clientWidth'; - private readonly _limitProps: { start: string; end: string }; + private readonly _limitProps: { start: 'top' | 'left'; end: 'bottom' | 'right' }; private _$scrollableAtPointer: dxElementWrapper | null = null; private _scrollSpeed: number | undefined; - constructor(orientation, component) { + constructor(orientation: ScrollOrientation, component: ScrollHelperOwner) { this._preventScroll = true; this._component = component; @@ -147,7 +294,7 @@ class ScrollHelper { } } - updateScrollable(elements, mousePosition) { + updateScrollable(elements: Element[], mousePosition: MousePosition): void { let isScrollableFound = false; elements.some((element) => { @@ -169,50 +316,52 @@ class ScrollHelper { } } - isScrolling() { + isScrolling(): boolean { return !!this._scrollSpeed; } - isScrollable($element) { + isScrollable($element: dxElementWrapper): boolean { return ($element.css(this._overFlowAttr) === 'auto' || $element.hasClass('dx-scrollable-container')) + // @ts-expect-error prop is declared without the getter form && $element.prop(this._scrollSizeProp) > Math.ceil(this._sizeAttr === 'width' ? getWidth($element) : getHeight($element)); } - _trySetScrollable(element, mousePosition) { - const that = this; + _trySetScrollable(element: Element, mousePosition: MousePosition): boolean { const $element = $(element); - let distanceToBorders; - const sensitivity = that._component.option('scrollSensitivity'); - let isScrollable = that.isScrollable($element); + const { scrollSensitivity: sensitivity } = this._component.option(); + let isScrollable = this.isScrollable($element); if (isScrollable) { - distanceToBorders = that._calculateDistanceToBorders($element, mousePosition); + const distanceToBorders = this._calculateDistanceToBorders($element, mousePosition); + const { start, end } = this._limitProps; - if (sensitivity > distanceToBorders[that._limitProps.start]) { - if (!that._preventScroll) { - that._scrollSpeed = -that._calculateScrollSpeed(distanceToBorders[that._limitProps.start]); - that._$scrollableAtPointer = $element; + if (sensitivity > distanceToBorders[start]) { + if (!this._preventScroll) { + this._scrollSpeed = -this._calculateScrollSpeed(distanceToBorders[start]); + this._$scrollableAtPointer = $element; } - } else if (sensitivity > distanceToBorders[that._limitProps.end]) { - if (!that._preventScroll) { - that._scrollSpeed = that._calculateScrollSpeed(distanceToBorders[that._limitProps.end]); - that._$scrollableAtPointer = $element; + } else if (sensitivity > distanceToBorders[end]) { + if (!this._preventScroll) { + this._scrollSpeed = this._calculateScrollSpeed(distanceToBorders[end]); + this._$scrollableAtPointer = $element; } } else { isScrollable = false; - that._preventScroll = false; + this._preventScroll = false; } } return isScrollable; } - _calculateDistanceToBorders($area, mousePosition) { + _calculateDistanceToBorders( + $area: dxElementWrapper, + mousePosition: MousePosition, + ): Record { const area = $area.get(0); - let areaBoundingRect; if (area) { - areaBoundingRect = getBoundingRect(area); + const areaBoundingRect: DOMRect = getBoundingRect(area); return { left: mousePosition.x - areaBoundingRect.left, @@ -224,48 +373,49 @@ class ScrollHelper { return {}; } - _calculateScrollSpeed(distance) { - const component = this._component; - const sensitivity = component.option('scrollSensitivity'); - const maxSpeed = component.option('scrollSpeed'); + _calculateScrollSpeed(distance: number): number { + const { scrollSensitivity: sensitivity, scrollSpeed: maxSpeed } = this._component.option(); return Math.ceil(((sensitivity - distance) / sensitivity) ** 2 * maxSpeed); } - scrollByStep() { - const that = this; - - if (that._$scrollableAtPointer && that._scrollSpeed) { - if (that._$scrollableAtPointer.hasClass('dx-scrollable-container')) { - const $scrollable = that._$scrollableAtPointer.closest('.dx-scrollable'); - const scrollableInstance: any = $scrollable.data('dxScrollable') || $scrollable.data('dxScrollView'); + scrollByStep(): void { + if (this._$scrollableAtPointer && this._scrollSpeed) { + if (this._$scrollableAtPointer.hasClass('dx-scrollable-container')) { + const $scrollable = this._$scrollableAtPointer.closest('.dx-scrollable'); + // @ts-expect-error data is declared without the getter form + const scrollableInstance: ScrollableInstance | undefined = $scrollable.data('dxScrollable') || $scrollable.data('dxScrollView'); if (scrollableInstance) { - const nextScrollPosition = scrollableInstance.scrollOffset()[that._limitProps.start] + that._scrollSpeed; + const nextScrollPosition = scrollableInstance + .scrollOffset()[this._limitProps.start] + this._scrollSpeed; - scrollableInstance.scrollTo({ [that._limitProps.start]: nextScrollPosition }); + scrollableInstance.scrollTo({ [this._limitProps.start]: nextScrollPosition }); } } else { - const nextScrollPosition = that._$scrollableAtPointer[that._scrollValue]() + that._scrollSpeed; + // @ts-expect-error scrollTop and scrollLeft are declared to return the wrapper + // eslint-disable-next-line @typescript-eslint/restrict-plus-operands + const nextScrollPosition = this._$scrollableAtPointer[this._scrollValue]() + + this._scrollSpeed; - that._$scrollableAtPointer[that._scrollValue](nextScrollPosition); + this._$scrollableAtPointer[this._scrollValue](nextScrollPosition); } - const dragMoveEvent = that._component._dragMoveEvent; + const dragMoveEvent = this._component._dragMoveEvent; if (dragMoveEvent) { - that._component.dragMoveHandler(dragMoveEvent); + this._component.dragMoveHandler(dragMoveEvent); } } } - reset() { + reset(): void { this._$scrollableAtPointer = null; this._scrollSpeed = 0; this._preventScroll = true; } - isOutsideScrollable($scrollable, event) { + isOutsideScrollable($scrollable: dxElementWrapper | undefined, event: DragEvent): boolean { if (!$scrollable) { return false; } @@ -280,10 +430,15 @@ class ScrollHelper { } } +interface ScrollAnimatorOwner { + _horizontalScrollHelper: ScrollHelper; + _verticalScrollHelper: ScrollHelper; +} + class ScrollAnimator extends Animator { - _strategy: any; + _strategy: ScrollAnimatorOwner; - constructor(strategy) { + constructor(strategy: ScrollAnimatorOwner) { super(); this._strategy = strategy; } @@ -297,7 +452,9 @@ class ScrollAnimator extends Animator { } } -class Draggable extends DOMComponent { +class Draggable< + TProperties extends DraggableBaseProperties = DraggableProperties, +> extends DOMComponent, TProperties> { _$sourceElement?: dxElementWrapper | null; _initScrollTop!: number; @@ -323,30 +480,32 @@ class Draggable extends DOMComponent { reset(): void {} // eslint-disable-next-line @typescript-eslint/no-unused-vars - dragMove(e): void {} + dragMove(e: DragEvent): void {} - dragEnter(): void {} + // eslint-disable-next-line @typescript-eslint/no-unused-vars + dragEnter(e?: DragEvent): void {} - dragLeave(): void {} + // eslint-disable-next-line @typescript-eslint/no-unused-vars + dragLeave(e?: DragEvent): void {} - dragEnd(sourceEvent): DeferredObj { + dragEnd(sourceEvent: DragEventArgs): DeferredObj | PromiseLike { const sourceDraggable = this._getSourceDraggable(); + // @ts-expect-error dragEnd passes the event args where the event is expected sourceDraggable._fireRemoveEvent(sourceEvent); return Deferred().resolve(); } // eslint-disable-next-line @typescript-eslint/no-unused-vars - _fireRemoveEvent(sourceEvent?) {} + _fireRemoveEvent(sourceEvent?: DragEvent): void {} - _getDefaultOptions(): DraggableProperties { + _getDefaultOptions(): TProperties { return { ...super._getDefaultOptions(), onDragStart: undefined, onDragMove: undefined, onDragEnd: undefined, - // @ts-expect-error onDragEnter: undefined, onDragLeave: undefined, onDragCancel: undefined, @@ -367,9 +526,8 @@ class Draggable extends DOMComponent { }; } - _setOptionsByReference() { - // @ts-expect-error ts-error - super._setOptionsByReference.apply(this, arguments); + _setOptionsByReference(): void { + super._setOptionsByReference(); extend(this._optionsByReference, { component: true, @@ -379,7 +537,7 @@ class Draggable extends DOMComponent { }); } - _init() { + _init(): void { super._init(); this._attachEventHandlers(); this._scrollAnimator = new ScrollAnimator(this); @@ -391,39 +549,47 @@ class Draggable extends DOMComponent { this._initScrollLeft = 0; } - _normalizeCursorOffset(offset) { + _normalizeCursorOffset(offset: CursorOffset): Offset { + let normalizedOffset: CursorOffset | { h?: number; v?: number } = offset; + if (isObject(offset)) { - offset = { - h: (offset as any).x, - v: (offset as any).y, + normalizedOffset = { + h: offset.x, + v: offset.y, }; } - offset = splitPair(offset).map((value) => parseFloat(value)); + const pair = splitPair(normalizedOffset).map((value) => parseFloat(value)); return { - left: offset[0], - top: offset.length === 1 ? offset[0] : offset[1], + left: pair[0], + top: pair.length === 1 ? pair[0] : pair[1], }; } - _getNormalizedCursorOffset(offset, options) { - if (isFunction(offset)) { - offset = offset.call(this, options); + _getNormalizedCursorOffset( + offset: CursorOffset | CursorOffsetCallback, + options: ElementOffsetOptions, + ): Offset { + let cursorOffset = offset; + + if (isFunction(cursorOffset)) { + cursorOffset = cursorOffset.call(this, options); } - return this._normalizeCursorOffset(offset); + return this._normalizeCursorOffset(cursorOffset); } - _calculateElementOffset(options) { - let elementOffset; - let dragElementOffset; + _calculateElementOffset(options: ElementOffsetOptions): Offset | undefined { + // eslint-disable-next-line @typescript-eslint/init-declarations + let elementOffset: Offset | undefined; const { event } = options; const $element = $(options.itemElement); const $dragElement = $(options.dragElement); const isCloned = this._dragElementIsCloned(); const cursorOffset = this.option('cursorOffset'); let normalizedCursorOffset = { left: 0, top: 0 }; - const currentLocate = this._initialLocate = locate($dragElement); + this._initialLocate = locate($dragElement); + const currentLocate = this._initialLocate; if (isCloned || options.initialOffset || cursorOffset) { elementOffset = options.initialOffset || $element.offset(); @@ -432,23 +598,29 @@ class Draggable extends DOMComponent { normalizedCursorOffset = this._getNormalizedCursorOffset(cursorOffset, options); if (isFinite(normalizedCursorOffset.left)) { + // @ts-expect-error offset can be undefined elementOffset.left = event.pageX; } if (isFinite(normalizedCursorOffset.top)) { + // @ts-expect-error offset can be undefined elementOffset.top = event.pageY; } } - dragElementOffset = $dragElement.offset(); - elementOffset.top -= dragElementOffset.top + (normalizedCursorOffset.top || 0) - currentLocate.top; - elementOffset.left -= dragElementOffset.left + (normalizedCursorOffset.left || 0) - currentLocate.left; + const dragElementOffset = $dragElement.offset(); + // @ts-expect-error offset can be undefined + elementOffset.top -= dragElementOffset.top + + (normalizedCursorOffset.top || 0) - currentLocate.top; + // @ts-expect-error offset can be undefined + elementOffset.left -= dragElementOffset.left + + (normalizedCursorOffset.left || 0) - currentLocate.left; } return elementOffset; } - _initPosition(options) { + _initPosition(options: ElementOffsetOptions): void { const $dragElement = $(options.dragElement); const elementOffset = this._calculateElementOffset(options); @@ -459,34 +631,36 @@ class Draggable extends DOMComponent { this._startPosition = locate($dragElement); } - _startAnimator() { + _startAnimator(): void { if (!this._scrollAnimator.inProgress()) { this._scrollAnimator.start(); } } - _stopAnimator() { + _stopAnimator(): void { this._scrollAnimator?.stop(); } - _addWidgetPrefix(className?) { + _addWidgetPrefix(className?: string): string { const componentName = this.NAME; return dasherize(componentName) + (className ? `-${className}` : ''); } - _getItemsSelector() { - return this.option('filter') || ''; + _getItemsSelector(): string { + const { filter } = this.option(); + + return filter || ''; } - _$content() { + _$content(): dxElementWrapper { const $element = this.$element(); const $wrapper = $element.children('.dx-template-wrapper'); return $wrapper.length ? $wrapper : $element; } - _attachEventHandlers() { + _attachEventHandlers(): void { if (this.option('disabled')) { return; } @@ -497,12 +671,18 @@ class Draggable extends DOMComponent { const data = { direction: this.option('dragDirection'), immediate: this.option('immediate'), - checkDropTarget: ($target, event) => { + checkDropTarget: ( + $target: dxElementWrapper, + event: DragEvent, + ): boolean | string | undefined => { const targetGroup = this.option('group'); - const sourceGroup = this._getSourceDraggable().option('group'); + const sourceGroup: string | undefined = this._getSourceDraggable().option('group'); const $scrollable = this._getScrollable($target); - if (this._verticalScrollHelper.isOutsideScrollable($scrollable, event) || this._horizontalScrollHelper.isOutsideScrollable($scrollable, event)) { + if ( + this._verticalScrollHelper.isOutsideScrollable($scrollable, event) + || this._horizontalScrollHelper.isOutsideScrollable($scrollable, event) + ) { return false; } @@ -515,12 +695,17 @@ class Draggable extends DOMComponent { eventsEngine.on($element, POINTERDOWN_EVENT_NAME, data, this._pointerDownHandler.bind(this)); } - if (itemsSelector[0] === '>') { - // @ts-expect-error ts-error + if (itemsSelector.startsWith('>')) { itemsSelector = itemsSelector.slice(1); } - // @ts-expect-error - eventsEngine.on($element, DRAGSTART_EVENT_NAME, itemsSelector, data, this._dragStartHandler.bind(this)); + eventsEngine.on( + $element, + DRAGSTART_EVENT_NAME, + itemsSelector, + data, + // @ts-expect-error eventsEngine is badly typed + this._dragStartHandler.bind(this), + ); eventsEngine.on($element, DRAG_EVENT_NAME, data, this.dragMoveHandler.bind(this)); eventsEngine.on($element, DRAGEND_EVENT_NAME, data, this._dragEndHandler.bind(this)); eventsEngine.on($element, DRAG_ENTER_EVENT_NAME, data, this._dragEnterHandler.bind(this)); @@ -531,11 +716,11 @@ class Draggable extends DOMComponent { } } - _dragElementIsCloned() { + _dragElementIsCloned(): boolean | undefined { return this._$dragElement?.hasClass(this._addWidgetPrefix(CLONE_CLASS)); } - _getDragTemplateArgs($element, $container) { + _getDragTemplateArgs($element: dxElementWrapper, $container: dxElementWrapper): DragTemplateArgs { return { container: getPublicElement($container), model: { @@ -545,16 +730,15 @@ class Draggable extends DOMComponent { }; } - _createDragElement($element) { + _createDragElement($element: dxElementWrapper): dxElementWrapper { let result = $element; const clone = this.option('clone'); const $container = this._getContainer(); - let template = this.option('dragTemplate'); + const dragTemplate = this.option('dragTemplate'); - if (template) { - template = this._getTemplate(template); + if (dragTemplate) { + const template = this._getTemplate(dragTemplate); result = $('
').appendTo($container); - // @ts-expect-error ts-error template.render(this._getDragTemplateArgs($element, result)); } else if (clone) { result = $('
').appendTo($container); @@ -566,7 +750,7 @@ class Draggable extends DOMComponent { return result .toggleClass(this._addWidgetPrefix(CLONE_CLASS), result.get(0) !== $element.get(0)) - .toggleClass('dx-rtl', this.option('rtlEnabled')); + .toggleClass('dx-rtl', this.option().rtlEnabled); } _resetDragElement(): void { @@ -578,21 +762,22 @@ class Draggable extends DOMComponent { this._$dragElement = null; } - _resetSourceElement() { + _resetSourceElement(): void { this._toggleDragSourceClass(false); this._$sourceElement = null; } - _detachEventHandlers() { + _detachEventHandlers(): void { eventsEngine.off(this._$content(), `.${DRAGGABLE}`); eventsEngine.off(this._getArea(), `.${DRAGGABLE}`); } - _move(position, $element?) { + _move(position: Partial, $element?: dxElementWrapper | null): void { + // @ts-expect-error the drag element can be null move($element || this._$dragElement, position); } - _getDraggableElement(e?): dxElementWrapper { + _getDraggableElement(e?: DragEvent): dxElementWrapper { const $sourceElement = this._getSourceElement(); if ($sourceElement) { @@ -607,8 +792,7 @@ class Draggable extends DOMComponent { let $target = $(e?.target); const itemsSelector = this._getItemsSelector(); - if (itemsSelector[0] === '>') { - // @ts-expect-error ts-error + if (itemsSelector.startsWith('>')) { const $items = this._$content().find(itemsSelector); if (!$items.is($target)) { $target = $target.closest($items); @@ -617,36 +801,38 @@ class Draggable extends DOMComponent { return $target; } - _getSourceElement() { + _getSourceElement(): dxElementWrapper | null | undefined { const draggable = this._getSourceDraggable(); return draggable._$sourceElement; } - _pointerDownHandler(e) { + _pointerDownHandler(e: DragEvent): void { if (needSkipEvent(e)) { return; } - const position: any = {}; + const position: Partial = {}; const $element = this.$element(); const { dragDirection } = this.option(); if (dragDirection === 'horizontal' || dragDirection === 'both') { - // @ts-expect-error ts-error - position.left = e.pageX - $element.offset().left + locate($element).left - getWidth($element) / 2; + // @ts-expect-error offset can be undefined + position.left = e.pageX - $element.offset().left + + locate($element).left - getWidth($element) / 2; } if (dragDirection === 'vertical' || dragDirection === 'both') { - // @ts-expect-error ts-error - position.top = e.pageY - $element.offset().top + locate($element).top - getHeight($element) / 2; + // @ts-expect-error offset can be undefined + position.top = e.pageY - $element.offset().top + + locate($element).top - getHeight($element) / 2; } this._move(position, $element); this._getAction('onDragMove')(this._getEventArgs(e)); } - _isValidElement(event, $element): boolean { + _isValidElement(event: DragEvent, $element: dxElementWrapper): boolean { const { handle } = this.option(); const $target = $(event.originalEvent?.target); @@ -661,7 +847,7 @@ class Draggable extends DOMComponent { return !$element.is('.dx-state-disabled, .dx-state-disabled *'); } - _dragStartHandler(e) { + _dragStartHandler(e: DragEvent): void { const $element = this._getDraggableElement(e); if (!this._isValidElement(e, $element)) { @@ -674,7 +860,6 @@ class Draggable extends DOMComponent { const dragStartArgs = this._getDragStartArgs(e, $element); this._getAction('onDragStart')(dragStartArgs); - // @ts-expect-error ts-error if (dragStartArgs.cancel) { e.cancel = true; return; @@ -690,11 +875,12 @@ class Draggable extends DOMComponent { if (!this._hasClonedDraggable() && this.option('autoScroll')) { this._initScrollTop = this._getScrollableScrollTop(); this._initScrollLeft = this._getScrollableScrollLeft(); - // @ts-expect-error ts-error + // @ts-expect-error offset can be undefined initialOffset = this._getDraggableElementOffset(initialOffset.left, initialOffset.top); } - const $dragElement = this._$dragElement = this._createDragElement($element); + this._$dragElement = this._createDragElement($element); + const $dragElement = this._$dragElement; this._toggleDraggingClass(true); this._toggleDragSourceClass(true); @@ -720,7 +906,9 @@ class Draggable extends DOMComponent { const elementHeight = getHeight($dragElement); const startOffset = { + // @ts-expect-error offset can be undefined left: $dragElement.offset().left - areaOffset.left, + // @ts-expect-error offset can be undefined top: $dragElement.offset().top - areaOffset.top, }; if ($area.length) { @@ -735,26 +923,27 @@ class Draggable extends DOMComponent { } } - _getAreaOffset($area) { - const offset = $area && (positionUtils as any).offset($area); + _getAreaOffset($area: dxElementWrapper): Offset { + const offset = $area && positionUtils.offset($area); return offset || { left: 0, top: 0 }; } - _toggleDraggingClass(value) { + _toggleDraggingClass(value: boolean): void { this._$dragElement?.toggleClass(this._addWidgetPrefix('dragging'), value); } - _toggleDragSourceClass(value, $element?) { + _toggleDragSourceClass(value: boolean, $element?: dxElementWrapper | null): void { const $sourceElement = $element || this._$sourceElement; $sourceElement?.toggleClass(this._addWidgetPrefix('source'), value); } - _setGestureCoverCursor($element) { + _setGestureCoverCursor($element: dxElementWrapper): void { + // @ts-expect-error css value can be undefined $(`.${GESTURE_COVER_CLASS}`).css('cursor', $element.css('cursor')); } - _getBoundOffset() { - let boundOffset = this.option('boundOffset'); + _getBoundOffset(): BoundOffsetQuad { + let { boundOffset } = this.option(); if (isFunction(boundOffset)) { boundOffset = boundOffset.call(this); @@ -764,7 +953,7 @@ class Draggable extends DOMComponent { } _getArea(): dxElementWrapper { - let area = this.option('boundary'); + let { boundary: area } = this.option(); if (isFunction(area)) { area = area.call(this); @@ -789,7 +978,7 @@ class Draggable extends DOMComponent { const scrollTop = this._getScrollableScrollTop(); const scrollLeft = this._getScrollableScrollLeft(); - const elementPosition = ($(this.element()) as any).css('position'); + const elementPosition = $(this.element()).css('position'); const isFixedPosition = elementPosition === 'fixed'; const result: Offset = { @@ -811,8 +1000,10 @@ class Draggable extends DOMComponent { }; } - _hasClonedDraggable() { - return this.option('clone') || this.option('dragTemplate'); + _hasClonedDraggable(): boolean | Properties['dragTemplate'] { + const { clone, dragTemplate } = this.option(); + + return clone || dragTemplate; } public dragMoveHandler(e: DragEvent): void { @@ -834,7 +1025,7 @@ class Draggable extends DOMComponent { return; } - this._getTargetDraggable().dragMove(e, scrollBy); + this._getTargetDraggable().dragMove(e); } // Without an active drag the gesture emitter must not call preventDefault on the @@ -852,24 +1043,31 @@ class Draggable extends DOMComponent { } private _updateScrollable(e: DragEvent): void { - const that = this; - - if (that.option('autoScroll')) { + if (this.option('autoScroll')) { const mousePosition = getMousePosition(e); - const allObjects = (domAdapter as any).elementsFromPoint(mousePosition.x, mousePosition.y, this.$element().get(0)); - - that._verticalScrollHelper.updateScrollable(allObjects, mousePosition); - that._horizontalScrollHelper.updateScrollable(allObjects, mousePosition); + const allObjects = domAdapter.elementsFromPoint( + mousePosition.x, + mousePosition.y, + // @ts-expect-error get is declared to return Element + this.$element().get(0), + ); + + this._verticalScrollHelper.updateScrollable(allObjects, mousePosition); + this._horizontalScrollHelper.updateScrollable(allObjects, mousePosition); } } - _getScrollable($element) { - let $scrollable; + _getScrollable($element: dxElementWrapper): dxElementWrapper | undefined { + // eslint-disable-next-line @typescript-eslint/init-declarations + let $scrollable: dxElementWrapper | undefined; $element.parents().toArray().some((parent) => { const $parent = $(parent); - if (this._horizontalScrollHelper.isScrollable($parent) || this._verticalScrollHelper.isScrollable($parent)) { + if ( + this._horizontalScrollHelper.isScrollable($parent) + || this._verticalScrollHelper.isScrollable($parent) + ) { $scrollable = $parent; return true; @@ -881,35 +1079,37 @@ class Draggable extends DOMComponent { return $scrollable; } - _getScrollableScrollTop() { + _getScrollableScrollTop(): number { + // @ts-expect-error scrollTop is declared to return the wrapper return this._getScrollable($(this.element()))?.scrollTop() ?? 0; } - _getScrollableScrollLeft() { + _getScrollableScrollLeft(): number { + // @ts-expect-error scrollLeft is declared to return the wrapper return this._getScrollable($(this.element()))?.scrollLeft() ?? 0; } - _defaultActionArgs() { - // @ts-expect-error ts-error - const args = super._defaultActionArgs.apply(this, arguments); + _defaultActionArgs(): DefaultActionArgs { + const args = super._defaultActionArgs(); const component = this.option('component'); if (component) { args.component = component; - // @ts-expect-error ts-error + // @ts-expect-error component is unknown args.element = component.element(); } return args; } - _getEventArgs(e): Record { + _getEventArgs(e: DragEvent): DragEventArgs { const sourceDraggable = this._getSourceDraggable(); const targetDraggable = this._getTargetDraggable(); return { event: e, itemData: sourceDraggable.option('itemData'), + // @ts-expect-error the source element can be null itemElement: getPublicElement(sourceDraggable._$sourceElement), fromComponent: sourceDraggable.option('component') || sourceDraggable, toComponent: targetDraggable.option('component') || targetDraggable, @@ -918,7 +1118,7 @@ class Draggable extends DOMComponent { }; } - _getDragStartArgs(e, $itemElement) { + _getDragStartArgs(e: DragEvent, $itemElement: dxElementWrapper): DragStartArgs { const args = this._getEventArgs(e); return { @@ -930,10 +1130,13 @@ class Draggable extends DOMComponent { } _revertItemToInitialPosition(): void { - !this._dragElementIsCloned() && this._move(this._initialLocate, this._$sourceElement); + if (!this._dragElementIsCloned()) { + // @ts-expect-error _initialLocate is set when the drag starts + this._move(this._initialLocate, this._$sourceElement); + } } - _dragEndHandler(e) { + _dragEndHandler(e: DragEvent): void { const d = Deferred(); const dragEndEventArgs = this._getEventArgs(e); const dropEventArgs = this._getEventArgs(e); @@ -973,7 +1176,7 @@ class Draggable extends DOMComponent { } } - _isTargetOverAnotherDraggable(e) { + _isTargetOverAnotherDraggable(e: DragEvent): boolean { const sourceDraggable = this._getSourceDraggable(); if (this === sourceDraggable) { @@ -985,11 +1188,12 @@ class Draggable extends DOMComponent { const $targetDraggableElement = this.$element(); const mousePosition = getMousePosition(e); - const elements = (domAdapter as any).elementsFromPoint(mousePosition.x, mousePosition.y, this.element()); + const elements = domAdapter.elementsFromPoint(mousePosition.x, mousePosition.y, this.element()); const firstWidgetElement = elements.filter((element) => { const $element = $(element); if ($element.hasClass(this._addWidgetPrefix())) { + // @ts-expect-error $dragElement can be null return !$element.closest($dragElement).length; } @@ -998,12 +1202,17 @@ class Draggable extends DOMComponent { const $sourceElement = this._getSourceElement(); const isTargetOverItself = firstWidgetElement === $sourceDraggableElement.get(0); + // @ts-expect-error $sourceElement can be null const isTargetOverNestedDraggable = $(firstWidgetElement).closest($sourceElement).length; - return !firstWidgetElement || firstWidgetElement === $targetDraggableElement.get(0) && !isTargetOverItself && !isTargetOverNestedDraggable; + return !firstWidgetElement || ( + firstWidgetElement === $targetDraggableElement.get(0) + && !isTargetOverItself + && !isTargetOverNestedDraggable + ); } - _dragEnterHandler(e): void { + _dragEnterHandler(e: DragEvent): void { this._fireDragEnterEvent(e); if (this._isTargetOverAnotherDraggable(e)) { @@ -1014,7 +1223,7 @@ class Draggable extends DOMComponent { sourceDraggable.dragEnter(e); } - _dragLeaveHandler(e): void { + _dragLeaveHandler(e: DragEvent): void { this._fireDragLeaveEvent(e); this._resetTargetDraggable(); @@ -1027,13 +1236,13 @@ class Draggable extends DOMComponent { sourceDraggable.dragLeave(e); } - _keydownHandler(e?): void { + _keydownHandler(e: DragEvent): void { if (this.dragInProgress && e.key === 'Escape') { this._keydownEscapeHandler(e); } } - _keydownEscapeHandler(e): void { + _keydownEscapeHandler(e: DragEvent): void { const $sourceElement = this._getSourceElement(); if (!$sourceElement) { return; @@ -1047,7 +1256,7 @@ class Draggable extends DOMComponent { } this.dragInProgress = false; - sourceDraggable?._toggleDraggingClass(false); + activeSourceDraggable?._toggleDraggingClass(false); this._detachEventHandlers(); this._revertItemToInitialPosition(); const targetDraggable = this._getTargetDraggable(); @@ -1055,25 +1264,26 @@ class Draggable extends DOMComponent { this._attachEventHandlers(); } - _getAction(name) { - return this[`_${name}Action`] || this._createActionByOption(name); + _getAction(name: string): ActionFn { + const action: ActionFn | undefined = this[`_${name}Action`]; + + return action || this._createActionByOption(name); } _getAnonymousTemplateName(): string { return ANONYMOUS_TEMPLATE_NAME; } - _initTemplates() { + _initTemplates(): void { if (!this.option('contentTemplate')) return; this._templateManager.addDefaultTemplates({ content: new EmptyTemplate(), }); - // @ts-expect-error ts-error - super._initTemplates.apply(this, arguments); + super._initTemplates(); } - _render() { + _render(): void { super._render(); this.$element().addClass(this._addWidgetPrefix()); @@ -1088,7 +1298,7 @@ class Draggable extends DOMComponent { } } - _optionChanged(args) { + _optionChanged(args: OptionChanged): void { const { name } = args; switch (name) { @@ -1100,6 +1310,7 @@ class Draggable extends DOMComponent { case 'onDragLeave': case 'onDragCancel': case 'onDraggableElementShown': + // @ts-expect-error the action properties are not declared this[`_${name}Action`] = this._createActionByOption(name); break; case 'dragTemplate': @@ -1118,6 +1329,7 @@ class Draggable extends DOMComponent { this._attachEventHandlers(); break; case 'onCancelByEsc': + // @ts-expect-error the event is not passed this._keydownHandler(); break; case 'autoScroll': @@ -1137,12 +1349,12 @@ class Draggable extends DOMComponent { } } - _getTargetDraggable() { - return targetDraggable || this; + _getTargetDraggable(): AnyDraggable { + return activeTargetDraggable || this; } - _getSourceDraggable() { - return sourceDraggable || this; + _getSourceDraggable(): AnyDraggable { + return activeSourceDraggable || this; } _setTargetDraggable(): void { @@ -1150,23 +1362,25 @@ class Draggable extends DOMComponent { const sourceDraggable = this._getSourceDraggable(); if (currentGroup && currentGroup === sourceDraggable.option('group')) { - targetDraggable = this; + // eslint-disable-next-line @typescript-eslint/no-this-alias + activeTargetDraggable = this; } } _setSourceDraggable(): void { - sourceDraggable = this; + // eslint-disable-next-line @typescript-eslint/no-this-alias + activeSourceDraggable = this; } _resetSourceDraggable(): void { - sourceDraggable = null; + activeSourceDraggable = null; } _resetTargetDraggable(): void { - targetDraggable = null; + activeTargetDraggable = null; } - _resetDragOptions(targetDraggable): void { + _resetDragOptions(targetDraggable: AnyDraggable): void { this.reset(); targetDraggable.reset(); this._stopAnimator(); @@ -1190,13 +1404,13 @@ class Draggable extends DOMComponent { this._stopAnimator(); } - _fireDragEnterEvent(sourceEvent): void { + _fireDragEnterEvent(sourceEvent: DragEvent): void { const args = this._getEventArgs(sourceEvent); this._getAction('onDragEnter')(args); } - _fireDragLeaveEvent(sourceEvent): void { + _fireDragLeaveEvent(sourceEvent: DragEvent): void { const args = this._getEventArgs(sourceEvent); this._getAction('onDragLeave')(args); diff --git a/packages/devextreme/js/__internal/m_sortable.ts b/packages/devextreme/js/__internal/m_sortable.ts index 96975c2b8d3a..c16caf8259b0 100644 --- a/packages/devextreme/js/__internal/m_sortable.ts +++ b/packages/devextreme/js/__internal/m_sortable.ts @@ -1,12 +1,12 @@ import type { AnimationConfig } from '@js/common/core/animation'; import { fx } from '@js/common/core/animation'; import { resetPosition } from '@js/common/core/animation/translator'; -import type { ChangedOptionInfo } from '@js/common/core/events'; import eventsEngine from '@js/common/core/events/core/events_engine'; import registerComponent from '@js/core/component_registrator'; import { getPublicElement } from '@js/core/element'; import type { dxElementWrapper } from '@js/core/renderer'; import $ from '@js/core/renderer'; +import type { DeferredObj } from '@js/core/utils/deferred'; import { Deferred } from '@js/core/utils/deferred'; import { extend } from '@js/core/utils/extend'; import { getBoundingRect } from '@js/core/utils/position'; @@ -15,7 +15,10 @@ import { } from '@js/core/utils/size'; import { getWindow } from '@js/core/utils/window'; import type { Properties } from '@js/ui/sortable'; -import type { DraggableProperties } from '@ts/m_draggable'; +import type { OptionChanged } from '@ts/core/widget/types'; +import type { + DragEvent, DragEventArgs, DraggableProperties, DragStartArgs, DragTemplateArgs, +} from '@ts/m_draggable'; import Draggable from '@ts/m_draggable'; import { isDefined } from '../core/utils/type'; @@ -27,35 +30,92 @@ const SORTABLE = 'dxSortable'; const PLACEHOLDER_CLASS = 'placeholder'; const CLONE_CLASS = 'clone'; -const isElementVisible = (itemElement) => $(itemElement).is(':visible'); +interface Position { + left: number; + top: number; +} + +interface Boundary { + left: number; + right: number; + top: number; + bottom: number; +} + +interface ItemPoint { + dropInsideItem: boolean; + index: number; + isValid: boolean; + left: number; + top: number; + width: number; + height: number; + $item: dxElementWrapper; +} + +interface AnimateConfig { + to?: { left?: number; top?: number }; + duration?: number; + easing?: string; +} + +interface SourceScrollableInfo { + element: dxElementWrapper; + scrollLeft: number; + scrollTop: number; +} + +type SortableEventArgs = DragEventArgs & { + fromIndex: number | null; + toIndex: number | null; + dropInsideItem: boolean; + promise?: DeferredObj | PromiseLike; + placeholderElement?: unknown; + dragElement?: unknown; +}; -const animate = (element, config) => { +type SortableDragStartArgs = DragStartArgs & { + fromIndex: number; +}; + +interface OptionChangedToIndexArgs { + value: number | null; + previousValue?: number | null; + fullUpdate?: boolean; +} + +const isElementVisible = (itemElement: Element): boolean => $(itemElement).is(':visible'); + +const animate = (element: HTMLElement | undefined, config: AnimateConfig): void => { if (!element) return; const left = config.to?.left || 0; const top = config.to?.top || 0; element.style.transform = `translate(${left}px,${top}px)`; - element.style.transition = (fx as any).off ? '' : `transform ${config.duration}ms ${config.easing}`; + // @ts-expect-error off is not declared + element.style.transition = fx.off ? '' : `transform ${config.duration}ms ${config.easing}`; }; -const stopAnimation = (element) => { +const stopAnimation = (element: HTMLElement | undefined): void => { if (!element) return; element.style.transform = ''; element.style.transition = ''; }; -function getScrollableBoundary($scrollable) { +function getScrollableBoundary($scrollable: dxElementWrapper): Boundary { const offset = $scrollable.offset(); - const { style } = $scrollable[0]; + const { style } = $scrollable[0] as HTMLElement; const paddingLeft = parseFloat(style.paddingLeft) || 0; const paddingRight = parseFloat(style.paddingRight) || 0; const paddingTop = parseFloat(style.paddingTop) || 0; // use clientWidth, because vertical scrollbar reduces content width - const width = $scrollable[0].clientWidth - (paddingLeft + paddingRight); + const width = ($scrollable[0] as HTMLElement).clientWidth - (paddingLeft + paddingRight); const height = getHeight($scrollable); + // @ts-expect-error offset can be undefined const left = offset.left + paddingLeft; + // @ts-expect-error offset can be undefined const top = offset.top + paddingTop; return { left, @@ -65,6 +125,10 @@ function getScrollableBoundary($scrollable) { }; } export interface SortableProperties extends Omit { + scrollSensitivity: number; + + scrollSpeed: number; + boundary?: DraggableProperties['boundary']; component?: unknown; @@ -73,42 +137,52 @@ export interface SortableProperties extends Omit; - draggableElementSize?: number; + allowDropInsideItem: boolean; + + allowReordering: boolean; + + moveItemOnDrop: boolean; // eslint-disable-next-line @typescript-eslint/no-explicit-any onPlaceholderPrepared?: ((e: any) => void) | null; } -class Sortable extends Draggable { +class Sortable extends Draggable { _$placeholderElement?: dxElementWrapper | null; _$scrollable?: dxElementWrapper; _$modifiedItem?: dxElementWrapper | null; - _sourceScrollableInfo?: any; + _sourceScrollableInfo?: SourceScrollableInfo | null; - _sourceScrollHandler?: (e) => void; + _sourceScrollHandler?: (e: Event) => void; - _modifiedItemMargin?: number; + _modifiedItemMargin?: string; _init(): void { super._init(); @@ -116,7 +190,6 @@ class Sortable extends Draggable { this._sourceScrollableInfo = null; } - // @ts-expect-error sortable events are not substitutable for draggable events _getDefaultOptions(): SortableProperties { return { ...super._getDefaultOptions(), @@ -152,7 +225,7 @@ class Sortable extends Draggable { }; } - reset() { + reset(): void { this.option({ dropInsideItem: false, toIndex: null, @@ -168,22 +241,22 @@ class Sortable extends Draggable { this._$placeholderElement = null; if (!this._isIndicateMode() && this._$modifiedItem) { - // @ts-expect-error ts-error + // @ts-expect-error css value can be undefined this._$modifiedItem.css('marginBottom', this._modifiedItemMargin); this._$modifiedItem = null; } } - _getPrevVisibleItem(items, index?) { + _getPrevVisibleItem(items: Element[], index?: number | null): Element | undefined { return items + // @ts-expect-error slice is declared without null .slice(0, index) .reverse() .filter(isElementVisible)[0]; } - _dragStartHandler(e) { - // @ts-expect-error ts-error - super._dragStartHandler.apply(this, arguments); + _dragStartHandler(e: DragEvent): void { + super._dragStartHandler(e); if (e.cancel === true) { return; @@ -197,12 +270,14 @@ class Sortable extends Draggable { this.option('fromIndexOffset', this.option('offset')); } - _subscribeToSourceScroll(e) { + _subscribeToSourceScroll(e: DragEvent): void { const $scrollable = this._getScrollable($(e.target)); if ($scrollable) { this._sourceScrollableInfo = { element: $scrollable, + // @ts-expect-error scrollLeft is declared to return the wrapper scrollLeft: $scrollable.scrollLeft(), + // @ts-expect-error scrollTop is declared to return the wrapper scrollTop: $scrollable.scrollTop(), }; @@ -211,30 +286,30 @@ class Sortable extends Draggable { } } - _unsubscribeFromSourceScroll() { + _unsubscribeFromSourceScroll(): void { if (this._sourceScrollableInfo) { eventsEngine.off(this._sourceScrollableInfo.element, 'scroll', this._sourceScrollHandler); this._sourceScrollableInfo = null; } } - _handleSourceScroll(e): void { + _handleSourceScroll(e: Event): void { const sourceScrollableInfo = this._sourceScrollableInfo; if (sourceScrollableInfo) { - ['scrollLeft', 'scrollTop'].forEach((scrollProp) => { - if (e.target[scrollProp] !== sourceScrollableInfo[scrollProp]) { - const scrollBy = e.target[scrollProp] - sourceScrollableInfo[scrollProp]; + (['scrollLeft', 'scrollTop'] as const).forEach((scrollProp) => { + const target = e.target as HTMLElement; + if (target[scrollProp] !== sourceScrollableInfo[scrollProp]) { + const scrollBy = target[scrollProp] - sourceScrollableInfo[scrollProp]; this._correctItemPoints(scrollBy); this._movePlaceholder(); - sourceScrollableInfo[scrollProp] = e.target[scrollProp]; + sourceScrollableInfo[scrollProp] = target[scrollProp]; } }); } } - _dragEnterHandler(e): void { - // @ts-expect-error ts-error - super._dragEnterHandler.apply(this, arguments); + _dragEnterHandler(e: DragEvent): void { + super._dragEnterHandler(e); if (this === this._getSourceDraggable()) { return; @@ -247,45 +322,45 @@ class Sortable extends Draggable { if (!this._isIndicateMode()) { const itemPoints = this.option('itemPoints'); - // @ts-expect-error ts-error + // @ts-expect-error itemPoints are set by _updateItemPoints const lastItemPoint = itemPoints[itemPoints.length - 1]; if (lastItemPoint) { const $element = this.$element(); const $sourceElement = this._getSourceElement(); const isVertical = this._isVerticalOrientation(); - const sourceElementSize = isVertical ? getOuterHeight($sourceElement, true) : getOuterWidth($sourceElement, true); + const sourceElementSize = isVertical + ? getOuterHeight($sourceElement, true) + : getOuterWidth($sourceElement, true); const scrollSize = $element.get(0)[isVertical ? 'scrollHeight' : 'scrollWidth']; const scrollPosition = $element.get(0)[isVertical ? 'scrollTop' : 'scrollLeft']; const positionProp = isVertical ? 'top' : 'left'; const lastPointPosition = lastItemPoint[positionProp]; - // @ts-expect-error ts-error + // @ts-expect-error offset can be undefined const elementPosition = $element.offset()[positionProp]; const freeSize = elementPosition + scrollSize - scrollPosition - lastPointPosition; - if (freeSize < sourceElementSize) { - if (isVertical) { - const items = this._getItems(); - const $lastItem = $(this._getPrevVisibleItem(items)); + if (freeSize < sourceElementSize && isVertical) { + const items = this._getItems(); + const $lastItem = $(this._getPrevVisibleItem(items)); - this._$modifiedItem = $lastItem; - this._modifiedItemMargin = ($lastItem.get(0) as any).style.marginBottom; + this._$modifiedItem = $lastItem; + this._modifiedItemMargin = ($lastItem.get(0) as HTMLElement).style.marginBottom; - $lastItem.css('marginBottom', sourceElementSize - freeSize); + $lastItem.css('marginBottom', sourceElementSize - freeSize); - const $sortable = $lastItem.closest('.dx-sortable'); - const sortable: any = $sortable.data('dxScrollable') || $sortable.data('dxScrollView'); + const $sortable = $lastItem.closest('.dx-sortable'); + // @ts-expect-error data is declared without the getter form + const sortable: { update: () => void } | undefined = $sortable.data('dxScrollable') || $sortable.data('dxScrollView'); - sortable?.update(); - } + sortable?.update(); } } } } - _dragLeaveHandler(): void { - // @ts-expect-error ts-error - super._dragLeaveHandler.apply(this, arguments); + _dragLeaveHandler(e: DragEvent): void { + super._dragLeaveHandler(e); if (this !== this._getSourceDraggable()) { this._unsubscribeFromSourceScroll(); @@ -298,14 +373,14 @@ class Sortable extends Draggable { } } - dragLeave() { + dragLeave(): void { if (this !== this._getTargetDraggable()) { this.option('toIndex', this.option('fromIndex')); } } // eslint-disable-next-line @typescript-eslint/no-unused-vars - _allowDrop(event) { + _allowDrop(event?: DragEvent): boolean { const targetDraggable = this._getTargetDraggable(); const $targetDraggable = targetDraggable.$element(); const $scrollable = this._getScrollable($targetDraggable); @@ -316,7 +391,6 @@ class Sortable extends Draggable { } = getScrollableBoundary($scrollable); const toIndex = this.option('toIndex'); const itemPoints = this.option('itemPoints'); - // @ts-expect-error ts-error const itemPoint = itemPoints?.filter((item) => item.index === toIndex)[0]; if (itemPoint && itemPoint.top !== undefined) { @@ -331,20 +405,19 @@ class Sortable extends Draggable { return true; } - dragEnd(sourceEvent) { + dragEnd(sourceEvent: DragEventArgs): DeferredObj | PromiseLike { this._unsubscribeFromSourceScroll(); const $sourceElement = this._getSourceElement(); const sourceDraggable = this._getSourceDraggable(); const isSourceDraggable = sourceDraggable.NAME !== this.NAME; - // @ts-expect-error bad options type - const toIndex: number = this.option('toIndex'); + const toIndex = this.option('toIndex'); const { event } = sourceEvent; const allowDrop = this._allowDrop(event); if (toIndex !== null && toIndex >= 0 && allowDrop) { - let cancelAdd; - let cancelRemove; + let cancelAdd: boolean | undefined = false; + let cancelRemove: boolean | undefined = false; if (sourceDraggable !== this) { cancelAdd = this._fireAddEvent(event); @@ -355,10 +428,13 @@ class Sortable extends Draggable { } if (isSourceDraggable) { + // @ts-expect-error the source element can be null resetPosition($sourceElement); } if (this.option('moveItemOnDrop')) { - !cancelAdd && this._moveItem($sourceElement, toIndex, cancelRemove); + if (!cancelAdd) { + this._moveItem($sourceElement, toIndex, cancelRemove); + } } if (sourceDraggable === this) { @@ -369,7 +445,7 @@ class Sortable extends Draggable { return Deferred().resolve(); } - dragMove(e) { + dragMove(e: DragEvent): void { const itemPoints = this.option('itemPoints'); if (!itemPoints) { @@ -381,12 +457,18 @@ class Sortable extends Draggable { const cursorPosition = isVertical ? e.pageY : e.pageX; const rtlEnabled = this.option('rtlEnabled'); - let itemPoint; - // @ts-expect-error ts-error - for (let i = itemPoints.length - 1; i >= 0; i--) { - const centerPosition = itemPoints[i + 1] && (itemPoints[i][axisName] + itemPoints[i + 1][axisName]) / 2; - - if ((!isVertical && rtlEnabled ? cursorPosition > centerPosition : centerPosition > cursorPosition) || centerPosition === undefined) { + // eslint-disable-next-line @typescript-eslint/init-declarations + let itemPoint: ItemPoint | undefined; + for (let i = itemPoints.length - 1; i >= 0; i -= 1) { + const centerPosition = itemPoints[i + 1] + && (itemPoints[i][axisName] + itemPoints[i + 1][axisName]) / 2; + + if ( + (!isVertical && rtlEnabled + ? cursorPosition > centerPosition + : centerPosition > cursorPosition) + || centerPosition === undefined + ) { itemPoint = itemPoints[i]; } else { break; @@ -402,7 +484,6 @@ class Sortable extends Draggable { } private _isIndicateMode(): boolean { - // @ts-expect-error bad option type return this.option('dropFeedbackMode') === 'indicate' || this.option('allowDropInsideItem'); } @@ -411,36 +492,39 @@ class Sortable extends Draggable { return undefined; } - // @ts-expect-error bad options type - const customCssClass: string = this.option('placeholderClassName'); + const customCssClass = this.option('placeholderClassName'); this._$placeholderElement = $('
') .addClass(this._addWidgetPrefix(PLACEHOLDER_CLASS)) .addClass(customCssClass ?? '') + // @ts-expect-error the drag element can be null .insertBefore(this._getSourceDraggable()._$dragElement); return this._$placeholderElement; } - _getItems() { + _getItems(): Element[] { const itemsSelector = this._getItemsSelector(); return this._$content() - // @ts-expect-error ts-error .find(itemsSelector) .not(`.${this._addWidgetPrefix(PLACEHOLDER_CLASS)}`) .not(`.${this._addWidgetPrefix(CLONE_CLASS)}`) .toArray(); } - _allowReordering() { + _allowReordering(): boolean { const sourceDraggable = this._getSourceDraggable(); const targetDraggable = this._getTargetDraggable(); return sourceDraggable !== targetDraggable || this.option('allowReordering'); } - _isValidPoint(visibleIndex, draggableVisibleIndex, dropInsideItem) { + _isValidPoint( + visibleIndex: number, + draggableVisibleIndex: number, + dropInsideItem?: boolean, + ): boolean { const allowDropInsideItem = this.option('allowDropInsideItem'); const allowReordering = dropInsideItem || this._allowReordering(); @@ -452,27 +536,32 @@ class Sortable extends Draggable { return true; } - return draggableVisibleIndex === -1 || visibleIndex !== draggableVisibleIndex && (dropInsideItem || visibleIndex !== (draggableVisibleIndex + 1)); + return draggableVisibleIndex === -1 || ( + visibleIndex !== draggableVisibleIndex + && (dropInsideItem || visibleIndex !== (draggableVisibleIndex + 1)) + ); } - _getItemPoints() { - const that = this; - let result: any[] = []; - let $item; - let offset; - let itemWidth; - const { rtlEnabled } = that.option(); - const isVertical = that._isVerticalOrientation(); - const itemElements = that._getItems(); + _getItemPoints(): ItemPoint[] { + let result: ItemPoint[] = []; + /* eslint-disable @typescript-eslint/init-declarations */ + let $item: dxElementWrapper | undefined; + let offset: Position | undefined; + let itemWidth: number | undefined; + /* eslint-enable @typescript-eslint/init-declarations */ + const { rtlEnabled } = this.option(); + const isVertical = this._isVerticalOrientation(); + const itemElements = this._getItems(); const visibleItemElements = itemElements.filter(isElementVisible); const visibleItemCount = visibleItemElements.length; const $draggableItem = this._getDraggableElement(); const draggableVisibleIndex = visibleItemElements.indexOf($draggableItem.get(0)); if (visibleItemCount) { - for (let i = 0; i <= visibleItemCount; i++) { - // @ts-expect-error ts-error - const needCorrectLeftPosition = !isVertical && (rtlEnabled ^ (i === visibleItemCount) as any); + for (let i = 0; i <= visibleItemCount; i += 1) { + // @ts-expect-error a boolean is used as a number operand + // eslint-disable-next-line no-bitwise + const needCorrectLeftPosition = !isVertical && (rtlEnabled ^ (i === visibleItemCount)); const needCorrectTopPosition = isVertical && i === visibleItemCount; if (i < visibleItemCount) { @@ -483,22 +572,26 @@ class Sortable extends Draggable { result.push({ dropInsideItem: false, + // @ts-expect-error offset can be undefined left: offset.left + (needCorrectLeftPosition ? itemWidth : 0), + // @ts-expect-error offset can be undefined top: offset.top + (needCorrectTopPosition ? result[i - 1].height : 0), + // @ts-expect-error $item is assigned in the loop index: i === visibleItemCount ? itemElements.length : itemElements.indexOf($item.get(0)), + // @ts-expect-error $item is assigned in the loop $item, width: getOuterWidth($item), height: getOuterHeight($item), - // @ts-expect-error ts-error - isValid: that._isValidPoint(i, draggableVisibleIndex), + isValid: this._isValidPoint(i, draggableVisibleIndex), }); } if (this.option('allowDropInsideItem')) { const points = result; result = []; - for (let i = 0; i < points.length; i++) { + for (let i = 0; i < points.length; i += 1) { result.push(points[i]); + // eslint-disable-next-line max-depth if (points[i + 1]) { result.push(extend({}, points[i], { dropInsideItem: true, @@ -510,6 +603,7 @@ class Sortable extends Draggable { } } } else { + // @ts-expect-error the empty list point has no position and size result.push({ dropInsideItem: false, index: 0, @@ -520,60 +614,60 @@ class Sortable extends Draggable { return result; } - _updateItemPoints(forceUpdate?) { + _updateItemPoints(forceUpdate?: boolean): void { if (forceUpdate || this.option('autoUpdate') || !this.option('itemPoints')) { this.option('itemPoints', this._getItemPoints()); } } - _correctItemPoints(scrollBy) { + _correctItemPoints(scrollBy: number): void { const itemPoints = this.option('itemPoints'); if (scrollBy && itemPoints && !this.option('autoUpdate')) { const isVertical = this._isVerticalOrientation(); const positionPropName = isVertical ? 'top' : 'left'; - // @ts-expect-error ts-error itemPoints.forEach((itemPoint) => { itemPoint[positionPropName] -= scrollBy; }); } } - _getElementIndex($itemElement) { + _getElementIndex($itemElement: dxElementWrapper | null | undefined): number { + // @ts-expect-error the source element can be null return this._getItems().indexOf($itemElement.get(0)); } - _getDragTemplateArgs($element) { - // @ts-expect-error ts-error - const args = super._getDragTemplateArgs.apply(this, arguments); - // @ts-expect-error ts-error + _getDragTemplateArgs($element: dxElementWrapper, $container: dxElementWrapper): DragTemplateArgs { + const args = super._getDragTemplateArgs($element, $container); args.model.fromIndex = this._getElementIndex($element); return args; } - _togglePlaceholder(value): void { + _togglePlaceholder(value: boolean): void { this._$placeholderElement?.toggle(value); } _isVerticalOrientation(): boolean { - // @ts-expect-error ts-error const { itemOrientation } = this.option(); return itemOrientation === 'vertical'; } - _normalizeToIndex(toIndex, skipOffsetting) { + _normalizeToIndex(toIndex: number | null, skipOffsetting?: boolean): number | null { const isAnotherDraggable = this._getSourceDraggable() !== this._getTargetDraggable(); const fromIndex = this._getActualFromIndex(); if (toIndex === null) { return fromIndex; } - // @ts-expect-error ts-error - return Math.max(isAnotherDraggable || fromIndex >= toIndex || skipOffsetting ? toIndex : toIndex - 1, 0); + return Math.max( + // @ts-expect-error fromIndex can be null + isAnotherDraggable || fromIndex >= toIndex || skipOffsetting ? toIndex : toIndex - 1, + 0, + ); } - _updatePlaceholderPosition(e, itemPoint) { + _updatePlaceholderPosition(e: DragEvent, itemPoint: ItemPoint): void { const sourceDraggable = this._getSourceDraggable(); const toIndex = this._normalizeToIndex(itemPoint.index, itemPoint.dropInsideItem); @@ -582,7 +676,9 @@ class Sortable extends Draggable { dropInsideItem: itemPoint.dropInsideItem, }); - itemPoint.isValid && this._getAction('onDragChange')(eventArgs); + if (itemPoint.isValid) { + this._getAction('onDragChange')(eventArgs); + } if (eventArgs.cancel || !itemPoint.isValid) { if (!itemPoint.isValid) { @@ -599,42 +695,45 @@ class Sortable extends Draggable { toIndex: itemPoint.index, }); this._getAction('onPlaceholderPrepared')(extend(this._getEventArgs(e), { - // @ts-expect-error ts-error + // @ts-expect-error the placeholder element can be null placeholderElement: getPublicElement(this._$placeholderElement), + // @ts-expect-error the drag element can be null dragElement: getPublicElement(sourceDraggable._$dragElement), })); this._updateItemPoints(); } - _makeWidthCorrection($item, width) { + _makeWidthCorrection($item: dxElementWrapper, width: number | string): number | string { this._$scrollable = this._getScrollable($item); + let correctedWidth = width; if (this._$scrollable) { const scrollableWidth = getWidth(this._$scrollable); - // @ts-expect-error ts-error + // @ts-expect-error offset can be undefined const overflowLeft = this._$scrollable.offset().left - $item.offset().left; const overflowRight = getOuterWidth($item) - overflowLeft - scrollableWidth; if (overflowLeft > 0) { - width -= overflowLeft; + // @ts-expect-error width is an empty string when it was not measured + correctedWidth -= overflowLeft; } if (overflowRight > 0) { - width -= overflowRight; + // @ts-expect-error width is an empty string when it was not measured + correctedWidth -= overflowRight; } } - return width; + return correctedWidth; } private _updatePlaceholderSizes( $placeholderElement: dxElementWrapper, $itemElement: dxElementWrapper, ): void { - // @ts-expect-error bad options type - const dropInsideItem: boolean = this.option('dropInsideItem'); + const dropInsideItem = this.option('dropInsideItem'); const isVertical = this._isVerticalOrientation(); - let width = ''; - let height = ''; + let width: number | string = ''; + let height: number | string = ''; $placeholderElement.toggleClass(this._addWidgetPrefix('placeholder-inside'), dropInsideItem); @@ -650,55 +749,66 @@ class Sortable extends Draggable { $placeholderElement.css({ width, height }); } - _moveItem($itemElement, index, cancelRemove) { - let $prevTargetItemElement; + _moveItem( + $itemElement: dxElementWrapper | null | undefined, + index: number, + cancelRemove?: boolean, + ): void { + let $item = $itemElement; const $itemElements = this._getItems(); const $targetItemElement = $itemElements[index]; const sourceDraggable = this._getSourceDraggable(); if (cancelRemove) { - $itemElement = $itemElement.clone(); - sourceDraggable._toggleDragSourceClass(false, $itemElement); + // @ts-expect-error the source element can be null + $item = $item.clone(); + sourceDraggable._toggleDragSourceClass(false, $item); } - if (!$targetItemElement) { - $prevTargetItemElement = $itemElements[index - 1]; - } + const $prevTargetItemElement = $targetItemElement ? undefined : $itemElements[index - 1]; - this._moveItemCore($itemElement, $targetItemElement, $prevTargetItemElement); + this._moveItemCore($item, $targetItemElement, $prevTargetItemElement); } - _moveItemCore($targetItem, item, prevItem) { + _moveItemCore( + $targetItem: dxElementWrapper | null | undefined, + item: Element | undefined, + prevItem: Element | undefined, + ): void { if (!item && !prevItem) { + // @ts-expect-error the source element can be null $targetItem.appendTo(this.$element()); } else if (prevItem) { + // @ts-expect-error the source element can be null $targetItem.insertAfter($(prevItem)); } else { + // @ts-expect-error the source element can be null $targetItem.insertBefore($(item)); } } - _getDragStartArgs(e, $itemElement) { - // @ts-expect-error ts-error - return extend(super._getDragStartArgs.apply(this, arguments), { + _getDragStartArgs(e: DragEvent, $itemElement: dxElementWrapper): SortableDragStartArgs { + const args: SortableDragStartArgs = extend(super._getDragStartArgs(e, $itemElement), { fromIndex: this._getElementIndex($itemElement), }); + + return args; } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - _getEventArgs(e) { + _getEventArgs(e: DragEvent): SortableEventArgs { const sourceDraggable = this._getSourceDraggable(); const targetDraggable = this._getTargetDraggable(); const dropInsideItem = targetDraggable.option('dropInsideItem'); - // @ts-expect-error ts-error - return extend(super._getEventArgs.apply(this, arguments), { + const args: SortableEventArgs = extend(super._getEventArgs(e), { fromIndex: sourceDraggable.option('fromIndex'), toIndex: this._normalizeToIndex(targetDraggable.option('toIndex'), dropInsideItem), dropInsideItem, }); + + return args; } - public _optionChanged(args: ChangedOptionInfo): void { + public _optionChanged(args: OptionChanged): void { const { name } = args; switch (name) { @@ -713,16 +823,17 @@ class Sortable extends Draggable { [false, true].forEach((isDragSource) => { const fromIndex = isDragSource ? args.value : args.previousValue; if (fromIndex !== null) { + // @ts-expect-error previousValue can be undefined const $fromElement = $(this._getItems()[fromIndex]); this._toggleDragSourceClass(isDragSource, $fromElement); } }); break; case 'dropInsideItem': - // @ts-expect-error ts-error - this._optionChangedDropInsideItem(args); + this._optionChangedDropInsideItem(); break; case 'toIndex': + // @ts-expect-error the option value is typed as possibly undefined this._optionChangedToIndex(args); break; case 'itemOrientation': @@ -743,22 +854,23 @@ class Sortable extends Draggable { } } - _optionChangedDropInsideItem() { + _optionChangedDropInsideItem(): void { if (this._isIndicateMode() && this._$placeholderElement) { this._movePlaceholder(); } } - _isPositionVisible(position) { + _isPositionVisible(position: Position): boolean { const $element = this.$element(); - let scrollContainer; + // eslint-disable-next-line @typescript-eslint/init-declarations + let scrollContainer: Element | undefined; if ($element.css('overflow') !== 'hidden') { scrollContainer = $element.get(0); } else { - // @ts-expect-error ts-error - $element.parents().each(function () { - if ($(this).css('overflow') !== 'visible') { - scrollContainer = this; + // @ts-expect-error each is declared with a callback that returns boolean + $element.parents().each((_, element) => { + if ($(element).css('overflow') !== 'visible') { + scrollContainer = element; return false; } @@ -773,7 +885,10 @@ class Sortable extends Draggable { const end = isVerticalOrientation ? 'bottom' : 'right'; const pageOffset = isVerticalOrientation ? window.pageYOffset : window.pageXOffset; - if (position[start] < (clientRect[start] + pageOffset) || position[start] > (clientRect[end] + pageOffset)) { + if ( + position[start] < (clientRect[start] + pageOffset) + || position[start] > (clientRect[end] + pageOffset) + ) { return false; } } @@ -781,7 +896,7 @@ class Sortable extends Draggable { return true; } - _optionChangedToIndex(args) { + _optionChangedToIndex(args: OptionChangedToIndexArgs): void { const toIndex = args.value; if (this._isIndicateMode()) { @@ -797,7 +912,7 @@ class Sortable extends Draggable { } } - update() { + update(): void { if (this.option('fromIndex') === null && this.option('toIndex') === null) { return; } @@ -810,9 +925,9 @@ class Sortable extends Draggable { this._optionChangedToIndex({ value: toIndex, fullUpdate: true }); } - _updateDragSourceClass() { + _updateDragSourceClass(): void { const fromIndex = this._getActualFromIndex(); - // @ts-expect-error ts-error + // @ts-expect-error fromIndex can be null const $fromElement = $(this._getItems()[fromIndex]); if ($fromElement.length) { this._$sourceElement = $fromElement; @@ -820,37 +935,36 @@ class Sortable extends Draggable { } } - _makeLeftCorrection(left) { - const that = this; - const $scrollable = that._$scrollable; + _makeLeftCorrection(left: number): number { + const $scrollable = this._$scrollable; + let correctedLeft = left; - if ($scrollable && that._isVerticalOrientation()) { - // @ts-expect-error ts-error - const overflowLeft = $scrollable.offset().left - left; + if ($scrollable && this._isVerticalOrientation()) { + // @ts-expect-error offset can be undefined + const overflowLeft = $scrollable.offset().left - correctedLeft; if (overflowLeft > 0) { - left += overflowLeft; + correctedLeft += overflowLeft; } } - return left; + return correctedLeft; } - _movePlaceholder() { - const that = this; + _movePlaceholder(): void { // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const $placeholderElement = that._$placeholderElement || that._createPlaceholder(); + const $placeholderElement = this._$placeholderElement || this._createPlaceholder(); if (!$placeholderElement) { return; } - const items = that._getItems(); - const toIndex = that.option('toIndex'); - const isVerticalOrientation = that._isVerticalOrientation(); + const items = this._getItems(); + const toIndex = this.option('toIndex'); + const isVerticalOrientation = this._isVerticalOrientation(); const rtlEnabled = this.option('rtlEnabled'); - const dropInsideItem = that.option('dropInsideItem'); - let position: any = null; - // @ts-expect-error ts-error - let itemElement = items[toIndex]; + const dropInsideItem = this.option('dropInsideItem'); + let position: Position | null | undefined = null; + // @ts-expect-error toIndex can be null + let itemElement: Element | undefined = items[toIndex]; if (itemElement) { const $itemElement = $(itemElement); @@ -858,25 +972,29 @@ class Sortable extends Draggable { position = $itemElement.offset(); if (!isVerticalOrientation && rtlEnabled && !dropInsideItem) { + // @ts-expect-error offset can be undefined position.left += getOuterWidth($itemElement, true); } } else { - const prevVisibleItemElement = itemElement = this._getPrevVisibleItem(items, toIndex); + itemElement = this._getPrevVisibleItem(items, toIndex); + const prevVisibleItemElement = itemElement; if (prevVisibleItemElement) { position = $(prevVisibleItemElement).offset(); if (isVerticalOrientation) { + // @ts-expect-error offset can be undefined position.top += getOuterHeight(prevVisibleItemElement, true); } else if (!rtlEnabled) { + // @ts-expect-error offset can be undefined position.left += getOuterWidth(prevVisibleItemElement, true); } } } - that._updatePlaceholderSizes($placeholderElement, $(itemElement)); + this._updatePlaceholderSizes($placeholderElement, $(itemElement)); - if (position && !that._isPositionVisible(position)) { + if (position && !this._isPositionVisible(position)) { position = null; } @@ -884,25 +1002,31 @@ class Sortable extends Draggable { const isLastVerticalPosition = isVerticalOrientation && toIndex === items.length; const outerPlaceholderHeight = getOuterHeight($placeholderElement); - position.left = that._makeLeftCorrection(position.left); + position.left = this._makeLeftCorrection(position.left); position.top = isLastVerticalPosition && position.top >= outerPlaceholderHeight ? position.top - outerPlaceholderHeight : position.top; - that._move(position, $placeholderElement); + this._move(position, $placeholderElement); } $placeholderElement.toggle(!!position); } - _getPositions(items, elementSize, fromIndex, toIndex) { - const positions: any[] = []; + _getPositions( + items: Element[], + elementSize: number, + fromIndex: number | null, + toIndex: number | null, + ): number[] { + const positions: number[] = []; - for (let i = 0; i < items.length; i++) { + for (let i = 0; i < items.length; i += 1) { let position = 0; if (toIndex === null || fromIndex === null) { positions.push(position); + // eslint-disable-next-line no-continue continue; } @@ -929,7 +1053,7 @@ class Sortable extends Draggable { return positions; } - _getDraggableElementSize(isVerticalOrientation) { + _getDraggableElementSize(isVerticalOrientation: boolean): number { const $draggableItem = this._getDraggableElement(); let size = this.option('draggableElementSize'); if (!size) { @@ -945,24 +1069,28 @@ class Sortable extends Draggable { } _getActualFromIndex(): number | null { - // @ts-expect-error ts-error const { fromIndex, fromIndexOffset, offset } = this.option(); return fromIndex == null ? null : fromIndex + fromIndexOffset - offset; } - _moveItems(prevToIndex, toIndex, fullUpdate) { + _moveItems( + prevToIndex: number | null | undefined, + toIndex: number | null, + fullUpdate?: boolean, + ): void { const fromIndex = this._getActualFromIndex(); const isVerticalOrientation = this._isVerticalOrientation(); const positionPropName = isVerticalOrientation ? 'top' : 'left'; const elementSize = this._getDraggableElementSize(isVerticalOrientation); const items = this._getItems(); + // @ts-expect-error prevToIndex is undefined when update() is called const prevPositions = this._getPositions(items, elementSize, fromIndex, prevToIndex); const positions = this._getPositions(items, elementSize, fromIndex, toIndex); const animationConfig = this.option('animation'); const rtlEnabled = this.option('rtlEnabled'); - for (let i = 0; i < items.length; i++) { - const itemElement = items[i]; + for (let i = 0; i < items.length; i += 1) { + const itemElement = items[i] as HTMLElement; const prevPosition = prevPositions[i]; const position = positions[i]; @@ -976,10 +1104,9 @@ class Sortable extends Draggable { } } - _toggleDragSourceClass(value, $element) { + _toggleDragSourceClass(value: boolean, $element?: dxElementWrapper | null): void { const $sourceElement = $element || this._$sourceElement; - // @ts-expect-error ts-error - super._toggleDragSourceClass.apply(this, arguments); + super._toggleDragSourceClass(value, $element); if (!this._isIndicateMode()) { $sourceElement?.toggleClass(this._addWidgetPrefix('source-hidden'), value); } @@ -990,7 +1117,7 @@ class Sortable extends Draggable { super._dispose(); } - _fireAddEvent(sourceEvent) { + _fireAddEvent(sourceEvent: DragEvent): boolean | undefined { const args = this._getEventArgs(sourceEvent); this._getAction('onAdd')(args); @@ -998,7 +1125,7 @@ class Sortable extends Draggable { return args.cancel; } - _fireRemoveEvent(sourceEvent) { + _fireRemoveEvent(sourceEvent: DragEvent): boolean | undefined { const sourceDraggable = this._getSourceDraggable(); const args = this._getEventArgs(sourceEvent); @@ -1007,7 +1134,7 @@ class Sortable extends Draggable { return args.cancel; } - _fireReorderEvent(sourceEvent) { + _fireReorderEvent(sourceEvent: DragEvent): DeferredObj | PromiseLike { const args = this._getEventArgs(sourceEvent); this._getAction('onReorder')(args);