From 28650c3a5f9b5e7619980c4a57cdc0e31fb706c1 Mon Sep 17 00:00:00 2001 From: Sergio Bur Date: Tue, 2 Jun 2026 13:27:28 +0200 Subject: [PATCH 01/10] refactor: refactor ts --- .../scheduler/workspaces/m_agenda.ts | 302 ++++++++++-------- .../scheduler/workspaces/m_timeline.ts | 188 ++++++----- .../scheduler/workspaces/m_timeline_day.ts | 7 +- .../scheduler/workspaces/m_timeline_month.ts | 48 +-- .../scheduler/workspaces/m_timeline_week.ts | 5 +- .../scheduler/workspaces/m_work_space.ts | 122 +++++-- .../scheduler/workspaces/m_work_space_day.ts | 7 +- .../workspaces/m_work_space_indicator.ts | 101 +++--- .../workspaces/m_work_space_month.ts | 78 +++-- .../scheduler/workspaces/m_work_space_week.ts | 7 +- 10 files changed, 531 insertions(+), 334 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_agenda.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_agenda.ts index 6fbc5e2ada1d..624e111583d1 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_agenda.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_agenda.ts @@ -2,12 +2,13 @@ import dateLocalization from '@js/common/core/localization/date'; import registerComponent from '@js/core/component_registrator'; import domAdapter from '@js/core/dom_adapter'; import { getPublicElement } from '@js/core/element'; -import $ from '@js/core/renderer'; +import $, { type dxElementWrapper } from '@js/core/renderer'; import { noop } from '@js/core/utils/common'; import dateUtils from '@js/core/utils/date'; import { extend } from '@js/core/utils/extend'; import { each } from '@js/core/utils/iterator'; import { setHeight, setOuterHeight } from '@js/core/utils/size'; +import type { OptionChanged } from '@ts/core/widget/types'; import { EMPTY_ACTIVE_STATE_UNIT } from '@ts/core/widget/widget'; import { @@ -23,7 +24,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 from './m_work_space'; +import WorkSpace, { type WorkspaceDateTableScrollableConfig, type WorkspaceOptionChangedOptions, type WorkspaceOptionsInternal } from './m_work_space'; const { tableCreator } = tableCreatorModule; @@ -40,40 +41,57 @@ const LAST_ROW_CLASS = 'dx-scheduler-date-table-last-row'; const INNER_CELL_MARGIN = 5; const OUTER_CELL_MARGIN = 20; +interface AgendaDefaultOptions extends WorkspaceOptionsInternal { + agendaDuration: number; + rowHeight: number; + noDataText: string; +} + +interface AgendaRenderOptions { + container: Element; + rowCount?: number; + cellCount?: number; + rowClass?: string; + cellClass?: string; + cellTemplate?: unknown; + getStartDate?: (rowIndex: number) => Date; +} + class SchedulerAgenda extends WorkSpace { - private startViewDate: any; + private startViewDate!: Date; private rows: number[][] = []; - private $rows: any; + private $rows: dxElementWrapper[] = []; - private $noDataContainer: any; + private $noDataContainer?: dxElementWrapper; - // eslint-disable-next-line class-methods-use-this protected _activeStateUnit(): string { return EMPTY_ACTIVE_STATE_UNIT; } - get type() { return VIEWS.AGENDA; } + get type(): string { return VIEWS.AGENDA; } - getStartViewDate() { + getStartViewDate(): Date { return this.startViewDate; } - _init() { + _init(): void { super._init(); } - _getDefaultOptions() { - return extend(super._getDefaultOptions(), { + _getDefaultOptions(): AgendaDefaultOptions { + const defaultOptions = extend(super._getDefaultOptions(), { // Number | "month" agendaDuration: 7, rowHeight: 60, noDataText: '', - }); + }) as AgendaDefaultOptions; + + return defaultOptions; } - _optionChanged(args) { + _optionChanged(args: OptionChanged): void { const { name } = args; const { value } = args; @@ -85,7 +103,7 @@ class SchedulerAgenda extends WorkSpace { this.recalculateAgenda(this.rows); break; case 'groups': - if (!value?.length) { + if (!Array.isArray(value) || !value.length) { if (this.$groupTable) { this.$groupTable.remove(); this.$groupTable = null; @@ -102,41 +120,41 @@ class SchedulerAgenda extends WorkSpace { } } - _renderFocusState() { return noop(); } + _renderFocusState(): void { return noop(); } - _renderFocusTarget() { return noop(); } + _renderFocusTarget(): void { return noop(); } - _cleanFocusState() { return noop(); } + _cleanFocusState(): void { return noop(); } - supportAllDayRow() { + supportAllDayRow(): boolean { return false; } - protected override isVerticalGroupedWorkSpace() { + protected override isVerticalGroupedWorkSpace(): boolean { return false; } - protected override getElementClass() { + protected override getElementClass(): string { return AGENDA_CLASS; } - protected override getRowCount() { + protected override getRowCount(): number { return this.option('agendaDuration') as number; } - getCellCount() { + getCellCount(): number { return 1; } - protected override getTimePanelRowCount() { + protected override getTimePanelRowCount(): number { return this.option('agendaDuration') as number; } - protected renderAllDayPanel() { return noop(); } + protected renderAllDayPanel(): void { return noop(); } - protected override updateAllDayVisibility() { return noop(); } + protected override updateAllDayVisibility(): void { return noop(); } - protected override initWorkSpaceUnits() { + protected override initWorkSpaceUnits(): void { this.initGroupTable(); this.$timePanel = $('').attr('aria-hidden', true).addClass(TIME_PANEL_CLASS); this.$dateTable = $('
').attr('aria-hidden', true).addClass(DATE_TABLE_CLASS); @@ -144,14 +162,14 @@ class SchedulerAgenda extends WorkSpace { this.$dateTableContainer = $('
').addClass('dx-scheduler-date-table-container'); } - private initGroupTable() { + private initGroupTable(): void { const groups = this.option('groups'); if (groups?.length) { this.$groupTable = $('
').attr('aria-hidden', true).addClass(GROUP_TABLE_CLASS); } } - protected override renderView() { + protected override renderView(): void { this.startViewDate = agendaUtils.calculateStartViewDate( this.option('currentDate'), this.option('startDayHour'), @@ -159,8 +177,8 @@ class SchedulerAgenda extends WorkSpace { this.rows = []; } - private recalculateAgenda(rows) { - let cellTemplates = []; + private recalculateAgenda(rows: number[][]): void { + let cellTemplates: unknown[] = []; this.cleanView(); if (this.rowsIsEmpty(rows)) { @@ -180,21 +198,26 @@ class SchedulerAgenda extends WorkSpace { this.$dateTableScrollable.update(); } - private renderNoData() { + private renderNoData(): void { this.$noDataContainer = $('
').addClass(NODATA_CONTAINER_CLASS) - .html(this.option('noDataText') as any); + .html(this.option('noDataText') as string); this.$dateTableScrollable.$content().append(this.$noDataContainer); } - protected override setTableSizes() { return noop(); } + protected override setTableSizes(): void { return noop(); } - protected override toggleHorizontalScrollClass() { return noop(); } + protected override toggleHorizontalScrollClass(): void { return noop(); } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - protected override createCrossScrollingConfig(argument?: any) { return noop(); } + protected override createCrossScrollingConfig(): Pick { + return { + direction: 'both', + onScroll: noop, + onEnd: noop, + }; + } - private setGroupHeaderCellsHeight() { + private setGroupHeaderCellsHeight(): void { const $cells = this.getGroupHeaderCells().filter((_, element) => !element.getAttribute('rowSpan')); const rows = this.removeEmptyRows(this.rows); @@ -202,72 +225,58 @@ class SchedulerAgenda extends WorkSpace { return; } - for (let i = 0; i < $cells.length; i++) { + for (let i = 0; i < $cells.length; i += 1) { const $cellContent = $cells.eq(i).find('.dx-scheduler-group-header-content'); setOuterHeight($cellContent, this.getGroupRowHeight(rows[i])); } } - private rowsIsEmpty(rows) { - let result = true; - - for (let i = 0; i < rows.length; i++) { - const groupRow = rows[i]; - - for (let j = 0; j < groupRow.length; j++) { - if (groupRow[j]) { - result = false; - break; - } - } - } - - return result; + private rowsIsEmpty(rows: number[][]): boolean { + return rows.every((groupRow) => groupRow.every((cell) => !cell)); } - protected override attachGroupCountClass() { + protected override attachGroupCountClass(): void { const className = getVerticalGroupCountClass(this.option('groups')); - (this.$element() as any).addClass(className); - } - - private removeEmptyRows(rows) { - const result: any[] = []; - const isEmpty = function (data) { - return !data.some((value) => value > 0); - }; - - for (let i = 0; i < rows.length; i++) { - if (rows[i].length && !isEmpty(rows[i])) { - result.push(rows[i]); - } + if (className) { + this.$element().addClass(className); } + } - return result; + private removeEmptyRows(rows: number[][]): number[][] { + const isEmpty = (data: number[]): boolean => !data.some((value) => value > 0); + return rows.filter((row) => row.length && !isEmpty(row)); } - protected override getGroupHeaderContainer() { - return this.$groupTable; + protected override getGroupHeaderContainer(): dxElementWrapper | null { + return this.$groupTable as dxElementWrapper | null; } - protected override makeGroupRows() { + protected override makeGroupRows(): { elements: dxElementWrapper; cellTemplates: unknown[] } { const resourceManager = this.option('getResourceManager')(); - const allAppointments = (this.option('getFilteredItems') as any)() as ListEntity[]; + const allAppointments = (this.option('getFilteredItems') as () => ListEntity[])(); const tree = reduceResourcesTree( resourceManager.resourceById, resourceManager.groupsTree, allAppointments, ); - const cellTemplate: any = this.option('resourceCellTemplate'); + const cellTemplate = this.option('resourceCellTemplate') as { + render?: (model: unknown) => unknown; + }; const getGroupHeaderContentClass = GROUP_HEADER_CONTENT_CLASS; - const cellTemplates: any[] = []; + const cellTemplates: unknown[] = []; const table = tableCreator.makeGroupedTableFromJSON(tree, { cellTag: 'th', groupTableClass: GROUP_TABLE_CLASS, groupRowClass: GROUP_ROW_CLASS, groupCellClass: this.getGroupHeaderClass(), - groupCellCustomContent(cell: HTMLDivElement, cellTextElement: HTMLElement, index: number, node: GroupNode) { + groupCellCustomContent( + cell: HTMLDivElement, + cellTextElement: HTMLElement, + index: number, + node: GroupNode, + ) { const container = domAdapter.createElement('div'); container.className = getGroupHeaderContentClass; const value = node.grouped[node.resourceIndex]; @@ -305,7 +314,7 @@ class SchedulerAgenda extends WorkSpace { }; } - protected override cleanView() { + protected override cleanView(): void { this.$dateTable.empty(); this.$timePanel.empty(); @@ -321,11 +330,11 @@ class SchedulerAgenda extends WorkSpace { } } - protected override createWorkSpaceElements() { + protected override createWorkSpaceElements(): void { this.createWorkSpaceStaticElements(); } - protected override createWorkSpaceStaticElements() { + protected override createWorkSpaceStaticElements(): void { this.$dateTableContainer.append(this.$dateTable); this.$dateTableScrollable.$content().append(this.$dateTableScrollableContent); @@ -337,7 +346,7 @@ class SchedulerAgenda extends WorkSpace { this.$element().append(this.$dateTableScrollable.$element()); } - protected renderDateTable() { + protected renderDateTable(): void { this.renderTableBody({ container: getPublicElement(this.$dateTable), rowClass: DATE_TABLE_ROW_CLASS, @@ -345,15 +354,29 @@ class SchedulerAgenda extends WorkSpace { }); } - protected override attachTablesEvents() { return noop(); } + protected override attachTablesEvents(): void { return noop(); } - protected override attachEvents() { return noop(); } + protected override attachEvents(): void { return noop(); } - isIndicationAvailable() { + isIndicationAvailable(): boolean { return false; } - private prepareCellTemplateOptions(text, date, rowIndex, $cell) { + private prepareCellTemplateOptions( + text: string, + date: Date | undefined, + rowIndex: number, + $cell: dxElementWrapper, + ): { + model: { + text: string; + date: Date | undefined; + groups: Record; + groupIndex: number | undefined; + }; + container: Element; + index: number; + } { const leaf = this.resourceManager.groupsLeafs[rowIndex]; const groups = leaf?.grouped ?? {}; const groupIndex = leaf?.groupIndex; @@ -370,31 +393,42 @@ class SchedulerAgenda extends WorkSpace { }; } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - protected renderTableBody(options: any, delayCellTemplateRendering?: any) { - const cellTemplates: any[] = []; - const cellTemplateOpt = options.cellTemplate; + protected renderTableBody( + options: AgendaRenderOptions, + delayCellTemplateRendering?: unknown, + ): void { + if (delayCellTemplateRendering) { + noop(); + } + const cellTemplates: unknown[] = []; + const cellTemplateOpt = options.cellTemplate as { + render?: (templateOptions: unknown) => unknown; + } | undefined; this.$rows = []; - let i; + let i = 0; - const fillTableBody = function (rowIndex, rowSize) { + const fillTableBody = (rowIndex: number, rowSize: number): void => { if (rowSize) { - let date; - let cellDateNumber; - let cellDayName; + const date = options.getStartDate?.(rowIndex); + let cellDateNumber = ''; + let cellDayName = ''; const $row = $('
'); const $td = $('
'); setHeight($td, this.getRowHeight(rowSize)); - if (options.getStartDate) { - date = options.getStartDate?.(rowIndex); - cellDateNumber = dateLocalization.format(date, 'd'); - cellDayName = dateLocalization.format(date, formatWeekday); + if (date) { + cellDateNumber = String(dateLocalization.format(date, 'd')); + cellDayName = String(dateLocalization.format(date, formatWeekday)); } if (cellTemplateOpt?.render) { - const templateOptions = this.prepareCellTemplateOptions(`${cellDateNumber} ${cellDayName}`, date, i, $td); + const templateOptions = this.prepareCellTemplateOptions( + `${cellDateNumber} ${cellDayName}`, + date, + i, + $td, + ); cellTemplates.push(cellTemplateOpt.render.bind(cellTemplateOpt, templateOptions)); } else if (cellDateNumber && cellDayName) { @@ -412,10 +446,10 @@ class SchedulerAgenda extends WorkSpace { $row.append($td); this.$rows.push($row); } - }.bind(this); + }; - for (i = 0; i < this.rows.length; i++) { - each(this.rows[i], fillTableBody); + for (i = 0; i < this.rows.length; i += 1) { + each(this.rows[i], fillTableBody.bind(this)); this.setLastRowClass(); } @@ -423,7 +457,7 @@ class SchedulerAgenda extends WorkSpace { this.applyCellTemplates(cellTemplates); } - private setLastRowClass() { + private setLastRowClass(): void { if (this.rows.length > 1 && this.$rows.length) { const $lastRow = this.$rows[this.$rows.length - 1]; @@ -431,7 +465,7 @@ class SchedulerAgenda extends WorkSpace { } } - protected renderTimePanel() { + protected renderTimePanel(): void { this.renderTableBody({ container: getPublicElement(this.$timePanel), rowCount: this.getTimePanelRowCount(), @@ -443,32 +477,26 @@ class SchedulerAgenda extends WorkSpace { }); } - private getTimePanelStartDate(rowIndex) { + private getTimePanelStartDate(rowIndex: number): Date { return agendaUtils.getDateByIndex( this.getStartViewDate(), rowIndex, ); } - private getRowHeight(rowSize) { - const baseHeight = this.option('rowHeight') as any; + private getRowHeight(rowSize: number): number { + const baseHeight = this.option('rowHeight') as number; const innerOffset = (rowSize - 1) * INNER_CELL_MARGIN; return rowSize ? (baseHeight * rowSize) + innerOffset + OUTER_CELL_MARGIN : 0; } - private getGroupRowHeight(groupRows) { + private getGroupRowHeight(groupRows: number[] | undefined): number { if (!groupRows) { - return; - } - - let result = 0; - - for (let i = 0; i < groupRows.length; i++) { - result += this.getRowHeight(groupRows[i]); + return 0; } - return result; + return groupRows.reduce((result, groupRow) => result + this.getRowHeight(groupRow), 0); } renderAgendaLayout(appointments: ListEntity[]): void { @@ -483,36 +511,44 @@ class SchedulerAgenda extends WorkSpace { this.recalculateAgenda(rows); } - getAgendaVerticalStepHeight() { - return this.option('rowHeight'); + getAgendaVerticalStepHeight(): number { + return this.option('rowHeight') as number; } - getEndViewDate() { + getEndViewDate(): Date { return agendaUtils.calculateEndViewDate( this.getStartViewDate(), - this.option('endDayHour') as any, - this.option('agendaDuration') as any, + this.option('endDayHour'), + this.option('agendaDuration') as number, ); } - getEndViewDateByEndDayHour() { + getEndViewDateByEndDayHour(): Date { return this.getEndViewDate(); } - updateScrollPosition(date) { + updateScrollPosition(date: Date): void { const newDate = this.timeZoneCalculator.createDate(date, 'toGrid'); const bounds = this.getVisibleBounds(); - const startDateHour = newDate.getHours(); - const startDateMinutes = newDate.getMinutes(); - if (this.needUpdateScrollPosition(startDateHour, startDateMinutes, bounds, newDate)) { + if (this.needUpdateScrollPosition(newDate, bounds)) { this.scrollTo(newDate); } } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - needUpdateScrollPosition(hours, minutes, bounds, newData?: any) { + needUpdateScrollPosition( + date: Date, + appointmentGroupValues?: unknown, + inAllDayRow?: boolean, + ): boolean { + if (appointmentGroupValues || inAllDayRow) { + noop(); + } + + const bounds = this.getVisibleBounds(); + const hours = date.getHours(); + const minutes = date.getMinutes(); let isUpdateNeeded = false; if (hours < bounds.top.hours || hours > bounds.bottom.hours) { @@ -530,15 +566,18 @@ class SchedulerAgenda extends WorkSpace { return isUpdateNeeded; } - renovatedRenderSupported() { return false; } + renovatedRenderSupported(): boolean { return false; } - override isVirtualScrolling() { return false; } + override isVirtualScrolling(): boolean { return false; } - protected override getTotalViewDuration() { - return dateUtils.dateToMilliseconds('day') * (this.option('intervalCount') as any); + protected override getTotalViewDuration(): number { + return dateUtils.dateToMilliseconds('day') * this.option('intervalCount'); } - getDOMElementsMetaData() { + getDOMElementsMetaData(): { + dateTableCellsMeta: Record[][]; + allDayPanelCellsMeta: Record[]; + } { return { dateTableCellsMeta: [[{}]], allDayPanelCellsMeta: [{}], @@ -546,6 +585,7 @@ class SchedulerAgenda extends WorkSpace { } } +// eslint-disable-next-line @typescript-eslint/no-explicit-any registerComponent('dxSchedulerAgenda', SchedulerAgenda as any); export default SchedulerAgenda; diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline.ts index 4628643d9f52..83a999dd6090 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline.ts @@ -1,13 +1,13 @@ import registerComponent from '@js/core/component_registrator'; -import $ from '@js/core/renderer'; +import $, { type dxElementWrapper } from '@js/core/renderer'; import { noop } from '@js/core/utils/common'; import dateUtils from '@js/core/utils/date'; import { extend } from '@js/core/utils/extend'; import { getBoundingRect } from '@js/core/utils/position'; import { getOuterHeight, getOuterWidth, setHeight } from '@js/core/utils/size'; import { hasWindow } from '@js/core/utils/window'; +import type { dxSchedulerOptions } from '@js/ui/scheduler'; import { getGlobalFormatByDataType } from '@ts/core/m_global_format_config'; -// NOTE: Renovation component import. import { HeaderPanelTimelineComponent } from '@ts/scheduler/r1/components/index'; import { timelineWeekUtils } from '@ts/scheduler/r1/utils/index'; @@ -19,7 +19,9 @@ import tableCreatorModule from '../m_table_creator'; import timezoneUtils from '../m_utils_time_zone'; import HorizontalShader from '../shaders/current_time_shader_horizontal'; import { getFirstVisibleDate } from '../utils/skipped_days'; -import SchedulerWorkSpace from './m_work_space_indicator'; +import type { WorkspaceDateTableScrollableConfig, WorkspaceHeaderScrollableConfig } from './m_work_space'; +import SchedulerWorkSpace, { type WorkSpaceIndicatorDefaultOptions } from './m_work_space_indicator'; +import type { ViewDataProviderOptions } from './view_model/m_types'; const { tableCreator } = tableCreatorModule; @@ -34,80 +36,94 @@ const HEADER_PANEL_WEEK_CELL_CLASS = 'dx-scheduler-header-panel-week-cell'; const HORIZONTAL = 'horizontal'; const toMs = dateUtils.dateToMilliseconds; +interface TimelineDefaultOptions extends WorkSpaceIndicatorDefaultOptions { + groupOrientation: 'vertical'; +} + class SchedulerTimeline extends SchedulerWorkSpace { - protected override $sidebarTable: any; + protected override $sidebarTable!: dxElementWrapper; - get verticalGroupTableClass() { return GROUP_TABLE_CLASS; } + get verticalGroupTableClass(): string { return GROUP_TABLE_CLASS; } readonly viewDirection = 'horizontal'; - get renovatedHeaderPanelComponent() { return HeaderPanelTimelineComponent; } + get renovatedHeaderPanelComponent(): typeof HeaderPanelTimelineComponent { + return HeaderPanelTimelineComponent; + } - getGroupTableWidth() { - return this.$sidebarTable ? getOuterWidth(this.$sidebarTable) : 0; + getGroupTableWidth(): number { + return this.$sidebarTable ? getOuterWidth(this.$sidebarTable) as number : 0; } - protected override getTotalRowCount(groupCount) { + protected override getTotalRowCount( + groupCount: number, + includeAllDayPanelRows?: unknown, + ): number { + if (includeAllDayPanelRows !== undefined) { + noop(); + } + if (this.isHorizontalGroupedWorkSpace()) { return this.getRowCount(); } - groupCount = groupCount || 1; - return this.getRowCount() * groupCount; + const totalGroupCount = groupCount || 1; + return this.getRowCount() * totalGroupCount; } - protected override getFormat(): any { - return getGlobalFormatByDataType('time') || 'shorttime'; + protected override getFormat(): string { + const format = getGlobalFormatByDataType('time'); + return typeof format === 'string' ? format : 'shorttime'; } - private getWorkSpaceHeight() { + private getWorkSpaceHeight(): number { if (this.option('crossScrollingEnabled') && hasWindow()) { - return getBoundingRect(this.$dateTable.get(0)).height; + return getBoundingRect(this.$dateTable.get(0)).height as number; } - return getBoundingRect((this.$element() as any).get(0)).height; + return getBoundingRect(this.$element().get(0)).height as number; } - protected override dateTableScrollableConfig() { + protected override dateTableScrollableConfig(): WorkspaceDateTableScrollableConfig { const config = super.dateTableScrollableConfig(); const timelineConfig = { direction: HORIZONTAL, }; - return this.option('crossScrollingEnabled') ? config : extend(config, timelineConfig); + return this.option('crossScrollingEnabled') ? config : extend(config, timelineConfig) as WorkspaceDateTableScrollableConfig; } - protected override needCreateCrossScrolling() { + protected override needCreateCrossScrolling(): boolean { return true; } - protected override headerScrollableConfig() { + protected override headerScrollableConfig(): WorkspaceHeaderScrollableConfig { const config = super.headerScrollableConfig(); return extend(config, { scrollByContent: true, - }); + }) as WorkspaceHeaderScrollableConfig; } - supportAllDayRow() { + supportAllDayRow(): boolean { return false; } - protected override getGroupHeaderContainer() { + protected override getGroupHeaderContainer(): dxElementWrapper { if (this.isHorizontalGroupedWorkSpace()) { - return this.$thead; + return this.$thead as dxElementWrapper; } return this.$sidebarTable; } - protected override insertAllDayRowsIntoDateTable() { + protected override insertAllDayRowsIntoDateTable(): boolean { return false; } - protected needRenderWeekHeader() { + protected needRenderWeekHeader(): boolean { return false; } - protected incrementDate(date) { + protected incrementDate(date: Date): void { const skippedDays = this.option('skippedDays') ?? []; const nextDate = new Date(date); nextDate.setDate(nextDate.getDate() + 1); @@ -115,7 +131,7 @@ class SchedulerTimeline extends SchedulerWorkSpace { const nextVisibleDate = getFirstVisibleDate( nextDate, skippedDays, - (currentDate) => { + (currentDate: Date) => { const result = new Date(currentDate); result.setDate(result.getDate() + 1); return result; @@ -125,12 +141,12 @@ class SchedulerTimeline extends SchedulerWorkSpace { date.setTime(nextVisibleDate.getTime()); } - getIndicationCellCount() { + getIndicationCellCount(): number { const timeDiff = this.getTimeDiff(); return this.calculateDurationInCells(timeDiff); } - private getTimeDiff() { + private getTimeDiff(): number { let today = this.getToday(); const date = this.getIndicationFirstViewDate(); @@ -144,16 +160,16 @@ class SchedulerTimeline extends SchedulerWorkSpace { return today.getTime() - date.getTime(); } - protected calculateDurationInCells(timeDiff) { + protected calculateDurationInCells(timeDiff: number): number { const today = this.getToday(); const differenceInDays = Math.floor(timeDiff / toMs('day')); const skippedDaysCount = this.getSkippedDaysCount( this.getIndicationFirstViewDate(), differenceInDays, ); - let duration = (timeDiff - differenceInDays * toMs('day') - (this.option('startDayHour') as any) * toMs('hour')) / this.getCellDuration(); + let duration = (timeDiff - differenceInDays * toMs('day') - this.option('startDayHour') * toMs('hour')) / this.getCellDuration(); - if (today.getHours() > (this.option('endDayHour') as any)) { + if (today.getHours() > this.option('endDayHour')) { duration = this.getCellCountInDay(); } @@ -163,7 +179,7 @@ class SchedulerTimeline extends SchedulerWorkSpace { return (differenceInDays - skippedDaysCount) * this.getCellCountInDay() + duration; } - getIndicationWidth() { + getIndicationWidth(): number { if (this.isGroupedByDate()) { const cellCount = this.getIndicationCellCount(); const integerPart = Math.floor(cellCount); @@ -174,15 +190,15 @@ class SchedulerTimeline extends SchedulerWorkSpace { return this.getIndicationCellCount() * this.getCellWidth(); } - protected override isVerticalShader() { + protected override isVerticalShader(): boolean { return false; } - protected override isCurrentTimeHeaderCell() { + protected override isCurrentTimeHeaderCell(): boolean { return false; } - protected override setTableSizes() { + protected override setTableSizes(): void { super.setTableSizes(); const minHeight = this.getWorkSpaceMinHeight(); @@ -192,7 +208,7 @@ class SchedulerTimeline extends SchedulerWorkSpace { this.virtualScrollingDispatcher.updateDimensions(); } - private getWorkSpaceMinHeight() { + private getWorkSpaceMinHeight(): number { let minHeight = this.getWorkSpaceHeight(); const workspaceContainerHeight = getOuterHeight(this.$flexContainer, true); @@ -204,32 +220,37 @@ class SchedulerTimeline extends SchedulerWorkSpace { return minHeight; } - protected override getCellCoordinatesByIndex(index) { + protected override getCellCoordinatesByIndex( + index: number, + ): { columnIndex: number; rowIndex: number } { return { columnIndex: index % this.getCellCount(), rowIndex: 0, }; } - protected override getCellElementByPosition(cellCoordinates, groupIndex) { + protected override getCellElementByPosition( + cellCoordinates: { rowIndex: number; columnIndex: number }, + groupIndex: number, + ): dxElementWrapper { const indexes = this.groupedStrategy.prepareCellIndexes(cellCoordinates, groupIndex); return this.$dateTable .find('tr') .eq(indexes.rowIndex) .find('td') - .eq(indexes.columnIndex); + .eq(indexes.columnIndex) as dxElementWrapper; } - protected override getWorkSpaceWidth() { - return getOuterWidth(this.$dateTable, true); + protected override getWorkSpaceWidth(): number { + return getOuterWidth(this.$dateTable, true) as number; } - private getIndicationFirstViewDate() { - return dateUtils.trimTime(new Date(this.getStartViewDate())); + private getIndicationFirstViewDate(): Date { + return dateUtils.trimTime(new Date(this.getStartViewDate())) as Date; } - protected override getIntervalBetween(currentDate, allDay) { + protected override getIntervalBetween(currentDate: Date, allDay?: boolean): number { const startDayHour = this.option('startDayHour'); const endDayHour = this.option('endDayHour'); const firstViewDate = this.getStartViewDate(); @@ -244,7 +265,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') as any) * toMs('hour'); + let result = cellCount * this.option('hoursInterval') * toMs('hour'); if (!allDay) { const hour = currentDate.getHours(); @@ -272,34 +293,33 @@ class SchedulerTimeline extends SchedulerWorkSpace { return result; } - getAllDayContainer() { + getAllDayContainer(): null { return null; } - getTimePanelWidth() { + getTimePanelWidth(): number { return 0; } // eslint-disable-next-line @typescript-eslint/no-unused-vars - getIntervalDuration(allDay) { + getIntervalDuration(allDay: boolean): number { return this.getCellDuration(); } - getCellMinWidth() { + getCellMinWidth(): number { return 0; } - getWorkSpaceLeftOffset() { + getWorkSpaceLeftOffset(): number { return 0; } - renderRAllDayPanel() {} + renderRAllDayPanel(): void {} - renderRTimeTable() {} + renderRTimeTable(): void {} - // eslint-disable-next-line @typescript-eslint/no-unused-vars - generateRenderOptions(argument?: any) { - const options = super.generateRenderOptions(true); + generateRenderOptions(isProvideVirtualCellsWidth?: boolean): ViewDataProviderOptions { + const options = super.generateRenderOptions(isProvideVirtualCellsWidth ?? true); return { ...options, @@ -312,39 +332,42 @@ class SchedulerTimeline extends SchedulerWorkSpace { // We need these methods for now but they are useless for renovation // ------------- - _init() { + _init(): void { super._init(); - (this.$element() as any).addClass(TIMELINE_CLASS); + this.$element().addClass(TIMELINE_CLASS); this.$sidebarTable = $('
') .addClass(GROUP_TABLE_CLASS); } - protected override getDefaultGroupStrategy() { + protected override getDefaultGroupStrategy(): 'vertical' { return 'vertical'; } - protected override toggleGroupingDirectionClass() { - (this.$element() as any).toggleClass(HORIZONTAL_GROUPED_WORKSPACE_CLASS, this.isHorizontalGroupedWorkSpace()); + protected override toggleGroupingDirectionClass(): void { + this.$element().toggleClass( + HORIZONTAL_GROUPED_WORKSPACE_CLASS, + this.isHorizontalGroupedWorkSpace(), + ); } - _getDefaultOptions() { + _getDefaultOptions(): TimelineDefaultOptions { return extend(super._getDefaultOptions(), { groupOrientation: 'vertical', - }); + }) as TimelineDefaultOptions; } - protected override createWorkSpaceElements() { + protected override createWorkSpaceElements(): void { this.createWorkSpaceScrollableElements(); } - protected override updateAllDayVisibility() { return noop(); } + protected override updateAllDayVisibility(): void { return noop(); } - protected override getDateHeaderTemplate() { + protected override getDateHeaderTemplate(): dxSchedulerOptions['timeCellTemplate'] { return this.option('timeCellTemplate'); } - protected override renderView() { + protected override renderView(): void { this.renderWorkSpace(); this.virtualScrollingDispatcher.updateDimensions(); @@ -359,14 +382,14 @@ class SchedulerTimeline extends SchedulerWorkSpace { this.updateHeaderEmptyCellWidth(); } - protected override setHorizontalGroupHeaderCellsHeight() { return noop(); } + protected override setHorizontalGroupHeaderCellsHeight(): void { return noop(); } - protected override getTimePanelCells() { - return (this.$element() as any) + protected override getTimePanelCells(): dxElementWrapper { + return this.$element() .find(`.${HEADER_PANEL_CELL_CLASS}:not(.${HEADER_PANEL_WEEK_CELL_CLASS})`); } - getCurrentTimePanelCellIndices() { + getCurrentTimePanelCellIndices(): number[] { const columnCountPerGroup = this.getCellCount(); const today = this.getToday(); const index = this.getCellIndexByDate(today); @@ -384,8 +407,17 @@ class SchedulerTimeline extends SchedulerWorkSpace { .map((_, groupIndex) => columnCountPerGroup * groupIndex + currentTimeColumnIndex); } - protected override renderIndicator(height, rtlOffset, $container, groupCount) { - let $indicator; + protected override renderIndicator( + height: number, + rtlOffset: number, + $container: dxElementWrapper, + groupCount: number, + ): void { + if (height !== undefined) { + noop(); + } + // eslint-disable-next-line @typescript-eslint/init-declarations + let $indicator: dxElementWrapper | undefined; const width = this.getIndicationWidth(); if (this.option('groupOrientation') === 'vertical') { @@ -393,8 +425,9 @@ class SchedulerTimeline extends SchedulerWorkSpace { setHeight($indicator, getBoundingRect($container.get(0)).height); $indicator.css('left', rtlOffset ? rtlOffset - width : width); } else { - for (let i = 0; i < groupCount; i++) { - const offset = this.isGroupedByDate() ? i * this.getCellWidth() : this.getCellCount() * this.getCellWidth() * i; + for (let i = 0; i < groupCount; i += 1) { + const offset = this.isGroupedByDate() ? i * this.getCellWidth() + : this.getCellCount() * this.getCellWidth() * i; $indicator = this.createIndicator($container); setHeight($indicator, getBoundingRect($container.get(0)).height); @@ -403,7 +436,7 @@ class SchedulerTimeline extends SchedulerWorkSpace { } } - protected override makeGroupRows(groups, groupByDate) { + protected override makeGroupRows(groups: unknown[], groupByDate: boolean): unknown { const tableCreatorStrategy = this.option('groupOrientation') === 'vertical' ? tableCreator.VERTICAL : tableCreator.HORIZONTAL; return tableCreator.makeGroupedTable( @@ -423,5 +456,6 @@ class SchedulerTimeline extends SchedulerWorkSpace { } } +// eslint-disable-next-line @typescript-eslint/no-explicit-any registerComponent('dxSchedulerTimeline', SchedulerTimeline as any); export default SchedulerTimeline; diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_day.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_day.ts index 415c4a32cd01..1d905c9b4db8 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_day.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_day.ts @@ -6,17 +6,18 @@ import SchedulerTimeline from './m_timeline'; const TIMELINE_CLASS = 'dx-scheduler-timeline-day'; class SchedulerTimelineDay extends SchedulerTimeline { - get type() { return VIEWS.TIMELINE_DAY; } + get type(): string { return VIEWS.TIMELINE_DAY; } - protected override getElementClass() { + protected override getElementClass(): string { return TIMELINE_CLASS; } - protected override needRenderWeekHeader() { + protected override needRenderWeekHeader(): boolean { return this.isWorkSpaceWithCount(); } } +// eslint-disable-next-line @typescript-eslint/no-explicit-any registerComponent('dxSchedulerTimelineDay', SchedulerTimelineDay as any); export default SchedulerTimelineDay; diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_month.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_month.ts index 96478987146a..98e20e61fa83 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_month.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_month.ts @@ -1,76 +1,86 @@ import registerComponent from '@js/core/component_registrator'; +import type { template } from '@js/core/templates/template'; import dateUtils from '@js/core/utils/date'; -// NOTE: Renovation component import. import { HeaderPanelComponent } from '@ts/scheduler/r1/components/index'; import { formatWeekdayAndDay, monthUtils } from '@ts/scheduler/r1/utils/index'; import { VIEWS } from '../utils/options/constants_view'; import SchedulerTimeline from './m_timeline'; +import type { ViewDataProviderOptions } from './view_model/m_types'; const TIMELINE_CLASS = 'dx-scheduler-timeline-month'; class SchedulerTimelineMonth extends SchedulerTimeline { - get type() { return VIEWS.TIMELINE_MONTH; } + get type(): string { return VIEWS.TIMELINE_MONTH; } readonly viewDirection = 'horizontal'; - get renovatedHeaderPanelComponent() { return HeaderPanelComponent; } + get renovatedHeaderPanelComponent(): typeof HeaderPanelComponent { return HeaderPanelComponent; } - protected override renderView() { + protected override renderView(): void { super.renderView(); this.updateScrollable(); } - protected override getElementClass() { + protected override getElementClass(): string { return TIMELINE_CLASS; } - protected override getDateHeaderTemplate() { + protected override getDateHeaderTemplate(): template | undefined { return this.option('dateCellTemplate'); } - protected override calculateDurationInCells(timeDiff) { + protected override calculateDurationInCells(timeDiff: number): number { return timeDiff / this.getCellDuration(); } - isIndicatorVisible() { + isIndicatorVisible(): boolean { return true; } - protected override getFormat() { + // @ts-expect-error + protected override getFormat(): (date: Date) => string { return formatWeekdayAndDay; } - protected override getIntervalBetween(currentDate) { + protected override getIntervalBetween(currentDate: Date): number { const firstViewDate = this.getStartViewDate(); const timeZoneOffset = dateUtils.getTimezonesDifference(firstViewDate, currentDate); + const startDayHour = this.option('startDayHour'); - return currentDate.getTime() - (firstViewDate.getTime() - (this.option('startDayHour') as any) * 3600000) - timeZoneOffset; + return currentDate.getTime() + - (firstViewDate.getTime() - startDayHour * 3600000) + - timeZoneOffset; } - protected override getViewStartByOptions() { + protected override getViewStartByOptions(): Date { + const currentDate: Date = this.option('currentDate') ?? new Date(); + const startDate: Date = this.option('startDate') ?? currentDate; + const firstMonthDate = dateUtils.getFirstMonthDate(startDate) ?? startDate; + return monthUtils.getViewStartByOptions( - this.option('startDate') as any, - this.option('currentDate') as any, - this.option('intervalCount') as any, - dateUtils.getFirstMonthDate(this.option('startDate') as any) as any, + startDate, + currentDate, + this.option('intervalCount'), + firstMonthDate, ); } - generateRenderOptions() { + generateRenderOptions(): ViewDataProviderOptions { const options = super.generateRenderOptions(true); return { ...options, - getDateForHeaderText: (_, date) => date, + getDateForHeaderText: (_, date: Date): Date => date, }; } - keepOriginalHours() { + keepOriginalHours(): boolean { return true; } } +// eslint-disable-next-line @typescript-eslint/no-explicit-any registerComponent('dxSchedulerTimelineMonth', SchedulerTimelineMonth as any); export default SchedulerTimelineMonth; diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_week.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_week.ts index 0d1a9eec32b4..941b85ccd88c 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_week.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_week.ts @@ -11,13 +11,14 @@ export default class SchedulerTimelineWeek extends SchedulerTimeline { return this.option('type') ?? VIEWS.TIMELINE_WEEK; } - protected override getElementClass() { + protected override getElementClass(): string { return this.type === VIEWS.TIMELINE_WORK_WEEK ? TIMELINE_WORK_WEEK_CLASS : TIMELINE_CLASS; } - protected override needRenderWeekHeader() { + protected override needRenderWeekHeader(): boolean { return true; } } +// eslint-disable-next-line @typescript-eslint/no-explicit-any registerComponent('dxSchedulerTimelineWeek', SchedulerTimelineWeek as any); diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts index 42e146c7bbfb..92ce47c8412a 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts @@ -29,9 +29,11 @@ import { import { isDefined } from '@js/core/utils/type'; import { getWindow, hasWindow } from '@js/core/utils/window'; import type { dxSchedulerOptions } 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 { OptionChanged } from '@ts/core/widget/types'; import { AllDayPanelTitleComponent, AllDayTableComponent, @@ -107,6 +109,40 @@ interface RenderRWorkspaceOptions { generateNewData: boolean; } +export interface ViewDateGenerationOptions { + startDayHour: number; + endDayHour: number; + hoursInterval: number; + interval?: number; + intervalCount: number; + startViewDate: Date; + firstDayOfWeek: number; + skippedDays?: number[]; + viewOffset: number; + viewType: ViewType; +} + +export interface WorkspaceDateTableScrollableConfig { + useKeyboard: boolean; + bounceEnabled: boolean; + updateManually: boolean; + onScroll: (event: ScrollEvent) => void; + onInitialized: (args: { component: Scrollable }) => void; + onOptionChanged: (args: { fullName: string; value: unknown; component: Scrollable }) => void; + direction?: 'both'; + onEnd?: () => void; +} + +export interface WorkspaceHeaderScrollableConfig { + useKeyboard: boolean; + showScrollbar: 'never'; + direction: 'horizontal'; + useNative: false; + updateManually: true; + bounceEnabled: false; + onScroll: (event: ScrollEvent) => void; +} + const { tableCreator } = tableCreatorModule; // The constant is needed so that the dragging is not sharp. To prevent small twitches @@ -190,7 +226,7 @@ const DEFAULT_WORKSPACE_RENDER_OPTIONS: RenderRWorkspaceOptions = { generateNewData: true, }; -type WorkspaceOptionsInternal = Omit & { +export type WorkspaceOptionsInternal = Omit & { groups: ResourceLoader[]; getResourceManager: () => ResourceManager; startDate?: Date; @@ -202,6 +238,30 @@ type WorkspaceOptionsInternal = Omit & { skippedDays?: number[]; type?: ViewType; }; + +export type WorkspaceOptionChangedOptions = WorkspaceOptionsInternal & { + onSelectionChanged?: unknown; + onSelectionEnd?: unknown; + onCellClick?: unknown; + onCellContextMenu?: unknown; + viewOffset?: number; + groupOrientation?: 'horizontal' | 'vertical'; + timeZoneCalculator?: unknown; + allDayExpanded?: boolean; + width?: number | string; + allowMultipleCellSelection?: boolean; + selectedCellData?: unknown; + scrolling?: unknown; + schedulerHeight?: number; + schedulerWidth?: number; + agendaDuration?: number | 'month'; + rowHeight?: number; + noDataText?: string; + showCurrentTimeIndicator?: boolean; + indicatorTime?: Date; + indicatorUpdateInterval?: number; + shadeUntilCurrentTime?: boolean; +}; class SchedulerWorkSpace extends Widget { private viewDataProviderValue: any; @@ -661,8 +721,8 @@ class SchedulerWorkSpace extends Widget { this.$allDayTitle = $('
').appendTo(this.$headerPanelEmptyCell); } - protected dateTableScrollableConfig() { - let config: any = { + protected dateTableScrollableConfig(): WorkspaceDateTableScrollableConfig { + let config: WorkspaceDateTableScrollableConfig = { useKeyboard: false, bounceEnabled: false, updateManually: true, @@ -675,14 +735,14 @@ class SchedulerWorkSpace extends Widget { // To prevent scroll container focus in native mode we set tabindex -1 to container // In simulated mode focusable behavior prevented by useKeyboard: false private option onInitialized: ({ component }) => { - const useKeyboardDisabled = component.option('useKeyboard') === false; - const useNativeEnabled = component.option('useNative') === true; + const useKeyboardDisabled = component.option('useKeyboard'); + const useNativeEnabled = component.option('useNative'); if (useKeyboardDisabled && useNativeEnabled) { $(component.container()).attr('tabindex', -1); } }, onOptionChanged: ({ fullName, value, component }) => { - const useKeyboardDisabled = component.option('useKeyboard') === false; + const useKeyboardDisabled = component.option('useKeyboard'); if (useKeyboardDisabled && fullName === 'useNative' && value === true) { $(component.container()).attr('tabindex', -1); } @@ -699,7 +759,7 @@ class SchedulerWorkSpace extends Widget { const currentOnScroll = config.onScroll; config = { ...config, - onScroll: (e) => { + onScroll: (e: ScrollEvent) => { currentOnScroll?.(e); this.virtualScrollingDispatcher.handleOnScrollEvent(e?.scrollOffset); @@ -710,14 +770,24 @@ class SchedulerWorkSpace extends Widget { return config; } - protected createCrossScrollingConfig({ onScroll }): any { + protected createCrossScrollingConfig( + { onScroll }: Pick, + ): Pick { return { direction: 'both', - onScroll: (event) => { - onScroll?.(); + onScroll: (event: ScrollEvent) => { + onScroll?.(event); + + const top = event.scrollOffset?.top; + const left = event.scrollOffset?.left; + + if (top !== undefined) { + this.scrollSync.sidebar({ top }); + } - this.scrollSync.sidebar({ top: event.scrollOffset.top }); - this.scrollSync.header({ left: event.scrollOffset.left }); + if (left !== undefined) { + this.scrollSync.header({ left }); + } }, onEnd: () => { (this.option('onScrollEnd') as any)(); @@ -725,7 +795,7 @@ class SchedulerWorkSpace extends Widget { }; } - protected headerScrollableConfig() { + protected headerScrollableConfig(): WorkspaceHeaderScrollableConfig { return { useKeyboard: false, showScrollbar: 'never', @@ -733,7 +803,7 @@ class SchedulerWorkSpace extends Widget { useNative: false, updateManually: true, bounceEnabled: false, - onScroll: (event) => { + onScroll: (event: ScrollEvent) => { this.scrollSync.dateTable({ left: event.scrollOffset.left }); }, }; @@ -862,14 +932,14 @@ class SchedulerWorkSpace extends Widget { ); } - generateRenderOptions(isProvideVirtualCellsWidth?: any): ViewDataProviderOptions { + generateRenderOptions(isProvideVirtualCellsWidth = false): ViewDataProviderOptions { const groupCount = this.getGroupCount(); const groupOrientation = groupCount > 0 ? this.option('groupOrientation') : this.getDefaultGroupStrategy(); - const options = { + const options: ViewDataProviderOptions = { groupByDate: this.option('groupByDate'), startRowIndex: 0, startCellIndex: 0, @@ -1305,7 +1375,7 @@ class SchedulerWorkSpace extends Widget { }; } - protected getDateGenerationOptions() { + protected getDateGenerationOptions(): ViewDateGenerationOptions { return { startDayHour: this.option('startDayHour'), endDayHour: this.option('endDayHour'), @@ -1316,7 +1386,7 @@ class SchedulerWorkSpace extends Widget { firstDayOfWeek: this.firstDayOfWeek(), skippedDays: this.option('skippedDays'), viewOffset: 0, - viewType: this.type, + viewType: this.type as ViewType, }; } @@ -2297,9 +2367,9 @@ class SchedulerWorkSpace extends Widget { this.virtualScrollingDispatcher.dispose(); } - _getDefaultOptions() { + _getDefaultOptions(): WorkspaceOptionsInternal { // @ts-expect-error - return extend(super._getDefaultOptions(), { + const defaultOptions = extend(super._getDefaultOptions(), { currentDate: new Date(), intervalCount: 1, startDate: null, @@ -2333,18 +2403,20 @@ class SchedulerWorkSpace extends Widget { allDayPanelMode: 'all', height: undefined, draggingMode: 'outlook', - onScrollEnd: () => {}, + onScrollEnd: noop, getHeaderHeight: undefined, - renderAppointments: () => {}, - onShowAllDayPanel: () => {}, - onSelectedCellsClick: () => {}, + renderAppointments: noop, + onShowAllDayPanel: noop, + onSelectedCellsClick: noop, timeZoneCalculator: undefined, schedulerHeight: undefined, schedulerWidth: undefined, }); + + return defaultOptions; } - _optionChanged(args) { + _optionChanged(args: OptionChanged): void { switch (args.name) { case 'startDayHour': case 'endDayHour': diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_day.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_day.ts index 1d6c689f2c7b..d36bd53066c9 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_day.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_day.ts @@ -6,13 +6,13 @@ import SchedulerWorkSpaceVertical from './m_work_space_vertical'; const DAY_CLASS = 'dx-scheduler-work-space-day'; class SchedulerWorkSpaceDay extends SchedulerWorkSpaceVertical { - get type() { return VIEWS.DAY; } + get type(): string { return VIEWS.DAY; } - protected override getElementClass() { + protected override getElementClass(): string { return DAY_CLASS; } - renderRHeaderPanel() { + renderRHeaderPanel(): void { if (this.option('intervalCount') === 1) { super.renderRHeaderPanel(false); } else { @@ -21,6 +21,7 @@ class SchedulerWorkSpaceDay extends SchedulerWorkSpaceVertical { } } +// eslint-disable-next-line @typescript-eslint/no-explicit-any registerComponent('dxSchedulerWorkSpaceDay', SchedulerWorkSpaceDay as any); export default SchedulerWorkSpaceDay; diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_indicator.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_indicator.ts index 637110fc6e29..2f4dcece1e0b 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_indicator.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_indicator.ts @@ -1,23 +1,34 @@ import registerComponent from '@js/core/component_registrator'; -import $ from '@js/core/renderer'; +import $, { type dxElementWrapper } from '@js/core/renderer'; import dateUtils from '@js/core/utils/date'; import { extend } from '@js/core/utils/extend'; import { getBoundingRect } from '@js/core/utils/position'; import { setWidth } from '@js/core/utils/size'; import { hasWindow } from '@js/core/utils/window'; import { dateUtilsTs } from '@ts/core/utils/date'; +import type { OptionChanged } from '@ts/core/widget/types'; import { getToday } from '@ts/scheduler/r1/utils/index'; import { HEADER_CURRENT_TIME_CELL_CLASS } from '../m_classes'; import timezoneUtils from '../m_utils_time_zone'; -import SchedulerWorkSpace from './m_work_space'; +import SchedulerWorkSpace, { + type WorkspaceOptionChangedOptions, + type WorkspaceOptionsInternal, +} from './m_work_space'; const toMs = dateUtils.dateToMilliseconds; const SCHEDULER_DATE_TIME_INDICATOR_CLASS = 'dx-scheduler-date-time-indicator'; +export interface WorkSpaceIndicatorDefaultOptions extends WorkspaceOptionsInternal { + showCurrentTimeIndicator: boolean; + indicatorTime: Date; + indicatorUpdateInterval: number; + shadeUntilCurrentTime: boolean; +} + class SchedulerWorkSpaceIndicator extends SchedulerWorkSpace { - private indicatorInterval: any; + private indicatorInterval?: ReturnType; protected getToday(): Date { const viewOffset = this.option('viewOffset') as number; @@ -35,7 +46,7 @@ class SchedulerWorkSpaceIndicator extends SchedulerWorkSpace { return false; } - isIndicationAvailable() { + isIndicationAvailable(): boolean { if (!hasWindow()) { return false; } @@ -45,7 +56,7 @@ class SchedulerWorkSpaceIndicator extends SchedulerWorkSpace { return today >= dateUtils.trimTime(new Date(this.getStartViewDate())); } - isIndicatorVisible() { + isIndicatorVisible(): boolean { const today = this.getToday(); // Subtracts 1 ms from the real endViewDate instead of 1 minute @@ -57,10 +68,15 @@ class SchedulerWorkSpaceIndicator extends SchedulerWorkSpace { return dateUtils.dateInRange(today, firstViewDate, endViewDate); } - protected renderIndicator(height, rtlOffset, $container, groupCount) { + protected renderIndicator( + height: number, + rtlOffset: number, + $container: dxElementWrapper, + groupCount: number, + ): void { const groupedByDate = this.isGroupedByDate(); const repeatCount = groupedByDate ? 1 : groupCount; - for (let i = 0; i < repeatCount; i++) { + Array.from({ length: repeatCount }).forEach((_, i) => { const $indicator = this.createIndicator($container); setWidth( @@ -68,44 +84,53 @@ class SchedulerWorkSpaceIndicator extends SchedulerWorkSpace { groupedByDate ? this.getCellWidth() * groupCount : this.getCellWidth(), ); this.groupedStrategy.shiftIndicator($indicator, height, rtlOffset, i); - } + }); } - protected createIndicator($container) { + protected createIndicator($container: dxElementWrapper): dxElementWrapper { const $indicator = $('
').addClass(SCHEDULER_DATE_TIME_INDICATOR_CLASS); $container.append($indicator); return $indicator; } - private getRtlOffset(width) { + private getRtlOffset(width: number): number { return this.option('rtlEnabled') ? getBoundingRect(this.$dateTableScrollable.$content().get(0)).width - this.getTimePanelWidth() - width : 0; } - protected setIndicationUpdateInterval() { + protected setIndicationUpdateInterval(): void { if (!this.option('showCurrentTimeIndicator') || this.option('indicatorUpdateInterval') === 0) { return; } this.clearIndicatorUpdateInterval(); - this.indicatorInterval = setInterval(() => { + const scheduleIndicatorUpdate = (): void => { this.renderCurrentDateTimeIndication(); - }, this.option('indicatorUpdateInterval')); + this.indicatorInterval = setTimeout( + scheduleIndicatorUpdate, + this.option('indicatorUpdateInterval'), + ); + }; + + this.indicatorInterval = setTimeout( + scheduleIndicatorUpdate, + this.option('indicatorUpdateInterval'), + ); } - private clearIndicatorUpdateInterval() { + private clearIndicatorUpdateInterval(): void { if (this.indicatorInterval) { - clearInterval(this.indicatorInterval); - delete this.indicatorInterval; + clearTimeout(this.indicatorInterval); + this.indicatorInterval = undefined; } } - protected isVerticalShader() { + protected isVerticalShader(): boolean { return true; } - getIndicationWidth() { + getIndicationWidth(): number { const cellCount = this.getCellCount(); const cellSpan = Math.min(this.getIndicatorDaysSpan(), cellCount); const width = cellSpan * this.getCellWidth(); @@ -114,7 +139,7 @@ class SchedulerWorkSpaceIndicator extends SchedulerWorkSpace { return Math.min(width, maxWidth); } - getIndicatorOffset() { + getIndicatorOffset(): number { const cellSpan = this.getIndicatorDaysSpan() - 1; const offset = cellSpan * this.getCellWidth(); @@ -137,7 +162,7 @@ class SchedulerWorkSpaceIndicator extends SchedulerWorkSpace { return Math.ceil((timeDiff + 1) / toMs('day')); } - getIndicationHeight() { + getIndicationHeight(): number { const today = timezoneUtils.getDateWithoutTimezoneChange(this.getToday()); const cellHeight = this.getCellHeight(); const date = new Date(this.getStartViewDate()); @@ -152,9 +177,9 @@ class SchedulerWorkSpaceIndicator extends SchedulerWorkSpace { return cellCount * cellHeight; } - _dispose() { + _dispose(): void { this.clearIndicatorUpdateInterval(); - super._dispose.apply(this, arguments as any); + super._dispose(); } renderCurrentDateTimeIndication(): void { @@ -186,8 +211,8 @@ class SchedulerWorkSpaceIndicator extends SchedulerWorkSpace { return false; } - protected override getHeaderPanelCellClass(i) { - const cellClass = super.getHeaderPanelCellClass(i); + protected override getHeaderPanelCellClass(i: number): string { + const cellClass = super.getHeaderPanelCellClass(i) as string; if (this.isCurrentTimeHeaderCell(i)) { return `${cellClass} ${HEADER_CURRENT_TIME_CELL_CLASS}`; @@ -196,30 +221,30 @@ class SchedulerWorkSpaceIndicator extends SchedulerWorkSpace { return cellClass; } - protected override cleanView() { + protected override cleanView(): void { super.cleanView(); this.cleanDateTimeIndicator(); } - _dimensionChanged() { + _dimensionChanged(): void { super._dimensionChanged(); this.renderCurrentDateTimeLineAndShader(); } - private cleanDateTimeIndicator() { - (this.$element() as any).find(`.${SCHEDULER_DATE_TIME_INDICATOR_CLASS}`).remove(); + private cleanDateTimeIndicator(): void { + this.$element().find(`.${SCHEDULER_DATE_TIME_INDICATOR_CLASS}`).remove(); } - protected override cleanWorkSpace() { + protected override cleanWorkSpace(): void { super.cleanWorkSpace(); this.renderDateTimeIndication(); this.setIndicationUpdateInterval(); } - _optionChanged(args) { + _optionChanged(args: OptionChanged): void { switch (args.name) { case 'showCurrentTimeIndicator': case 'indicatorTime': @@ -242,16 +267,16 @@ class SchedulerWorkSpaceIndicator extends SchedulerWorkSpace { } } - _getDefaultOptions() { + _getDefaultOptions(): WorkSpaceIndicatorDefaultOptions { return extend(super._getDefaultOptions(), { showCurrentTimeIndicator: true, indicatorTime: new Date(), indicatorUpdateInterval: 5 * toMs('minute'), shadeUntilCurrentTime: true, - }); + }) as WorkSpaceIndicatorDefaultOptions; } - protected getCurrentTimePanelCellIndices() { + protected getCurrentTimePanelCellIndices(): number[] { const rowCountPerGroup = this.getTimePanelRowCount(); const today = this.getToday(); const index = this.getCellIndexByDate(today); @@ -261,10 +286,9 @@ class SchedulerWorkSpaceIndicator extends SchedulerWorkSpace { return []; } - let cellIndices; - if (currentTimeRowIndex === 0) { - cellIndices = [currentTimeRowIndex]; - } else { + let cellIndices: number[] = [currentTimeRowIndex]; + + if (currentTimeRowIndex !== 0) { cellIndices = currentTimeRowIndex % 2 === 0 ? [currentTimeRowIndex - 1, currentTimeRowIndex] : [currentTimeRowIndex, currentTimeRowIndex + 1]; @@ -275,7 +299,7 @@ class SchedulerWorkSpaceIndicator extends SchedulerWorkSpace { : 1; return [...new Array(verticalGroupCount)] - .reduce((currentIndices, _, groupIndex) => [ + .reduce((currentIndices, _, groupIndex) => [ ...currentIndices, ...cellIndices.map((cellIndex) => rowCountPerGroup * groupIndex + cellIndex), ], []); @@ -307,5 +331,6 @@ class SchedulerWorkSpaceIndicator extends SchedulerWorkSpace { } } +// eslint-disable-next-line @typescript-eslint/no-explicit-any registerComponent('dxSchedulerWorkSpace', SchedulerWorkSpaceIndicator as any); export default SchedulerWorkSpaceIndicator; diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_month.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_month.ts index 7585eb598a71..f53be27fa317 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_month.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_month.ts @@ -3,12 +3,12 @@ import { noop } from '@js/core/utils/common'; import dateUtils from '@js/core/utils/date'; import { getBoundingRect } from '@js/core/utils/position'; import { hasWindow } from '@js/core/utils/window'; -// NOTE: Renovation component import. import { DateTableMonthComponent } from '@ts/scheduler/r1/components/index'; import { formatWeekday, monthUtils } from '@ts/scheduler/r1/utils/index'; import { utils } from '../m_utils'; import { VIEWS } from '../utils/options/constants_view'; +import type { ViewDateGenerationOptions } from './m_work_space'; import SchedulerWorkSpace from './m_work_space_indicator'; const MONTH_CLASS = 'dx-scheduler-work-space-month'; @@ -16,24 +16,28 @@ const MONTH_CLASS = 'dx-scheduler-work-space-month'; const toMs = dateUtils.dateToMilliseconds; class SchedulerWorkSpaceMonth extends SchedulerWorkSpace { - get type() { return VIEWS.MONTH; } + get type(): string { return VIEWS.MONTH; } - protected override getElementClass() { + protected override getElementClass(): string { return MONTH_CLASS; } - protected override getFormat() { + protected override getFormat(): (date: Date) => string { return formatWeekday; } - protected override getIntervalBetween(currentDate) { + protected override getIntervalBetween(currentDate: Date): number { const firstViewDate = this.getStartViewDate(); const timeZoneOffset = dateUtils.getTimezonesDifference(firstViewDate, currentDate); + const startDayHour = this.option('startDayHour'); - return currentDate.getTime() - (firstViewDate.getTime() - (this.option('startDayHour') as any) * 3600000) - timeZoneOffset; + return currentDate.getTime() + - (firstViewDate.getTime() - startDayHour * 3600000) + - timeZoneOffset; } - protected override getDateGenerationOptions() { + protected override getDateGenerationOptions(): ViewDateGenerationOptions + & { cellCountInDay: number } { return { ...super.getDateGenerationOptions(), cellCountInDay: 1, @@ -45,8 +49,8 @@ class SchedulerWorkSpaceMonth extends SchedulerWorkSpace { * getCellWidth method need remove. Details in T712431 there is a test for this bug, * when changing the layout, the test will also be useless */ - getCellWidth() { - return this.cache.memo('cellWidth', () => { + getCellWidth(): number | undefined { + const cellWidth = this.cache.memo('cellWidth', (): number | undefined => { const DAYS_IN_WEEK = 7; let averageWidth = 0; @@ -57,13 +61,17 @@ class SchedulerWorkSpaceMonth extends SchedulerWorkSpace { return cells.length === 0 ? undefined : averageWidth / DAYS_IN_WEEK; }); + + return cellWidth as number | undefined; } - protected override insertAllDayRowsIntoDateTable() { + protected override insertAllDayRowsIntoDateTable(): boolean { return false; } - protected override getCellCoordinatesByIndex(index) { + protected override getCellCoordinatesByIndex( + index: number, + ): { rowIndex: number; columnIndex: number } { const rowIndex = Math.floor(index / this.getCellCount()); const columnIndex = index - this.getCellCount() * rowIndex; @@ -73,61 +81,64 @@ class SchedulerWorkSpaceMonth extends SchedulerWorkSpace { }; } - protected override needCreateCrossScrolling() { - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - return this.option('crossScrollingEnabled') || this.isVerticalGroupedWorkSpace(); + protected override needCreateCrossScrolling(): boolean { + return this.option('crossScrollingEnabled') ?? this.isVerticalGroupedWorkSpace(); } - protected override getViewStartByOptions() { + protected override getViewStartByOptions(): Date { + const currentDate: Date = this.option('currentDate') ?? new Date(); + const startDate: Date = this.option('startDate') ?? currentDate; + const firstMonthDate = dateUtils.getFirstMonthDate(startDate) ?? startDate; + return monthUtils.getViewStartByOptions( - this.option('startDate') as any, - this.option('currentDate') as any, - this.option('intervalCount') as any, - dateUtils.getFirstMonthDate(this.option('startDate')) as any, + startDate, + currentDate, + this.option('intervalCount'), + firstMonthDate, ); } - protected override updateIndex(index) { + protected override updateIndex(index: number): number { return index; } - isIndicationAvailable() { + isIndicationAvailable(): boolean { return false; } - getIntervalDuration() { + getIntervalDuration(): number { return toMs('day'); } - getTimePanelWidth() { + getTimePanelWidth(): number { return 0; } - supportAllDayRow() { + supportAllDayRow(): boolean { return false; } - keepOriginalHours() { + keepOriginalHours(): boolean { return true; } - getWorkSpaceLeftOffset() { + getWorkSpaceLeftOffset(): number { return 0; } - needApplyCollectorOffset() { + needApplyCollectorOffset(): boolean { return true; } - protected override getHeaderDate() { + protected override getHeaderDate(): Date { return this.getViewStartByOptions(); } - renderRAllDayPanel() {} + renderRAllDayPanel(): void {} - renderRTimeTable() {} + renderRTimeTable(): void {} - renderRDateTable() { + renderRDateTable(): void { utils.renovation.renderComponent( this, this.$dateTable, @@ -141,7 +152,7 @@ class SchedulerWorkSpaceMonth extends SchedulerWorkSpace { // We need these methods for now but they are useless for renovation // ------------- - protected override createWorkSpaceElements() { + protected override createWorkSpaceElements(): void { if (this.isVerticalGroupedWorkSpace()) { this.createWorkSpaceScrollableElements(); } else { @@ -149,9 +160,10 @@ class SchedulerWorkSpaceMonth extends SchedulerWorkSpace { } } - protected override updateAllDayVisibility() { return noop(); } + protected override updateAllDayVisibility(): void { return noop(); } } +// eslint-disable-next-line @typescript-eslint/no-explicit-any registerComponent('dxSchedulerWorkSpaceMonth', SchedulerWorkSpaceMonth as any); export default SchedulerWorkSpaceMonth; diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_week.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_week.ts index 62087a0b31e6..3676aae58f25 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_week.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_week.ts @@ -11,15 +11,16 @@ class SchedulerWorkSpaceWeek extends SchedulerWorkSpaceVertical { return this.option('type') ?? VIEWS.WEEK; } - protected override getElementClass() { + protected override getElementClass(): string { return this.type === VIEWS.WORK_WEEK ? WORK_WEEK_CLASS : WEEK_CLASS; } - protected override calculateViewStartDate() { - return weekUtils.calculateViewStartDate(this.option('startDate') as any, this.firstDayOfWeek()); + protected override calculateViewStartDate(): Date { + return weekUtils.calculateViewStartDate(this.option('startDate') as Date, this.firstDayOfWeek()); } } +// eslint-disable-next-line @typescript-eslint/no-explicit-any registerComponent('dxSchedulerWorkSpaceWeek', SchedulerWorkSpaceWeek as any); export default SchedulerWorkSpaceWeek; From 37966bab1a5a581f5dd1b1e3c14dba9cb878e794 Mon Sep 17 00:00:00 2001 From: Sergio Bur Date: Tue, 2 Jun 2026 13:35:40 +0200 Subject: [PATCH 02/10] fix: small fixes --- .../scheduler/workspaces/m_agenda.ts | 1 - .../scheduler/workspaces/m_work_space.ts | 6 +++--- .../workspaces/m_work_space_indicator.ts | 18 +++++------------- .../scheduler/workspaces/m_work_space_month.ts | 12 ++++-------- 4 files changed, 12 insertions(+), 25 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_agenda.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_agenda.ts index 624e111583d1..09f5b3bb6bb9 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_agenda.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_agenda.ts @@ -211,7 +211,6 @@ class SchedulerAgenda extends WorkSpace { protected override createCrossScrollingConfig(): Pick { return { - direction: 'both', onScroll: noop, onEnd: noop, }; diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts index 92ce47c8412a..7705dc9b6c18 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts @@ -735,14 +735,14 @@ class SchedulerWorkSpace extends Widget { // To prevent scroll container focus in native mode we set tabindex -1 to container // In simulated mode focusable behavior prevented by useKeyboard: false private option onInitialized: ({ component }) => { - const useKeyboardDisabled = component.option('useKeyboard'); - const useNativeEnabled = component.option('useNative'); + const useKeyboardDisabled = !(component.option('useKeyboard') as unknown as boolean); + const useNativeEnabled = component.option('useNative') as unknown as boolean; if (useKeyboardDisabled && useNativeEnabled) { $(component.container()).attr('tabindex', -1); } }, onOptionChanged: ({ fullName, value, component }) => { - const useKeyboardDisabled = component.option('useKeyboard'); + const useKeyboardDisabled = !(component.option('useKeyboard') as unknown as boolean); if (useKeyboardDisabled && fullName === 'useNative' && value === true) { $(component.container()).attr('tabindex', -1); } diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_indicator.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_indicator.ts index 2f4dcece1e0b..776fbfdf7b88 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_indicator.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_indicator.ts @@ -105,24 +105,16 @@ class SchedulerWorkSpaceIndicator extends SchedulerWorkSpace { this.clearIndicatorUpdateInterval(); - const scheduleIndicatorUpdate = (): void => { + // eslint-disable-next-line no-restricted-globals + this.indicatorInterval = setInterval(() => { this.renderCurrentDateTimeIndication(); - this.indicatorInterval = setTimeout( - scheduleIndicatorUpdate, - this.option('indicatorUpdateInterval'), - ); - }; - - this.indicatorInterval = setTimeout( - scheduleIndicatorUpdate, - this.option('indicatorUpdateInterval'), - ); + }, this.option('indicatorUpdateInterval')); } private clearIndicatorUpdateInterval(): void { if (this.indicatorInterval) { - clearTimeout(this.indicatorInterval); - this.indicatorInterval = undefined; + clearInterval(this.indicatorInterval); + delete this.indicatorInterval; } } diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_month.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_month.ts index f53be27fa317..3f48e543ac2e 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_month.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_month.ts @@ -82,19 +82,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 { - 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('startDate'), + this.option('currentDate'), this.option('intervalCount'), - firstMonthDate, + dateUtils.getFirstMonthDate(this.option('startDate')) as Date, ); } From a336d68448ba16d81f8523394936adbe4de74254 Mon Sep 17 00:00:00 2001 From: Sergio Bur Date: Wed, 3 Jun 2026 11:35:51 +0200 Subject: [PATCH 03/10] refactor: changes --- packages/devextreme/js/__internal/scheduler/m_scheduler.ts | 5 +++-- .../js/__internal/scheduler/workspaces/m_work_space.ts | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts index 985099ed3a92..812a805e0809 100644 --- a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts +++ b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts @@ -33,6 +33,7 @@ import type { } from '@js/ui/scheduler'; import errors from '@js/ui/widget/ui.errors'; import { dateUtilsTs } from '@ts/core/utils/date'; +import type { OptionChanged } from '@ts/core/widget/types'; import { createA11yStatusContainer } from './a11y_status/a11y_status_render'; import { getA11yStatusText } from './a11y_status/a11y_status_text'; @@ -79,7 +80,7 @@ import type { IFieldExpr } from './utils/index'; import { macroTaskArray } from './utils/index'; import { isAgendaWorkspaceComponent } from './utils/is_agenda_workpace_component'; import { VIEWS } from './utils/options/constants_view'; -import type { NormalizedView } from './utils/options/types'; +import type { NormalizedView, SafeSchedulerOptions } from './utils/options/types'; import { getAppointmentGroupValues, setAppointmentGroupValues } from './utils/resource_manager/appointment_groups_utils'; import { ResourceManager } from './utils/resource_manager/resource_manager'; import AppointmentLayoutManager from './view_model/appointments_layout_manager'; @@ -266,7 +267,7 @@ class Scheduler extends SchedulerOptionsBaseWidget { return resolveCallbacks.promise(); } - _optionChanged(args) { + _optionChanged(args: OptionChanged): void { this.schedulerOptionChanged(args); const { value, name } = args; diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts index 7705dc9b6c18..cf4f65294486 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts @@ -28,7 +28,6 @@ import { } from '@js/core/utils/size'; import { isDefined } from '@js/core/utils/type'; import { getWindow, hasWindow } from '@js/core/utils/window'; -import type { dxSchedulerOptions } 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'; @@ -74,6 +73,7 @@ import tableCreatorModule from '../m_table_creator'; import { utils } from '../m_utils'; import VerticalShader from '../shaders/current_time_shader_vertical'; import type { ResourceLoader } from '../utils/loader/resource_loader'; +import type { SafeSchedulerOptions } from '../utils/options/types'; import { getAppointmentGroupIndex, getSafeGroupValues, @@ -226,7 +226,7 @@ const DEFAULT_WORKSPACE_RENDER_OPTIONS: RenderRWorkspaceOptions = { generateNewData: true, }; -export type WorkspaceOptionsInternal = Omit & { +export type WorkspaceOptionsInternal = Omit & { groups: ResourceLoader[]; getResourceManager: () => ResourceManager; startDate?: Date; @@ -262,6 +262,7 @@ export type WorkspaceOptionChangedOptions = WorkspaceOptionsInternal & { indicatorUpdateInterval?: number; shadeUntilCurrentTime?: boolean; }; + class SchedulerWorkSpace extends Widget { private viewDataProviderValue: any; From 6843e773d26e63bc139aff2dcb5e76296c0c03db Mon Sep 17 00:00:00 2001 From: Sergio Bur Date: Wed, 3 Jun 2026 12:16:21 +0200 Subject: [PATCH 04/10] fix: fix build --- packages/devextreme/js/__internal/scheduler/m_scheduler.ts | 1 + .../devextreme/js/__internal/scheduler/workspaces/m_agenda.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts index 812a805e0809..d231a0e591f1 100644 --- a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts +++ b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts @@ -540,6 +540,7 @@ class Scheduler extends SchedulerOptionsBaseWidget { this.updateOption('workSpace', name, value); this.repaint(); break; + // @ts-expect-error case 'skippedDays': break; case 'indicatorTime': diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_agenda.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_agenda.ts index 09f5b3bb6bb9..272814f925c8 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_agenda.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_agenda.ts @@ -200,7 +200,7 @@ class SchedulerAgenda extends WorkSpace { private renderNoData(): void { this.$noDataContainer = $('
').addClass(NODATA_CONTAINER_CLASS) - .html(this.option('noDataText') as string); + .html(this.option('noDataText')); this.$dateTableScrollable.$content().append(this.$noDataContainer); } From 90b3df655ce34c383578c0f3e5268bcdb0db842e Mon Sep 17 00:00:00 2001 From: Sergei Burkatskii Date: Wed, 3 Jun 2026 13:33:53 +0200 Subject: [PATCH 05/10] fix: fix type for interval var Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Sergei Burkatskii --- .../__internal/scheduler/workspaces/m_work_space_indicator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_indicator.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_indicator.ts index 776fbfdf7b88..beffb15deae3 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_indicator.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_indicator.ts @@ -28,7 +28,7 @@ export interface WorkSpaceIndicatorDefaultOptions extends WorkspaceOptionsIntern } class SchedulerWorkSpaceIndicator extends SchedulerWorkSpace { - private indicatorInterval?: ReturnType; + private indicatorInterval?: ReturnType; protected getToday(): Date { const viewOffset = this.option('viewOffset') as number; From 950f790229d5cc537df680ffd0d47cb968516192 Mon Sep 17 00:00:00 2001 From: Sergio Bur Date: Wed, 3 Jun 2026 13:42:04 +0200 Subject: [PATCH 06/10] fix: copilot review --- .../devextreme/js/__internal/scheduler/workspaces/m_timeline.ts | 2 +- .../js/__internal/scheduler/workspaces/m_timeline_month.ts | 1 - .../js/__internal/scheduler/workspaces/m_work_space.ts | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline.ts index 83a999dd6090..90653ee44024 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline.ts @@ -70,7 +70,7 @@ class SchedulerTimeline extends SchedulerWorkSpace { return this.getRowCount() * totalGroupCount; } - protected override getFormat(): string { + protected override getFormat(): string | ((date: Date) => string) { const format = getGlobalFormatByDataType('time'); return typeof format === 'string' ? format : 'shorttime'; } diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_month.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_month.ts index 98e20e61fa83..da37cfd36648 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_month.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_month.ts @@ -39,7 +39,6 @@ class SchedulerTimelineMonth extends SchedulerTimeline { return true; } - // @ts-expect-error protected override getFormat(): (date: Date) => string { return formatWeekdayAndDay; } diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts index cf4f65294486..67ced775bdaf 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts @@ -1320,7 +1320,7 @@ class SchedulerWorkSpace extends Widget { }); } - protected getFormat() { return abstract(); } + protected getFormat(): string | ((date: Date) => string) { return abstract(); } getWorkArea() { return this.$dateTableContainer; From 617442667c335e8779e355f2af215a7ccc429420 Mon Sep 17 00:00:00 2001 From: Sergio Bur Date: Wed, 3 Jun 2026 13:45:50 +0200 Subject: [PATCH 07/10] fix: copilot review --- .../js/__internal/scheduler/workspaces/m_timeline.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline.ts index 90653ee44024..065dbb2c0964 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline.ts @@ -57,12 +57,9 @@ class SchedulerTimeline extends SchedulerWorkSpace { protected override getTotalRowCount( groupCount: number, + // eslint-disable-next-line @typescript-eslint/no-unused-vars includeAllDayPanelRows?: unknown, ): number { - if (includeAllDayPanelRows !== undefined) { - noop(); - } - if (this.isHorizontalGroupedWorkSpace()) { return this.getRowCount(); } From 500543bdd1e3d54e36a1b7beb45a3acaa00f5618 Mon Sep 17 00:00:00 2001 From: Sergio Bur Date: Wed, 3 Jun 2026 14:02:25 +0200 Subject: [PATCH 08/10] fix: copilot review --- .../__internal/scheduler/workspaces/m_agenda.ts | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_agenda.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_agenda.ts index 272814f925c8..fc6bf142d9cb 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_agenda.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_agenda.ts @@ -529,22 +529,12 @@ class SchedulerAgenda extends WorkSpace { updateScrollPosition(date: Date): void { const newDate = this.timeZoneCalculator.createDate(date, 'toGrid'); - const bounds = this.getVisibleBounds(); - - if (this.needUpdateScrollPosition(newDate, bounds)) { + if (this.needUpdateScrollPosition(newDate)) { this.scrollTo(newDate); } } - needUpdateScrollPosition( - date: Date, - appointmentGroupValues?: unknown, - inAllDayRow?: boolean, - ): boolean { - if (appointmentGroupValues || inAllDayRow) { - noop(); - } - + override needUpdateScrollPosition(date: Date): boolean { const bounds = this.getVisibleBounds(); const hours = date.getHours(); const minutes = date.getMinutes(); @@ -558,7 +548,7 @@ class SchedulerAgenda extends WorkSpace { isUpdateNeeded = true; } - if (hours === bounds.bottom.hours && minutes > bounds.top.minutes) { + if (hours === bounds.bottom.hours && minutes > bounds.bottom.minutes) { isUpdateNeeded = true; } From 21061062b17e4d942cd43f84495dd09072679c01 Mon Sep 17 00:00:00 2001 From: Sergio Bur Date: Wed, 3 Jun 2026 14:05:17 +0200 Subject: [PATCH 09/10] fix: copilot review --- .../js/__internal/scheduler/workspaces/m_work_space.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts index 67ced775bdaf..979b5ccda416 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts @@ -736,14 +736,14 @@ class SchedulerWorkSpace extends Widget { // To prevent scroll container focus in native mode we set tabindex -1 to container // In simulated mode focusable behavior prevented by useKeyboard: false private option onInitialized: ({ component }) => { - const useKeyboardDisabled = !(component.option('useKeyboard') as unknown as boolean); - const useNativeEnabled = component.option('useNative') as unknown as boolean; + const useKeyboardDisabled = (component.option('useKeyboard') as unknown as boolean | undefined) === false; + const useNativeEnabled = (component.option('useNative') as unknown as boolean | undefined) === true; if (useKeyboardDisabled && useNativeEnabled) { $(component.container()).attr('tabindex', -1); } }, onOptionChanged: ({ fullName, value, component }) => { - const useKeyboardDisabled = !(component.option('useKeyboard') as unknown as boolean); + const useKeyboardDisabled = (component.option('useKeyboard') as unknown as boolean | undefined) === false; if (useKeyboardDisabled && fullName === 'useNative' && value === true) { $(component.container()).attr('tabindex', -1); } From b7d430e7d6eecd8b307a3c1c91a83648a7d66d89 Mon Sep 17 00:00:00 2001 From: Sergio Bur Date: Wed, 3 Jun 2026 14:07:37 +0200 Subject: [PATCH 10/10] fix: copilot review --- .../js/__internal/scheduler/workspaces/m_timeline.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline.ts index 065dbb2c0964..ae77989e1817 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline.ts @@ -68,8 +68,7 @@ class SchedulerTimeline extends SchedulerWorkSpace { } protected override getFormat(): string | ((date: Date) => string) { - const format = getGlobalFormatByDataType('time'); - return typeof format === 'string' ? format : 'shorttime'; + return getGlobalFormatByDataType('time') as string | ((date: Date) => string) || 'shorttime'; } private getWorkSpaceHeight(): number {