From 5d06cf118985bd00e9471bd7bdd69fd3f0077da9 Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii Date: Wed, 10 Jun 2026 17:20:34 +0200 Subject: [PATCH 01/22] refactor: refactor work_space --- .../__tests__/__mock__/mock_scheduler.ts | 2 +- .../__tests__/workspace.base.test.ts | 2 +- .../js/__internal/scheduler/m_scheduler.ts | 4 +- .../js/__internal/scheduler/utils.ts | 3 +- .../__internal/scheduler/workspaces/agenda.ts | 10 +- .../scheduler/workspaces/timeline.ts | 8 +- .../scheduler/workspaces/virtual_scrolling.ts | 4 +- .../{m_work_space.ts => work_space.ts} | 1418 ++++++++++------- .../work_space_grouped_strategy_horizontal.ts | 2 +- .../work_space_grouped_strategy_vertical.ts | 5 +- .../workspaces/work_space_indicator.ts | 2 +- .../scheduler/workspaces/work_space_month.ts | 7 +- 12 files changed, 906 insertions(+), 561 deletions(-) rename packages/devextreme/js/__internal/scheduler/workspaces/{m_work_space.ts => work_space.ts} (67%) diff --git a/packages/devextreme/js/__internal/scheduler/__tests__/__mock__/mock_scheduler.ts b/packages/devextreme/js/__internal/scheduler/__tests__/__mock__/mock_scheduler.ts index fed1a99c3eb8..eef2332334a9 100644 --- a/packages/devextreme/js/__internal/scheduler/__tests__/__mock__/mock_scheduler.ts +++ b/packages/devextreme/js/__internal/scheduler/__tests__/__mock__/mock_scheduler.ts @@ -2,7 +2,7 @@ import { jest } from '@jest/globals'; import { logger } from '@ts/core/utils/m_console'; import DOMComponent from '@ts/core/widget/dom_component'; -import SchedulerWorkSpace from '../../workspaces/m_work_space'; +import SchedulerWorkSpace from '../../workspaces/work_space'; type ClassRects = Record>; diff --git a/packages/devextreme/js/__internal/scheduler/__tests__/workspace.base.test.ts b/packages/devextreme/js/__internal/scheduler/__tests__/workspace.base.test.ts index 6d4b97c3aadd..6b4b519ae3d7 100644 --- a/packages/devextreme/js/__internal/scheduler/__tests__/workspace.base.test.ts +++ b/packages/devextreme/js/__internal/scheduler/__tests__/workspace.base.test.ts @@ -4,10 +4,10 @@ import { import { logger } from '@ts/core/utils/m_console'; import { getResourceManagerMock } from '../__mock__/resource_manager.mock'; -import type SchedulerWorkSpace from '../workspaces/m_work_space'; import SchedulerTimelineDay from '../workspaces/timeline_day'; import SchedulerTimelineMonth from '../workspaces/timeline_month'; import SchedulerTimelineWeek from '../workspaces/timeline_week'; +import type SchedulerWorkSpace from '../workspaces/work_space'; import SchedulerWorkSpaceDay from '../workspaces/work_space_day'; import SchedulerWorkSpaceMonth from '../workspaces/work_space_month'; import SchedulerWorkSpaceWeek from '../workspaces/work_space_week'; diff --git a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts index 9f6c5c7d607c..5a02a006afb0 100644 --- a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts +++ b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts @@ -171,12 +171,12 @@ class Scheduler extends SchedulerOptionsBaseWidget { private a11yStatus!: dxElementWrapper; - // TODO: used externally in m_appointment_drag_behavior.ts, m_subscribes.ts, workspaces/m_work_space.ts + // TODO: used externally in m_appointment_drag_behavior.ts, m_subscribes.ts, workspaces/work_space.ts _workSpace: any; private header?: SchedulerHeader; - // TODO: used externally in m_appointment_drag_behavior.ts, m_subscribes.ts, workspaces/m_work_space.ts + // TODO: used externally in m_appointment_drag_behavior.ts, m_subscribes.ts, workspaces/work_space.ts _appointments: any; private appointmentDragController!: AppointmentDragController; diff --git a/packages/devextreme/js/__internal/scheduler/utils.ts b/packages/devextreme/js/__internal/scheduler/utils.ts index 65dfddc1be7d..f330c8aef070 100644 --- a/packages/devextreme/js/__internal/scheduler/utils.ts +++ b/packages/devextreme/js/__internal/scheduler/utils.ts @@ -6,9 +6,10 @@ import { getOuterHeight, setHeight, setWidth } from '@js/core/utils/size'; import { APPOINTMENT_SETTINGS_KEY } from './constants'; import type { AppointmentViewModelPlain } from './view_model/types'; -interface RenovationWidget { +export interface RenovationWidget { $element: () => dxElementWrapper; option: (options: Record) => void; + dispose: () => void; } type CreateComponentFn = ( diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/agenda.ts b/packages/devextreme/js/__internal/scheduler/workspaces/agenda.ts index 654066de8ec0..7bbcccd44745 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/agenda.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/agenda.ts @@ -27,7 +27,7 @@ import { VIEWS } from '../utils/options/constants_view'; import { reduceResourcesTree } from '../utils/resource_manager/agenda_group_utils'; import type { GroupNode } from '../utils/resource_manager/types'; import type { ListEntity } from '../view_model/types'; -import WorkSpace, { type WorkspaceOptionsInternal } from './m_work_space'; +import WorkSpace, { type WorkspaceOptionsInternal } from './work_space'; const { tableCreator } = tableCreatorModule; @@ -210,7 +210,9 @@ class SchedulerAgenda extends WorkSpace { } private setGroupHeaderCellsHeight(): void { - const $cells = this.getGroupHeaderCells().filter((_, element) => !element.getAttribute('rowSpan')); + const $cells = $( + this.getGroupHeaderCells().toArray().filter((element) => !element.getAttribute('rowSpan')), + ); const rows = this.removeEmptyRows(this.rows); if (!rows.length) { @@ -331,7 +333,7 @@ class SchedulerAgenda extends WorkSpace { this.$dateTableScrollableContent.prepend(this.$groupTable); } - this.$dateTableScrollableContent.append(this.$timePanel, this.$dateTableContainer); + this.$dateTableScrollableContent.append([this.$timePanel, this.$dateTableContainer]); this.$element().append(this.$dateTableScrollable.$element()); } @@ -513,7 +515,7 @@ class SchedulerAgenda extends WorkSpace { } updateScrollPosition(date: Date): void { - const newDate = this.timeZoneCalculator.createDate(date, 'toGrid'); + const newDate = this.timeZoneCalculator?.createDate(date, 'toGrid') ?? date; if (this.needUpdateScrollPosition(newDate)) { this.scrollTo(newDate); diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/timeline.ts b/packages/devextreme/js/__internal/scheduler/workspaces/timeline.ts index 98710b1d3f64..ef6c6e700d65 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/timeline.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/timeline.ts @@ -21,8 +21,8 @@ import tableCreatorModule, { type GroupRows } from '../table_creator'; import type { ResourceLoader } from '../utils/loader/resource_loader'; import { getFirstVisibleDate } from '../utils/skipped_days'; import timezoneUtils from '../utils_time_zone'; -import type { WorkspaceOptionsInternal } from './m_work_space'; import type { ViewDataProviderOptions } from './view_model/types'; +import type { WorkspaceOptionsInternal } from './work_space'; import SchedulerWorkSpace from './work_space_indicator'; const { tableCreator } = tableCreatorModule; @@ -104,7 +104,7 @@ class SchedulerTimeline extends SchedulerWorkSpace { protected override getGroupHeaderContainer(): dxElementWrapper { if (this.isHorizontalGroupedWorkSpace()) { - return this.$thead as dxElementWrapper; + return this.$thead; } return this.$sidebarTable; } @@ -227,13 +227,13 @@ class SchedulerTimeline extends SchedulerWorkSpace { cellCoordinates: { rowIndex: number; columnIndex: number }, groupIndex: number, ): dxElementWrapper { - const indexes = this.groupedStrategy.prepareCellIndexes(cellCoordinates, groupIndex); + const indexes = this.groupedStrategy.prepareCellIndexes(cellCoordinates, groupIndex, false); return this.$dateTable .find('tr') .eq(indexes.rowIndex) .find('td') - .eq(indexes.columnIndex) as dxElementWrapper; + .eq(indexes.columnIndex); } protected override getWorkSpaceWidth(): number { diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/virtual_scrolling.ts b/packages/devextreme/js/__internal/scheduler/workspaces/virtual_scrolling.ts index 7e6bfc895432..485d506496e5 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/virtual_scrolling.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/virtual_scrolling.ts @@ -8,7 +8,7 @@ import { getWindow } from '@js/core/utils/window'; import type { ScrollOffset } from '@ts/core/utils/scroll'; import type { CellPositionData, ViewCellData } from '../types'; -import type SchedulerWorkSpace from './m_work_space'; +import type SchedulerWorkSpace from './work_space'; const DEFAULT_CELL_HEIGHT = 50; const MIN_CELL_WIDTH = 1; @@ -805,7 +805,7 @@ export class VirtualScrollingRenderer { this.renderAppointments(); } - // TODO: make private once external usage in m_work_space.ts is removed + // TODO: make private once external usage in work_space.ts is removed _renderGrid(): void { this.workspace.renderWorkSpace({ generateNewData: false, diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts similarity index 67% rename from packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts rename to packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts index c551c0b1c2a0..9d50392edab3 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts @@ -12,7 +12,7 @@ import pointerEvents from '@js/common/core/events/pointer'; import { addNamespace, isMouseEvent } from '@js/common/core/events/utils/index'; import domAdapter from '@js/core/dom_adapter'; import { getPublicElement } from '@js/core/element'; -import type { dxElementWrapper } from '@js/core/renderer'; +import type { Coordinates, dxElementWrapper } from '@js/core/renderer'; import $ from '@js/core/renderer'; import type { TemplateBase } from '@js/core/templates/template_base'; import { noop } from '@js/core/utils/common'; @@ -31,12 +31,20 @@ import { import { isDefined } from '@js/core/utils/type'; import { getWindow, hasWindow } from '@js/core/utils/window'; import type { - AllDayPanelMode, CellClickEvent, CellContextMenuEvent, ScrollMode, + AllDayPanelMode, + AppointmentDraggingEndEvent, + AppointmentDraggingRemoveEvent, + AppointmentDraggingStartEvent, + CellClickEvent, + CellContextMenuEvent, + ScrollMode, } from '@js/ui/scheduler'; import type { ScrollEvent } from '@js/ui/scroll_view'; import errors from '@js/ui/widget/ui.errors'; import Widget from '@js/ui/widget/ui.widget'; -import { getMemoizeScrollTo } from '@ts/core/utils/scroll'; +import type { TranslateVector } from '@ts/common/core/animation/translator'; +import { getMemoizeScrollTo, type ScrollToFunc } from '@ts/core/utils/scroll'; +import type { ActionConfig } from '@ts/core/widget/component'; import type { OptionChanged } from '@ts/core/widget/types'; import { AllDayPanelTitleComponent, @@ -75,9 +83,17 @@ import { Cache } from '../global_cache'; import AppointmentDragBehavior from '../m_appointment_drag_behavior'; import { CompactAppointmentsHelper } from '../m_compact_appointments_helper'; import type { SubscribeKey, SubscribeMethods } from '../m_subscribes'; +import type HorizontalCurrentTimeShader from '../shaders/current_time_shader_horizontal'; import VerticalShader from '../shaders/current_time_shader_vertical'; import tableCreatorModule, { type GroupRows } from '../table_creator'; -import type { ViewCellData } from '../types'; +import type { + CellPositionData, + CellRect, + DOMMetaData, + GroupBoundsOffset, + ViewCellData, +} from '../types'; +import type { RenovationWidget } from '../utils'; import { utils } from '../utils'; import type { ResourceLoader } from '../utils/loader/resource_loader'; import { @@ -88,7 +104,8 @@ import { getLeafGroupValues } from '../utils/resource_manager/group_utils'; import type { ResourceManager } from '../utils/resource_manager/resource_manager'; import type { GroupValues, RawGroupValues } from '../utils/resource_manager/types'; import { getSkippedDaysCount as countSkippedDays } from '../utils/skipped_days'; -import type { ListEntity } from '../view_model/types'; +import type { CollectorCSS, RealSize } from '../view_model/generate_view_model/steps/add_geometry/types'; +import type { AppointmentViewModelPlain, ListEntity, PanelName } from '../view_model/types'; import { CellsSelectionController } from './cells_selection_controller'; import CellsSelectionState from './cells_selection_state'; import { @@ -100,7 +117,7 @@ import { } from './helpers/position_helper'; import type { ViewDataProviderOptions } from './view_model/types'; import ViewDataProvider from './view_model/view_data_provider'; -import { VirtualScrollingDispatcher, VirtualScrollingRenderer } from './virtual_scrolling'; +import { VirtualScrollingDispatcher, type VirtualScrollingDispatcherOptions, VirtualScrollingRenderer } from './virtual_scrolling'; import HorizontalGroupedStrategy from './work_space_grouped_strategy_horizontal'; import VerticalGroupedStrategy from './work_space_grouped_strategy_vertical'; @@ -129,6 +146,26 @@ export interface ViewDateGenerationOptions { viewType: ViewType; } +interface ScrollSync { + dateTable: ScrollToFunc; + header: ScrollToFunc; + sidebar: ScrollToFunc; +} + +interface NormalizedCellData { + startDate: Date; + endDate: Date; + startDateUTC: Date | false | undefined; + endDateUTC: Date | false | undefined; + groups: ViewCellData['groups']; + groupIndex: ViewCellData['groupIndex']; + allDay: ViewCellData['allDay']; + index?: ViewCellData['index']; + isFirstGroupCell?: ViewCellData['isFirstGroupCell']; + isLastGroupCell?: ViewCellData['isLastGroupCell']; + key?: ViewCellData['key']; +} + const { tableCreator } = tableCreatorModule; // The constant is needed so that the dragging is not sharp. To prevent small twitches @@ -212,6 +249,21 @@ const DEFAULT_WORKSPACE_RENDER_OPTIONS: RenderRWorkspaceOptions = { generateNewData: true, }; +interface SelectedCellsEventArgs { + selectedCellData: ViewCellData[]; +} + +interface WorkspaceOptionActionMap { + onSelectionChanged: SelectedCellsEventArgs; + onSelectionEnd: SelectedCellsEventArgs; + onCellClick: Pick; + onCellContextMenu: Pick; +} + +type WorkspaceCoordinates = Coordinates & { groupIndex?: number }; + +type DroppableCellData = Pick; + export interface WorkspaceOptionsInternal { newAppointments: boolean; resources: ResourceLoader[]; @@ -237,8 +289,8 @@ export interface WorkspaceOptionsInternal { dateCellTemplate: TemplateBase | null; allowMultipleCellSelection: boolean; selectedCellData: ViewCellData[]; - onSelectionChanged: ((args: { selectedCellData: ViewCellData[] }) => void); - onSelectionEnd: ((args: { selectedCellData: ViewCellData[] }) => void); + onSelectionChanged: ((args: SelectedCellsEventArgs) => void); + onSelectionEnd: ((args: SelectedCellsEventArgs) => void); groupByDate: boolean; skippedDays: number[]; scrolling: { @@ -278,121 +330,131 @@ export interface WorkspaceOptionsInternal { } class SchedulerWorkSpace extends Widget { - private viewDataProviderValue: any; + private viewDataProviderValue: ViewDataProvider | null = null; - private cacheValue: any; + private cacheValue: Cache | null = null; - private cellsSelectionStateValue: any; + private cellsSelectionStateValue: CellsSelectionState | null = null; - private cellsSelectionControllerValue: any; + private cellsSelectionControllerValue: CellsSelectionController | null = null; protected $dateTableScrollable!: Scrollable; - private selectionChangedAction: any; + private selectionChangedAction: + | ((args: WorkspaceOptionActionMap['onSelectionChanged']) => void) + | undefined; - private selectionEndAction: any; + private selectionEndAction: + | ((args: WorkspaceOptionActionMap['onSelectionEnd']) => void) + | undefined; private isSelectionStartedOnCell = false; private documentPointerUpHandler: (() => void) | undefined; - private isCellClick: any; + private isCellClick?: boolean; - private contextMenuHandled: any; + private contextMenuHandled?: boolean; - _disposed: any; + _disposed: boolean | undefined; protected getToday?(): Date; - protected $allDayPanel: any; + protected $allDayPanel!: dxElementWrapper; - private $allDayTitle: any; + private $allDayTitle!: dxElementWrapper; - private $headerPanelEmptyCell: any; + private $headerPanelEmptyCell!: dxElementWrapper; - protected groupedStrategy: any; + protected groupedStrategy!: HorizontalGroupedStrategy | VerticalGroupedStrategy; - public virtualScrollingDispatcher: any; + public virtualScrollingDispatcher!: VirtualScrollingDispatcher; - private scrollSync: any; + private scrollSync: Partial = {}; - private $headerPanel: any; + private $headerPanel!: dxElementWrapper; - protected $dateTable: any; + protected $dateTable!: dxElementWrapper; - private $allDayTable: any; + private $allDayTable: dxElementWrapper | undefined; - private renderer: any; + private renderer!: VirtualScrollingRenderer; - _createAction: any; + _createAction!: (fn: (e: { event: Event }) => void) => (event?: unknown) => void; - private cellClickAction: any; + private cellClickAction: + | ((args: WorkspaceOptionActionMap['onCellClick']) => void) + | undefined; - _createActionByOption: any; + _createActionByOption!: ( + name: keyof WorkspaceOptionsInternal, + options?: ActionConfig, + ) => (event?: unknown) => void; - private showPopup: any; + private showPopup?: boolean; - private readonly NAME: any; + private readonly NAME!: string; - private contextMenuAction: any; + private contextMenuAction: + | ((args: WorkspaceOptionActionMap['onCellContextMenu']) => void) + | undefined; protected $groupTable: dxElementWrapper | null | undefined; - protected $thead: any; + protected $thead!: dxElementWrapper; - private headerScrollable: any; + private headerScrollable!: Scrollable; - protected $sidebarScrollable: any; + protected $sidebarScrollable!: Scrollable; - private preventDefaultDragging: any; + private preventDefaultDragging = false; - public dragBehavior: any; + public dragBehavior: AppointmentDragBehavior | null = null; - protected $dateTableContainer: any; + protected $dateTableContainer!: dxElementWrapper; - protected $timePanel: any; + protected $timePanel!: dxElementWrapper; positionHelper!: PositionHelper; - protected $headerPanelContainer: any; + protected $headerPanelContainer!: dxElementWrapper; - private $headerTablesContainer: any; + private $headerTablesContainer!: dxElementWrapper; - private $fixedContainer: any; + private $fixedContainer!: dxElementWrapper; - private $allDayContainer: any; + private $allDayContainer!: dxElementWrapper; - protected $dateTableScrollableContent: any; + protected $dateTableScrollableContent!: dxElementWrapper; - private $sidebarScrollableContent: any; + private $sidebarScrollableContent!: dxElementWrapper; - private allDayTitles!: any[]; + private allDayTitles!: dxElementWrapper[]; - private allDayTables!: any[]; + private allDayTables!: dxElementWrapper[]; - private allDayPanels!: any[]; + private allDayPanels!: dxElementWrapper[]; - protected $flexContainer: any; + protected $flexContainer!: dxElementWrapper; - protected shader: any; + protected shader!: VerticalShader | HorizontalCurrentTimeShader; - protected $sidebarTable: any; + protected $sidebarTable: dxElementWrapper | undefined; - private interval: any; + private interval?: ReturnType; - private renovatedAllDayPanel: any; + private renovatedAllDayPanel: RenovationWidget | undefined; - public renovatedDateTable: any; + public renovatedDateTable: RenovationWidget | undefined; - private renovatedTimePanel: any; + private renovatedTimePanel: RenovationWidget | undefined; - private renovatedGroupPanel: any; + private renovatedGroupPanel: RenovationWidget | undefined; - public renovatedHeaderPanel: any; + public renovatedHeaderPanel: RenovationWidget | undefined; readonly viewDirection: 'vertical' | 'horizontal' = 'vertical'; - // eslint-disable-next-line class-methods-use-this protected _activeStateUnit(): string { return CELL_SELECTOR; } @@ -403,17 +465,12 @@ class SchedulerWorkSpace extends Widget { } get viewDataProvider(): ViewDataProvider { - if (!this.viewDataProviderValue) { - this.viewDataProviderValue = new ViewDataProvider(this.type as ViewType); - } + this.viewDataProviderValue ??= new ViewDataProvider(this.type as ViewType); return this.viewDataProviderValue; } - get cache() { - if (!this.cacheValue) { - this.cacheValue = new Cache(); - } - + get cache(): Cache { + this.cacheValue ??= new Cache(); return this.cacheValue; } @@ -421,11 +478,12 @@ class SchedulerWorkSpace extends Widget { return this.option('getResourceManager')(); } - get cellsSelectionState() { - if (!this.cellsSelectionStateValue) { - this.cellsSelectionStateValue = new CellsSelectionState(this.viewDataProvider); + get cellsSelectionState(): CellsSelectionState { + const isFirstInit = this.cellsSelectionStateValue == null; + this.cellsSelectionStateValue ??= new CellsSelectionState(this.viewDataProvider); - const selectedCellsOption: any = this.option('selectedCellData'); + if (isFirstInit) { + const selectedCellsOption = this.option('selectedCellData'); if (selectedCellsOption?.length > 0) { const validSelectedCells = selectedCellsOption.map((selectedCell) => { @@ -453,27 +511,24 @@ class SchedulerWorkSpace extends Widget { return this.cellsSelectionStateValue; } - get cellsSelectionController() { - if (!this.cellsSelectionControllerValue) { - this.cellsSelectionControllerValue = new CellsSelectionController(); - } - + get cellsSelectionController(): CellsSelectionController { + this.cellsSelectionControllerValue ??= new CellsSelectionController(); return this.cellsSelectionControllerValue; } - get isAllDayPanelVisible() { + get isAllDayPanelVisible(): boolean { return this.isShowAllDayPanel() && this.supportAllDayRow(); } - get verticalGroupTableClass() { return WORKSPACE_VERTICAL_GROUP_TABLE_CLASS; } + get verticalGroupTableClass(): string { return WORKSPACE_VERTICAL_GROUP_TABLE_CLASS; } - get renovatedHeaderPanelComponent() { return HeaderPanelComponent; } + get renovatedHeaderPanelComponent(): typeof HeaderPanelComponent { return HeaderPanelComponent; } - get timeZoneCalculator(): any { + get timeZoneCalculator(): TimeZoneCalculator | undefined { return this.option('timeZoneCalculator'); } - get isDefaultDraggingMode() { + get isDefaultDraggingMode(): boolean { return this.option('draggingMode') === 'default'; } @@ -488,32 +543,47 @@ class SchedulerWorkSpace extends Widget { funcName: Subject, ...args: Parameters ): ReturnType | undefined { - const notifyScheduler = this.option('notifyScheduler') as NotifyScheduler | undefined; + const notifyScheduler = this.option('notifyScheduler'); if (!notifyScheduler) { return undefined; } + // notifyScheduler.invoke delegates to scheduler.fire which is loosely typed + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return notifyScheduler.invoke(funcName, ...args); } - _supportedKeys() { - const clickHandler = function (e) { + _supportedKeys(): Record void> { + const clickHandler = function clickHandler( + this: SchedulerWorkSpace, + e: { preventDefault: () => void; stopPropagation: () => void; target: unknown }, + ): void { e.preventDefault(); e.stopPropagation(); const selectedCells = this.getSelectedCellsData(); if (selectedCells?.length) { - const selectedCellsElement = selectedCells.map((cellData) => this.getCellByData(cellData)).filter((cell) => Boolean(cell)); + const selectedCellsElement = selectedCells + .map((cellData) => this.getCellByData(cellData)) + .filter((cell): cell is dxElementWrapper => Boolean(cell)); e.target = selectedCellsElement; this.showPopup = true; - this.cellClickAction({ event: e, cellElement: $(selectedCellsElement), cellData: selectedCells[0] }); + const cellElements = selectedCellsElement.map(($cell) => $cell.get(0)); + + this.cellClickAction?.( + { + event: e as CellClickEvent['event'], + cellElement: getPublicElement($(cellElements)), + cellData: selectedCells[0], + }, + ); } }; - const onArrowPressed = (e, key) => { + const onArrowPressed = (e: KeyboardEvent, key: 'up' | 'down' | 'left' | 'right'): void => { e.preventDefault(); e.stopPropagation(); @@ -529,9 +599,13 @@ class SchedulerWorkSpace extends Widget { const isHorizontalGrouping = this.isHorizontalGroupedWorkSpace(); const focusedCellPosition = this.viewDataProvider.findCellPositionInMap(focusedCellData); + const isAllDayPanelCellBool = Boolean(isAllDayPanelCell); const edgeIndices = isHorizontalGrouping && isMultiSelection && !isGroupedByDate - ? this.viewDataProvider.getGroupEdgeIndices(focusedCellData.groupIndex, isAllDayPanelCell) - : this.viewDataProvider.getViewEdgeIndices(isAllDayPanelCell); + ? this.viewDataProvider.getGroupEdgeIndices( + focusedCellData.groupIndex ?? 0, + isAllDayPanelCellBool, + ) + : this.viewDataProvider.getViewEdgeIndices(isAllDayPanelCellBool); const nextCellData = this.cellsSelectionController.handleArrowClick({ focusedCellPosition, @@ -539,24 +613,24 @@ class SchedulerWorkSpace extends Widget { isRTL, isGroupedByDate, groupCount, - isMultiSelection, + isMultiSelection: Boolean(isMultiSelection), isMultiSelectionAllowed, - viewType: this.type, + viewType: this.type as ViewType, key, getCellDataByPosition: this.viewDataProvider.getCellData.bind(this.viewDataProvider), - isAllDayPanelCell, + isAllDayPanelCell: isAllDayPanelCellBool, focusedCellData, }); this.processNextSelectedCell( nextCellData, focusedCellData, - isMultiSelection && isMultiSelectionAllowed, + Boolean(isMultiSelection) && isMultiSelectionAllowed, ); } }; - // @ts-expect-error + // @ts-expect-error Widget protected methods are not in d.ts return extend(super._supportedKeys(), { enter: clickHandler, space: clickHandler, @@ -572,20 +646,24 @@ class SchedulerWorkSpace extends Widget { leftArrow: (e) => { onArrowPressed(e, 'left'); }, - }); + }) as Record void>; } private isRTL(): boolean { return this.option('rtlEnabled'); } - private moveToCell($cell, isMultiSelection) { + private moveToCell($cell: dxElementWrapper, isMultiSelection: boolean): void { if (!isDefined($cell) || !$cell.length) { return; } const isMultiSelectionAllowed = this.option('allowMultipleCellSelection'); const currentCellData = this.getFullCellData($cell); + if (!currentCellData) { + return; + } + const focusedCell = this.cellsSelectionState.getFocusedCell(); if (!focusedCell) { @@ -609,7 +687,11 @@ class SchedulerWorkSpace extends Widget { ); } - private processNextSelectedCell(nextCellData, focusedCellData, isMultiSelection) { + private processNextSelectedCell( + nextCellData: ViewCellData, + focusedCellData: ViewCellData, + isMultiSelection: boolean, + ): void { const nextCellPosition = this.viewDataProvider.findCellPositionInMap(nextCellData); if (!nextCellPosition) { @@ -622,13 +704,23 @@ class SchedulerWorkSpace extends Widget { : this.domGetDateCell(nextCellPosition); const isNextCellAllDay = nextCellData.allDay; - this.setSelectedCellsStateAndUpdateSelection(isNextCellAllDay, nextCellPosition, isMultiSelection, $cell); + this.setSelectedCellsStateAndUpdateSelection( + Boolean(isNextCellAllDay), + nextCellPosition, + isMultiSelection, + $cell, + ); this.$dateTableScrollable.scrollToElement($cell); } } - private setSelectedCellsStateAndUpdateSelection(isAllDay, cellPosition, isMultiSelection, $nextFocusedCell) { + private setSelectedCellsStateAndUpdateSelection( + isAllDay: boolean, + cellPosition: CellPositionData, + isMultiSelection: boolean, + $nextFocusedCell: dxElementWrapper, + ): void { const nextCellCoordinates = { rowIndex: cellPosition.rowIndex, columnIndex: cellPosition.columnIndex, @@ -651,12 +743,12 @@ class SchedulerWorkSpace extends Widget { this.updateSelectedCellDataOption(this.getSelectedCellsData(), $nextFocusedCell); } - private hasAllDayClass($cell) { + private hasAllDayClass($cell: dxElementWrapper): boolean { return $cell.hasClass(ALL_DAY_TABLE_CELL_CLASS); } - _focusInHandler(e) { - const $target = $(e.target); + _focusInHandler(e: FocusEvent): void { + const $target = $(e.target as Element | null); const $focusTarget = this._focusTarget(); // T1312256: On macOS, e.target can be a child element of the workspace root const isTargetInsideWorkspace = $target.is($focusTarget) @@ -665,8 +757,8 @@ class SchedulerWorkSpace extends Widget { if (isTargetInsideWorkspace && this.isCellClick) { delete this.isCellClick; delete this.contextMenuHandled; - // @ts-expect-error - super._focusInHandler.apply(this, arguments); + // @ts-expect-error Widget protected methods are not in d.ts + super._focusInHandler(e); this.cellsSelectionState.restoreSelectedAndFocusedCells(); @@ -689,9 +781,9 @@ class SchedulerWorkSpace extends Widget { } } - _focusOutHandler() { - // @ts-expect-error - super._focusOutHandler.apply(this, arguments); + _focusOutHandler(e: FocusEvent): void { + // @ts-expect-error Widget protected methods are not in d.ts + super._focusOutHandler(e); if (!this.contextMenuHandled && !this._disposed) { this.cellsSelectionState.releaseSelectedAndFocusedCells(); @@ -701,33 +793,33 @@ class SchedulerWorkSpace extends Widget { } } - _focusTarget() { + _focusTarget(): dxElementWrapper { return this.$element(); } - protected isVerticalGroupedWorkSpace() { // TODO move to the Model + protected isVerticalGroupedWorkSpace(): boolean { // TODO move to the Model return Boolean(this.option('groups')?.length) && this.option('groupOrientation') === 'vertical'; } - protected isHorizontalGroupedWorkSpace() { + protected isHorizontalGroupedWorkSpace(): boolean { return Boolean(this.option('groups')?.length) && this.option('groupOrientation') === 'horizontal'; } - protected isWorkSpaceWithCount() { + protected isWorkSpaceWithCount(): boolean { return this.option('intervalCount') > 1; } - private isWorkspaceWithOddCells() { + private isWorkspaceWithOddCells(): boolean { return this.option('hoursInterval') === 0.5 && !this.isVirtualScrolling(); } - private getRealGroupOrientation() { + private getRealGroupOrientation(): 'vertical' | 'horizontal' { return this.isVerticalGroupedWorkSpace() ? 'vertical' : 'horizontal'; } - createRAllDayPanelElements() { + createRAllDayPanelElements(): void { this.$allDayPanel = $('
').addClass(ALL_DAY_PANEL_CLASS); this.$allDayTitle = $('
').appendTo(this.$headerPanelEmptyCell); } @@ -738,7 +830,9 @@ class SchedulerWorkSpace extends Widget { bounceEnabled: false, updateManually: true, onScroll: () => { - this.groupedStrategy.cache?.clear(); + if (this.groupedStrategy instanceof VerticalGroupedStrategy) { + this.groupedStrategy.cache.clear(); + } }, // TODO (Scrollable:useKeyboard) -> remove this WA // after ScrollView private option "useKeyboard" will be extended to useNative: true @@ -770,7 +864,7 @@ class SchedulerWorkSpace extends Widget { const currentOnScroll = config.onScroll; config = { ...config, - onScroll: (e: ScrollEvent) => { + onScroll: (e: ScrollEvent): void => { currentOnScroll?.(e); this.virtualScrollingDispatcher.handleOnScrollEvent(e?.scrollOffset); @@ -786,22 +880,22 @@ class SchedulerWorkSpace extends Widget { ): Pick { return { direction: 'both', - onScroll: (event: ScrollEvent) => { + onScroll: (event: ScrollEvent): void => { onScroll?.(event); const top = event.scrollOffset?.top; const left = event.scrollOffset?.left; if (top !== undefined) { - this.scrollSync.sidebar({ top }); + this.scrollSync.sidebar?.({ top }); } if (left !== undefined) { - this.scrollSync.header({ left }); + this.scrollSync.header?.({ left }); } }, - onEnd: () => { - (this.option('onScrollEnd') as any)(); + onEnd: (): void => { + this.option('onScrollEnd')(); }, }; } @@ -814,13 +908,13 @@ class SchedulerWorkSpace extends Widget { useNative: false, updateManually: true, bounceEnabled: false, - onScroll: (event: ScrollEvent) => { - this.scrollSync.dateTable({ left: event.scrollOffset.left }); + onScroll: (event: ScrollEvent): void => { + this.scrollSync.dateTable?.({ left: event.scrollOffset.left }); }, }; } - _visibilityChanged(visible) { + _visibilityChanged(visible: boolean): void { this.cache.clear(); if (visible) { @@ -832,7 +926,7 @@ class SchedulerWorkSpace extends Widget { } } - protected setTableSizes() { + protected setTableSizes(): void { this.cache.clear(); this.attachTableClasses(); @@ -866,11 +960,11 @@ class SchedulerWorkSpace extends Widget { this.updateScrollable(); } - getWorkSpaceMinWidth() { + getWorkSpaceMinWidth(): number { return this.groupedStrategy.getWorkSpaceMinWidth(); } - _dimensionChanged() { + _dimensionChanged(): void { // NOTE: It's a base widget method. Be careful :) // @ts-expect-error if (!this._isVisible()) { @@ -888,13 +982,13 @@ class SchedulerWorkSpace extends Widget { this.cache.clear(); } - protected needCreateCrossScrolling() { + protected needCreateCrossScrolling(): boolean { return this.option('crossScrollingEnabled'); } - protected getElementClass() { return noop(); } + protected getElementClass(): string { noop(); return ''; } - protected getRowCount() { + protected getRowCount(): number { return this.viewDataProvider.getRowCount({ intervalCount: this.option('intervalCount'), currentDate: this.option('currentDate'), @@ -905,7 +999,7 @@ class SchedulerWorkSpace extends Widget { }); } - protected getCellCount() { + protected getCellCount(): number { return this.viewDataProvider.getCellCount({ intervalCount: this.option('intervalCount'), currentDate: this.option('currentDate'), @@ -916,58 +1010,59 @@ class SchedulerWorkSpace extends Widget { }); } - private isVirtualModeOn() { + private isVirtualModeOn(): boolean { return this.option('scrolling.mode') === 'virtual'; } - isVirtualScrolling() { + isVirtualScrolling(): boolean { return this.renovatedRenderSupported() && this.isVirtualModeOn(); } - private initVirtualScrolling() { + private initVirtualScrolling(): void { if (this.virtualScrollingDispatcher) { this.virtualScrollingDispatcher.dispose(); - this.virtualScrollingDispatcher = null; } - this.virtualScrollingDispatcher = new VirtualScrollingDispatcher(this.getVirtualScrollingDispatcherOptions()); + this.virtualScrollingDispatcher = new VirtualScrollingDispatcher( + this.getVirtualScrollingDispatcherOptions(), + ); this.virtualScrollingDispatcher.attachScrollableEvents(); this.renderer = new VirtualScrollingRenderer(this); } - isGroupedAllDayPanel() { + isGroupedAllDayPanel(): boolean { return calculateIsGroupedAllDayPanel( this.option('groups').length, - this.option('groupOrientation') as any, - this.isAllDayPanelVisible as any, + this.option('groupOrientation'), + this.isAllDayPanelVisible, ); } generateRenderOptions(isProvideVirtualCellsWidth = false): ViewDataProviderOptions { const groupCount = this.getGroupCount(); - const groupOrientation = groupCount > 0 + const groupOrientation: GroupOrientation = groupCount > 0 ? this.option('groupOrientation') : this.getDefaultGroupStrategy(); - const options: ViewDataProviderOptions = { + const options = { groupByDate: this.option('groupByDate'), startRowIndex: 0, startCellIndex: 0, groupOrientation, - today: this.getToday?.(), + today: this.getToday?.() ?? new Date(), getResourceManager: this.option('getResourceManager'), isProvideVirtualCellsWidth, isAllDayPanelVisible: this.isAllDayPanelVisible, selectedCells: this.cellsSelectionState.getSelectedCells(), focusedCell: this.cellsSelectionState.getFocusedCell(), headerCellTextFormat: this.getFormat(), - getDateForHeaderText: (_, date) => date, + getDateForHeaderText: (_: number, date: Date): Date => date, viewOffset: this.option('viewOffset'), startDayHour: this.option('startDayHour'), endDayHour: this.option('endDayHour'), cellDuration: this.getCellDuration(), - viewType: this.type, + viewType: this.type as ViewType, intervalCount: this.option('intervalCount'), hoursInterval: this.option('hoursInterval'), currentDate: this.option('currentDate'), @@ -979,18 +1074,18 @@ class SchedulerWorkSpace extends Widget { ...this.virtualScrollingDispatcher.getRenderState(), }; - return options; + return options as ViewDataProviderOptions; } - renovatedRenderSupported() { return true; } + renovatedRenderSupported(): boolean { return true; } - private updateGroupTableHeight() { + private updateGroupTableHeight(): void { if (this.isVerticalGroupedWorkSpace() && hasWindow()) { this.setHorizontalGroupHeaderCellsHeight(); } } - updateHeaderEmptyCellWidth() { + updateHeaderEmptyCellWidth(): void { if (hasWindow() && this.isRenderHeaderPanelEmptyCell()) { const timePanelWidth = this.getTimePanelWidth(); const groupPanelWidth = this.getGroupTableWidth(); @@ -999,31 +1094,33 @@ class SchedulerWorkSpace extends Widget { } } - updateHeaderPanelScrollbarPadding() { + updateHeaderPanelScrollbarPadding(): void { if (hasWindow() && this.$headerPanelContainer) { const scrollbarWidth = this.getScrollbarWidth(); this.$headerPanelContainer.css('paddingRight', `${scrollbarWidth}px`); } } - private getScrollbarWidth() { + private getScrollbarWidth(): number { const containerElement = $(this.$dateTableScrollable.container()).get(0) as HTMLElement; const scrollbarWidth = containerElement.offsetWidth - containerElement.clientWidth; return scrollbarWidth; } - private isGroupsSpecified(groupValues?: GroupValues) { + private isGroupsSpecified(groupValues?: GroupValues): number | GroupValues | undefined { return this.option('groups')?.length && groupValues; } - private getGroupIndexByGroupValues(groupValues?: RawGroupValues | GroupValues) { + private getGroupIndexByGroupValues( + groupValues?: RawGroupValues | GroupValues, + ): number | undefined { return groupValues && getAppointmentGroupIndex( getSafeGroupValues(groupValues), this.resourceManager.groupsLeafs, )[0]; } - protected getViewStartByOptions() { + protected getViewStartByOptions(): Date { return getViewStartByOptions( this.option('startDate'), this.option('currentDate'), @@ -1032,33 +1129,32 @@ class SchedulerWorkSpace extends Widget { ); } - protected getTotalViewDuration() { + protected getTotalViewDuration(): number { return this.viewDataProvider.getIntervalDuration(this.option('intervalCount')); } - protected getHeaderDate() { + protected getHeaderDate(): Date { return this.getStartViewDate(); } - protected calculateViewStartDate() { + protected calculateViewStartDate(): Date | undefined { return calculateViewStartDate(this.option('startDate')); } - protected firstDayOfWeek() { + protected firstDayOfWeek(): number { return this.viewDataProvider.getFirstDayOfWeek(this.option('firstDayOfWeek')); } - protected attachEvents() { + protected attachEvents(): void { this.createSelectionChangedAction(); this.createSelectionEndAction(); this.attachClickEvent(); this.attachContextMenuEvent(); } - private attachClickEvent() { - const that = this; + private attachClickEvent(): void { const pointerDownAction = this._createAction((e) => { - that.pointerDownHandler(e.event); + this.pointerDownHandler(e.event); }); this.createCellClickAction(); @@ -1066,8 +1162,8 @@ class SchedulerWorkSpace extends Widget { const cellSelector = `.${DATE_TABLE_CELL_CLASS},.${ALL_DAY_TABLE_CELL_CLASS}`; const $element = this.$element(); - (eventsEngine.off as any)($element, SCHEDULER_WORKSPACE_DXPOINTERDOWN_EVENT_NAME); - (eventsEngine.off as any)($element, SCHEDULER_CELL_DXCLICK_EVENT_NAME); + eventsEngine.off($element, SCHEDULER_WORKSPACE_DXPOINTERDOWN_EVENT_NAME); + eventsEngine.off($element, SCHEDULER_CELL_DXCLICK_EVENT_NAME); eventsEngine.on($element, SCHEDULER_WORKSPACE_DXPOINTERDOWN_EVENT_NAME, (e) => { if (isMouseEvent(e) && e.which > 1) { e.preventDefault(); @@ -1076,38 +1172,60 @@ class SchedulerWorkSpace extends Widget { pointerDownAction({ event: e }); }); eventsEngine.on($element, SCHEDULER_CELL_DXCLICK_EVENT_NAME, cellSelector, (e) => { - const $cell = $(e.target); - that.cellClickAction({ event: e, cellElement: getPublicElement($cell), cellData: that.getCellData($cell) }); + const $cell = $(e.target as Element | null); + this.cellClickAction?.( + { + event: e, + cellElement: getPublicElement($cell), + cellData: this.getCellData($cell), + }, + ); }); if (this.documentPointerUpHandler) { - (eventsEngine.off as any)(domAdapter.getDocument(), SCHEDULER_TABLE_DXPOINTERUP_EVENT_NAME, this.documentPointerUpHandler); + eventsEngine.off( + domAdapter.getDocument(), + SCHEDULER_TABLE_DXPOINTERUP_EVENT_NAME, + this.documentPointerUpHandler, + ); } - this.documentPointerUpHandler = () => { + this.documentPointerUpHandler = (): void => { if (this.isSelectionStartedOnCell && !this._disposed) { this.fireSelectionEndEvent(); this.isSelectionStartedOnCell = false; } }; - eventsEngine.on(domAdapter.getDocument(), SCHEDULER_TABLE_DXPOINTERUP_EVENT_NAME, this.documentPointerUpHandler); + eventsEngine.on( + domAdapter.getDocument(), + SCHEDULER_TABLE_DXPOINTERUP_EVENT_NAME, + this.documentPointerUpHandler, + ); + } + + private createWorkspaceOptionAction( + optionName: K, + config?: ActionConfig, + ): (args: WorkspaceOptionActionMap[K]) => void { + return this._createActionByOption(optionName, config) as ( + args: WorkspaceOptionActionMap[K], + ) => void; } - private createCellClickAction() { - this.cellClickAction = this._createActionByOption('onCellClick', { - afterExecute: (e) => this.cellClickHandler(e.args[0].event), + private createCellClickAction(): void { + this.cellClickAction = this.createWorkspaceOptionAction('onCellClick', { + afterExecute: () => this.cellClickHandler(), }); } - private createSelectionChangedAction() { - this.selectionChangedAction = this._createActionByOption('onSelectionChanged'); + private createSelectionChangedAction(): void { + this.selectionChangedAction = this.createWorkspaceOptionAction('onSelectionChanged'); } - private createSelectionEndAction() { - this.selectionEndAction = this._createActionByOption('onSelectionEnd'); + private createSelectionEndAction(): void { + this.selectionEndAction = this.createWorkspaceOptionAction('onSelectionEnd'); } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - private cellClickHandler(argument?: any) { + private cellClickHandler(): void { if (this.showPopup) { delete this.showPopup; this.isSelectionStartedOnCell = false; @@ -1115,8 +1233,8 @@ class SchedulerWorkSpace extends Widget { } } - private pointerDownHandler(e) { - const $target = $(e.target); + private pointerDownHandler(e: { target: EventTarget | null; which?: number }): void { + const $target = $(e.target as Element | null); if (!$target.hasClass(DATE_TABLE_CELL_CLASS) && !$target.hasClass(ALL_DAY_TABLE_CELL_CLASS)) { this.isCellClick = false; @@ -1136,27 +1254,26 @@ class SchedulerWorkSpace extends Widget { } } - private handleSelectedCellsClick() { + private handleSelectedCellsClick(): void { const selectedCells = this.getSelectedCellsData(); const firstCellData = selectedCells[0]; const lastCellData = selectedCells[selectedCells.length - 1]; - const result: any = { + const result: NormalizedCellData = { startDate: firstCellData.startDate, endDate: lastCellData.endDate, startDateUTC: firstCellData.startDateUTC, endDateUTC: lastCellData.endDateUTC, + groups: lastCellData.groups, + groupIndex: lastCellData.groupIndex, + allDay: lastCellData.allDay, }; - if (lastCellData.allDay !== undefined) { - result.allDay = lastCellData.allDay; - } - - (this.option('onSelectedCellsClick') as any)(result, lastCellData.groups); + this.option('onSelectedCellsClick')(result, lastCellData.groups as GroupValues); } - private attachContextMenuEvent() { + private attachContextMenuEvent(): void { this.createContextMenuAction(); const cellSelector = `.${DATE_TABLE_CELL_CLASS},.${ALL_DAY_TABLE_CELL_CLASS}`; @@ -1167,17 +1284,23 @@ class SchedulerWorkSpace extends Widget { eventsEngine.on($element, eventName, cellSelector, this.contextMenuHandler.bind(this)); } - private contextMenuHandler(e) { - const $cell = $(e.target); - this.contextMenuAction({ event: e, cellElement: getPublicElement($cell), cellData: this.getCellData($cell) }); + private contextMenuHandler(e: { target: EventTarget | null }): void { + const $cell = $(e.target as Element | null); + this.contextMenuAction?.( + { + event: e as CellContextMenuEvent['event'], + cellElement: getPublicElement($cell), + cellData: this.getCellData($cell), + }, + ); this.contextMenuHandled = true; } - private createContextMenuAction() { - this.contextMenuAction = this._createActionByOption('onCellContextMenu'); + private createContextMenuAction(): void { + this.contextMenuAction = this.createWorkspaceOptionAction('onCellContextMenu'); } - protected getGroupHeaderContainer() { + protected getGroupHeaderContainer(): dxElementWrapper | null | undefined { if (this.isVerticalGroupedWorkSpace()) { return this.$groupTable; } @@ -1185,26 +1308,26 @@ class SchedulerWorkSpace extends Widget { return this.$thead; } - private getDateHeaderContainer() { + private getDateHeaderContainer(): dxElementWrapper { return this.$thead; } - private getCalculateHeaderCellRepeatCount() { + private getCalculateHeaderCellRepeatCount(): number { return this.groupedStrategy.calculateHeaderCellRepeatCount(); } - protected updateScrollable() { + protected updateScrollable(): void { this.$dateTableScrollable.update(); this.headerScrollable?.update(); this.$sidebarScrollable?.update(); this.updateHeaderPanelScrollbarPadding(); } - protected getTimePanelRowCount() { + protected getTimePanelRowCount(): number { return this.getCellCountInDay(); } - protected getCellCountInDay() { + protected getCellCountInDay(): number { const hoursInterval = this.option('hoursInterval'); const startDayHour = this.option('startDayHour'); const endDayHour = this.option('endDayHour'); @@ -1212,12 +1335,12 @@ class SchedulerWorkSpace extends Widget { return this.viewDataProvider.getCellCountInDay(startDayHour, endDayHour, hoursInterval); } - private getTotalCellCount(groupCount) { + private getTotalCellCount(groupCount: number): number { return this.groupedStrategy.getTotalCellCount(groupCount); } - protected getTotalRowCount(groupCount: number, includeAllDayPanelRows?: boolean) { - let result = this.groupedStrategy.getTotalRowCount(groupCount); + protected getTotalRowCount(groupCount: number, includeAllDayPanelRows?: boolean): number { + let result = this.groupedStrategy.getTotalRowCount(); if (includeAllDayPanelRows && this.isAllDayPanelVisible) { result += groupCount; @@ -1226,11 +1349,11 @@ class SchedulerWorkSpace extends Widget { return result; } - private getGroupIndex(rowIndex, columnIndex) { + private getGroupIndex(rowIndex: number, columnIndex: number): number { return this.groupedStrategy.getGroupIndex(rowIndex, columnIndex); } - calculateEndDate(startDate) { + calculateEndDate(startDate: Date): Date { const { viewDataGenerator } = this.viewDataProvider; return viewDataGenerator.calculateEndDate( @@ -1240,46 +1363,51 @@ class SchedulerWorkSpace extends Widget { ); } - protected getGroupCount() { + protected getGroupCount(): number { return this.resourceManager.groupCount(); } - protected attachTablesEvents() { + protected attachTablesEvents(): void { const element = this.$element(); this.attachDragEvents(element); this.attachPointerEvents(element); } - private detachDragEvents(element) { - (eventsEngine.off as any)(element, DragEventNames.ENTER); - (eventsEngine.off as any)(element, DragEventNames.LEAVE); - (eventsEngine.off as any)(element, DragEventNames.DROP); + private detachDragEvents(element: dxElementWrapper): void { + eventsEngine.off(element, DragEventNames.ENTER); + eventsEngine.off(element, DragEventNames.LEAVE); + eventsEngine.off(element, DragEventNames.DROP); } - private attachDragEvents(element) { + private attachDragEvents(element: dxElementWrapper): void { if (this.option('newAppointments')) { return; } this.detachDragEvents(element); - const onDragEnter = (e) => { + const onDragEnter = (e: { target: Element }): void => { if (!this.preventDefaultDragging) { this.removeDroppableCellClass(); $(e.target).addClass(DATE_TABLE_DROPPABLE_CELL_CLASS); } }; - const removeClasses = () => { + const removeClasses = (): void => { if (!this.preventDefaultDragging) { this.removeDroppableCellClass(); } }; - const onCheckDropTarget = (target, event) => !this.isOutsideScrollable(target, event); + const onCheckDropTarget = ( + target: Element, + event: { pageX: number; pageY: number }, + ): boolean => !this.isOutsideScrollable(target, event); - (eventsEngine.on as any)( + // eventsEngine.on supports 5-arg (element, event, selector, data, handler) form at runtime + const onFivePlusArgs = eventsEngine.on as (...args: unknown[]) => void; + onFivePlusArgs( element, DragEventNames.ENTER, DRAG_AND_DROP_SELECTOR, @@ -1298,85 +1426,104 @@ class SchedulerWorkSpace extends Widget { } this.dragBehavior.dragBetweenComponentsPromise?.then(() => { - this.dragBehavior.removeDroppableClasses(); + this.dragBehavior?.removeDroppableClasses(); }); }); } - private attachPointerEvents(element) { + private attachPointerEvents(element: dxElementWrapper): void { let isPointerDown = false; - (eventsEngine.off as any)(element, SCHEDULER_CELL_DXPOINTERMOVE_EVENT_NAME); - (eventsEngine.off as any)(element, SCHEDULER_CELL_DXPOINTERDOWN_EVENT_NAME); - - eventsEngine.on(element, SCHEDULER_CELL_DXPOINTERDOWN_EVENT_NAME, DRAG_AND_DROP_SELECTOR, (e) => { - if ((isMouseEvent(e) || (e.originalEvent && isMouseEvent(e.originalEvent))) && e.which === 1) { - isPointerDown = true; - (this.$element() as any).addClass(WORKSPACE_WITH_MOUSE_SELECTION_CLASS); - (eventsEngine.off as any)(domAdapter.getDocument(), SCHEDULER_CELL_DXPOINTERUP_EVENT_NAME); - eventsEngine.on(domAdapter.getDocument(), SCHEDULER_CELL_DXPOINTERUP_EVENT_NAME, () => { - isPointerDown = false; - (this.$element() as any).removeClass(WORKSPACE_WITH_MOUSE_SELECTION_CLASS); - }); - } - }); + eventsEngine.off(element, SCHEDULER_CELL_DXPOINTERMOVE_EVENT_NAME); + eventsEngine.off(element, SCHEDULER_CELL_DXPOINTERDOWN_EVENT_NAME); - eventsEngine.on(element, SCHEDULER_CELL_DXPOINTERMOVE_EVENT_NAME, DRAG_AND_DROP_SELECTOR, (e) => { - if (isPointerDown && this.$dateTableScrollable) { - e.preventDefault(); - e.stopPropagation(); - this.moveToCell($(e.target), true); - } - }); + eventsEngine.on( + element, + SCHEDULER_CELL_DXPOINTERDOWN_EVENT_NAME, + DRAG_AND_DROP_SELECTOR, + (e: { + type: string; + target: Element; + originalEvent: Event; + which: number; + }): void => { + if (( + isMouseEvent(e) || (e.originalEvent && isMouseEvent(e.originalEvent)) + ) && e.which === 1) { + isPointerDown = true; + this.$element().addClass(WORKSPACE_WITH_MOUSE_SELECTION_CLASS); + eventsEngine.off(domAdapter.getDocument(), SCHEDULER_CELL_DXPOINTERUP_EVENT_NAME); + eventsEngine.on(domAdapter.getDocument(), SCHEDULER_CELL_DXPOINTERUP_EVENT_NAME, () => { + isPointerDown = false; + this.$element().removeClass(WORKSPACE_WITH_MOUSE_SELECTION_CLASS); + }); + } + }, + ); + + eventsEngine.on( + element, + SCHEDULER_CELL_DXPOINTERMOVE_EVENT_NAME, + DRAG_AND_DROP_SELECTOR, + (e) => { + if (isPointerDown && this.$dateTableScrollable) { + e.preventDefault(); + e.stopPropagation(); + this.moveToCell($(e.target), true); + } + }, + ); } - protected getFormat(): string | ((date: Date) => string) { return abstract(); } + protected getFormat(): string | ((date: Date) => string) { + return abstract() as string; + } - getWorkArea() { + getWorkArea(): dxElementWrapper { return this.$dateTableContainer; } - getScrollable() { + getScrollable(): Scrollable { return this.$dateTableScrollable; } - getScrollableScrollTop() { + getScrollableScrollTop(): number { return this.$dateTableScrollable.scrollTop(); } - getGroupedScrollableScrollTop(allDay) { + getGroupedScrollableScrollTop(allDay: boolean): number { return this.groupedStrategy.getScrollableScrollTop(allDay); } - getScrollableScrollLeft() { + getScrollableScrollLeft(): number { return this.$dateTableScrollable.scrollLeft(); } - getScrollableOuterWidth() { + getScrollableOuterWidth(): number { return this.$dateTableScrollable.scrollWidth(); } - getScrollableContainer() { + getScrollableContainer(): dxElementWrapper { return $(this.$dateTableScrollable.container()); } - getHeaderPanelHeight() { - return this.$headerPanel && getOuterHeight(this.$headerPanel, true); + getHeaderPanelHeight(): number | false { + return this.$headerPanel && getOuterHeight(this.$headerPanel, true) as number; } getTimePanelWidth(): number { - return this.$timePanel && getBoundingRect(this.$timePanel.get(0)).width; + return this.$timePanel && getBoundingRect(this.$timePanel.get(0)).width as number; } getGroupTableWidth(): number { - return this.$groupTable ? getOuterWidth(this.$groupTable) : 0; + return this.$groupTable ? getOuterWidth(this.$groupTable) as number : 0; } - getWorkSpaceLeftOffset() { + getWorkSpaceLeftOffset(): number { return this.groupedStrategy.getLeftOffset(); } - protected getCellCoordinatesByIndex(index) { + protected getCellCoordinatesByIndex(index: number): CellPositionData { const columnIndex = Math.floor(index / this.getRowCount()); const rowIndex = index - this.getRowCount() * columnIndex; @@ -1402,7 +1549,7 @@ class SchedulerWorkSpace extends Widget { } // TODO: refactor current time indicator - protected getIntervalBetween(currentDate, allDay) { + protected getIntervalBetween(currentDate: Date, allDay: boolean): number { const firstViewDate = this.getStartViewDate(); const startDayTime = this.option('startDayHour') * HOUR_MS; @@ -1422,7 +1569,7 @@ class SchedulerWorkSpace extends Widget { return result; } - protected getSkippedDaysCount(startDate: Date, days: number) { + protected getSkippedDaysCount(startDate: Date, days: number): number { return countSkippedDays( startDate, days, @@ -1430,24 +1577,24 @@ class SchedulerWorkSpace extends Widget { ); } - private getDaysOfInterval(fullInterval, startDayTime) { + private getDaysOfInterval(fullInterval: number, startDayTime: number): number { return Math.floor((fullInterval + startDayTime) / DAY_MS); } - protected updateIndex(index) { + protected updateIndex(index: number): number { return index * this.getRowCount(); } - getDroppableCell() { + getDroppableCell(): dxElementWrapper { return this.getDateTables().find(`.${DATE_TABLE_DROPPABLE_CELL_CLASS}`); } protected getWorkSpaceWidth(): number { - return this.cache.memo('workspaceWidth', () => { + return this.cache.memo('workspaceWidth', (): number => { if (this.needCreateCrossScrolling()) { - return getBoundingRect(this.$dateTable.get(0)).width; + return getBoundingRect(this.$dateTable.get(0)).width as number; } - const totalWidth = getBoundingRect((this.$element() as any).get(0)).width; + const totalWidth = getBoundingRect(this.$element().get(0)).width; const timePanelWidth = this.getTimePanelWidth(); const groupTableWidth = this.getGroupTableWidth(); @@ -1455,12 +1602,20 @@ class SchedulerWorkSpace extends Widget { }); } - protected getCellElementByPosition(cellCoordinates, groupIndex, inAllDayRow) { - const indexes = this.groupedStrategy.prepareCellIndexes(cellCoordinates, groupIndex, inAllDayRow); + protected getCellElementByPosition( + cellCoordinates: CellPositionData, + groupIndex: number, + inAllDayRow: boolean, + ): dxElementWrapper { + const indexes = this.groupedStrategy.prepareCellIndexes( + cellCoordinates, + groupIndex, + inAllDayRow, + ); return this.domGetDateCell(indexes); } - private domGetDateCell(position) { + private domGetDateCell(position: CellPositionData): dxElementWrapper { return this.$dateTable .find(`tr:not(.${VIRTUAL_ROW_CLASS})`) .eq(position.rowIndex) @@ -1468,36 +1623,36 @@ class SchedulerWorkSpace extends Widget { .eq(position.columnIndex); } - private domGetAllDayPanelCell(columnIndex) { + private domGetAllDayPanelCell(columnIndex: number): dxElementWrapper { return this.$allDayPanel .find('tr').eq(0) .find('td').eq(columnIndex); } - protected getCells(allDay?: any, direction?: any) { + protected getCells(allDay?: boolean, direction?: string): dxElementWrapper { const cellClass = allDay ? ALL_DAY_TABLE_CELL_CLASS : DATE_TABLE_CELL_CLASS; if (direction === 'vertical') { - let result: any = []; - for (let i = 1; ; i++) { - const cells = (this.$element() as any).find(`tr .${cellClass}:nth-child(${i})`); + let result: Element[] = []; + for (let i = 1; ; i += 1) { + const cells = this.$element().find(`tr .${cellClass}:nth-child(${i})`); if (!cells.length) break; result = result.concat(cells.toArray()); } return $(result); } - return (this.$element() as any).find(`.${cellClass}`); + return this.$element().find(`.${cellClass}`); } - private getFirstAndLastDataTableCell() { + private getFirstAndLastDataTableCell(): Element[] { const selector = this.isVirtualScrolling() ? `.${DATE_TABLE_CELL_CLASS}, .${VIRTUAL_CELL_CLASS}` : `.${DATE_TABLE_CELL_CLASS}`; - const $cells = (this.$element() as any).find(selector); - return [$cells[0], $cells[$cells.length - 1]]; + const $cells = this.$element().find(selector); + return [$cells.get(0), $cells.get(-1)]; } - private getAllCells(allDay) { + private getAllCells(allDay: boolean): dxElementWrapper { if (this.isVerticalGroupedWorkSpace()) { return this.$dateTable.find(`td:not(.${VIRTUAL_CELL_CLASS})`); } @@ -1506,22 +1661,31 @@ class SchedulerWorkSpace extends Widget { ? ALL_DAY_TABLE_CELL_CLASS : DATE_TABLE_CELL_CLASS; - return (this.$element() as any).find(`.${cellClass}`); + return this.$element().find(`.${cellClass}`); } - protected setHorizontalGroupHeaderCellsHeight() { + protected setHorizontalGroupHeaderCellsHeight(): void { const { height } = getBoundingRect(this.$dateTable.get(0)); setOuterHeight(this.$groupTable, height); } - protected getGroupHeaderCells() { - return (this.$element() as any).find(`.${GROUP_HEADER_CLASS}`); + protected getGroupHeaderCells(): dxElementWrapper { + return this.$element().find(`.${GROUP_HEADER_CLASS}`); } - private getScrollCoordinates(date, groupIndex?: any, allDay?: any) { + private getScrollCoordinates( + date: Date, + groupIndex?: number, + allDay?: boolean, + ): Coordinates | undefined { const currentDate = date || new Date(this.option('currentDate')); - const cell = this.viewDataProvider.findGlobalCellPosition(currentDate, groupIndex, allDay, true); + const cell = this.viewDataProvider.findGlobalCellPosition( + currentDate, + groupIndex, + allDay, + true, + ); if (!cell) { return undefined; @@ -1533,18 +1697,20 @@ class SchedulerWorkSpace extends Widget { cell.cellData, cell.position, currentDate, - isDateAndTimeView(this.type as any), + isDateAndTimeView(this.type as ViewType), this.viewDirection === 'vertical', ); } - private isOutsideScrollable(target, event) { + private isOutsideScrollable(target: Element, event: { pageX: number; pageY: number }): boolean { const $dateTableScrollableElement = this.$dateTableScrollable.$element(); const scrollableSize = getBoundingRect($dateTableScrollableElement.get(0)); const window = getWindow(); const isTargetInAllDayPanel = !$(target).closest($dateTableScrollableElement).length; - const isOutsideHorizontalScrollable = event.pageX < scrollableSize.left || event.pageX > (scrollableSize.left + scrollableSize.width + (window.scrollX || 0)); - const isOutsideVerticalScrollable = event.pageY < scrollableSize.top || event.pageY > (scrollableSize.top + scrollableSize.height + (window.scrollY || 0)); + const isOutsideHorizontalScrollable = event.pageX < scrollableSize.left + || event.pageX > (scrollableSize.left + scrollableSize.width + (window.scrollX || 0)); + const isOutsideVerticalScrollable = event.pageY < scrollableSize.top + || event.pageY > (scrollableSize.top + scrollableSize.height + (window.scrollY || 0)); if (isTargetInAllDayPanel && !isOutsideHorizontalScrollable) { return false; @@ -1553,15 +1719,15 @@ class SchedulerWorkSpace extends Widget { return isOutsideVerticalScrollable || isOutsideHorizontalScrollable; } - supportAllDayRow() { + supportAllDayRow(): boolean { return true; } - keepOriginalHours() { + keepOriginalHours(): boolean { return false; } - private normalizeCellData(cellData) { + private normalizeCellData(cellData: Partial): NormalizedCellData { return extend(true, {}, { startDate: cellData.startDate, endDate: cellData.endDate, @@ -1570,22 +1736,22 @@ class SchedulerWorkSpace extends Widget { groups: cellData.groups, groupIndex: cellData.groupIndex, allDay: cellData.allDay, - }); + }) as NormalizedCellData; } - private getSelectedCellsData() { + private getSelectedCellsData(): NormalizedCellData[] { const selected = this.cellsSelectionState.getSelectedCells(); - return selected?.map(this.normalizeCellData.bind(this)); + return selected?.map(this.normalizeCellData.bind(this)) ?? []; } - getCellData($cell) { + getCellData($cell: dxElementWrapper): NormalizedCellData { const cellData = this.getFullCellData($cell) ?? {}; return this.normalizeCellData(cellData); } - private getFullCellData($cell) { + private getFullCellData($cell: dxElementWrapper): ViewCellData | undefined { const currentCell = $cell[0]; if (currentCell) { return this.getDataByCell($cell); @@ -1594,15 +1760,15 @@ class SchedulerWorkSpace extends Widget { return undefined; } - private getVirtualRowOffset() { + private getVirtualRowOffset(): number { return this.virtualScrollingDispatcher.virtualRowOffset; } - private getVirtualCellOffset() { + private getVirtualCellOffset(): number { return this.virtualScrollingDispatcher.virtualCellOffset; } - private getDataByCell($cell) { + private getDataByCell($cell: dxElementWrapper): ViewCellData | undefined { const rowIndex = $cell.parent().index() - this.virtualScrollingDispatcher.topVirtualRowsCount; const columnIndex = $cell.index() - this.virtualScrollingDispatcher.leftVirtualCellsCount; @@ -1614,21 +1780,21 @@ class SchedulerWorkSpace extends Widget { return cellData || undefined; } - isGroupedByDate() { + isGroupedByDate(): boolean { return this.option('groupByDate') && this.isHorizontalGroupedWorkSpace() && this.getGroupCount() > 0; } // TODO: refactor current time indicator - getCellIndexByDate(date, inAllDayRow?: any) { + getCellIndexByDate(date: Date, inAllDayRow?: boolean): number { const { viewDataGenerator } = this.viewDataProvider; const timeInterval = inAllDayRow ? 24 * 60 * 60 * 1000 : viewDataGenerator.getInterval(this.option('hoursInterval')); - const startViewDateOffset = getStartViewDateTimeOffset(this.getStartViewDate(), this.option('startDayHour') as any); - const dateTimeStamp = this.getIntervalBetween(date, inAllDayRow) + startViewDateOffset; + const startViewDateOffset = getStartViewDateTimeOffset(this.getStartViewDate(), this.option('startDayHour')); + const dateTimeStamp = this.getIntervalBetween(date, inAllDayRow ?? false) + startViewDateOffset; let index = Math.floor(dateTimeStamp / timeInterval); @@ -1643,7 +1809,7 @@ class SchedulerWorkSpace extends Widget { return index; } - getDataByDroppableCell() { + getDataByDroppableCell(): DroppableCellData { const cellData = this.getCellData($(this.getDroppableCell())); const { allDay } = cellData; const { startDate } = cellData; @@ -1657,27 +1823,27 @@ class SchedulerWorkSpace extends Widget { }; } - getDateRange() { + getDateRange(): Date[] { return [ this.getStartViewDate(), this.getEndViewDateByEndDayHour(), ]; } - getCellMinWidth() { + getCellMinWidth(): number { return DATE_TABLE_MIN_CELL_WIDTH; } // Mappings - getCellWidth() { + getCellWidth(): number { return getCellWidth(this.getDOMElementsMetaData()); } - getCellHeight() { + getCellHeight(): number { return getCellHeight(this.getDOMElementsMetaData()); } - getAllDayHeight() { + getAllDayHeight(): number { return getAllDayHeight( this.option('showAllDayPanel'), this.isVerticalGroupedWorkSpace(), @@ -1685,7 +1851,7 @@ class SchedulerWorkSpace extends Widget { ); } - getMaxAllowedPosition(groupIndex) { + getMaxAllowedPosition(groupIndex: number): number { return getMaxAllowedPosition( groupIndex, this.viewDataProvider, @@ -1694,15 +1860,16 @@ class SchedulerWorkSpace extends Widget { ); } - getAllDayOffset() { + getAllDayOffset(): number { return this.groupedStrategy.getAllDayOffset(); } // NOTE: refactor leftIndex calculation - getCellIndexByCoordinates(coordinates, allDay?) { + getCellIndexByCoordinates(coordinates: Coordinates, allDay?: boolean): number { const { horizontalScrollingState, verticalScrollingState } = this.virtualScrollingDispatcher; - const cellCount = horizontalScrollingState?.itemCount ?? this.getTotalCellCount(this.getGroupCount()); + const cellCount = horizontalScrollingState?.itemCount + ?? this.getTotalCellCount(this.getGroupCount()); const cellWidth = this.getCellWidth(); const cellHeight = allDay ? this.getAllDayHeight() : this.getCellHeight(); @@ -1710,7 +1877,9 @@ class SchedulerWorkSpace extends Widget { const leftCoordinateOffset = horizontalScrollingState?.virtualItemSizeBefore ?? 0; const topCoordinateOffset = verticalScrollingState?.virtualItemSizeBefore ?? 0; - const topIndex = Math.floor(Math.floor(coordinates.top - topCoordinateOffset) / Math.floor(cellHeight)); + const topIndex = Math.floor( + Math.floor(coordinates.top - topCoordinateOffset) / Math.floor(cellHeight), + ); let leftIndex = (coordinates.left - leftCoordinateOffset) / cellWidth; leftIndex = Math.floor(leftIndex + CELL_INDEX_CALCULATION_EPSILON); @@ -1721,34 +1890,34 @@ class SchedulerWorkSpace extends Widget { return cellCount * topIndex + leftIndex; } - getStartViewDate() { + getStartViewDate(): Date { return this.viewDataProvider.getStartViewDate(); } - getEndViewDate() { + getEndViewDate(): Date { return this.viewDataProvider.getLastCellEndDate(); } - getEndViewDateByEndDayHour() { + getEndViewDateByEndDayHour(): Date { return this.viewDataProvider.getLastViewDateByEndDayHour(this.option('endDayHour')); } - getCellDuration() { + getCellDuration(): number { return getCellDuration( - this.type as any, - this.option('startDayHour') as any, - this.option('endDayHour') as any, - this.option('hoursInterval') as any, + this.type as ViewType, + this.option('startDayHour'), + this.option('endDayHour'), + this.option('hoursInterval'), ); } - getIntervalDuration(allDay) { + getIntervalDuration(allDay: boolean): number { return allDay ? toMs('day') : this.getCellDuration(); } - getVisibleDayDuration() { + getVisibleDayDuration(): number { const startDayHour = this.option('startDayHour'); const endDayHour = this.option('endDayHour'); const hoursInterval = this.option('hoursInterval'); @@ -1756,32 +1925,44 @@ class SchedulerWorkSpace extends Widget { return this.viewDataProvider.getVisibleDayDuration(startDayHour, endDayHour, hoursInterval); } - getGroupBounds(coordinates) { + getGroupBounds( + coordinates: WorkspaceCoordinates, + ): GroupBoundsOffset | undefined { const groupBounds = this.groupedStrategy instanceof VerticalGroupedStrategy - ? this.getGroupBoundsVertical(coordinates.groupIndex) + ? this.getGroupBoundsVertical(coordinates.groupIndex ?? 0) : this.getGroupBoundsHorizontal(coordinates); - return this.isRTL() + return this.isRTL() && groupBounds ? this.getGroupBoundsRtlCorrection(groupBounds) : groupBounds; } - getGroupBoundsVertical(groupIndex) { + getGroupBoundsVertical(groupIndex: number): GroupBoundsOffset | undefined { const $firstAndLastCells = this.getFirstAndLastDataTableCell(); - return this.groupedStrategy.getGroupBoundsOffset(groupIndex, $firstAndLastCells); + if (this.groupedStrategy instanceof VerticalGroupedStrategy) { + return this.groupedStrategy.getGroupBoundsOffset(groupIndex, [ + $firstAndLastCells[0], $firstAndLastCells[1], + ]); + } + return undefined; } - getGroupBoundsHorizontal(coordinates) { + getGroupBoundsHorizontal( + coordinates: WorkspaceCoordinates, + ): GroupBoundsOffset | undefined { const cellCount = this.getCellCount(); const $cells = this.getCells(); const cellWidth = this.getCellWidth(); const { groupedDataMap } = this.viewDataProvider; - return this.groupedStrategy - .getGroupBoundsOffset(cellCount, $cells, cellWidth, coordinates, groupedDataMap); + if (this.groupedStrategy instanceof HorizontalGroupedStrategy) { + return this.groupedStrategy + .getGroupBoundsOffset(cellCount, $cells, cellWidth, coordinates, groupedDataMap); + } + return undefined; } - getGroupBoundsRtlCorrection(groupBounds) { + getGroupBoundsRtlCorrection(groupBounds: GroupBoundsOffset): GroupBoundsOffset { const cellWidth = this.getCellWidth(); return { @@ -1791,39 +1972,46 @@ class SchedulerWorkSpace extends Widget { }; } - needRecalculateResizableArea() { + needRecalculateResizableArea(): boolean { return this.isVerticalGroupedWorkSpace() && this.getScrollable().scrollTop() !== 0; } - getCellByCoordinates(coordinates, allDay) { + getCellByCoordinates( + coordinates: Coordinates, + allDay: boolean, + ): dxElementWrapper { const $cells = this.getCells(allDay); const cellIndex = this.getCellIndexByCoordinates(coordinates, allDay); return $cells.eq(cellIndex); } - getVisibleBounds() { // TODO - this method is only used by the Agenda - const result: any = {}; + // TODO - this method is only used by the Agenda + getVisibleBounds(): { + top: { hours: number; minutes: number }; + bottom: { hours: number; minutes: number }; + } { const $scrollable = this.getScrollable().$element(); const cellHeight = this.getCellHeight(); const scrolledCellCount = this.getScrollableScrollTop() / cellHeight; const totalCellCount = scrolledCellCount + getHeight($scrollable) / cellHeight; - result.top = { - hours: Math.floor(scrolledCellCount * (this.option('hoursInterval') as any)) + (this.option('startDayHour') as any), - minutes: scrolledCellCount % 2 ? 30 : 0, - }; - - result.bottom = { - hours: Math.floor(totalCellCount * (this.option('hoursInterval') as any)) + (this.option('startDayHour') as any), - minutes: Math.floor(totalCellCount) % 2 ? 30 : 0, + const result = { + top: { + hours: Math.floor(scrolledCellCount * this.option('hoursInterval')) + this.option('startDayHour'), + minutes: scrolledCellCount % 2 ? 30 : 0, + }, + bottom: { + hours: Math.floor(totalCellCount * this.option('hoursInterval')) + this.option('startDayHour'), + minutes: Math.floor(totalCellCount) % 2 ? 30 : 0, + }, }; return result; } - updateScrollPosition(date, appointmentGroupValues?: GroupValues, allDay = false) { - const newDate = this.timeZoneCalculator.createDate(date, 'toGrid'); + updateScrollPosition(date: Date, appointmentGroupValues?: GroupValues, allDay = false): void { + const newDate = this.timeZoneCalculator?.createDate(date, 'toGrid') ?? date; const inAllDayRow = allDay && this.isAllDayPanelVisible; if (this.needUpdateScrollPosition(newDate, appointmentGroupValues, inAllDayRow)) { @@ -1831,7 +2019,11 @@ class SchedulerWorkSpace extends Widget { } } - needUpdateScrollPosition(date, appointmentGroupValues?: GroupValues, inAllDayRow = false) { + needUpdateScrollPosition( + date: Date, + appointmentGroupValues?: GroupValues, + inAllDayRow = false, + ): boolean { const cells = this.getCellsInViewport(inAllDayRow); const groupIndex = this.isGroupsSpecified(appointmentGroupValues) ? this.getGroupIndexByGroupValues(appointmentGroupValues) @@ -1859,7 +2051,7 @@ class SchedulerWorkSpace extends Widget { }, true); } - private getCellsInViewport(inAllDayRow) { + private getCellsInViewport(inAllDayRow: boolean): dxElementWrapper[] { const $scrollable = this.getScrollable().$element(); const cellHeight = this.getCellHeight(); const cellWidth = this.getCellWidth(); @@ -1867,7 +2059,8 @@ class SchedulerWorkSpace extends Widget { const scrollableScrollTop = this.getScrollableScrollTop(); const scrollableScrollLeft = this.getScrollableScrollLeft(); - const fullScrolledRowCount = scrollableScrollTop / cellHeight - this.virtualScrollingDispatcher.topVirtualRowsCount; + const fullScrolledRowCount = scrollableScrollTop / cellHeight + - this.virtualScrollingDispatcher.topVirtualRowsCount; let scrolledRowCount = Math.floor(fullScrolledRowCount); if (scrollableScrollTop % cellHeight !== 0) { @@ -1885,10 +2078,10 @@ class SchedulerWorkSpace extends Widget { const columnCount = Math.floor(fullScrolledColumnCount + getWidth($scrollable) / cellWidth); const $cells = this.getAllCells(inAllDayRow); - const result: any = []; + const result: dxElementWrapper[] = []; - $cells.each(function (index) { - const $cell = $(this); + $cells.toArray().forEach((_, index) => { + const $cell = $cells.eq(index); const columnIndex = index % totalColumnCount; const rowIndex = index / totalColumnCount; @@ -1903,7 +2096,7 @@ class SchedulerWorkSpace extends Widget { return result; } - scrollTo(date: Date, groupValues?: RawGroupValues | GroupValues, allDay = false, throwWarning = true, align: 'start' | 'center' = 'center') { + scrollTo(date: Date, groupValues?: RawGroupValues | GroupValues, allDay = false, throwWarning = true, align: 'start' | 'center' = 'center'): void { if (!this.isValidScrollDate(date, throwWarning)) { return; } @@ -1940,6 +2133,7 @@ class SchedulerWorkSpace extends Widget { } if (this.option('templatesRenderAsynchronously')) { + // eslint-disable-next-line no-restricted-globals setTimeout(() => { scrollable.scrollBy({ left, top }); }); @@ -1948,29 +2142,31 @@ class SchedulerWorkSpace extends Widget { } } - private isValidScrollDate(date, throwWarning = true) { + private isValidScrollDate(date: Date, throwWarning = true): boolean { const viewOffset = this.option('viewOffset'); const min = new Date(this.getStartViewDate().getTime() + viewOffset); const max = new Date(this.getEndViewDate().getTime() + viewOffset); if (date < min || date > max) { - throwWarning && errors.log('W1008', date); + if (throwWarning) { + errors.log('W1008', date); + } return false; } return true; } - needApplyCollectorOffset() { + needApplyCollectorOffset(): boolean { return false; } - removeDroppableCellClass($cellElement?: any) { - const $cell = $cellElement || this.getDroppableCell(); + removeDroppableCellClass($cellElement?: dxElementWrapper): void { + const $cell = $cellElement ?? this.getDroppableCell(); $cell?.removeClass(DATE_TABLE_DROPPABLE_CELL_CLASS); } - private getCoordinatesByCell($cell) { + private getCoordinatesByCell($cell: dxElementWrapper): CellPositionData { const columnIndex = $cell.index() - this.virtualScrollingDispatcher.leftVirtualCellsCount; let rowIndex = $cell.parent().index(); const isAllDayCell = this.hasAllDayClass($cell); @@ -1983,15 +2179,15 @@ class SchedulerWorkSpace extends Widget { return { rowIndex, columnIndex }; } - private isShowAllDayPanel() { + private isShowAllDayPanel(): boolean { return this.option('showAllDayPanel'); } - protected getTimePanelCells() { - return (this.$element() as any).find(`.${TIME_PANEL_CELL_CLASS}`); + protected getTimePanelCells(): dxElementWrapper { + return this.$element().find(`.${TIME_PANEL_CELL_CLASS}`); } - protected getRDateTableProps() { + protected getRDateTableProps(): Record { return { viewData: this.viewDataProvider.viewData, viewContext: this.getR1ComponentsViewContext(), @@ -2011,22 +2207,34 @@ class SchedulerWorkSpace extends Widget { }; } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - private updateSelectedCellDataOption(selectedCellData, $nextFocusedCell?: any) { - this.option('selectedCellData', selectedCellData); - this.selectionChangedAction({ selectedCellData }); + private updateSelectedCellDataOption( + selectedCellData: NormalizedCellData[], + // eslint-disable-next-line @typescript-eslint/no-unused-vars + nextFocusedCell?: dxElementWrapper, + ): void { + this.option('selectedCellData', selectedCellData as ViewCellData[]); + this.selectionChangedAction?.({ + selectedCellData: selectedCellData as ViewCellData[], + }); } - private fireSelectionEndEvent() { + private fireSelectionEndEvent(): void { const selectedCellData = this.option('selectedCellData') ?? []; if (selectedCellData.length > 0 && this.selectionEndAction) { this.selectionEndAction({ selectedCellData }); } } - private getCellByData(cellData) { + private getCellByData(cellData: NormalizedCellData | ViewCellData): dxElementWrapper | undefined { const { allDay } = cellData; - const position = this.viewDataProvider.findCellPositionInMap(cellData); + const position = this.viewDataProvider.findCellPositionInMap( + { + startDate: cellData.startDate, + allDay: cellData.allDay, + groupIndex: cellData.groupIndex, + index: cellData.index ?? 0, + }, + ); if (!position) { return undefined; @@ -2038,27 +2246,32 @@ class SchedulerWorkSpace extends Widget { } // Must replace all DOM manipulations - getDOMElementsMetaData() { + getDOMElementsMetaData(): DOMMetaData { return this.cache.memo('cellElementsMeta', () => ({ dateTableCellsMeta: this.getDateTableDOMElementsInfo(), allDayPanelCellsMeta: this.getAllDayPanelDOMElementsInfo(), })); } - getPanelDOMSize(panelName: 'allDayPanel' | 'regularPanel'): { width: number; height: number } { + private toRealSize(element: Element | undefined): RealSize { + const { width, height } = getBoundingRect(element) as Pick; + return { width, height }; + } + + getPanelDOMSize(panelName: PanelName): RealSize { return panelName === 'allDayPanel' - ? this.cache.memo('allDayPanelSize', () => getBoundingRect(this.$allDayPanel.get(0))) - : this.cache.memo('regularPanelSize', () => getBoundingRect(this.getDateTable().get(0))); + ? this.cache.memo('allDayPanelSize', () => this.toRealSize(this.$allDayPanel.get(0))) + : this.cache.memo('regularPanelSize', () => this.toRealSize(this.getDateTable().get(0))); } - getCollectorDimension(isCollectorCompact: boolean, panelName: 'allDayPanel' | 'regularPanel') { + getCollectorDimension(isCollectorCompact: boolean, panelName: PanelName): CollectorCSS { return this.cache.memo(`collectorSize-${panelName}`, () => CompactAppointmentsHelper.measureCollectorDimensions( panelName === 'allDayPanel' ? this.getAllDayContainer() : this.getFixedContainer(), isCollectorCompact, )); } - private getDateTableDOMElementsInfo() { + private getDateTableDOMElementsInfo(): CellRect[][] { const dateTableCells = this.getAllCells(false); if (!dateTableCells.length || !hasWindow()) { return [[{ @@ -2072,9 +2285,9 @@ class SchedulerWorkSpace extends Widget { const columnsCount = this.viewDataProvider.getColumnsCount(); - const result: any = []; + const result: CellRect[][] = []; - dateTableCells.each((index, cell) => { + dateTableCells.toArray().forEach((cell, index) => { const rowIndex = Math.floor(index / columnsCount); if (result.length === rowIndex) { @@ -2087,7 +2300,7 @@ class SchedulerWorkSpace extends Widget { return result; } - private getAllDayPanelDOMElementsInfo() { + private getAllDayPanelDOMElementsInfo(): CellRect[] { const result = []; if (this.isAllDayPanelVisible && !this.isVerticalGroupedWorkSpace() && hasWindow()) { @@ -2102,7 +2315,7 @@ class SchedulerWorkSpace extends Widget { const allDayAppointmentContainer = this.$allDayPanel; const allDayPanelRect = getBoundingRect(allDayAppointmentContainer.get(0)); - allDayCells.each((_, cell) => { + allDayCells.toArray().forEach((cell) => { this.addCellMetaData(result, cell, allDayPanelRect); }); } @@ -2110,7 +2323,11 @@ class SchedulerWorkSpace extends Widget { return result; } - private addCellMetaData(cellMetaDataArray, cell, parentRect) { + private addCellMetaData( + cellMetaDataArray: CellRect[], + cell: Element, + parentRect: Coordinates, + ): void { const cellRect = getBoundingRect(cell); cellMetaDataArray.push({ @@ -2130,7 +2347,7 @@ class SchedulerWorkSpace extends Widget { timePanel, dateTable, allDayPanel, - }: RenderComponentOptions = DEFAULT_WORKSPACE_RENDER_OPTIONS.renderComponents) { + }: RenderComponentOptions = DEFAULT_WORKSPACE_RENDER_OPTIONS.renderComponents): void { if (header) { this.renderRHeaderPanel(); } @@ -2148,7 +2365,7 @@ class SchedulerWorkSpace extends Widget { } } - renderRDateTable() { + renderRDateTable(): void { utils.renovation.renderComponent( this, this.$dateTable, @@ -2158,7 +2375,7 @@ class SchedulerWorkSpace extends Widget { ); } - renderRGroupPanel() { + renderRGroupPanel(): void { const options = { viewContext: this.getR1ComponentsViewContext(), groups: this.option('groups'), @@ -2173,19 +2390,22 @@ class SchedulerWorkSpace extends Widget { if (this.option('groups')?.length) { this.attachGroupCountClass(); - utils.renovation.renderComponent( - this, - this.getGroupHeaderContainer(), - GroupPanelComponent, - 'renovatedGroupPanel', - options, - ); + const $groupHeaderContainer = this.getGroupHeaderContainer(); + if ($groupHeaderContainer) { + utils.renovation.renderComponent( + this, + $groupHeaderContainer, + GroupPanelComponent, + 'renovatedGroupPanel', + options, + ); + } } else { this.detachGroupCountClass(); } } - renderRAllDayPanel() { + renderRAllDayPanel(): void { const visible = this.isAllDayPanelVisible && !this.isGroupedAllDayPanel(); if (visible) { @@ -2196,10 +2416,12 @@ class SchedulerWorkSpace extends Widget { viewContext: this.getR1ComponentsViewContext(), dataCellTemplate: this.option('dataCellTemplate'), startCellIndex: 0, - ...this.virtualScrollingDispatcher.horizontalVirtualScrolling?.getRenderState() || {}, + ...(this.virtualScrollingDispatcher.horizontalVirtualScrolling?.getRenderState() ?? {}), }; - utils.renovation.renderComponent(this, this.$allDayTable, AllDayTableComponent, 'renovatedAllDayPanel', options); + if (this.$allDayTable) { + utils.renovation.renderComponent(this, this.$allDayTable, AllDayTableComponent, 'renovatedAllDayPanel', options); + } utils.renovation.renderComponent(this, this.$allDayTitle, AllDayPanelTitleComponent, 'renovatedAllDayPanelTitle', {}); } @@ -2207,7 +2429,7 @@ class SchedulerWorkSpace extends Widget { this.updateScrollable(); } - renderRTimeTable() { + renderRTimeTable(): void { utils.renovation.renderComponent( this, this.$timePanel, @@ -2222,7 +2444,7 @@ class SchedulerWorkSpace extends Widget { ); } - renderRHeaderPanel(isRenderDateHeader = true) { + renderRHeaderPanel(isRenderDateHeader = true): void { if (this.option('groups')?.length) { this.attachGroupCountClass(); } else { @@ -2257,7 +2479,8 @@ class SchedulerWorkSpace extends Widget { } const point = this.getPointFromDragTarget($dragTarget); - const elements = (domAdapter as any).elementsFromPoint(point.x, point.y); + // @ts-expect-error + const elements = domAdapter.elementsFromPoint(point.x, point.y) as Element[]; const cell = elements.find((element) => element.classList.contains('dx-scheduler-date-table-cell') || element.classList.contains('dx-scheduler-all-day-table-cell')); @@ -2265,7 +2488,7 @@ class SchedulerWorkSpace extends Widget { return cell ? $(cell) : null; } - private getPointFromDragTarget($dragTarget: dxElementWrapper): { x: number; y: number } { + private getPointFromDragTarget($dragTarget: dxElementWrapper): TranslateVector { const THRESHOLD = 10; const dragElementContainer = $dragTarget.get(0); @@ -2300,7 +2523,7 @@ class SchedulerWorkSpace extends Widget { // ------------ // TODO: dragBehavior when old impl is removed - initDragBehavior(scheduler) { + initDragBehavior(scheduler: { element: () => Element }): void { if (!this.dragBehavior && scheduler) { this.dragBehavior = new AppointmentDragBehavior(scheduler); @@ -2313,9 +2536,19 @@ class SchedulerWorkSpace extends Widget { } } - private createDragBehavior($targetElement, $rootElement) { - const getItemData = (itemElement, appointments) => appointments._getItemData(itemElement); - const getItemSettings = ($itemElement) => $itemElement.data(APPOINTMENT_SETTINGS_KEY); + private createDragBehavior( + $targetElement: dxElementWrapper, + $rootElement: dxElementWrapper, + ): void { + const getItemData = ( + itemElement: Element | dxElementWrapper, + appointments: AppointmentsList, + ): unknown => appointments._getItemData(itemElement); + + const getItemSettings = ( + $itemElement: dxElementWrapper, + ): AppointmentViewModelPlain => ( + $itemElement.data(APPOINTMENT_SETTINGS_KEY) as unknown as AppointmentViewModelPlain); const options = { getItemData, @@ -2325,21 +2558,30 @@ class SchedulerWorkSpace extends Widget { this.createDragBehaviorBase($targetElement, $rootElement, options); } - protected createDragBehaviorBase(targetElement, rootElement, options) { - const container = (this.$element() as any).find(`.${FIXED_CONTAINER_CLASS}`); + protected createDragBehaviorBase( + targetElement: dxElementWrapper, + rootElement: dxElementWrapper, + options: DragBehaviorOptions, + ): void { + const container = this.$element().find(`.${FIXED_CONTAINER_CLASS}`); - const disableDefaultDragging = () => { + const disableDefaultDragging = (): void => { if (!this.isDefaultDraggingMode) { this.preventDefaultDragging = true; } }; - const enableDefaultDragging = () => { + const enableDefaultDragging = (): void => { if (!this.isDefaultDraggingMode) { this.preventDefaultDragging = false; } }; + if (!this.dragBehavior) { + return; + } + + // eslint-disable-next-line @typescript-eslint/no-use-before-define this.dragBehavior.addTo(targetElement, createDragBehaviorConfig( container, rootElement, @@ -2359,16 +2601,20 @@ class SchedulerWorkSpace extends Widget { // We do not need these methods in renovation // -------------- - protected isRenderHeaderPanelEmptyCell() { + protected isRenderHeaderPanelEmptyCell(): boolean { return this.isVerticalGroupedWorkSpace(); } - _dispose() { + _dispose(): void { // @ts-expect-error super._dispose(); if (this.documentPointerUpHandler) { - (eventsEngine.off as any)(domAdapter.getDocument(), SCHEDULER_TABLE_DXPOINTERUP_EVENT_NAME, this.documentPointerUpHandler); + eventsEngine.off( + domAdapter.getDocument(), + SCHEDULER_TABLE_DXPOINTERUP_EVENT_NAME, + this.documentPointerUpHandler, + ); this.documentPointerUpHandler = undefined; } this.virtualScrollingDispatcher.dispose(); @@ -2420,7 +2666,7 @@ class SchedulerWorkSpace extends Widget { schedulerWidth: undefined, }); - return defaultOptions; + return defaultOptions as WorkspaceOptionsInternal; } _optionChanged(args: OptionChanged): void { @@ -2517,12 +2763,12 @@ class SchedulerWorkSpace extends Widget { } } - updateShowAllDayPanel() { + updateShowAllDayPanel(): void { const isHiddenAllDayPanel = this.option('allDayPanelMode') === 'hidden'; - (this.option('onShowAllDayPanel') as any)(!isHiddenAllDayPanel); + this.option('onShowAllDayPanel')(!isHiddenAllDayPanel); } - private getVirtualScrollingDispatcherOptions() { + private getVirtualScrollingDispatcherOptions(): VirtualScrollingDispatcherOptions { return { getCellHeight: this.getCellHeight.bind(this), getCellWidth: this.getCellWidth.bind(this), @@ -2530,13 +2776,12 @@ class SchedulerWorkSpace extends Widget { isRTL: this.isRTL.bind(this), getSchedulerHeight: () => this.option('schedulerHeight'), getSchedulerWidth: () => this.option('schedulerWidth'), - getViewHeight: () => ((this.$element() as any).height ? (this.$element() as any).height() : getHeight(this.$element())), - getViewWidth: () => ((this.$element() as any).width ? (this.$element() as any).width() : getWidth(this.$element())), + getViewHeight: () => getHeight(this.$element()) as number, + getViewWidth: () => getWidth(this.$element()) as number, getWindowHeight: () => getWindow().innerHeight, getWindowWidth: () => getWindow().innerWidth, - getScrolling: () => this.option('scrolling'), + getScrolling: (): WorkspaceOptionsInternal['scrolling'] => this.option('scrolling'), getScrollableOuterWidth: this.getScrollableOuterWidth.bind(this), - getScrollable: this.getScrollable.bind(this), createAction: this._createAction.bind(this), updateRender: this.updateRender.bind(this), updateGrid: this.updateGrid.bind(this), @@ -2547,18 +2792,20 @@ class SchedulerWorkSpace extends Widget { }; } - protected cleanWorkSpace() { + protected cleanWorkSpace(): void { this.cleanView(); this.toggleGroupedClass(); this.toggleWorkSpaceWithOddCells(); this.virtualScrollingDispatcher.updateDimensions(true); this.renderView(); - this.option('crossScrollingEnabled') && this.setTableSizes(); + if (this.option('crossScrollingEnabled')) { + this.setTableSizes(); + } this.cache.clear(); } - _init() { + _init(): void { this.scrollSync = {}; this.viewDataProviderValue = null; this.cellsSelectionStateValue = null; @@ -2573,12 +2820,12 @@ class SchedulerWorkSpace extends Widget { this.toggleGroupByDateClass(); this.toggleWorkSpaceWithOddCells(); - (this.$element() as any) + this.$element() .addClass(COMPONENT_CLASS) .addClass(this.getElementClass()); } - private initPositionHelper() { + private initPositionHelper(): void { this.positionHelper = new PositionHelper({ viewDataProvider: this.viewDataProvider, isGroupedByDate: this.isGroupedByDate(), @@ -2590,13 +2837,13 @@ class SchedulerWorkSpace extends Widget { }); } - private initGrouping() { + private initGrouping(): void { this.initGroupedStrategy(); this.toggleGroupingDirectionClass(); this.toggleGroupByDateClass(); } - isVerticalOrientation() { + isVerticalOrientation(): boolean { const orientation = this.option('groups')?.length ? this.option('groupOrientation') : this.getDefaultGroupStrategy(); @@ -2604,7 +2851,7 @@ class SchedulerWorkSpace extends Widget { return orientation === 'vertical'; } - private initGroupedStrategy() { + private initGroupedStrategy(): void { const Strategy = this.isVerticalOrientation() ? VerticalGroupedStrategy : HorizontalGroupedStrategy; @@ -2612,44 +2859,52 @@ class SchedulerWorkSpace extends Widget { this.groupedStrategy = new Strategy(this); } - protected getDefaultGroupStrategy() { + protected getDefaultGroupStrategy(): GroupOrientation { return 'horizontal'; } - protected toggleHorizontalScrollClass() { - (this.$element() as any).toggleClass(WORKSPACE_WITH_BOTH_SCROLLS_CLASS, this.option('crossScrollingEnabled')); + protected toggleHorizontalScrollClass(): void { + this.$element().toggleClass(WORKSPACE_WITH_BOTH_SCROLLS_CLASS, this.option('crossScrollingEnabled')); } - private toggleGroupByDateClass() { - (this.$element() as any).toggleClass(WORKSPACE_WITH_GROUP_BY_DATE_CLASS, this.isGroupedByDate()); + private toggleGroupByDateClass(): void { + this.$element().toggleClass(WORKSPACE_WITH_GROUP_BY_DATE_CLASS, this.isGroupedByDate()); } - private toggleWorkSpaceCountClass() { - (this.$element() as any).toggleClass(WORKSPACE_WITH_COUNT_CLASS, this.isWorkSpaceWithCount()); + private toggleWorkSpaceCountClass(): void { + this.$element().toggleClass(WORKSPACE_WITH_COUNT_CLASS, this.isWorkSpaceWithCount()); } - protected toggleWorkSpaceWithOddCells() { - (this.$element() as any).toggleClass(WORKSPACE_WITH_ODD_CELLS_CLASS, this.isWorkspaceWithOddCells()); + protected toggleWorkSpaceWithOddCells(): void { + this.$element().toggleClass(WORKSPACE_WITH_ODD_CELLS_CLASS, this.isWorkspaceWithOddCells()); } - protected toggleGroupingDirectionClass() { - (this.$element() as any).toggleClass(VERTICAL_GROUPED_WORKSPACE_CLASS, this.isVerticalGroupedWorkSpace()); + protected toggleGroupingDirectionClass(): void { + this.$element().toggleClass( + VERTICAL_GROUPED_WORKSPACE_CLASS, + this.isVerticalGroupedWorkSpace(), + ); } - protected getDateTableCellClass(rowIndex?: any, columnIndex?: any) { + protected getDateTableCellClass(rowIndex?: number, columnIndex?: number): string { const cellClass = `${DATE_TABLE_CELL_CLASS} ${HORIZONTAL_SIZES_CLASS} ${VERTICAL_SIZES_CLASS}`; return this.groupedStrategy - .addAdditionalGroupCellClasses(cellClass, columnIndex + 1, rowIndex, columnIndex); + .addAdditionalGroupCellClasses( + cellClass, + (columnIndex ?? 0) + 1, + rowIndex ?? 0, + columnIndex ?? 0, + ); } - protected getGroupHeaderClass(i?: any) { + protected getGroupHeaderClass(i?: number): string { const cellClass = GROUP_HEADER_CLASS; - return this.groupedStrategy.addAdditionalGroupCellClasses(cellClass, i + 1); + return this.groupedStrategy.addAdditionalGroupCellClasses(cellClass, (i ?? 0) + 1, 0, 0); } - protected initWorkSpaceUnits() { + protected initWorkSpaceUnits(): void { this.$headerPanelContainer = $('
').addClass('dx-scheduler-header-panel-container'); this.$headerTablesContainer = $('
').addClass('dx-scheduler-header-tables-container'); this.$headerPanel = $('').attr('aria-hidden', true); @@ -2672,21 +2927,25 @@ class SchedulerWorkSpace extends Widget { this.$groupTable = $('
').addClass(WORKSPACE_VERTICAL_GROUP_TABLE_CLASS); } - private initAllDayPanelElements() { + private initAllDayPanelElements(): void { this.allDayTitles = []; this.allDayTables = []; this.allDayPanels = []; } - private initDateTableScrollable() { + private initDateTableScrollable(): void { const $dateTableScrollable = $('
').addClass(SCHEDULER_DATE_TABLE_SCROLLABLE_CLASS); // @ts-expect-error - this.$dateTableScrollable = this._createComponent($dateTableScrollable, Scrollable, this.dateTableScrollableConfig()); + this.$dateTableScrollable = this._createComponent( + $dateTableScrollable, + Scrollable, + this.dateTableScrollableConfig(), + ); this.scrollSync.dateTable = getMemoizeScrollTo(() => this.$dateTableScrollable); } - protected createWorkSpaceElements() { + protected createWorkSpaceElements(): void { if (this.option('crossScrollingEnabled')) { this.createWorkSpaceScrollableElements(); } else { @@ -2694,15 +2953,15 @@ class SchedulerWorkSpace extends Widget { } } - protected createWorkSpaceStaticElements() { + protected createWorkSpaceStaticElements(): void { this.$dateTableContainer.append(this.$dateTable); if (this.isVerticalGroupedWorkSpace()) { this.$dateTableContainer.append(this.$allDayContainer); this.$dateTableScrollableContent.append( - this.$groupTable, - this.$timePanel, - this.$dateTableContainer, + [this.$groupTable, + this.$timePanel, + this.$dateTableContainer].filter(Boolean) as dxElementWrapper[], ); this.$dateTableScrollable.$content().append( this.$dateTableScrollableContent, @@ -2711,13 +2970,14 @@ class SchedulerWorkSpace extends Widget { this.$headerTablesContainer.append(this.$headerPanel); } else { this.$dateTableScrollableContent.append( - this.$timePanel, - this.$dateTableContainer, + [this.$timePanel, this.$dateTableContainer], ); this.$dateTableScrollable.$content().append(this.$dateTableScrollableContent); - this.$headerTablesContainer.append(this.$headerPanel, this.$allDayPanel); - this.$allDayPanel?.append(this.$allDayContainer, this.$allDayTable); + this.$headerTablesContainer.append([this.$headerPanel, this.$allDayPanel]); + if (this.$allDayPanel && this.$allDayTable) { + this.$allDayPanel.append([this.$allDayContainer, this.$allDayTable]); + } } this.appendHeaderPanelEmptyCellIfNecessary(); @@ -2729,7 +2989,7 @@ class SchedulerWorkSpace extends Widget { .append(this.$dateTableScrollable.$element()); } - protected createWorkSpaceScrollableElements() { + protected createWorkSpaceScrollableElements(): void { this.$element().append(this.$fixedContainer); this.$flexContainer = $('
').addClass('dx-scheduler-work-space-flex-container'); @@ -2753,31 +3013,41 @@ class SchedulerWorkSpace extends Widget { if (this.isVerticalGroupedWorkSpace()) { this.$dateTableContainer.append(this.$allDayContainer); - this.$sidebarScrollableContent.append(this.$groupTable, this.$timePanel); + this.$sidebarScrollableContent.append( + [this.$groupTable, this.$timePanel].filter(Boolean) as dxElementWrapper[], + ); } else { this.headerScrollable.$content().append(this.$allDayPanel); - this.$allDayPanel?.append(this.$allDayContainer, this.$allDayTable); + if (this.$allDayPanel && this.$allDayTable) { + this.$allDayPanel.append([this.$allDayContainer, this.$allDayTable]); + } this.$sidebarScrollableContent.append(this.$timePanel); } this.$sidebarScrollable.$content().append(this.$sidebarScrollableContent); } - private appendHeaderPanelEmptyCellIfNecessary() { - this.isRenderHeaderPanelEmptyCell() && this.$headerPanelContainer.append(this.$headerPanelEmptyCell); + private appendHeaderPanelEmptyCellIfNecessary(): void { + if (this.isRenderHeaderPanelEmptyCell()) { + this.$headerPanelContainer.append(this.$headerPanelEmptyCell); + } } - private createHeaderScrollable() { + private createHeaderScrollable(): void { const $headerScrollable = $('
') .addClass(SCHEDULER_HEADER_SCROLLABLE_CLASS) .appendTo(this.$headerTablesContainer); // @ts-expect-error - this.headerScrollable = this._createComponent($headerScrollable, Scrollable, this.headerScrollableConfig()); + this.headerScrollable = this._createComponent( + $headerScrollable, + Scrollable, + this.headerScrollableConfig(), + ); this.scrollSync.header = getMemoizeScrollTo(() => this.headerScrollable); } - private createSidebarScrollable() { + private createSidebarScrollable(): void { const $timePanelScrollable = $('
') .addClass(SCHEDULER_SIDEBAR_SCROLLABLE_CLASS) .appendTo(this.$flexContainer); @@ -2791,33 +3061,35 @@ class SchedulerWorkSpace extends Widget { updateManually: true, bounceEnabled: false, onScroll: (event) => { - this.scrollSync.dateTable({ top: event.scrollOffset.top }); + this.scrollSync.dateTable?.({ top: event.scrollOffset.top }); }, }); this.scrollSync.sidebar = getMemoizeScrollTo(() => this.$sidebarScrollable); } - private attachTableClasses() { + private attachTableClasses(): void { this.addTableClass(this.$dateTable, DATE_TABLE_CLASS); if (this.isVerticalGroupedWorkSpace()) { const groupCount = this.getGroupCount(); - for (let i = 0; i < groupCount; i++) { + for (let i = 0; i < groupCount; i += 1) { this.addTableClass(this.allDayTables[i], ALL_DAY_TABLE_CLASS); } } } - private attachHeaderTableClasses() { + private attachHeaderTableClasses(): void { this.addTableClass(this.$headerPanel, HEADER_PANEL_CLASS); } - private addTableClass($el, className) { - ($el && !$el.hasClass(className)) && $el.addClass(className); + private addTableClass($el: dxElementWrapper | undefined, className: string): void { + if ($el && !$el.hasClass(className)) { + $el.addClass(className); + } } - _initMarkup() { + _initMarkup(): void { this.cache.clear(); this.initWorkSpaceUnits(); @@ -2842,18 +3114,18 @@ class SchedulerWorkSpace extends Widget { this.attachEvents(); } - _render() { + _render(): void { // @ts-expect-error super._render(); this.renderDateTimeIndication(); this.setIndicationUpdateInterval(); } - private toggleGroupedClass() { - (this.$element() as any).toggleClass(GROUPED_WORKSPACE_CLASS, this.getGroupCount() > 0); + private toggleGroupedClass(): void { + this.$element().toggleClass(GROUPED_WORKSPACE_CLASS, this.getGroupCount() > 0); } - protected renderView() { + protected renderView(): void { if (this.isVerticalGroupedWorkSpace()) { this.renderRGroupPanel(); } @@ -2867,7 +3139,7 @@ class SchedulerWorkSpace extends Widget { this.shader = new VerticalShader(this); } - updateCellsSelection() { + updateCellsSelection(): void { const renderOptions = this.generateRenderOptions(); this.viewDataProvider.updateViewData(renderOptions); this.renderRWorkSpace({ @@ -2877,24 +3149,26 @@ class SchedulerWorkSpace extends Widget { }); } - protected renderDateTimeIndication() { return noop(); } + protected renderDateTimeIndication(): void { return noop(); } protected renderCurrentDateTimeLineAndShader(): void { return noop(); } protected renderCurrentDateTimeIndication(): void { return noop(); } - protected setIndicationUpdateInterval() { return noop(); } + protected setIndicationUpdateInterval(): void { return noop(); } - protected detachGroupCountClass() { + protected detachGroupCountClass(): void { VERTICAL_GROUP_COUNT_CLASSES.forEach((className) => { this.$element().removeClass(className); }); } - protected attachGroupCountClass() { + protected attachGroupCountClass(): void { const className = this.groupedStrategy.getGroupCountClass(this.option('groups')); - this.$element().addClass(className); + if (className) { + this.$element().addClass(className); + } } protected getDateHeaderTemplate(): TemplateBase | null | undefined { @@ -2913,15 +3187,17 @@ class SchedulerWorkSpace extends Widget { this.$element().toggleClass(WORKSPACE_WITH_COLLAPSED_ALL_DAY_CLASS, isExpanded); } - private getDateTables() { - return this.$dateTable.add(this.$allDayTable); + private getDateTables(): dxElementWrapper { + return this.$allDayTable + ? this.$dateTable.add(this.$allDayTable) + : this.$dateTable; } - private getDateTable() { + private getDateTable(): dxElementWrapper { return this.$dateTable; } - private removeAllDayElements() { + private removeAllDayElements(): void { this.$allDayTable?.remove(); this.$allDayTitle?.remove(); } @@ -2936,21 +3212,21 @@ class SchedulerWorkSpace extends Widget { delete this.interval; } - _clean() { - (eventsEngine.off as any)(domAdapter.getDocument(), SCHEDULER_CELL_DXPOINTERUP_EVENT_NAME); + _clean(): void { + eventsEngine.off(domAdapter.getDocument(), SCHEDULER_CELL_DXPOINTERUP_EVENT_NAME); this.disposeRenovatedComponents(); // @ts-expect-error super._clean(); } - private cleanTableWidths() { + private cleanTableWidths(): void { this.$headerPanel.css('width', ''); this.$dateTable.css('width', ''); this.$allDayTable?.css('width', ''); } - private disposeRenovatedComponents() { + private disposeRenovatedComponents(): void { this.renovatedAllDayPanel?.dispose(); this.renovatedAllDayPanel = undefined; @@ -2967,29 +3243,29 @@ class SchedulerWorkSpace extends Widget { this.renovatedHeaderPanel = undefined; } - getGroupedStrategy() { + getGroupedStrategy(): HorizontalGroupedStrategy | VerticalGroupedStrategy { return this.groupedStrategy; } - getFixedContainer() { + getFixedContainer(): dxElementWrapper { return this.$fixedContainer; } - getAllDayContainer() { + getAllDayContainer(): dxElementWrapper | null { return this.$allDayContainer; } - updateRender() { + updateRender(): void { this.renderer.updateRender(); } - updateGrid() { + updateGrid(): void { this.renderer._renderGrid(); } - renderAppointments() { - (this.option('renderAppointments') as any)(); - this.dragBehavior?.updateDragSource(); + renderAppointments(): void { + (this.option().renderAppointments)(); + this.dragBehavior?.updateDragSource(undefined, undefined); } renderWorkSpace({ @@ -3005,14 +3281,17 @@ class SchedulerWorkSpace extends Widget { this.initPositionHelper(); } - protected renderGroupHeader() { + protected renderGroupHeader(): (() => dxElementWrapper)[] { const $container = this.getGroupHeaderContainer(); const groupCount = this.getGroupCount(); let cellTemplates: (() => dxElementWrapper)[] = []; - if (groupCount) { + if (groupCount && $container) { const groupRows = this.makeGroupRows(this.option('groups'), this.option('groupByDate')); this.attachGroupCountClass(); - $container.append(groupRows.elements); + const elements = Array.isArray(groupRows.elements) + ? groupRows.elements + : [groupRows.elements]; + $container.append(elements); cellTemplates = groupRows.cellTemplates; } else { this.detachGroupCountClass(); @@ -3021,14 +3300,15 @@ class SchedulerWorkSpace extends Widget { return cellTemplates; } - protected applyCellTemplates(templates) { + protected applyCellTemplates(templates: (() => void)[] | undefined): void { templates?.forEach((template) => { template(); }); } protected makeGroupRows(groups: ResourceLoader[], groupByDate: boolean): GroupRows { - const tableCreatorStrategy = this.isVerticalGroupedWorkSpace() ? tableCreator.VERTICAL : tableCreator.HORIZONTAL; + const tableCreatorStrategy = this.isVerticalGroupedWorkSpace() + ? tableCreator.VERTICAL : tableCreator.HORIZONTAL; return tableCreator.makeGroupedTable( tableCreatorStrategy, @@ -3046,7 +3326,10 @@ class SchedulerWorkSpace extends Widget { ); } - protected getGroupsForDateHeaderTemplate(templateIndex, indexMultiplier = 1) { + protected getGroupsForDateHeaderTemplate(templateIndex: number, indexMultiplier = 1): { + groups?: RawGroupValues | GroupValues; + groupIndex?: number; + } { if (this.isHorizontalGroupedWorkSpace() && !this.isGroupedByDate()) { const groupIndex = this.getGroupIndex(0, templateIndex * indexMultiplier); const groups = getLeafGroupValues(this.resourceManager.groupsLeafs, groupIndex); @@ -3063,49 +3346,95 @@ class SchedulerWorkSpace extends Widget { protected getHeaderPanelCellClass(i: number): string { const cellClass = `${HEADER_PANEL_CELL_CLASS} ${HORIZONTAL_SIZES_CLASS}`; - return this.groupedStrategy.addAdditionalGroupCellClasses(cellClass, i + 1, undefined, undefined, this.isGroupedByDate()); + return this.groupedStrategy.addAdditionalGroupCellClasses( + cellClass, + i + 1, + 0, + 0, + this.isGroupedByDate(), + ); } - protected insertAllDayRowsIntoDateTable() { + protected insertAllDayRowsIntoDateTable(): boolean { return this.groupedStrategy.insertAllDayRowsIntoDateTable(); } } +type DomAdapterWithElementsFromPoint = typeof domAdapter & { + elementsFromPoint: (x: number, y: number, element?: Element) => Element[]; +}; + +const domAdapterExt = domAdapter as DomAdapterWithElementsFromPoint; + +interface AppointmentsList { + option: (name: string) => { length: number }; + _renderItem: (index: number, data: Record) => dxElementWrapper; + // eslint-disable-next-line @typescript-eslint/naming-convention + _getItemData: (element: Element | dxElementWrapper) => unknown; +} + +interface DragBehaviorOptions { + getItemData: (itemElement: Element | dxElementWrapper, appointments: AppointmentsList) => unknown; + getItemSettings: ( + $itemElement: dxElementWrapper, + e?: Record, + ) => AppointmentViewModelPlain; + initialPosition?: Coordinates; + isSetCursorOffset?: boolean; + filter?: string; +} + // TODO: remove dragBehavior when old impl is removed const createDragBehaviorConfig = ( - container, - rootElement, - isDefaultDraggingMode, - dragBehavior, - enableDefaultDragging, - disableDefaultDragging, - getDroppableCell, - getDateTables, - removeDroppableCellClass, - getCellWidth, - options, -) => { - const state: any = { + container: dxElementWrapper, + rootElement: dxElementWrapper, + isDefaultDraggingMode: boolean, + dragBehavior: AppointmentDragBehavior, + enableDefaultDragging: () => void, + disableDefaultDragging: () => void, + getDroppableCell: () => dxElementWrapper, + getDateTables: () => dxElementWrapper, + removeDroppableCellClass: () => void, + getCellWidthCallback: () => number, + options: DragBehaviorOptions, +): { + container: dxElementWrapper; + dragTemplate: () => dxElementWrapper | undefined; + onDragStart: (e: AppointmentDraggingStartEvent) => void; + onDragMove: () => void; + onDragEnd: (e: AppointmentDraggingEndEvent) => void; + onDragCancel: (e: AppointmentDraggingRemoveEvent) => void; + cursorOffset: (() => TranslateVector) | undefined; + filter: string | undefined; +} => { + const state: { + dragElement: dxElementWrapper | undefined; + itemData: unknown; + } = { dragElement: undefined, itemData: undefined, }; - const isItemDisabled = () => { + const isItemDisabled = (): boolean => { const { itemData } = state; if (itemData) { - const getter: any = compileGetter('disabled'); + const getter = compileGetter('disabled') as (obj: unknown) => boolean; return getter(itemData); } return true; }; - const createDragAppointment = (itemData, settings, appointments) => { + const createDragAppointment = ( + itemData: unknown, + settings: AppointmentViewModelPlain, + appointments: AppointmentsList, + ): dxElementWrapper => { const appointmentIndex = appointments.option('items').length; const $item = appointments._renderItem(appointmentIndex, { - itemData, ...settings, + itemData, isCompact: false, virtual: false, sortedIndex: -1, @@ -3114,44 +3443,49 @@ const createDragBehaviorConfig = ( return $item; }; - const onDragStart = (e) => { + const onDragStart = (e: AppointmentDraggingStartEvent): void => { if (!isDefaultDraggingMode) { disableDefaultDragging(); } + if (!e.event) { + return; + } + const canceled = e.cancel; - const { event } = e; - const $itemElement = $(e.itemElement); - const appointments = e.component._appointments; + const $itemElement = $(e.itemElement as Element | null); + // @ts-expect-error + const appointments = (e.component)._appointments as AppointmentsList; - state.itemData = options.getItemData(e.itemElement, appointments); + state.itemData = options.getItemData(e.itemElement as Element, appointments); const settings = options.getItemSettings($itemElement, e); const { initialPosition } = options; if (!isItemDisabled()) { - event.data = event.data || {}; + e.event.data = e.event.data ?? {}; if (!canceled) { - if (!settings.isCompact) { + if (!('isCompact' in settings && settings.isCompact)) { dragBehavior.updateDragSource(state.itemData, settings); } state.dragElement = createDragAppointment(state.itemData, settings, appointments); - event.data.itemElement = state.dragElement; - event.data.initialPosition = initialPosition ?? locate($(state.dragElement)); - event.data.itemData = state.itemData; - event.data.itemSettings = settings; + e.event.data.itemElement = state.dragElement; + e.event.data.initialPosition = initialPosition + ?? locate($(state.dragElement)); + e.event.data.itemData = state.itemData; + e.event.data.itemSettings = settings; - dragBehavior.onDragStart(event.data); + dragBehavior.onDragStart(e.event.data); resetPosition($(state.dragElement)); } } }; - const getElementsFromPoint = () => { + const getElementsFromPoint = (): Element[] => { const appointmentWidth = getWidth(state.dragElement); - const cellWidth = getCellWidth(); + const cellWidth = getCellWidthCallback(); const isWideAppointment = appointmentWidth > cellWidth; const isNarrowAppointment = appointmentWidth <= DRAGGING_MOUSE_FAULT; const dragElementContainer = $(state.dragElement).parent().get(0); @@ -3160,14 +3494,22 @@ const createDragBehaviorConfig = ( const newY = boundingRect.top; if (isWideAppointment) { - return (domAdapter as any).elementsFromPoint(newX + DRAGGING_MOUSE_FAULT, newY + DRAGGING_MOUSE_FAULT, dragElementContainer); + return domAdapterExt.elementsFromPoint( + newX + DRAGGING_MOUSE_FAULT, + newY + DRAGGING_MOUSE_FAULT, + dragElementContainer, + ); } if (isNarrowAppointment) { - return (domAdapter as any).elementsFromPoint(newX, newY, dragElementContainer); + return domAdapterExt.elementsFromPoint(newX, newY, dragElementContainer); } - return (domAdapter as any).elementsFromPoint(newX + appointmentWidth / 2, newY + DRAGGING_MOUSE_FAULT, dragElementContainer); + return domAdapterExt.elementsFromPoint( + newX + appointmentWidth / 2, + newY + DRAGGING_MOUSE_FAULT, + dragElementContainer, + ); }; - const onDragMove = () => { + const onDragMove = (): void => { if (isDefaultDraggingMode) { return; } @@ -3190,7 +3532,7 @@ const createDragBehaviorConfig = ( }); if (droppableCell) { - if (!getDroppableCell().is(droppableCell)) { + if (!getDroppableCell().is(droppableCell as unknown as dxElementWrapper)) { removeDroppableCellClass(); } @@ -3200,7 +3542,7 @@ const createDragBehaviorConfig = ( } }; - const onDragEnd = (e) => { + const onDragEnd = (e: AppointmentDraggingEndEvent): void => { if (!isDefaultDraggingMode) { enableDefaultDragging(); } @@ -3213,17 +3555,17 @@ const createDragBehaviorConfig = ( removeDroppableCellClass(); }; - const onDragCancel = (e) => { + const onDragCancel = (e: AppointmentDraggingRemoveEvent): void => { if (!isDefaultDraggingMode) { enableDefaultDragging(); } removeDroppableCellClass(); - e.itemElement?.removeClass?.(APPOINTMENT_DRAG_SOURCE_CLASS); + (e.itemElement as unknown as dxElementWrapper)?.removeClass(APPOINTMENT_DRAG_SOURCE_CLASS); }; const cursorOffset = options.isSetCursorOffset - ? () => { + ? (): TranslateVector => { const $dragElement = $(state.dragElement); return { x: getWidth($dragElement) / 2, @@ -3234,7 +3576,7 @@ const createDragBehaviorConfig = ( return { container, - dragTemplate: () => state.dragElement, + dragTemplate: (): dxElementWrapper | undefined => state.dragElement, onDragStart, onDragMove, onDragEnd, diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space_grouped_strategy_horizontal.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space_grouped_strategy_horizontal.ts index a198f642f4de..619e5d3ca8f6 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space_grouped_strategy_horizontal.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space_grouped_strategy_horizontal.ts @@ -6,7 +6,7 @@ import type { GroupBoundsOffset, } from '@ts/scheduler/types'; import { WORK_SPACE_BORDER_PX } from '@ts/scheduler/workspaces/const'; -import type SchedulerWorkSpace from '@ts/scheduler/workspaces/m_work_space'; +import type SchedulerWorkSpace from '@ts/scheduler/workspaces/work_space'; import { FIRST_GROUP_CELL_CLASS, LAST_GROUP_CELL_CLASS } from '../classes'; import type { ResourceLoader } from '../utils/loader/resource_loader'; diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space_grouped_strategy_vertical.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space_grouped_strategy_vertical.ts index 2b771c2ee15b..836ad6e6b729 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space_grouped_strategy_vertical.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space_grouped_strategy_vertical.ts @@ -1,4 +1,3 @@ -import type { DxElement } from '@js/core/element'; import type { dxElementWrapper } from '@js/core/renderer'; import { getBoundingRect } from '@js/core/utils/position'; import { calculateDayDuration, getVerticalGroupCountClass } from '@ts/scheduler/r1/utils/index'; @@ -8,7 +7,7 @@ import { WORK_SPACE_BORDER_PX } from '@ts/scheduler/workspaces/const'; import { FIRST_GROUP_CELL_CLASS, LAST_GROUP_CELL_CLASS } from '../classes'; import { Cache } from '../global_cache'; import type { ResourceLoader } from '../utils/loader/resource_loader'; -import type SchedulerWorkSpace from './m_work_space'; +import type SchedulerWorkSpace from './work_space'; class VerticalGroupedStrategy { cache = new Cache(); @@ -92,7 +91,7 @@ class VerticalGroupedStrategy { return this.workspace.getTimePanelWidth() + this.workspace.getGroupTableWidth(); } - getGroupBoundsOffset(groupIndex: number, [$firstCell, $lastCell]: [DxElement, DxElement]) + getGroupBoundsOffset(groupIndex: number, [$firstCell, $lastCell]: [Element, Element]) : GroupBoundsOffset { return this.cache.memo(`groupBoundsOffset${groupIndex}`, () => { const startDayHour = this.workspace.option('startDayHour'); diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space_indicator.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space_indicator.ts index f16304fb5f15..c56cea319a43 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space_indicator.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space_indicator.ts @@ -13,7 +13,7 @@ import { HEADER_CURRENT_TIME_CELL_CLASS } from '../classes'; import timezoneUtils from '../utils_time_zone'; import SchedulerWorkSpace, { type WorkspaceOptionsInternal, -} from './m_work_space'; +} from './work_space'; const toMs = dateUtils.dateToMilliseconds; diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space_month.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space_month.ts index d6d02254304e..ef6ba62d1945 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space_month.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space_month.ts @@ -8,7 +8,7 @@ import { formatWeekday, monthUtils } from '@ts/scheduler/r1/utils/index'; import { utils } from '../utils'; import { VIEWS } from '../utils/options/constants_view'; -import type { ViewDateGenerationOptions } from './m_work_space'; +import type { ViewDateGenerationOptions } from './work_space'; import SchedulerWorkSpace from './work_space_indicator'; const MONTH_CLASS = 'dx-scheduler-work-space-month'; @@ -55,14 +55,15 @@ class SchedulerWorkSpaceMonth extends SchedulerWorkSpace { let averageWidth = 0; const cells = this.getCells().slice(0, DAYS_IN_WEEK); - cells.each((index, element) => { + interface EachCells { each: (fn: (index: number, el: Element) => void) => void; } + (cells as unknown as EachCells).each((_, element) => { averageWidth += hasWindow() ? getBoundingRect(element).width : 0; }); return cells.length === 0 ? 0 : averageWidth / DAYS_IN_WEEK; }); - return cellWidth as number; + return cellWidth; } protected override insertAllDayRowsIntoDateTable(): boolean { From 140a58f82f8c093f2896a53ef0e3f32675e6c71f Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii Date: Wed, 10 Jun 2026 19:12:30 +0200 Subject: [PATCH 02/22] fix: fix build --- .../shaders/current_time_shader_horizontal.ts | 2 +- .../shaders/current_time_shader_vertical.ts | 12 +++--- .../scheduler/workspaces/work_space.ts | 41 ++++++++++++------- .../scheduler/workspaces/work_space_month.ts | 11 +++-- .../workSpace.markup-0.tests.js | 2 +- 5 files changed, 42 insertions(+), 26 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/shaders/current_time_shader_horizontal.ts b/packages/devextreme/js/__internal/scheduler/shaders/current_time_shader_horizontal.ts index fdb256f73fd8..8ac262925dd6 100644 --- a/packages/devextreme/js/__internal/scheduler/shaders/current_time_shader_horizontal.ts +++ b/packages/devextreme/js/__internal/scheduler/shaders/current_time_shader_horizontal.ts @@ -2,7 +2,7 @@ import type { dxElementWrapper } from '@js/core/renderer'; import { getBoundingRect } from '@js/core/utils/position'; import { setWidth } from '@js/core/utils/size'; -import type SchedulerWorkSpace from '../workspaces/m_work_space'; +import type SchedulerWorkSpace from '../workspaces/work_space'; import CurrentTimeShader from './current_time_shader'; class HorizontalCurrentTimeShader extends CurrentTimeShader { diff --git a/packages/devextreme/js/__internal/scheduler/shaders/current_time_shader_vertical.ts b/packages/devextreme/js/__internal/scheduler/shaders/current_time_shader_vertical.ts index 18a62a49d5f1..f32ec8b2f58f 100644 --- a/packages/devextreme/js/__internal/scheduler/shaders/current_time_shader_vertical.ts +++ b/packages/devextreme/js/__internal/scheduler/shaders/current_time_shader_vertical.ts @@ -1,7 +1,7 @@ import $, { type dxElementWrapper } from '@js/core/renderer'; import { setHeight, setWidth } from '@js/core/utils/size'; -import type SchedulerWorkSpace from '../workspaces/m_work_space'; +import type SchedulerWorkSpace from '../workspaces/work_space'; import CurrentTimeShader from './current_time_shader'; const DATE_TIME_SHADER_ALL_DAY_CLASS = 'dx-scheduler-date-time-shader-all-day'; @@ -141,23 +141,23 @@ class VerticalCurrentTimeShader extends CurrentTimeShader { } private getShaderOffset(i: number, width: number): number { - return this.workSpace.getGroupedStrategy().getShaderOffset(i, width) as number; + return this.workSpace.getGroupedStrategy().getShaderOffset(i, width); } private getShaderTopOffset(i: number): number { - return this.workSpace.getGroupedStrategy().getShaderTopOffset(i) as number; + return this.workSpace.getGroupedStrategy().getShaderTopOffset(i); } private getShaderHeight(): number { - return this.workSpace.getGroupedStrategy().getShaderHeight() as number; + return this.workSpace.getGroupedStrategy().getShaderHeight(); } private getShaderMaxHeight(): number { - return this.workSpace.getGroupedStrategy().getShaderMaxHeight() as number; + return this.workSpace.getGroupedStrategy().getShaderMaxHeight(); } private getShaderWidth(): number { - return this.workSpace.getGroupedStrategy().getShaderWidth() as number; + return this.workSpace.getGroupedStrategy().getShaderWidth(); } clean(): void { diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts index 9d50392edab3..c6da9341f9a1 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts @@ -330,13 +330,13 @@ export interface WorkspaceOptionsInternal { } class SchedulerWorkSpace extends Widget { - private viewDataProviderValue: ViewDataProvider | null = null; + private viewDataProviderValue!: ViewDataProvider | null; - private cacheValue: Cache | null = null; + private cacheValue!: Cache | null; - private cellsSelectionStateValue: CellsSelectionState | null = null; + private cellsSelectionStateValue!: CellsSelectionState | null; - private cellsSelectionControllerValue: CellsSelectionController | null = null; + private cellsSelectionControllerValue!: CellsSelectionController | null; protected $dateTableScrollable!: Scrollable; @@ -370,7 +370,7 @@ class SchedulerWorkSpace extends Widget { public virtualScrollingDispatcher!: VirtualScrollingDispatcher; - private scrollSync: Partial = {}; + private scrollSync!: Partial; private $headerPanel!: dxElementWrapper; @@ -1260,16 +1260,23 @@ class SchedulerWorkSpace extends Widget { const firstCellData = selectedCells[0]; const lastCellData = selectedCells[selectedCells.length - 1]; - const result: NormalizedCellData = { + const result: { + startDate: Date; + endDate: Date; + startDateUTC: NormalizedCellData['startDateUTC']; + endDateUTC: NormalizedCellData['endDateUTC']; + allDay?: NormalizedCellData['allDay']; + } = { startDate: firstCellData.startDate, endDate: lastCellData.endDate, startDateUTC: firstCellData.startDateUTC, endDateUTC: lastCellData.endDateUTC, - groups: lastCellData.groups, - groupIndex: lastCellData.groupIndex, - allDay: lastCellData.allDay, }; + if (lastCellData.allDay !== undefined) { + result.allDay = lastCellData.allDay; + } + this.option('onSelectedCellsClick')(result, lastCellData.groups as GroupValues); } @@ -3207,6 +3214,10 @@ class SchedulerWorkSpace extends Widget { this.cleanTableWidths(); this.cellsSelectionState.clearSelectedAndFocusedCells(); + delete this.isCellClick; + delete this.showPopup; + this.isSelectionStartedOnCell = false; + this.shader?.clean(); delete this.interval; @@ -3288,10 +3299,12 @@ class SchedulerWorkSpace extends Widget { if (groupCount && $container) { const groupRows = this.makeGroupRows(this.option('groups'), this.option('groupByDate')); this.attachGroupCountClass(); - const elements = Array.isArray(groupRows.elements) - ? groupRows.elements - : [groupRows.elements]; - $container.append(elements); + const { elements } = groupRows; + if (Array.isArray(elements)) { + $container.append(elements); + } else { + $container.append(elements); + } cellTemplates = groupRows.cellTemplates; } else { this.detachGroupCountClass(); @@ -3561,7 +3574,7 @@ const createDragBehaviorConfig = ( } removeDroppableCellClass(); - (e.itemElement as unknown as dxElementWrapper)?.removeClass(APPOINTMENT_DRAG_SOURCE_CLASS); + $(e.itemElement as Element).removeClass(APPOINTMENT_DRAG_SOURCE_CLASS); }; const cursorOffset = options.isSetCursorOffset diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space_month.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space_month.ts index ef6ba62d1945..c5dc8cf5b091 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space_month.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space_month.ts @@ -55,10 +55,13 @@ class SchedulerWorkSpaceMonth extends SchedulerWorkSpace { let averageWidth = 0; const cells = this.getCells().slice(0, DAYS_IN_WEEK); - interface EachCells { each: (fn: (index: number, el: Element) => void) => void; } - (cells as unknown as EachCells).each((_, element) => { - averageWidth += hasWindow() ? getBoundingRect(element).width : 0; - }); + + cells.each( + (index: number, element: Element): boolean => { + averageWidth += hasWindow() ? getBoundingRect(element).width : 0; + return true; + }, + ); return cells.length === 0 ? 0 : averageWidth / DAYS_IN_WEEK; }); diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/workSpace.markup-0.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/workSpace.markup-0.tests.js index cf7aaf4e7190..45f731f4459e 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/workSpace.markup-0.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.scheduler/workSpace.markup-0.tests.js @@ -1,5 +1,5 @@ import $ from 'jquery'; -import SchedulerWorkSpace from '__internal/scheduler/workspaces/m_work_space'; +import SchedulerWorkSpace from '__internal/scheduler/workspaces/work_space'; import SchedulerWorkSpaceHorizontalStrategy from '__internal/scheduler/workspaces/work_space_grouped_strategy_horizontal'; import '__internal/scheduler/m_scheduler'; From 5ed657bef33dbdc83bb323862913bd8b467140a1 Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii Date: Thu, 11 Jun 2026 14:17:47 +0200 Subject: [PATCH 03/22] fix: fix header class --- .../js/__internal/scheduler/workspaces/work_space.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts index c6da9341f9a1..74d1f1d0bfa9 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts @@ -2908,7 +2908,11 @@ class SchedulerWorkSpace extends Widget { protected getGroupHeaderClass(i?: number): string { const cellClass = GROUP_HEADER_CLASS; - return this.groupedStrategy.addAdditionalGroupCellClasses(cellClass, (i ?? 0) + 1, 0, 0); + if (i === undefined) { + return cellClass; + } + + return this.groupedStrategy.addAdditionalGroupCellClasses(cellClass, i + 1, 0, 0); } protected initWorkSpaceUnits(): void { From 4ad272e0822c15c3246316bac030e328e60b485b Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii Date: Thu, 11 Jun 2026 15:05:06 +0200 Subject: [PATCH 04/22] refactor: remove // @ts-ignore --- .../scheduler/workspaces/work_space.ts | 265 +++++++++--------- 1 file changed, 129 insertions(+), 136 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts index 74d1f1d0bfa9..b54f0579d4ce 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts @@ -30,6 +30,7 @@ import { } from '@js/core/utils/size'; import { isDefined } from '@js/core/utils/type'; import { getWindow, hasWindow } from '@js/core/utils/window'; +import type { DxEvent, InitializedEventInfo } from '@js/events'; import type { AllDayPanelMode, AppointmentDraggingEndEvent, @@ -41,11 +42,11 @@ import type { } from '@js/ui/scheduler'; import type { ScrollEvent } from '@js/ui/scroll_view'; import errors from '@js/ui/widget/ui.errors'; -import Widget from '@js/ui/widget/ui.widget'; import type { TranslateVector } from '@ts/common/core/animation/translator'; import { getMemoizeScrollTo, type ScrollToFunc } from '@ts/core/utils/scroll'; import type { ActionConfig } from '@ts/core/widget/component'; import type { OptionChanged } from '@ts/core/widget/types'; +import Widget from '@ts/core/widget/widget'; import { AllDayPanelTitleComponent, AllDayTableComponent, @@ -307,7 +308,7 @@ export interface WorkspaceOptionsInternal { onShowAllDayPanel: (isVisible: boolean) => void; getHeaderHeight: (() => number); onScrollEnd: () => void; - onInitialized: (e: { element: dxElementWrapper }) => void; + onInitialized: (e: InitializedEventInfo) => void onDisposing: () => void; notifyScheduler: NotifyScheduler; @@ -475,7 +476,7 @@ class SchedulerWorkSpace extends Widget { } get resourceManager(): ResourceManager { - return this.option('getResourceManager')(); + return this.option().getResourceManager(); } get cellsSelectionState(): CellsSelectionState { @@ -483,7 +484,7 @@ class SchedulerWorkSpace extends Widget { this.cellsSelectionStateValue ??= new CellsSelectionState(this.viewDataProvider); if (isFirstInit) { - const selectedCellsOption = this.option('selectedCellData'); + const selectedCellsOption = this.option().selectedCellData; if (selectedCellsOption?.length > 0) { const validSelectedCells = selectedCellsOption.map((selectedCell) => { @@ -525,11 +526,11 @@ class SchedulerWorkSpace extends Widget { get renovatedHeaderPanelComponent(): typeof HeaderPanelComponent { return HeaderPanelComponent; } get timeZoneCalculator(): TimeZoneCalculator | undefined { - return this.option('timeZoneCalculator'); + return this.option().timeZoneCalculator; } get isDefaultDraggingMode(): boolean { - return this.option('draggingMode') === 'default'; + return this.option().draggingMode === 'default'; } notifyObserver( @@ -543,7 +544,7 @@ class SchedulerWorkSpace extends Widget { funcName: Subject, ...args: Parameters ): ReturnType | undefined { - const notifyScheduler = this.option('notifyScheduler'); + const { notifyScheduler } = this.option(); if (!notifyScheduler) { return undefined; @@ -592,7 +593,7 @@ class SchedulerWorkSpace extends Widget { if (focusedCellData) { const isAllDayPanelCell = focusedCellData.allDay && !this.isVerticalGroupedWorkSpace(); const isMultiSelection = e.shiftKey; - const isMultiSelectionAllowed = this.option('allowMultipleCellSelection'); + const isMultiSelectionAllowed = this.option().allowMultipleCellSelection; const isRTL = this.isRTL(); const groupCount = this.getGroupCount(); const isGroupedByDate = this.isGroupedByDate(); @@ -630,7 +631,6 @@ class SchedulerWorkSpace extends Widget { } }; - // @ts-expect-error Widget protected methods are not in d.ts return extend(super._supportedKeys(), { enter: clickHandler, space: clickHandler, @@ -650,7 +650,7 @@ class SchedulerWorkSpace extends Widget { } private isRTL(): boolean { - return this.option('rtlEnabled'); + return this.option().rtlEnabled; } private moveToCell($cell: dxElementWrapper, isMultiSelection: boolean): void { @@ -658,7 +658,7 @@ class SchedulerWorkSpace extends Widget { return; } - const isMultiSelectionAllowed = this.option('allowMultipleCellSelection'); + const isMultiSelectionAllowed = this.option().allowMultipleCellSelection; const currentCellData = this.getFullCellData($cell); if (!currentCellData) { return; @@ -747,7 +747,7 @@ class SchedulerWorkSpace extends Widget { return $cell.hasClass(ALL_DAY_TABLE_CELL_CLASS); } - _focusInHandler(e: FocusEvent): void { + _focusInHandler(e: DxEvent): void { const $target = $(e.target as Element | null); const $focusTarget = this._focusTarget(); // T1312256: On macOS, e.target can be a child element of the workspace root @@ -757,7 +757,6 @@ class SchedulerWorkSpace extends Widget { if (isTargetInsideWorkspace && this.isCellClick) { delete this.isCellClick; delete this.contextMenuHandled; - // @ts-expect-error Widget protected methods are not in d.ts super._focusInHandler(e); this.cellsSelectionState.restoreSelectedAndFocusedCells(); @@ -781,8 +780,7 @@ class SchedulerWorkSpace extends Widget { } } - _focusOutHandler(e: FocusEvent): void { - // @ts-expect-error Widget protected methods are not in d.ts + _focusOutHandler(e: DxEvent): void { super._focusOutHandler(e); if (!this.contextMenuHandled && !this._disposed) { @@ -798,19 +796,19 @@ class SchedulerWorkSpace extends Widget { } protected isVerticalGroupedWorkSpace(): boolean { // TODO move to the Model - return Boolean(this.option('groups')?.length) && this.option('groupOrientation') === 'vertical'; + return Boolean(this.option().groups?.length) && this.option().groupOrientation === 'vertical'; } protected isHorizontalGroupedWorkSpace(): boolean { - return Boolean(this.option('groups')?.length) && this.option('groupOrientation') === 'horizontal'; + return Boolean(this.option().groups?.length) && this.option().groupOrientation === 'horizontal'; } protected isWorkSpaceWithCount(): boolean { - return this.option('intervalCount') > 1; + return this.option().intervalCount > 1; } private isWorkspaceWithOddCells(): boolean { - return this.option('hoursInterval') === 0.5 && !this.isVirtualScrolling(); + return this.option().hoursInterval === 0.5 && !this.isVirtualScrolling(); } private getRealGroupOrientation(): 'vertical' | 'horizontal' { @@ -895,7 +893,7 @@ class SchedulerWorkSpace extends Widget { } }, onEnd: (): void => { - this.option('onScrollEnd')(); + this.option().onScrollEnd(); }, }; } @@ -965,13 +963,11 @@ class SchedulerWorkSpace extends Widget { } _dimensionChanged(): void { - // NOTE: It's a base widget method. Be careful :) - // @ts-expect-error if (!this._isVisible()) { return; } - if (this.option('crossScrollingEnabled')) { + if (this.option().crossScrollingEnabled) { this.setTableSizes(); } @@ -983,35 +979,35 @@ class SchedulerWorkSpace extends Widget { } protected needCreateCrossScrolling(): boolean { - return this.option('crossScrollingEnabled'); + return this.option().crossScrollingEnabled; } protected getElementClass(): string { noop(); return ''; } protected getRowCount(): number { return this.viewDataProvider.getRowCount({ - intervalCount: this.option('intervalCount'), - currentDate: this.option('currentDate'), + intervalCount: this.option().intervalCount, + currentDate: this.option().currentDate, viewType: this.type, - hoursInterval: this.option('hoursInterval'), - startDayHour: this.option('startDayHour'), - endDayHour: this.option('endDayHour'), + hoursInterval: this.option().hoursInterval, + startDayHour: this.option().startDayHour, + endDayHour: this.option().endDayHour, }); } protected getCellCount(): number { return this.viewDataProvider.getCellCount({ - intervalCount: this.option('intervalCount'), - currentDate: this.option('currentDate'), + intervalCount: this.option().intervalCount, + currentDate: this.option().currentDate, viewType: this.type, - hoursInterval: this.option('hoursInterval'), - startDayHour: this.option('startDayHour'), - endDayHour: this.option('endDayHour'), + hoursInterval: this.option().hoursInterval, + startDayHour: this.option().startDayHour, + endDayHour: this.option().endDayHour, }); } private isVirtualModeOn(): boolean { - return this.option('scrolling.mode') === 'virtual'; + return this.option().scrolling.mode === 'virtual'; } isVirtualScrolling(): boolean { @@ -1032,8 +1028,8 @@ class SchedulerWorkSpace extends Widget { isGroupedAllDayPanel(): boolean { return calculateIsGroupedAllDayPanel( - this.option('groups').length, - this.option('groupOrientation'), + this.option().groups.length, + this.option().groupOrientation, this.isAllDayPanelVisible, ); } @@ -1042,34 +1038,34 @@ class SchedulerWorkSpace extends Widget { const groupCount = this.getGroupCount(); const groupOrientation: GroupOrientation = groupCount > 0 - ? this.option('groupOrientation') + ? this.option().groupOrientation : this.getDefaultGroupStrategy(); const options = { - groupByDate: this.option('groupByDate'), + groupByDate: this.option().groupByDate, startRowIndex: 0, startCellIndex: 0, groupOrientation, today: this.getToday?.() ?? new Date(), - getResourceManager: this.option('getResourceManager'), + getResourceManager: this.option().getResourceManager, isProvideVirtualCellsWidth, isAllDayPanelVisible: this.isAllDayPanelVisible, selectedCells: this.cellsSelectionState.getSelectedCells(), focusedCell: this.cellsSelectionState.getFocusedCell(), headerCellTextFormat: this.getFormat(), getDateForHeaderText: (_: number, date: Date): Date => date, - viewOffset: this.option('viewOffset'), - startDayHour: this.option('startDayHour'), - endDayHour: this.option('endDayHour'), + viewOffset: this.option().viewOffset, + startDayHour: this.option().startDayHour, + endDayHour: this.option().endDayHour, cellDuration: this.getCellDuration(), viewType: this.type as ViewType, - intervalCount: this.option('intervalCount'), - hoursInterval: this.option('hoursInterval'), - currentDate: this.option('currentDate'), - startDate: this.option('startDate'), - firstDayOfWeek: this.option('firstDayOfWeek'), - showCurrentTimeIndicator: this.option('showCurrentTimeIndicator'), - skippedDays: this.option('skippedDays'), + intervalCount: this.option().intervalCount, + hoursInterval: this.option().hoursInterval, + currentDate: this.option().currentDate, + startDate: this.option().startDate, + firstDayOfWeek: this.option().firstDayOfWeek, + showCurrentTimeIndicator: this.option().showCurrentTimeIndicator, + skippedDays: this.option().skippedDays, ...this.virtualScrollingDispatcher.getRenderState(), }; @@ -1108,7 +1104,7 @@ class SchedulerWorkSpace extends Widget { } private isGroupsSpecified(groupValues?: GroupValues): number | GroupValues | undefined { - return this.option('groups')?.length && groupValues; + return this.option().groups?.length && groupValues; } private getGroupIndexByGroupValues( @@ -1122,15 +1118,15 @@ class SchedulerWorkSpace extends Widget { protected getViewStartByOptions(): Date { return getViewStartByOptions( - this.option('startDate'), - this.option('currentDate'), + this.option().startDate, + this.option().currentDate, this.getTotalViewDuration(), - this.option('startDate') ? this.calculateViewStartDate() : undefined, + this.option().startDate ? this.calculateViewStartDate() : undefined, ); } protected getTotalViewDuration(): number { - return this.viewDataProvider.getIntervalDuration(this.option('intervalCount')); + return this.viewDataProvider.getIntervalDuration(this.option().intervalCount); } protected getHeaderDate(): Date { @@ -1138,11 +1134,11 @@ class SchedulerWorkSpace extends Widget { } protected calculateViewStartDate(): Date | undefined { - return calculateViewStartDate(this.option('startDate')); + return calculateViewStartDate(this.option().startDate); } protected firstDayOfWeek(): number { - return this.viewDataProvider.getFirstDayOfWeek(this.option('firstDayOfWeek')); + return this.viewDataProvider.getFirstDayOfWeek(this.option().firstDayOfWeek); } protected attachEvents(): void { @@ -1277,7 +1273,7 @@ class SchedulerWorkSpace extends Widget { result.allDay = lastCellData.allDay; } - this.option('onSelectedCellsClick')(result, lastCellData.groups as GroupValues); + this.option().onSelectedCellsClick(result, lastCellData.groups as GroupValues); } private attachContextMenuEvent(): void { @@ -1335,9 +1331,9 @@ class SchedulerWorkSpace extends Widget { } protected getCellCountInDay(): number { - const hoursInterval = this.option('hoursInterval'); - const startDayHour = this.option('startDayHour'); - const endDayHour = this.option('endDayHour'); + const { hoursInterval } = this.option(); + const { startDayHour } = this.option(); + const { endDayHour } = this.option(); return this.viewDataProvider.getCellCountInDay(startDayHour, endDayHour, hoursInterval); } @@ -1365,8 +1361,8 @@ class SchedulerWorkSpace extends Widget { return viewDataGenerator.calculateEndDate( startDate, - viewDataGenerator.getInterval(this.option('hoursInterval')), - this.option('endDayHour'), + viewDataGenerator.getInterval(this.option().hoursInterval), + this.option().endDayHour, ); } @@ -1388,7 +1384,7 @@ class SchedulerWorkSpace extends Widget { } private attachDragEvents(element: dxElementWrapper): void { - if (this.option('newAppointments')) { + if (this.option().newAppointments) { return; } @@ -1542,14 +1538,14 @@ class SchedulerWorkSpace extends Widget { protected getDateGenerationOptions(): ViewDateGenerationOptions { return { - startDayHour: this.option('startDayHour'), - endDayHour: this.option('endDayHour'), - hoursInterval: this.option('hoursInterval'), - interval: this.viewDataProvider.viewDataGenerator?.getInterval(this.option('hoursInterval')), - intervalCount: this.option('intervalCount'), + startDayHour: this.option().startDayHour, + endDayHour: this.option().endDayHour, + hoursInterval: this.option().hoursInterval, + interval: this.viewDataProvider.viewDataGenerator?.getInterval(this.option().hoursInterval), + intervalCount: this.option().intervalCount, startViewDate: this.getStartViewDate(), firstDayOfWeek: this.firstDayOfWeek(), - skippedDays: this.option('skippedDays'), + skippedDays: this.option().skippedDays, viewOffset: 0, viewType: this.type as ViewType, }; @@ -1559,7 +1555,7 @@ class SchedulerWorkSpace extends Widget { protected getIntervalBetween(currentDate: Date, allDay: boolean): number { const firstViewDate = this.getStartViewDate(); - const startDayTime = this.option('startDayHour') * HOUR_MS; + const startDayTime = this.option().startDayHour * HOUR_MS; const timeZoneOffset = dateUtils.getTimezonesDifference(firstViewDate, currentDate); const fullInterval = currentDate.getTime() - firstViewDate.getTime() - timeZoneOffset; const days = this.getDaysOfInterval(fullInterval, startDayTime); @@ -1580,7 +1576,7 @@ class SchedulerWorkSpace extends Widget { return countSkippedDays( startDate, days, - this.option('skippedDays'), + this.option().skippedDays, ); } @@ -1685,7 +1681,7 @@ class SchedulerWorkSpace extends Widget { groupIndex?: number, allDay?: boolean, ): Coordinates | undefined { - const currentDate = date || new Date(this.option('currentDate')); + const currentDate = date || new Date(this.option().currentDate); const cell = this.viewDataProvider.findGlobalCellPosition( currentDate, @@ -1788,7 +1784,7 @@ class SchedulerWorkSpace extends Widget { } isGroupedByDate(): boolean { - return this.option('groupByDate') + return this.option().groupByDate && this.isHorizontalGroupedWorkSpace() && this.getGroupCount() > 0; } @@ -1799,8 +1795,11 @@ class SchedulerWorkSpace extends Widget { const timeInterval = inAllDayRow ? 24 * 60 * 60 * 1000 - : viewDataGenerator.getInterval(this.option('hoursInterval')); - const startViewDateOffset = getStartViewDateTimeOffset(this.getStartViewDate(), this.option('startDayHour')); + : viewDataGenerator.getInterval(this.option().hoursInterval); + const startViewDateOffset = getStartViewDateTimeOffset( + this.getStartViewDate(), + this.option().startDayHour, + ); const dateTimeStamp = this.getIntervalBetween(date, inAllDayRow ?? false) + startViewDateOffset; let index = Math.floor(dateTimeStamp / timeInterval); @@ -1852,7 +1851,7 @@ class SchedulerWorkSpace extends Widget { getAllDayHeight(): number { return getAllDayHeight( - this.option('showAllDayPanel'), + this.option().showAllDayPanel, this.isVerticalGroupedWorkSpace(), this.getDOMElementsMetaData(), ); @@ -1862,7 +1861,7 @@ class SchedulerWorkSpace extends Widget { return getMaxAllowedPosition( groupIndex, this.viewDataProvider, - this.option('rtlEnabled'), + this.option().rtlEnabled, this.getDOMElementsMetaData(), ); } @@ -1906,15 +1905,15 @@ class SchedulerWorkSpace extends Widget { } getEndViewDateByEndDayHour(): Date { - return this.viewDataProvider.getLastViewDateByEndDayHour(this.option('endDayHour')); + return this.viewDataProvider.getLastViewDateByEndDayHour(this.option().endDayHour); } getCellDuration(): number { return getCellDuration( this.type as ViewType, - this.option('startDayHour'), - this.option('endDayHour'), - this.option('hoursInterval'), + this.option().startDayHour, + this.option().endDayHour, + this.option().hoursInterval, ); } @@ -1925,9 +1924,9 @@ class SchedulerWorkSpace extends Widget { } getVisibleDayDuration(): number { - const startDayHour = this.option('startDayHour'); - const endDayHour = this.option('endDayHour'); - const hoursInterval = this.option('hoursInterval'); + const { startDayHour } = this.option(); + const { endDayHour } = this.option(); + const { hoursInterval } = this.option(); return this.viewDataProvider.getVisibleDayDuration(startDayHour, endDayHour, hoursInterval); } @@ -2005,11 +2004,13 @@ class SchedulerWorkSpace extends Widget { const result = { top: { - hours: Math.floor(scrolledCellCount * this.option('hoursInterval')) + this.option('startDayHour'), + hours: Math.floor(scrolledCellCount * this.option().hoursInterval) + + this.option().startDayHour, minutes: scrolledCellCount % 2 ? 30 : 0, }, bottom: { - hours: Math.floor(totalCellCount * this.option('hoursInterval')) + this.option('startDayHour'), + hours: Math.floor(totalCellCount * this.option().hoursInterval) + + this.option().startDayHour, minutes: Math.floor(totalCellCount) % 2 ? 30 : 0, }, }; @@ -2123,7 +2124,7 @@ class SchedulerWorkSpace extends Widget { const $scrollable = scrollable.$element(); const cellWidth = this.getCellWidth(); - const offset = this.option('rtlEnabled') + const offset = this.option().rtlEnabled ? cellWidth : 0; const scrollableHeight = getHeight($scrollable); @@ -2150,7 +2151,7 @@ class SchedulerWorkSpace extends Widget { } private isValidScrollDate(date: Date, throwWarning = true): boolean { - const viewOffset = this.option('viewOffset'); + const { viewOffset } = this.option(); const min = new Date(this.getStartViewDate().getTime() + viewOffset); const max = new Date(this.getEndViewDate().getTime() + viewOffset); @@ -2187,7 +2188,7 @@ class SchedulerWorkSpace extends Widget { } private isShowAllDayPanel(): boolean { - return this.option('showAllDayPanel'); + return this.option().showAllDayPanel; } protected getTimePanelCells(): dxElementWrapper { @@ -2198,9 +2199,9 @@ class SchedulerWorkSpace extends Widget { return { viewData: this.viewDataProvider.viewData, viewContext: this.getR1ComponentsViewContext(), - dataCellTemplate: this.option('dataCellTemplate'), - addDateTableClass: !this.option('crossScrollingEnabled') || this.isVirtualScrolling(), - groupOrientation: this.option('groupOrientation'), + dataCellTemplate: this.option().dataCellTemplate, + addDateTableClass: !this.option().crossScrollingEnabled || this.isVirtualScrolling(), + groupOrientation: this.option().groupOrientation, addVerticalSizesClassToRows: false, }; } @@ -2210,7 +2211,7 @@ class SchedulerWorkSpace extends Widget { view: { type: this.type as ViewType, }, - crossScrollingEnabled: Boolean(this.option('crossScrollingEnabled')), + crossScrollingEnabled: Boolean(this.option().crossScrollingEnabled), }; } @@ -2226,7 +2227,7 @@ class SchedulerWorkSpace extends Widget { } private fireSelectionEndEvent(): void { - const selectedCellData = this.option('selectedCellData') ?? []; + const selectedCellData = this.option().selectedCellData ?? []; if (selectedCellData.length > 0 && this.selectionEndAction) { this.selectionEndAction({ selectedCellData }); } @@ -2385,17 +2386,17 @@ class SchedulerWorkSpace extends Widget { renderRGroupPanel(): void { const options = { viewContext: this.getR1ComponentsViewContext(), - groups: this.option('groups'), - groupOrientation: this.option('groupOrientation'), + groups: this.option().groups, + groupOrientation: this.option().groupOrientation, groupByDate: this.isGroupedByDate(), - resourceCellTemplate: this.option('resourceCellTemplate'), + resourceCellTemplate: this.option().resourceCellTemplate, className: this.verticalGroupTableClass, groupPanelData: this.viewDataProvider.getGroupPanelData( this.generateRenderOptions(), ), }; - if (this.option('groups')?.length) { + if (this.option().groups?.length) { this.attachGroupCountClass(); const $groupHeaderContainer = this.getGroupHeaderContainer(); if ($groupHeaderContainer) { @@ -2421,7 +2422,7 @@ class SchedulerWorkSpace extends Widget { const options = { viewData: this.viewDataProvider.viewData, viewContext: this.getR1ComponentsViewContext(), - dataCellTemplate: this.option('dataCellTemplate'), + dataCellTemplate: this.option().dataCellTemplate, startCellIndex: 0, ...(this.virtualScrollingDispatcher.horizontalVirtualScrolling?.getRenderState() ?? {}), }; @@ -2445,14 +2446,14 @@ class SchedulerWorkSpace extends Widget { { viewContext: this.getR1ComponentsViewContext(), timePanelData: this.viewDataProvider.timePanelData, - timeCellTemplate: this.option('timeCellTemplate'), - groupOrientation: this.option('groupOrientation'), + timeCellTemplate: this.option().timeCellTemplate, + groupOrientation: this.option().groupOrientation, }, ); } renderRHeaderPanel(isRenderDateHeader = true): void { - if (this.option('groups')?.length) { + if (this.option().groups?.length) { this.attachGroupCountClass(); } else { this.detachGroupCountClass(); @@ -2469,12 +2470,12 @@ class SchedulerWorkSpace extends Widget { groupPanelData: this.viewDataProvider.getGroupPanelData( this.generateRenderOptions(), ), - dateCellTemplate: this.option('dateCellTemplate'), - timeCellTemplate: this.option('timeCellTemplate'), - groups: this.option('groups'), + dateCellTemplate: this.option().dateCellTemplate, + timeCellTemplate: this.option().timeCellTemplate, + groups: this.option().groups, groupByDate: this.isGroupedByDate(), - groupOrientation: this.option('groupOrientation'), - resourceCellTemplate: this.option('resourceCellTemplate'), + groupOrientation: this.option().groupOrientation, + resourceCellTemplate: this.option().resourceCellTemplate, isRenderDateHeader, }, ); @@ -2613,7 +2614,6 @@ class SchedulerWorkSpace extends Widget { } _dispose(): void { - // @ts-expect-error super._dispose(); if (this.documentPointerUpHandler) { @@ -2628,7 +2628,6 @@ class SchedulerWorkSpace extends Widget { } _getDefaultOptions(): WorkspaceOptionsInternal { - // @ts-expect-error const defaultOptions = extend(super._getDefaultOptions(), { currentDate: new Date(), intervalCount: 1, @@ -2748,7 +2747,6 @@ class SchedulerWorkSpace extends Widget { this.renderAppointments(); break; case 'width': - // @ts-expect-error super._optionChanged(args); this._dimensionChanged(); break; @@ -2765,14 +2763,13 @@ class SchedulerWorkSpace extends Widget { this.virtualScrollingDispatcher.updateDimensions(true); break; default: - // @ts-expect-error super._optionChanged(args); } } updateShowAllDayPanel(): void { - const isHiddenAllDayPanel = this.option('allDayPanelMode') === 'hidden'; - this.option('onShowAllDayPanel')(!isHiddenAllDayPanel); + const isHiddenAllDayPanel = this.option().allDayPanelMode === 'hidden'; + this.option().onShowAllDayPanel(!isHiddenAllDayPanel); } private getVirtualScrollingDispatcherOptions(): VirtualScrollingDispatcherOptions { @@ -2781,13 +2778,13 @@ class SchedulerWorkSpace extends Widget { getCellWidth: this.getCellWidth.bind(this), getCellMinWidth: this.getCellMinWidth.bind(this), isRTL: this.isRTL.bind(this), - getSchedulerHeight: () => this.option('schedulerHeight'), - getSchedulerWidth: () => this.option('schedulerWidth'), + getSchedulerHeight: () => this.option().schedulerHeight, + getSchedulerWidth: () => this.option().schedulerWidth, getViewHeight: () => getHeight(this.$element()) as number, getViewWidth: () => getWidth(this.$element()) as number, getWindowHeight: () => getWindow().innerHeight, getWindowWidth: () => getWindow().innerWidth, - getScrolling: (): WorkspaceOptionsInternal['scrolling'] => this.option('scrolling'), + getScrolling: (): WorkspaceOptionsInternal['scrolling'] => this.option().scrolling, getScrollableOuterWidth: this.getScrollableOuterWidth.bind(this), createAction: this._createAction.bind(this), updateRender: this.updateRender.bind(this), @@ -2806,7 +2803,7 @@ class SchedulerWorkSpace extends Widget { this.virtualScrollingDispatcher.updateDimensions(true); this.renderView(); - if (this.option('crossScrollingEnabled')) { + if (this.option().crossScrollingEnabled) { this.setTableSizes(); } this.cache.clear(); @@ -2817,7 +2814,6 @@ class SchedulerWorkSpace extends Widget { this.viewDataProviderValue = null; this.cellsSelectionStateValue = null; - // @ts-expect-error super._init(); this.initGrouping(); @@ -2836,7 +2832,7 @@ class SchedulerWorkSpace extends Widget { this.positionHelper = new PositionHelper({ viewDataProvider: this.viewDataProvider, isGroupedByDate: this.isGroupedByDate(), - rtlEnabled: this.option('rtlEnabled'), + rtlEnabled: this.option().rtlEnabled, isVerticalGrouping: this.isVerticalGroupedWorkSpace(), groupCount: this.getGroupCount(), isVirtualScrolling: this.isVirtualScrolling(), @@ -2851,8 +2847,8 @@ class SchedulerWorkSpace extends Widget { } isVerticalOrientation(): boolean { - const orientation = this.option('groups')?.length - ? this.option('groupOrientation') + const orientation = this.option().groups?.length + ? this.option().groupOrientation : this.getDefaultGroupStrategy(); return orientation === 'vertical'; @@ -2871,7 +2867,10 @@ class SchedulerWorkSpace extends Widget { } protected toggleHorizontalScrollClass(): void { - this.$element().toggleClass(WORKSPACE_WITH_BOTH_SCROLLS_CLASS, this.option('crossScrollingEnabled')); + this.$element().toggleClass( + WORKSPACE_WITH_BOTH_SCROLLS_CLASS, + this.option().crossScrollingEnabled, + ); } private toggleGroupByDateClass(): void { @@ -2947,7 +2946,6 @@ class SchedulerWorkSpace extends Widget { private initDateTableScrollable(): void { const $dateTableScrollable = $('
').addClass(SCHEDULER_DATE_TABLE_SCROLLABLE_CLASS); - // @ts-expect-error this.$dateTableScrollable = this._createComponent( $dateTableScrollable, Scrollable, @@ -2957,7 +2955,7 @@ class SchedulerWorkSpace extends Widget { } protected createWorkSpaceElements(): void { - if (this.option('crossScrollingEnabled')) { + if (this.option().crossScrollingEnabled) { this.createWorkSpaceScrollableElements(); } else { this.createWorkSpaceStaticElements(); @@ -3049,7 +3047,6 @@ class SchedulerWorkSpace extends Widget { .addClass(SCHEDULER_HEADER_SCROLLABLE_CLASS) .appendTo(this.$headerTablesContainer); - // @ts-expect-error this.headerScrollable = this._createComponent( $headerScrollable, Scrollable, @@ -3063,7 +3060,6 @@ class SchedulerWorkSpace extends Widget { .addClass(SCHEDULER_SIDEBAR_SCROLLABLE_CLASS) .appendTo(this.$flexContainer); - // @ts-expect-error this.$sidebarScrollable = this._createComponent($timePanelScrollable, Scrollable, { useKeyboard: false, showScrollbar: 'never', @@ -3111,10 +3107,9 @@ class SchedulerWorkSpace extends Widget { this.createWorkSpaceElements(); - // @ts-expect-error super._initMarkup(); - if (!this.option('crossScrollingEnabled')) { + if (!this.option().crossScrollingEnabled) { this.attachTableClasses(); this.attachHeaderTableClasses(); } @@ -3126,7 +3121,6 @@ class SchedulerWorkSpace extends Widget { } _render(): void { - // @ts-expect-error super._render(); this.renderDateTimeIndication(); this.setIndicationUpdateInterval(); @@ -3175,7 +3169,7 @@ class SchedulerWorkSpace extends Widget { } protected attachGroupCountClass(): void { - const className = this.groupedStrategy.getGroupCountClass(this.option('groups')); + const className = this.groupedStrategy.getGroupCountClass(this.option().groups); if (className) { this.$element().addClass(className); @@ -3183,7 +3177,7 @@ class SchedulerWorkSpace extends Widget { } protected getDateHeaderTemplate(): TemplateBase | null | undefined { - return this.option('dateCellTemplate'); + return this.option().dateCellTemplate; } protected updateAllDayVisibility(): void { @@ -3192,7 +3186,7 @@ class SchedulerWorkSpace extends Widget { } private updateAllDayExpansion(): void { - const isExpanded = !this.option('allDayExpanded') && this.isShowAllDayPanel(); + const isExpanded = !this.option().allDayExpanded && this.isShowAllDayPanel(); this.cache.clear(); this.$element().toggleClass(WORKSPACE_WITH_COLLAPSED_ALL_DAY_CLASS, isExpanded); @@ -3231,7 +3225,6 @@ class SchedulerWorkSpace extends Widget { eventsEngine.off(domAdapter.getDocument(), SCHEDULER_CELL_DXPOINTERUP_EVENT_NAME); this.disposeRenovatedComponents(); - // @ts-expect-error super._clean(); } @@ -3301,7 +3294,7 @@ class SchedulerWorkSpace extends Widget { const groupCount = this.getGroupCount(); let cellTemplates: (() => dxElementWrapper)[] = []; if (groupCount && $container) { - const groupRows = this.makeGroupRows(this.option('groups'), this.option('groupByDate')); + const groupRows = this.makeGroupRows(this.option().groups, this.option().groupByDate); this.attachGroupCountClass(); const { elements } = groupRows; if (Array.isArray(elements)) { @@ -3337,7 +3330,7 @@ class SchedulerWorkSpace extends Widget { groupHeaderContentClass: GROUP_HEADER_CONTENT_CLASS, }, this.getCellCount() || 1, - this.option('resourceCellTemplate'), + this.option().resourceCellTemplate, this.getGroupCount(), groupByDate, ); From 236c0b72b611afb55303504c374a44bd608b98a7 Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii Date: Thu, 11 Jun 2026 15:13:57 +0200 Subject: [PATCH 05/22] refactor: remove dead code --- .../scheduler/workspaces/work_space.ts | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts index b54f0579d4ce..8c3fc581e837 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts @@ -430,12 +430,8 @@ class SchedulerWorkSpace extends Widget { private $sidebarScrollableContent!: dxElementWrapper; - private allDayTitles!: dxElementWrapper[]; - private allDayTables!: dxElementWrapper[]; - private allDayPanels!: dxElementWrapper[]; - protected $flexContainer!: dxElementWrapper; protected shader!: VerticalShader | HorizontalCurrentTimeShader; @@ -811,12 +807,6 @@ class SchedulerWorkSpace extends Widget { return this.option().hoursInterval === 0.5 && !this.isVirtualScrolling(); } - private getRealGroupOrientation(): 'vertical' | 'horizontal' { - return this.isVerticalGroupedWorkSpace() - ? 'vertical' - : 'horizontal'; - } - createRAllDayPanelElements(): void { this.$allDayPanel = $('
').addClass(ALL_DAY_PANEL_CLASS); this.$allDayTitle = $('
').appendTo(this.$headerPanelEmptyCell); @@ -1311,14 +1301,6 @@ class SchedulerWorkSpace extends Widget { return this.$thead; } - private getDateHeaderContainer(): dxElementWrapper { - return this.$thead; - } - - private getCalculateHeaderCellRepeatCount(): number { - return this.groupedStrategy.calculateHeaderCellRepeatCount(); - } - protected updateScrollable(): void { this.$dateTableScrollable.update(); this.headerScrollable?.update(); @@ -1763,14 +1745,6 @@ class SchedulerWorkSpace extends Widget { return undefined; } - private getVirtualRowOffset(): number { - return this.virtualScrollingDispatcher.virtualRowOffset; - } - - private getVirtualCellOffset(): number { - return this.virtualScrollingDispatcher.virtualCellOffset; - } - private getDataByCell($cell: dxElementWrapper): ViewCellData | undefined { const rowIndex = $cell.parent().index() - this.virtualScrollingDispatcher.topVirtualRowsCount; const columnIndex = $cell.index() - this.virtualScrollingDispatcher.leftVirtualCellsCount; @@ -2938,9 +2912,7 @@ class SchedulerWorkSpace extends Widget { } private initAllDayPanelElements(): void { - this.allDayTitles = []; this.allDayTables = []; - this.allDayPanels = []; } private initDateTableScrollable(): void { From 5ed6bfe4b2beb2d2aa34ccbfdcced13f3c6191f1 Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii Date: Thu, 11 Jun 2026 15:27:42 +0200 Subject: [PATCH 06/22] refactor: type options for workspaces module --- .../__internal/scheduler/workspaces/agenda.ts | 34 ++++++++++--------- .../scheduler/workspaces/timeline.ts | 19 +++++------ .../scheduler/workspaces/timeline_month.ts | 10 +++--- .../scheduler/workspaces/timeline_week.ts | 2 +- .../scheduler/workspaces/work_space.ts | 4 +-- .../scheduler/workspaces/work_space_day.ts | 2 +- .../work_space_grouped_strategy_vertical.ts | 12 +++---- .../workspaces/work_space_indicator.ts | 10 +++--- .../scheduler/workspaces/work_space_month.ts | 12 +++---- .../scheduler/workspaces/work_space_week.ts | 4 +-- 10 files changed, 54 insertions(+), 55 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/agenda.ts b/packages/devextreme/js/__internal/scheduler/workspaces/agenda.ts index 7bbcccd44745..594ac9b11938 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/agenda.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/agenda.ts @@ -135,7 +135,7 @@ class SchedulerAgenda extends WorkSpace { } protected override getRowCount(): number { - return this.option('agendaDuration'); + return this.option().agendaDuration; } getCellCount(): number { @@ -143,7 +143,7 @@ class SchedulerAgenda extends WorkSpace { } protected override getTimePanelRowCount(): number { - return this.option('agendaDuration'); + return this.option().agendaDuration; } protected renderAllDayPanel(): void { return noop(); } @@ -159,7 +159,7 @@ class SchedulerAgenda extends WorkSpace { } private initGroupTable(): void { - const groups = this.option('groups'); + const { groups } = this.option(); if (groups?.length) { this.$groupTable = $('
').attr('aria-hidden', true).addClass(GROUP_TABLE_CLASS); } @@ -167,8 +167,8 @@ class SchedulerAgenda extends WorkSpace { protected override renderView(): void { this.startViewDate = agendaUtils.calculateStartViewDate( - this.option('currentDate'), - this.option('startDayHour'), + this.option().currentDate, + this.option().startDayHour, ); this.rows = []; } @@ -196,7 +196,7 @@ class SchedulerAgenda extends WorkSpace { private renderNoData(): void { this.$noDataContainer = $('
').addClass(NODATA_CONTAINER_CLASS) - .html(this.option('noDataText')); + .html(this.option().noDataText); this.$dateTableScrollable.$content().append(this.$noDataContainer); } @@ -230,7 +230,7 @@ class SchedulerAgenda extends WorkSpace { } protected override attachGroupCountClass(): void { - const className = getVerticalGroupCountClass(this.option('groups')); + const className = getVerticalGroupCountClass(this.option().groups); if (className) { this.$element().addClass(className); } @@ -246,8 +246,8 @@ class SchedulerAgenda extends WorkSpace { } protected override makeGroupRows(): GroupRows { - const resourceManager = this.option('getResourceManager')(); - const allAppointments = (this.option('getFilteredItems') as () => ListEntity[])(); + const resourceManager = this.option().getResourceManager(); + const allAppointments = this.option().getFilteredItems(); const tree = reduceResourcesTree( resourceManager.resourceById, resourceManager.groupsTree, @@ -278,7 +278,9 @@ class SchedulerAgenda extends WorkSpace { const resourceItem = resource?.items .find((rItem) => rItem.id === value); + // @ts-expect-error if (cellTemplate?.render) { + // @ts-expect-error cellTemplates.push(cellTemplate.render.bind(cellTemplate, { model: { data: resourceData, @@ -459,7 +461,7 @@ class SchedulerAgenda extends WorkSpace { cellCount: 1, rowClass: TIME_PANEL_ROW_CLASS, cellClass: TIME_PANEL_CELL_CLASS, - cellTemplate: this.option('dateCellTemplate'), + cellTemplate: this.option().dateCellTemplate, getStartDate: this.getTimePanelStartDate.bind(this), }); } @@ -472,7 +474,7 @@ class SchedulerAgenda extends WorkSpace { } private getRowHeight(rowSize: number): number { - const baseHeight = this.option('rowHeight'); + const baseHeight = this.option().rowHeight; const innerOffset = (rowSize - 1) * INNER_CELL_MARGIN; return rowSize ? (baseHeight * rowSize) + innerOffset + OUTER_CELL_MARGIN : 0; @@ -491,7 +493,7 @@ class SchedulerAgenda extends WorkSpace { const rows = agendaUtils.calculateRows( appointments, - this.option('agendaDuration'), + this.option().agendaDuration, this.getStartViewDate(), this.resourceManager.groupCount(), ); @@ -499,14 +501,14 @@ class SchedulerAgenda extends WorkSpace { } getAgendaVerticalStepHeight(): number { - return this.option('rowHeight'); + return this.option().rowHeight; } getEndViewDate(): Date { return agendaUtils.calculateEndViewDate( this.getStartViewDate(), - this.option('endDayHour'), - this.option('agendaDuration'), + this.option().endDayHour, + this.option().agendaDuration, ); } @@ -548,7 +550,7 @@ class SchedulerAgenda extends WorkSpace { override isVirtualScrolling(): boolean { return false; } protected override getTotalViewDuration(): number { - return dateUtils.dateToMilliseconds('day') * this.option('intervalCount'); + return dateUtils.dateToMilliseconds('day') * this.option().intervalCount; } getDOMElementsMetaData(): { diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/timeline.ts b/packages/devextreme/js/__internal/scheduler/workspaces/timeline.ts index ef6c6e700d65..bf52d84ecc0f 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/timeline.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/timeline.ts @@ -118,7 +118,7 @@ class SchedulerTimeline extends SchedulerWorkSpace { } protected incrementDate(date: Date): void { - const skippedDays = this.option('skippedDays') ?? []; + const skippedDays = this.option().skippedDays ?? []; const nextDate = new Date(date); nextDate.setDate(nextDate.getDate() + 1); @@ -161,9 +161,9 @@ class SchedulerTimeline extends SchedulerWorkSpace { this.getIndicationFirstViewDate(), differenceInDays, ); - let duration = (timeDiff - differenceInDays * toMs('day') - this.option('startDayHour') * toMs('hour')) / this.getCellDuration(); + let duration = (timeDiff - differenceInDays * toMs('day') - this.option().startDayHour * toMs('hour')) / this.getCellDuration(); - if (today.getHours() > this.option('endDayHour')) { + if (today.getHours() > this.option().endDayHour) { duration = this.getCellCountInDay(); } @@ -245,8 +245,7 @@ class SchedulerTimeline extends SchedulerWorkSpace { } protected override getIntervalBetween(currentDate: Date, allDay?: boolean): number { - const startDayHour = this.option('startDayHour'); - const endDayHour = this.option('endDayHour'); + const { startDayHour, endDayHour } = this.option(); const firstViewDate = this.getStartViewDate(); const firstViewDateTime = firstViewDate.getTime(); const hiddenInterval = (24 - endDayHour + startDayHour) * toMs('hour'); @@ -259,7 +258,7 @@ class SchedulerTimeline extends SchedulerWorkSpace { const skippedDaysCount = this.getSkippedDaysCount(firstViewDate, fullDays); const cellCount = this.getCellCountInDay() * (fullDays - skippedDaysCount); const gapBeforeAppt = apptStart - dateUtils.trimTime(new Date(currentDate)).getTime(); - let result = cellCount * this.option('hoursInterval') * toMs('hour'); + let result = cellCount * this.option().hoursInterval * toMs('hour'); if (!allDay) { const hour = currentDate.getHours(); @@ -358,7 +357,7 @@ class SchedulerTimeline extends SchedulerWorkSpace { protected override updateAllDayVisibility(): void { return noop(); } protected override getDateHeaderTemplate(): TemplateBase | null | undefined { - return this.option('timeCellTemplate'); + return this.option().timeCellTemplate; } protected override renderView(): void { @@ -411,7 +410,7 @@ class SchedulerTimeline extends SchedulerWorkSpace { let $indicator: dxElementWrapper | undefined; const width = this.getIndicationWidth(); - if (this.option('groupOrientation') === 'vertical') { + if (this.option().groupOrientation === 'vertical') { $indicator = this.createIndicator($container); setHeight($indicator, getBoundingRect($container.get(0)).height); $indicator.css('left', rtlOffset ? rtlOffset - width : width); @@ -431,7 +430,7 @@ class SchedulerTimeline extends SchedulerWorkSpace { groups: ResourceLoader[], groupByDate: boolean, ): GroupRows { - const tableCreatorStrategy = this.option('groupOrientation') === 'vertical' ? tableCreator.VERTICAL : tableCreator.HORIZONTAL; + const tableCreatorStrategy = this.option().groupOrientation === 'vertical' ? tableCreator.VERTICAL : tableCreator.HORIZONTAL; return tableCreator.makeGroupedTable( tableCreatorStrategy, @@ -443,7 +442,7 @@ class SchedulerTimeline extends SchedulerWorkSpace { groupHeaderContentClass: GROUP_HEADER_CONTENT_CLASS, }, this.getCellCount() || 1, - this.option('resourceCellTemplate'), + this.option().resourceCellTemplate, this.getTotalRowCount(this.getGroupCount()), groupByDate, ); diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/timeline_month.ts b/packages/devextreme/js/__internal/scheduler/workspaces/timeline_month.ts index cc634be97cac..ed6867dd362a 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/timeline_month.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/timeline_month.ts @@ -28,7 +28,7 @@ class SchedulerTimelineMonth extends SchedulerTimeline { } protected override getDateHeaderTemplate(): TemplateBase | null | undefined { - return this.option('dateCellTemplate'); + return this.option().dateCellTemplate; } protected override calculateDurationInCells(timeDiff: number): number { @@ -46,7 +46,7 @@ class SchedulerTimelineMonth extends SchedulerTimeline { protected override getIntervalBetween(currentDate: Date): number { const firstViewDate = this.getStartViewDate(); const timeZoneOffset = dateUtils.getTimezonesDifference(firstViewDate, currentDate); - const startDayHour = this.option('startDayHour'); + const { startDayHour } = this.option(); return currentDate.getTime() - (firstViewDate.getTime() - startDayHour * 3600000) @@ -54,14 +54,14 @@ class SchedulerTimelineMonth extends SchedulerTimeline { } protected override getViewStartByOptions(): Date { - const currentDate: Date = this.option('currentDate') ?? new Date(); - const startDate: Date = this.option('startDate') ?? currentDate; + const currentDate: Date = this.option().currentDate ?? new Date(); + const startDate: Date = this.option().startDate ?? currentDate; const firstMonthDate = dateUtils.getFirstMonthDate(startDate) ?? startDate; return monthUtils.getViewStartByOptions( startDate, currentDate, - this.option('intervalCount'), + this.option().intervalCount, firstMonthDate, ); } diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/timeline_week.ts b/packages/devextreme/js/__internal/scheduler/workspaces/timeline_week.ts index 8b1bba366d13..e511fd6541f5 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/timeline_week.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/timeline_week.ts @@ -8,7 +8,7 @@ const TIMELINE_WORK_WEEK_CLASS = 'dx-scheduler-timeline-work-week'; export default class SchedulerTimelineWeek extends SchedulerTimeline { get type(): string { - return this.option('type') ?? VIEWS.TIMELINE_WEEK; + return this.option().type ?? VIEWS.TIMELINE_WEEK; } protected override getElementClass(): string { diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts index 8c3fc581e837..1ebf8f6f6de8 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts @@ -394,8 +394,6 @@ class SchedulerWorkSpace extends Widget { private showPopup?: boolean; - private readonly NAME!: string; - private contextMenuAction: | ((args: WorkspaceOptionActionMap['onCellContextMenu']) => void) | undefined; @@ -1271,7 +1269,7 @@ class SchedulerWorkSpace extends Widget { const cellSelector = `.${DATE_TABLE_CELL_CLASS},.${ALL_DAY_TABLE_CELL_CLASS}`; const $element = this.$element(); - const eventName = addNamespace(contextMenuEventName, this.NAME); + const eventName = addNamespace(contextMenuEventName, this.NAME as string); eventsEngine.off($element, eventName, cellSelector); eventsEngine.on($element, eventName, cellSelector, this.contextMenuHandler.bind(this)); diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space_day.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space_day.ts index 23856c9a3862..6a17bf22c137 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space_day.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space_day.ts @@ -13,7 +13,7 @@ class SchedulerWorkSpaceDay extends SchedulerWorkSpaceVertical { } renderRHeaderPanel(): void { - if (this.option('intervalCount') === 1) { + if (this.option().intervalCount === 1) { super.renderRHeaderPanel(false); } else { super.renderRHeaderPanel(true); diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space_grouped_strategy_vertical.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space_grouped_strategy_vertical.ts index 836ad6e6b729..4f9db3a7352e 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space_grouped_strategy_vertical.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space_grouped_strategy_vertical.ts @@ -45,7 +45,7 @@ class VerticalGroupedStrategy { } insertAllDayRowsIntoDateTable(): boolean { - return this.workspace.option('showAllDayPanel'); + return this.workspace.option().showAllDayPanel; } getTotalCellCount(): number { @@ -94,9 +94,9 @@ class VerticalGroupedStrategy { getGroupBoundsOffset(groupIndex: number, [$firstCell, $lastCell]: [Element, Element]) : GroupBoundsOffset { return this.cache.memo(`groupBoundsOffset${groupIndex}`, () => { - const startDayHour = this.workspace.option('startDayHour'); - const endDayHour = this.workspace.option('endDayHour'); - const hoursInterval = this.workspace.option('hoursInterval'); + const { + startDayHour, endDayHour, hoursInterval, getHeaderHeight, showAllDayPanel, + } = this.workspace.option(); const dayHeight = (calculateDayDuration(startDayHour, endDayHour) / hoursInterval) * this.workspace.getCellHeight(); @@ -104,9 +104,9 @@ class VerticalGroupedStrategy { // @ts-expect-error const headerRowHeight = getBoundingRect(this.workspace.$headerPanelContainer.get(0)).height; - let topOffset = groupIndex * dayHeight + headerRowHeight + this.workspace.option('getHeaderHeight')() - scrollTop; + let topOffset = groupIndex * dayHeight + headerRowHeight + getHeaderHeight() - scrollTop; - if (this.workspace.option('showAllDayPanel') && this.workspace.supportAllDayRow()) { + if (showAllDayPanel && this.workspace.supportAllDayRow()) { topOffset += this.workspace.getCellHeight() * (groupIndex + 1); } diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space_indicator.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space_indicator.ts index c56cea319a43..56e321f777d8 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space_indicator.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space_indicator.ts @@ -23,8 +23,8 @@ class SchedulerWorkSpaceIndicator extends SchedulerWorkSpace { private indicatorInterval?: ReturnType; protected getToday(): Date { - const viewOffset = this.option('viewOffset'); - const today = getToday(this.option('indicatorTime'), this.timeZoneCalculator); + const { viewOffset, indicatorTime } = this.option(); + const today = getToday(indicatorTime, this.timeZoneCalculator); return dateUtilsTs.addOffsets(today, -viewOffset); } @@ -91,7 +91,7 @@ class SchedulerWorkSpaceIndicator extends SchedulerWorkSpace { } protected setIndicationUpdateInterval(): void { - if (!this.option('showCurrentTimeIndicator') || this.option('indicatorUpdateInterval') === 0) { + if (!this.option().showCurrentTimeIndicator || this.option().indicatorUpdateInterval === 0) { return; } @@ -100,7 +100,7 @@ class SchedulerWorkSpaceIndicator extends SchedulerWorkSpace { // eslint-disable-next-line no-restricted-globals this.indicatorInterval = setInterval(() => { this.renderCurrentDateTimeIndication(); - }, this.option('indicatorUpdateInterval')); + }, this.option().indicatorUpdateInterval); } private clearIndicatorUpdateInterval(): void { @@ -135,7 +135,7 @@ class SchedulerWorkSpaceIndicator extends SchedulerWorkSpace { const viewStartTime = this.getStartViewDate().getTime(); let timeDiff = today.getTime() - viewStartTime; - if (((this.option('skippedDays')) ?? []).length > 0) { + if (((this.option().skippedDays) ?? []).length > 0) { const skippedDaysDuration = this.getSkippedDaysCount( this.getStartViewDate(), Math.round(timeDiff / toMs('day')), diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space_month.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space_month.ts index c5dc8cf5b091..44876907568e 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space_month.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space_month.ts @@ -29,7 +29,7 @@ class SchedulerWorkSpaceMonth extends SchedulerWorkSpace { protected override getIntervalBetween(currentDate: Date): number { const firstViewDate = this.getStartViewDate(); const timeZoneOffset = dateUtils.getTimezonesDifference(firstViewDate, currentDate); - const startDayHour = this.option('startDayHour'); + const { startDayHour } = this.option(); return currentDate.getTime() - (firstViewDate.getTime() - startDayHour * 3600000) @@ -86,15 +86,15 @@ class SchedulerWorkSpaceMonth extends SchedulerWorkSpace { } protected override needCreateCrossScrolling(): boolean { - return this.option('crossScrollingEnabled') || this.isVerticalGroupedWorkSpace(); + return this.option().crossScrollingEnabled || this.isVerticalGroupedWorkSpace(); } protected override getViewStartByOptions(): Date { return monthUtils.getViewStartByOptions( - this.option('startDate'), - this.option('currentDate'), - this.option('intervalCount'), - dateUtils.getFirstMonthDate(this.option('startDate')) as Date, + this.option().startDate, + this.option().currentDate, + this.option().intervalCount, + dateUtils.getFirstMonthDate(this.option().startDate) as Date, ); } diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space_week.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space_week.ts index 2f908bce3879..65bd178dbbfc 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space_week.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space_week.ts @@ -8,7 +8,7 @@ const WEEK_CLASS = 'dx-scheduler-work-space-week'; const WORK_WEEK_CLASS = 'dx-scheduler-work-space-work-week'; class SchedulerWorkSpaceWeek extends SchedulerWorkSpaceVertical { get type(): string { - return this.option('type') ?? VIEWS.WEEK; + return this.option().type ?? VIEWS.WEEK; } protected override getElementClass(): string { @@ -16,7 +16,7 @@ class SchedulerWorkSpaceWeek extends SchedulerWorkSpaceVertical { } protected override calculateViewStartDate(): Date { - return weekUtils.calculateViewStartDate(this.option('startDate') as Date, this.firstDayOfWeek()); + return weekUtils.calculateViewStartDate(this.option().startDate as Date, this.firstDayOfWeek()); } } From 92c9d62afc8b658cd882cb187ecdaa228b086fca Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii Date: Thu, 11 Jun 2026 15:41:17 +0200 Subject: [PATCH 07/22] fix: fix tests --- .../js/__internal/scheduler/workspaces/work_space.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts index 1ebf8f6f6de8..adef8e349f34 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts @@ -3267,11 +3267,7 @@ class SchedulerWorkSpace extends Widget { const groupRows = this.makeGroupRows(this.option().groups, this.option().groupByDate); this.attachGroupCountClass(); const { elements } = groupRows; - if (Array.isArray(elements)) { - $container.append(elements); - } else { - $container.append(elements); - } + $container.append(Array.isArray(elements) ? elements : elements.toArray()); cellTemplates = groupRows.cellTemplates; } else { this.detachGroupCountClass(); From 32b43a49e131174c73f5efa39fea5ba6082139bb Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii Date: Thu, 11 Jun 2026 15:46:47 +0200 Subject: [PATCH 08/22] refactor: small improvements --- .../scheduler/workspaces/work_space.ts | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts index adef8e349f34..60b4c7073055 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts @@ -308,7 +308,7 @@ export interface WorkspaceOptionsInternal { onShowAllDayPanel: (isVisible: boolean) => void; getHeaderHeight: (() => number); onScrollEnd: () => void; - onInitialized: (e: InitializedEventInfo) => void + onInitialized: (e: InitializedEventInfo) => void; onDisposing: () => void; notifyScheduler: NotifyScheduler; @@ -544,7 +544,6 @@ class SchedulerWorkSpace extends Widget { return undefined; } - // notifyScheduler.invoke delegates to scheduler.fire which is loosely typed // eslint-disable-next-line @typescript-eslint/no-unsafe-return return notifyScheduler.invoke(funcName, ...args); } @@ -970,7 +969,7 @@ class SchedulerWorkSpace extends Widget { return this.option().crossScrollingEnabled; } - protected getElementClass(): string { noop(); return ''; } + protected getElementClass(): string { return ''; } protected getRowCount(): number { return this.viewDataProvider.getRowCount({ @@ -1691,9 +1690,9 @@ class SchedulerWorkSpace extends Widget { const window = getWindow(); const isTargetInAllDayPanel = !$(target).closest($dateTableScrollableElement).length; const isOutsideHorizontalScrollable = event.pageX < scrollableSize.left - || event.pageX > (scrollableSize.left + scrollableSize.width + (window.scrollX || 0)); + || event.pageX > (scrollableSize.left + scrollableSize.width + (window.scrollX || 0)); const isOutsideVerticalScrollable = event.pageY < scrollableSize.top - || event.pageY > (scrollableSize.top + scrollableSize.height + (window.scrollY || 0)); + || event.pageY > (scrollableSize.top + scrollableSize.height + (window.scrollY || 0)); if (isTargetInAllDayPanel && !isOutsideHorizontalScrollable) { return false; @@ -1847,7 +1846,7 @@ class SchedulerWorkSpace extends Widget { const { horizontalScrollingState, verticalScrollingState } = this.virtualScrollingDispatcher; const cellCount = horizontalScrollingState?.itemCount - ?? this.getTotalCellCount(this.getGroupCount()); + ?? this.getTotalCellCount(this.getGroupCount()); const cellWidth = this.getCellWidth(); const cellHeight = allDay ? this.getAllDayHeight() : this.getCellHeight(); @@ -1977,12 +1976,12 @@ class SchedulerWorkSpace extends Widget { const result = { top: { hours: Math.floor(scrolledCellCount * this.option().hoursInterval) - + this.option().startDayHour, + + this.option().startDayHour, minutes: scrolledCellCount % 2 ? 30 : 0, }, bottom: { hours: Math.floor(totalCellCount * this.option().hoursInterval) - + this.option().startDayHour, + + this.option().startDayHour, minutes: Math.floor(totalCellCount) % 2 ? 30 : 0, }, }; @@ -2040,7 +2039,7 @@ class SchedulerWorkSpace extends Widget { const scrollableScrollLeft = this.getScrollableScrollLeft(); const fullScrolledRowCount = scrollableScrollTop / cellHeight - - this.virtualScrollingDispatcher.topVirtualRowsCount; + - this.virtualScrollingDispatcher.topVirtualRowsCount; let scrolledRowCount = Math.floor(fullScrolledRowCount); if (scrollableScrollTop % cellHeight !== 0) { @@ -2112,7 +2111,7 @@ class SchedulerWorkSpace extends Widget { top = 0; } - if (this.option('templatesRenderAsynchronously')) { + if (this._options.silent('templatesRenderAsynchronously')) { // eslint-disable-next-line no-restricted-globals setTimeout(() => { scrollable.scrollBy({ left, top }); @@ -2192,7 +2191,7 @@ class SchedulerWorkSpace extends Widget { // eslint-disable-next-line @typescript-eslint/no-unused-vars nextFocusedCell?: dxElementWrapper, ): void { - this.option('selectedCellData', selectedCellData as ViewCellData[]); + this.option().selectedCellData = selectedCellData as ViewCellData[]; this.selectionChangedAction?.({ selectedCellData: selectedCellData as ViewCellData[], }); @@ -3448,7 +3447,7 @@ const createDragBehaviorConfig = ( e.event.data.itemElement = state.dragElement; e.event.data.initialPosition = initialPosition - ?? locate($(state.dragElement)); + ?? locate($(state.dragElement)); e.event.data.itemData = state.itemData; e.event.data.itemSettings = settings; From 317ed1d44daedfd8fd08135b8c1d9b00afbe8163 Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii Date: Thu, 11 Jun 2026 17:03:47 +0200 Subject: [PATCH 09/22] fix: try to fix hasHorizontalScroll method --- e2e/testcafe-devextreme/helpers/domUtils.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/e2e/testcafe-devextreme/helpers/domUtils.ts b/e2e/testcafe-devextreme/helpers/domUtils.ts index 085e8c0b183e..1c0b4f9570c7 100644 --- a/e2e/testcafe-devextreme/helpers/domUtils.ts +++ b/e2e/testcafe-devextreme/helpers/domUtils.ts @@ -137,8 +137,8 @@ export const addFocusableElementBefore = ClientFunction(( return button.id; }); -export const hasHorizontalScroll = async (container: Selector): Promise => { - const scrollWidth = await container.scrollWidth; - const clientWidth = await container.clientWidth; - return scrollWidth > clientWidth; -}; +export const hasHorizontalScroll = ClientFunction((selector) => { + const element = selector(); + + return element.scrollWidth > element.clientWidth; +}); From 4649fda1c56238e5da2f9b6942cf14f39121ebdb Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii Date: Thu, 11 Jun 2026 17:11:58 +0200 Subject: [PATCH 10/22] fix: fix test --- .../js/__internal/scheduler/workspaces/work_space.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts index 60b4c7073055..332cac748045 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts @@ -2866,6 +2866,10 @@ class SchedulerWorkSpace extends Widget { protected getDateTableCellClass(rowIndex?: number, columnIndex?: number): string { const cellClass = `${DATE_TABLE_CELL_CLASS} ${HORIZONTAL_SIZES_CLASS} ${VERTICAL_SIZES_CLASS}`; + if (rowIndex === undefined && columnIndex === undefined) { + return cellClass; + } + return this.groupedStrategy .addAdditionalGroupCellClasses( cellClass, From f0e85f5ef4d3662dc3fe144c9f131ea35b4b5379 Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii Date: Thu, 11 Jun 2026 19:15:45 +0200 Subject: [PATCH 11/22] refactor: add proper type to _supportedKeys --- .../js/__internal/scheduler/workspaces/work_space.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts index 332cac748045..e8e8fb037626 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts @@ -46,6 +46,7 @@ import type { TranslateVector } from '@ts/common/core/animation/translator'; import { getMemoizeScrollTo, type ScrollToFunc } from '@ts/core/utils/scroll'; import type { ActionConfig } from '@ts/core/widget/component'; import type { OptionChanged } from '@ts/core/widget/types'; +import type { SupportedKeys } from '@ts/core/widget/widget'; import Widget from '@ts/core/widget/widget'; import { AllDayPanelTitleComponent, @@ -548,7 +549,7 @@ class SchedulerWorkSpace extends Widget { return notifyScheduler.invoke(funcName, ...args); } - _supportedKeys(): Record void> { + _supportedKeys(): SupportedKeys { const clickHandler = function clickHandler( this: SchedulerWorkSpace, e: { preventDefault: () => void; stopPropagation: () => void; target: unknown }, @@ -639,7 +640,7 @@ class SchedulerWorkSpace extends Widget { leftArrow: (e) => { onArrowPressed(e, 'left'); }, - }) as Record void>; + }) as SupportedKeys; } private isRTL(): boolean { @@ -1387,13 +1388,12 @@ class SchedulerWorkSpace extends Widget { event: { pageX: number; pageY: number }, ): boolean => !this.isOutsideScrollable(target, event); - // eventsEngine.on supports 5-arg (element, event, selector, data, handler) form at runtime - const onFivePlusArgs = eventsEngine.on as (...args: unknown[]) => void; - onFivePlusArgs( + eventsEngine.on( element, DragEventNames.ENTER, DRAG_AND_DROP_SELECTOR, { checkDropTarget: onCheckDropTarget }, + // @ts-expect-error onDragEnter, ); eventsEngine.on(element, DragEventNames.LEAVE, removeClasses); From 3b1894cfe3b5674bb93002c1aec523fe563cb5e7 Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii Date: Fri, 12 Jun 2026 11:37:58 +0200 Subject: [PATCH 12/22] refactor: refactor _supportedKeys --- .../js/__internal/scheduler/workspaces/work_space.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts index e8e8fb037626..5d83f5b6c4d7 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts @@ -550,10 +550,9 @@ class SchedulerWorkSpace extends Widget { } _supportedKeys(): SupportedKeys { - const clickHandler = function clickHandler( - this: SchedulerWorkSpace, + const clickHandler = ( e: { preventDefault: () => void; stopPropagation: () => void; target: unknown }, - ): void { + ): void => { e.preventDefault(); e.stopPropagation(); @@ -625,7 +624,7 @@ class SchedulerWorkSpace extends Widget { } }; - return extend(super._supportedKeys(), { + const supportedKeys: SupportedKeys = { enter: clickHandler, space: clickHandler, downArrow: (e) => { @@ -640,7 +639,9 @@ class SchedulerWorkSpace extends Widget { leftArrow: (e) => { onArrowPressed(e, 'left'); }, - }) as SupportedKeys; + }; + + return extend(super._supportedKeys(), supportedKeys) as SupportedKeys; } private isRTL(): boolean { From 6089c30107874a73bcd06e932178abd311e4c4ff Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii Date: Fri, 12 Jun 2026 11:50:52 +0200 Subject: [PATCH 13/22] refactor: minor edits --- .../scheduler/workspaces/work_space.ts | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts index 5d83f5b6c4d7..8b4a0ff89a61 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts @@ -1276,7 +1276,7 @@ class SchedulerWorkSpace extends Widget { eventsEngine.on($element, eventName, cellSelector, this.contextMenuHandler.bind(this)); } - private contextMenuHandler(e: { target: EventTarget | null }): void { + private contextMenuHandler(e: DxEvent): void { const $cell = $(e.target as Element | null); this.contextMenuAction?.( { @@ -1312,9 +1312,7 @@ class SchedulerWorkSpace extends Widget { } protected getCellCountInDay(): number { - const { hoursInterval } = this.option(); - const { startDayHour } = this.option(); - const { endDayHour } = this.option(); + const { hoursInterval, startDayHour, endDayHour } = this.option(); return this.viewDataProvider.getCellCountInDay(startDayHour, endDayHour, hoursInterval); } @@ -1896,9 +1894,7 @@ class SchedulerWorkSpace extends Widget { } getVisibleDayDuration(): number { - const { startDayHour } = this.option(); - const { endDayHour } = this.option(); - const { hoursInterval } = this.option(); + const { startDayHour, endDayHour, hoursInterval } = this.option(); return this.viewDataProvider.getVisibleDayDuration(startDayHour, endDayHour, hoursInterval); } @@ -3186,13 +3182,12 @@ class SchedulerWorkSpace extends Widget { this.cleanTableWidths(); this.cellsSelectionState.clearSelectedAndFocusedCells(); - delete this.isCellClick; - delete this.showPopup; + this.isCellClick = false; + this.showPopup = false; + this.interval = undefined; this.isSelectionStartedOnCell = false; this.shader?.clean(); - - delete this.interval; } _clean(): void { From 78329043d591c9b5cdbd6ede6d7aa67449317ab1 Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii Date: Fri, 12 Jun 2026 11:55:58 +0200 Subject: [PATCH 14/22] refactor: minor changes --- .../scheduler/workspaces/work_space.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts index 8b4a0ff89a61..833379421789 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts @@ -593,13 +593,12 @@ class SchedulerWorkSpace extends Widget { const isHorizontalGrouping = this.isHorizontalGroupedWorkSpace(); const focusedCellPosition = this.viewDataProvider.findCellPositionInMap(focusedCellData); - const isAllDayPanelCellBool = Boolean(isAllDayPanelCell); const edgeIndices = isHorizontalGrouping && isMultiSelection && !isGroupedByDate ? this.viewDataProvider.getGroupEdgeIndices( focusedCellData.groupIndex ?? 0, - isAllDayPanelCellBool, + Boolean(isAllDayPanelCell), ) - : this.viewDataProvider.getViewEdgeIndices(isAllDayPanelCellBool); + : this.viewDataProvider.getViewEdgeIndices(Boolean(isAllDayPanelCell)); const nextCellData = this.cellsSelectionController.handleArrowClick({ focusedCellPosition, @@ -612,7 +611,7 @@ class SchedulerWorkSpace extends Widget { viewType: this.type as ViewType, key, getCellDataByPosition: this.viewDataProvider.getCellData.bind(this.viewDataProvider), - isAllDayPanelCell: isAllDayPanelCellBool, + isAllDayPanelCell: Boolean(isAllDayPanelCell), focusedCellData, }); @@ -2954,8 +2953,10 @@ class SchedulerWorkSpace extends Widget { this.$dateTableScrollable.$content().append(this.$dateTableScrollableContent); this.$headerTablesContainer.append([this.$headerPanel, this.$allDayPanel]); - if (this.$allDayPanel && this.$allDayTable) { - this.$allDayPanel.append([this.$allDayContainer, this.$allDayTable]); + if (this.$allDayPanel) { + this.$allDayPanel.append( + [this.$allDayContainer, this.$allDayTable].filter(Boolean) as dxElementWrapper[], + ); } } @@ -2997,8 +2998,10 @@ class SchedulerWorkSpace extends Widget { ); } else { this.headerScrollable.$content().append(this.$allDayPanel); - if (this.$allDayPanel && this.$allDayTable) { - this.$allDayPanel.append([this.$allDayContainer, this.$allDayTable]); + if (this.$allDayPanel) { + this.$allDayPanel.append( + [this.$allDayContainer, this.$allDayTable].filter(Boolean) as dxElementWrapper[], + ); } this.$sidebarScrollableContent.append(this.$timePanel); } From 2a301e1159c427f755d6af8b672a5647ad27fe59 Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii Date: Fri, 12 Jun 2026 12:01:22 +0200 Subject: [PATCH 15/22] refactor: refactor normalizeCellData --- .../js/__internal/scheduler/workspaces/work_space.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts index 833379421789..a18849bbd7a5 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts @@ -1708,15 +1708,15 @@ class SchedulerWorkSpace extends Widget { } private normalizeCellData(cellData: Partial): NormalizedCellData { - return extend(true, {}, { - startDate: cellData.startDate, - endDate: cellData.endDate, + return { + startDate: cellData.startDate ?? new Date(), + endDate: cellData.endDate ?? new Date(), startDateUTC: cellData.startDate && this.timeZoneCalculator?.createDate(cellData.startDate, 'fromGrid'), endDateUTC: cellData.endDate && this.timeZoneCalculator?.createDate(cellData.endDate, 'fromGrid'), - groups: cellData.groups, + groups: cellData.groups ? extend(true, {}, cellData.groups) : cellData.groups, groupIndex: cellData.groupIndex, allDay: cellData.allDay, - }) as NormalizedCellData; + }; } private getSelectedCellsData(): NormalizedCellData[] { From 5ac903d730a6ee3972b25ca0b31c2a0da07c046f Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii Date: Fri, 12 Jun 2026 12:03:17 +0200 Subject: [PATCH 16/22] refactor: refactor getPanelDOMSize --- .../__internal/scheduler/workspaces/work_space.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts index a18849bbd7a5..60558a88e1be 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts @@ -2228,15 +2228,15 @@ class SchedulerWorkSpace extends Widget { })); } - private toRealSize(element: Element | undefined): RealSize { - const { width, height } = getBoundingRect(element) as Pick; - return { width, height }; - } - getPanelDOMSize(panelName: PanelName): RealSize { + const getElementSize = (element: Element | undefined): RealSize => { + const { width, height } = getBoundingRect(element) as Pick; + return { width, height }; + }; + return panelName === 'allDayPanel' - ? this.cache.memo('allDayPanelSize', () => this.toRealSize(this.$allDayPanel.get(0))) - : this.cache.memo('regularPanelSize', () => this.toRealSize(this.getDateTable().get(0))); + ? this.cache.memo('allDayPanelSize', () => getElementSize(this.$allDayPanel.get(0))) + : this.cache.memo('regularPanelSize', () => getElementSize(this.getDateTable().get(0))); } getCollectorDimension(isCollectorCompact: boolean, panelName: PanelName): CollectorCSS { From 9bea530fd1659021a5920f46c38600dc91393299 Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii Date: Fri, 12 Jun 2026 13:27:06 +0200 Subject: [PATCH 17/22] fix: fix tests --- .../scheduler/workspaces/work_space.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts index 4121cfbc1f3d..2592cd3f9f2a 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts @@ -158,9 +158,9 @@ interface ScrollSync { interface NormalizedCellData { startDate: Date; endDate: Date; - startDateUTC: Date | false | undefined; - endDateUTC: Date | false | undefined; - groups: ViewCellData['groups']; + startDateUTC?: Date | false | undefined; + endDateUTC?: Date | false | undefined; + groups?: ViewCellData['groups']; groupIndex: ViewCellData['groupIndex']; allDay: ViewCellData['allDay']; index?: ViewCellData['index']; @@ -1709,14 +1709,18 @@ class SchedulerWorkSpace extends Widget { } private normalizeCellData(cellData: Partial): NormalizedCellData { + const startDateUTC = cellData.startDate + && this.timeZoneCalculator?.createDate(cellData.startDate, 'fromGrid'); + const endDateUTC = cellData.endDate + && this.timeZoneCalculator?.createDate(cellData.endDate, 'fromGrid'); return { startDate: cellData.startDate ?? new Date(), endDate: cellData.endDate ?? new Date(), - startDateUTC: cellData.startDate && this.timeZoneCalculator?.createDate(cellData.startDate, 'fromGrid'), - endDateUTC: cellData.endDate && this.timeZoneCalculator?.createDate(cellData.endDate, 'fromGrid'), - groups: cellData.groups ? extend(true, {}, cellData.groups) : cellData.groups, - groupIndex: cellData.groupIndex, + ...(startDateUTC !== undefined && { startDateUTC }), + ...(endDateUTC !== undefined && { endDateUTC }), allDay: cellData.allDay, + ...(cellData.groups !== undefined && { groups: { ...cellData.groups } }), + groupIndex: cellData.groupIndex, }; } From d95195c7e0e9d6b8af055a3c1e6a9058cbdd37a2 Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii Date: Mon, 15 Jun 2026 10:17:39 +0200 Subject: [PATCH 18/22] fix: fix tests --- .../js/__internal/scheduler/workspaces/work_space.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts index 2592cd3f9f2a..7bebee82b4fd 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts @@ -162,7 +162,7 @@ interface NormalizedCellData { endDateUTC?: Date | false | undefined; groups?: ViewCellData['groups']; groupIndex: ViewCellData['groupIndex']; - allDay: ViewCellData['allDay']; + allDay?: ViewCellData['allDay']; index?: ViewCellData['index']; isFirstGroupCell?: ViewCellData['isFirstGroupCell']; isLastGroupCell?: ViewCellData['isLastGroupCell']; @@ -1718,7 +1718,7 @@ class SchedulerWorkSpace extends Widget { endDate: cellData.endDate ?? new Date(), ...(startDateUTC !== undefined && { startDateUTC }), ...(endDateUTC !== undefined && { endDateUTC }), - allDay: cellData.allDay, + ...(cellData.allDay !== undefined && { allDay: cellData.allDay }), ...(cellData.groups !== undefined && { groups: { ...cellData.groups } }), groupIndex: cellData.groupIndex, }; From f8a133cfaa24ae5d325c75b108240a9e3761ce45 Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii Date: Mon, 15 Jun 2026 14:45:34 +0200 Subject: [PATCH 19/22] refactor: review --- .../js/__internal/scheduler/workspaces/work_space.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts index a28271dd3b11..256d67f2ed4b 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts @@ -1030,10 +1030,9 @@ class SchedulerWorkSpace extends Widget { ? this.option().groupOrientation : this.getDefaultGroupStrategy(); - const options = { + const renderState = this.virtualScrollingDispatcher.getRenderState(); + const options: ViewDataProviderOptions = { groupByDate: this.option().groupByDate, - startRowIndex: 0, - startCellIndex: 0, groupOrientation, today: this.getToday?.() ?? new Date(), getResourceManager: this.option().getResourceManager, @@ -1056,10 +1055,13 @@ class SchedulerWorkSpace extends Widget { showCurrentTimeIndicator: this.option().showCurrentTimeIndicator, skippedDays: this.option().skippedDays, - ...this.virtualScrollingDispatcher.getRenderState(), + ...renderState, + startRowIndex: renderState.startRowIndex ?? 0, + startCellIndex: renderState.startCellIndex ?? 0, + cellCount: renderState.cellCount ?? 0, }; - return options as ViewDataProviderOptions; + return options; } renovatedRenderSupported(): boolean { return true; } From f210b7d27fba7ed1b3719d9eae841d2736d96c06 Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii Date: Mon, 15 Jun 2026 15:06:26 +0200 Subject: [PATCH 20/22] refactor: review --- .../scheduler/workspaces/work_space.ts | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts index 256d67f2ed4b..6f3f6cca742e 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts @@ -274,7 +274,9 @@ export interface WorkspaceOptionsInternal { getResourceManager: () => ResourceManager; getFilteredItems: () => ListEntity[]; noDataText: string; - firstDayOfWeek: number; + activeStateEnabled: boolean; + hoverStateEnabled: boolean; + firstDayOfWeek?: number; startDayHour: number; endDayHour: number; viewOffset: number; @@ -296,20 +298,20 @@ export interface WorkspaceOptionsInternal { onSelectionChanged: ((args: SelectedCellsEventArgs) => void); onSelectionEnd: ((args: SelectedCellsEventArgs) => void); groupByDate: boolean; - skippedDays: number[]; + skippedDays?: number[]; scrolling: { mode: ScrollMode; - orientation: ScrollDirection; + orientation?: ScrollDirection; }; draggingMode: 'outlook' | 'default'; - timeZoneCalculator: TimeZoneCalculator; + timeZoneCalculator?: TimeZoneCalculator; schedulerHeight: string | number | undefined; schedulerWidth: string | number | undefined; allDayPanelMode: AllDayPanelMode; onSelectedCellsClick: (result: object, groups: GroupValues) => void; renderAppointments: () => void; onShowAllDayPanel: (isVisible: boolean) => void; - getHeaderHeight: (() => number); + getHeaderHeight?: (() => number); onScrollEnd: () => void; onInitialized: (e: InitializedEventInfo) => void; onDisposing: () => void; @@ -1051,7 +1053,7 @@ class SchedulerWorkSpace extends Widget { hoursInterval: this.option().hoursInterval, currentDate: this.option().currentDate, startDate: this.option().startDate, - firstDayOfWeek: this.option().firstDayOfWeek, + firstDayOfWeek: this.option().firstDayOfWeek ?? 0, showCurrentTimeIndicator: this.option().showCurrentTimeIndicator, skippedDays: this.option().skippedDays, @@ -1129,7 +1131,7 @@ class SchedulerWorkSpace extends Widget { } protected firstDayOfWeek(): number { - return this.viewDataProvider.getFirstDayOfWeek(this.option().firstDayOfWeek); + return this.viewDataProvider.getFirstDayOfWeek(this.option().firstDayOfWeek ?? 0); } protected attachEvents(): void { @@ -2614,10 +2616,11 @@ class SchedulerWorkSpace extends Widget { } _getDefaultOptions(): WorkspaceOptionsInternal { - const defaultOptions = extend(super._getDefaultOptions(), { + const defaultOptions: WorkspaceOptionsInternal = { + ...super._getDefaultOptions(), currentDate: new Date(), intervalCount: 1, - startDate: null, + startDate: undefined, firstDayOfWeek: undefined, startDayHour: 0, endDayHour: 24, @@ -2628,7 +2631,7 @@ class SchedulerWorkSpace extends Widget { groups: [], showAllDayPanel: true, allDayExpanded: false, - onCellClick: null, + onCellClick: undefined, crossScrollingEnabled: false, dataCellTemplate: null, timeCellTemplate: null, @@ -2646,7 +2649,6 @@ class SchedulerWorkSpace extends Widget { mode: 'standard', }, allDayPanelMode: 'all', - height: undefined, draggingMode: 'outlook', onScrollEnd: noop, getHeaderHeight: undefined, @@ -2656,9 +2658,9 @@ class SchedulerWorkSpace extends Widget { timeZoneCalculator: undefined, schedulerHeight: undefined, schedulerWidth: undefined, - }); + }; - return defaultOptions as WorkspaceOptionsInternal; + return defaultOptions; } _optionChanged(args: OptionChanged): void { From 448b1cb40f801d6b3be190d88396389aaa8184a0 Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii Date: Mon, 15 Jun 2026 15:16:08 +0200 Subject: [PATCH 21/22] refactor: review --- .../js/__internal/scheduler/workspaces/work_space.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts index 6f3f6cca742e..a5baccf4f561 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts @@ -46,7 +46,7 @@ import type { TranslateVector } from '@ts/common/core/animation/translator'; import { getMemoizeScrollTo, type ScrollToFunc } from '@ts/core/utils/scroll'; import type { ActionConfig } from '@ts/core/widget/component'; import type { OptionChanged } from '@ts/core/widget/types'; -import type { SupportedKeys } from '@ts/core/widget/widget'; +import type { SupportedKeys, WidgetProperties } from '@ts/core/widget/widget'; import Widget from '@ts/core/widget/widget'; import { AllDayPanelTitleComponent, @@ -268,7 +268,7 @@ type WorkspaceCoordinates = Coordinates & { groupIndex?: number }; type DroppableCellData = Pick; -export interface WorkspaceOptionsInternal { +export interface WorkspaceOptionsInternal extends WidgetProperties { newAppointments: boolean; resources: ResourceLoader[]; getResourceManager: () => ResourceManager; @@ -2128,7 +2128,7 @@ class SchedulerWorkSpace extends Widget { top = 0; } - if (this._options.silent('templatesRenderAsynchronously')) { + if (this.option().templatesRenderAsynchronously) { // eslint-disable-next-line no-restricted-globals setTimeout(() => { scrollable.scrollBy({ left, top }); From 894321df494db6e50e514901be3c3d518f08df02 Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii Date: Mon, 15 Jun 2026 15:30:59 +0200 Subject: [PATCH 22/22] fix: fix tests --- .../scheduler/workspaces/view_model/types.ts | 2 +- .../js/__internal/scheduler/workspaces/work_space.ts | 11 +++++------ .../scheduler/workspaces/work_space_month.ts | 6 ++++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/types.ts b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/types.ts index e6be7f254c6e..7d11791d92ef 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/types.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/types.ts @@ -21,7 +21,7 @@ interface CommonOptions extends CountGenerationConfig { viewType: ViewType; startDate?: Date; skippedDays?: number[]; - cellCount: number; + cellCount?: number; isProvideVirtualCellsWidth: boolean; isGenerateTimePanelData?: boolean; isGenerateWeekDaysHeaderData?: boolean; diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts index a5baccf4f561..a0799c73adcc 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space.ts @@ -327,7 +327,7 @@ export interface WorkspaceOptionsInternal extends WidgetProperties { intervalCount: this.option().intervalCount, hoursInterval: this.option().hoursInterval, currentDate: this.option().currentDate, - startDate: this.option().startDate, + startDate: this.option().startDate ?? undefined, firstDayOfWeek: this.option().firstDayOfWeek ?? 0, showCurrentTimeIndicator: this.option().showCurrentTimeIndicator, skippedDays: this.option().skippedDays, @@ -1060,7 +1060,6 @@ class SchedulerWorkSpace extends Widget { ...renderState, startRowIndex: renderState.startRowIndex ?? 0, startCellIndex: renderState.startCellIndex ?? 0, - cellCount: renderState.cellCount ?? 0, }; return options; @@ -1111,7 +1110,7 @@ class SchedulerWorkSpace extends Widget { protected getViewStartByOptions(): Date { return getViewStartByOptions( - this.option().startDate, + this.option().startDate ?? undefined, this.option().currentDate, this.getTotalViewDuration(), this.option().startDate ? this.calculateViewStartDate() : undefined, @@ -1127,7 +1126,7 @@ class SchedulerWorkSpace extends Widget { } protected calculateViewStartDate(): Date | undefined { - return calculateViewStartDate(this.option().startDate); + return calculateViewStartDate(this.option().startDate ?? undefined); } protected firstDayOfWeek(): number { @@ -2620,7 +2619,7 @@ class SchedulerWorkSpace extends Widget { ...super._getDefaultOptions(), currentDate: new Date(), intervalCount: 1, - startDate: undefined, + startDate: null, firstDayOfWeek: undefined, startDayHour: 0, endDayHour: 24, diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/work_space_month.ts b/packages/devextreme/js/__internal/scheduler/workspaces/work_space_month.ts index d4a3dff366c2..057442ddf883 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/work_space_month.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/work_space_month.ts @@ -91,11 +91,13 @@ class SchedulerWorkSpaceMonth extends SchedulerWorkSpace { } protected override getViewStartByOptions(): Date { + const startDate = this.option().startDate ?? undefined; + return monthUtils.getViewStartByOptions( - this.option().startDate, + startDate, this.option().currentDate, this.option().intervalCount, - dateUtils.getFirstMonthDate(this.option().startDate) as Date, + startDate ? dateUtils.getFirstMonthDate(startDate) as Date : undefined, ); }