diff --git a/packages/devextreme/js/__internal/grids/grid_core/views/m_rows_view.ts b/packages/devextreme/js/__internal/grids/grid_core/views/m_rows_view.ts index b233cb384dc9..6ab36758241c 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/views/m_rows_view.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/views/m_rows_view.ts @@ -1105,7 +1105,7 @@ class RowsView extends ColumnsView { if (loadPanel.option('visible') && !isLoading) { that._hideLoadingTimeoutID = setTimeout(() => { loadPanel.option(visibilityOptions); - }, LOADPANEL_HIDE_TIMEOUT); + }, LOADPANEL_HIDE_TIMEOUT) as unknown as number; } else { loadPanel.option(visibilityOptions); } diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts index c98e89f02440..df1c65236567 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts @@ -1,4 +1,4 @@ -import '@js/ui/scheduler/recurrence_editor'; +import '../m_recurrence_editor'; import '@js/ui/text_area'; import '@js/ui/tag_box'; import '@js/ui/switch'; @@ -13,8 +13,8 @@ import DataSource from '@js/data/data_source'; import messageLocalization from '@js/localization/message'; import { Semaphore } from '@js/renovation/ui/scheduler/utils/semaphore/semaphore'; import Form from '@js/ui/form'; -import { createAppointmentAdapter } from '@js/ui/scheduler/appointmentAdapter'; +import { createAppointmentAdapter } from '../m_appointment_adapter'; import timeZoneDataUtils from '../timezones/m_utils_timezones_data'; const SCREEN_SIZE_OF_SINGLE_COLUMN = 600; diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts index 3f776e337225..f72623e6b009 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts @@ -9,9 +9,9 @@ import { isPopupFullScreenNeeded, } from '@js/renovation/ui/scheduler/appointment_edit_form/popup_config'; import Popup from '@js/ui/popup/ui.popup'; -import { createAppointmentAdapter } from '@js/ui/scheduler/appointmentAdapter'; -import { hide as hideLoading, show as showLoading } from '@js/ui/scheduler/loading'; +import { createAppointmentAdapter } from '../m_appointment_adapter'; +import { hide as hideLoading, show as showLoading } from '../m_loading'; import { getNormalizedResources } from '../resources/m_utils'; const toMs = dateUtils.dateToMilliseconds; diff --git a/packages/devextreme/js/__internal/scheduler/appointments/data_provider/m_appointment_filter.ts b/packages/devextreme/js/__internal/scheduler/appointments/data_provider/m_appointment_filter.ts index d3fac5dffaad..08024b265c31 100644 --- a/packages/devextreme/js/__internal/scheduler/appointments/data_provider/m_appointment_filter.ts +++ b/packages/devextreme/js/__internal/scheduler/appointments/data_provider/m_appointment_filter.ts @@ -11,9 +11,9 @@ import { isDateAndTimeView as calculateIsDateAndTimeView, isTimelineView, } from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/base'; -import { createAppointmentAdapter } from '@js/ui/scheduler/appointmentAdapter'; -import { getRecurrenceProcessor } from '@js/ui/scheduler/recurrence'; +import { createAppointmentAdapter } from '../../m_appointment_adapter'; +import { getRecurrenceProcessor } from '../../m_recurrence'; import { getResourcesDataByGroups, } from '../../resources/m_utils'; diff --git a/packages/devextreme/js/__internal/scheduler/appointments/data_provider/m_utils.ts b/packages/devextreme/js/__internal/scheduler/appointments/data_provider/m_utils.ts index 6d6f5903dd6f..0bdafeec179c 100644 --- a/packages/devextreme/js/__internal/scheduler/appointments/data_provider/m_utils.ts +++ b/packages/devextreme/js/__internal/scheduler/appointments/data_provider/m_utils.ts @@ -1,8 +1,9 @@ import dateUtils from '@js/core/utils/date'; import dateSerialization from '@js/core/utils/date_serialization'; -import { ExpressionUtils } from '@js/ui/scheduler/expressionUtils'; import timeZoneUtils from '@js/ui/scheduler/utils.timeZone'; +import { ExpressionUtils } from '../../m_expression_utils'; + const toMs = dateUtils.dateToMilliseconds; const FULL_DATE_FORMAT = 'yyyyMMddTHHmmss'; diff --git a/packages/devextreme/js/__internal/scheduler/appointments/m_appointment.ts b/packages/devextreme/js/__internal/scheduler/appointments/m_appointment.ts index 550ac278f1e2..d573f4fc86d0 100644 --- a/packages/devextreme/js/__internal/scheduler/appointments/m_appointment.ts +++ b/packages/devextreme/js/__internal/scheduler/appointments/m_appointment.ts @@ -11,6 +11,8 @@ import { addNamespace } from '@js/events/utils/index'; import dateLocalization from '@js/localization/date'; import messageLocalization from '@js/localization/message'; import Resizable from '@js/ui/resizable'; +import { hide, show } from '@js/ui/tooltip/ui.tooltip'; + import { ALL_DAY_APPOINTMENT_CLASS, APPOINTMENT_CONTENT_CLASSES, @@ -21,10 +23,9 @@ import { REDUCED_APPOINTMENT_CLASS, REDUCED_APPOINTMENT_ICON, REDUCED_APPOINTMENT_PARTS_CLASSES, -} from '@js/ui/scheduler/classes'; -import { ExpressionUtils } from '@js/ui/scheduler/expressionUtils'; -import { getRecurrenceProcessor } from '@js/ui/scheduler/recurrence'; -import { hide, show } from '@js/ui/tooltip/ui.tooltip'; +} from '../m_classes'; +import { ExpressionUtils } from '../m_expression_utils'; +import { getRecurrenceProcessor } from '../m_recurrence'; const DEFAULT_HORIZONTAL_HANDLES = 'left right'; const DEFAULT_VERTICAL_HANDLES = 'top bottom'; @@ -191,7 +192,7 @@ export class Appointment extends DOMComponent { } _renderReducedAppointment() { - const reducedPart = this.option('reduced'); + const reducedPart: any = this.option('reduced'); if (!reducedPart) { return; @@ -253,7 +254,7 @@ export class Appointment extends DOMComponent { } _renderDirection() { - (this.$element() as any).addClass(DIRECTION_APPOINTMENT_CLASSES[this.option('direction')]); + (this.$element() as any).addClass(DIRECTION_APPOINTMENT_CLASSES[this.option('direction') as any]); } _createResizingConfig() { diff --git a/packages/devextreme/js/__internal/scheduler/appointments/m_appointment_collection.ts b/packages/devextreme/js/__internal/scheduler/appointments/m_appointment_collection.ts index fc702bb1e06c..2ef1bb5a28bc 100644 --- a/packages/devextreme/js/__internal/scheduler/appointments/m_appointment_collection.ts +++ b/packages/devextreme/js/__internal/scheduler/appointments/m_appointment_collection.ts @@ -20,13 +20,13 @@ import eventsEngine from '@js/events/core/events_engine'; import { name as dblclickEvent } from '@js/events/double_click'; import { addNamespace, isFakeClickEvent } from '@js/events/utils/index'; import CollectionWidget from '@js/ui/collection/ui.collection_widget.edit'; -import { createAppointmentAdapter } from '@js/ui/scheduler/appointmentAdapter'; -import { APPOINTMENT_DRAG_SOURCE_CLASS, APPOINTMENT_ITEM_CLASS } from '@js/ui/scheduler/classes'; -import { APPOINTMENT_SETTINGS_KEY } from '@js/ui/scheduler/constants'; -import { ExpressionUtils } from '@js/ui/scheduler/expressionUtils'; -import { getRecurrenceProcessor } from '@js/ui/scheduler/recurrence'; import timeZoneUtils from '@js/ui/scheduler/utils.timeZone'; +import { createAppointmentAdapter } from '../m_appointment_adapter'; +import { APPOINTMENT_DRAG_SOURCE_CLASS, APPOINTMENT_ITEM_CLASS } from '../m_classes'; +import { APPOINTMENT_SETTINGS_KEY } from '../m_constants'; +import { ExpressionUtils } from '../m_expression_utils'; +import { getRecurrenceProcessor } from '../m_recurrence'; import { getAppointmentTakesSeveralDays, sortAppointmentsByStartDate } from './data_provider/m_utils'; import { AgendaAppointment, Appointment } from './m_appointment'; import { createAgendaAppointmentLayout, createAppointmentLayout } from './m_appointment_layout'; diff --git a/packages/devextreme/js/__internal/scheduler/appointments/m_appointment_layout.ts b/packages/devextreme/js/__internal/scheduler/appointments/m_appointment_layout.ts index 70e34fc3f013..594c4fbcf02b 100644 --- a/packages/devextreme/js/__internal/scheduler/appointments/m_appointment_layout.ts +++ b/packages/devextreme/js/__internal/scheduler/appointments/m_appointment_layout.ts @@ -1,7 +1,8 @@ import domAdapter from '@js/core/dom_adapter'; import $ from '@js/core/renderer'; import messageLocalization from '@js/localization/message'; -import { APPOINTMENT_CONTENT_CLASSES } from '@js/ui/scheduler/classes'; + +import { APPOINTMENT_CONTENT_CLASSES } from '../m_classes'; const allDayText = ` ${messageLocalization.format('dxScheduler-allDay')}: `; diff --git a/packages/devextreme/js/__internal/scheduler/appointments/m_render.ts b/packages/devextreme/js/__internal/scheduler/appointments/m_render.ts index bb860c6f7008..4d727da0213a 100644 --- a/packages/devextreme/js/__internal/scheduler/appointments/m_render.ts +++ b/packages/devextreme/js/__internal/scheduler/appointments/m_render.ts @@ -1,8 +1,8 @@ import $ from '@js/core/renderer'; -import { utils } from '@js/ui/scheduler/utils'; // @ts-expect-error import dxrAppointmentLayout from '../../../renovation/ui/scheduler/appointment/layout.j'; +import { utils } from '../m_utils'; // This is temporary - to creating appointments from the old code export const renderAppointments = (options) => { diff --git a/packages/devextreme/js/__internal/scheduler/appointments/m_settings_generator.ts b/packages/devextreme/js/__internal/scheduler/appointments/m_settings_generator.ts index 97bd2075fe32..ce19d8c34a95 100644 --- a/packages/devextreme/js/__internal/scheduler/appointments/m_settings_generator.ts +++ b/packages/devextreme/js/__internal/scheduler/appointments/m_settings_generator.ts @@ -3,11 +3,11 @@ import dateUtils from '@js/core/utils/date'; import { extend } from '@js/core/utils/extend'; import { isEmptyObject } from '@js/core/utils/type'; import { isDateAndTimeView } from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/base'; -import { createAppointmentAdapter } from '@js/ui/scheduler/appointmentAdapter'; -import { ExpressionUtils } from '@js/ui/scheduler/expressionUtils'; -import { getRecurrenceProcessor } from '@js/ui/scheduler/recurrence'; import timeZoneUtils from '@js/ui/scheduler/utils.timeZone'; +import { createAppointmentAdapter } from '../m_appointment_adapter'; +import { ExpressionUtils } from '../m_expression_utils'; +import { getRecurrenceProcessor } from '../m_recurrence'; import { createResourcesTree, getDataAccessors, getGroupCount, getResourceTreeLeaves, } from '../resources/m_utils'; diff --git a/packages/devextreme/js/__internal/scheduler/appointments/rendering_strategies/m_strategy_agenda.ts b/packages/devextreme/js/__internal/scheduler/appointments/rendering_strategies/m_strategy_agenda.ts index 6a903e2d0811..a058df29930a 100644 --- a/packages/devextreme/js/__internal/scheduler/appointments/rendering_strategies/m_strategy_agenda.ts +++ b/packages/devextreme/js/__internal/scheduler/appointments/rendering_strategies/m_strategy_agenda.ts @@ -1,8 +1,8 @@ import dateUtils from '@js/core/utils/date'; import { each } from '@js/core/utils/iterator'; -import { createAppointmentAdapter } from '@js/ui/scheduler/appointmentAdapter'; -import { ExpressionUtils } from '@js/ui/scheduler/expressionUtils'; +import { createAppointmentAdapter } from '../../m_appointment_adapter'; +import { ExpressionUtils } from '../../m_expression_utils'; import { groupAppointmentsByResources } from '../../resources/m_utils'; import { getAppointmentTakesSeveralDays, replaceWrongEndDate } from '../data_provider/m_utils'; import BaseRenderingStrategy from './m_strategy_base'; diff --git a/packages/devextreme/js/__internal/scheduler/appointments/rendering_strategies/m_strategy_base.ts b/packages/devextreme/js/__internal/scheduler/appointments/rendering_strategies/m_strategy_base.ts index c108b95d5443..a777f2b6e696 100644 --- a/packages/devextreme/js/__internal/scheduler/appointments/rendering_strategies/m_strategy_base.ts +++ b/packages/devextreme/js/__internal/scheduler/appointments/rendering_strategies/m_strategy_base.ts @@ -2,10 +2,10 @@ import dateUtils from '@js/core/utils/date'; import { extend } from '@js/core/utils/extend'; import { isNumeric, isObject } from '@js/core/utils/type'; import { getAppointmentTakesAllDay } from '@js/renovation/ui/scheduler/appointment/utils/getAppointmentTakesAllDay'; -import { createAppointmentAdapter } from '@js/ui/scheduler/appointmentAdapter'; import timeZoneUtils from '@js/ui/scheduler/utils.timeZone'; import { current as currentTheme } from '@js/ui/themes'; +import { createAppointmentAdapter } from '../../m_appointment_adapter'; import { AppointmentSettingsGenerator } from '../m_settings_generator'; import AdaptivePositioningStrategy from './m_appointments_positioning_strategy_adaptive'; import AppointmentPositioningStrategy from './m_appointments_positioning_strategy_base'; diff --git a/packages/devextreme/js/__internal/scheduler/appointments/rendering_strategies/m_strategy_horizontal.ts b/packages/devextreme/js/__internal/scheduler/appointments/rendering_strategies/m_strategy_horizontal.ts index fc0c7429e112..52393225f600 100644 --- a/packages/devextreme/js/__internal/scheduler/appointments/rendering_strategies/m_strategy_horizontal.ts +++ b/packages/devextreme/js/__internal/scheduler/appointments/rendering_strategies/m_strategy_horizontal.ts @@ -1,7 +1,7 @@ import dateUtils from '@js/core/utils/date'; import getSkippedHoursInRange from '@js/renovation/ui/scheduler/view_model/appointments/utils/getSkippedHoursInRange'; -import { ExpressionUtils } from '@js/ui/scheduler/expressionUtils'; +import { ExpressionUtils } from '../../m_expression_utils'; import BaseAppointmentsStrategy from './m_strategy_base'; const DEFAULT_APPOINTMENT_HEIGHT = 60; diff --git a/packages/devextreme/js/__internal/scheduler/appointments/rendering_strategies/m_strategy_horizontal_month.ts b/packages/devextreme/js/__internal/scheduler/appointments/rendering_strategies/m_strategy_horizontal_month.ts index 18a6da045a4f..6a1e3ce44dc2 100644 --- a/packages/devextreme/js/__internal/scheduler/appointments/rendering_strategies/m_strategy_horizontal_month.ts +++ b/packages/devextreme/js/__internal/scheduler/appointments/rendering_strategies/m_strategy_horizontal_month.ts @@ -1,6 +1,6 @@ import dateUtils from '@js/core/utils/date'; -import { getGroupWidth } from '@js/ui/scheduler/workspaces/helpers/positionHelper'; +import { getGroupWidth } from '../../workspaces/helpers/m_position_helper'; import HorizontalMonthLineRenderingStrategy from './m_strategy_horizontal_month_line'; const MONTH_APPOINTMENT_HEIGHT_RATIO = 0.6; diff --git a/packages/devextreme/js/__internal/scheduler/appointments/rendering_strategies/m_strategy_vertical.ts b/packages/devextreme/js/__internal/scheduler/appointments/rendering_strategies/m_strategy_vertical.ts index c7f7cf090f6d..b049a73ce27d 100644 --- a/packages/devextreme/js/__internal/scheduler/appointments/rendering_strategies/m_strategy_vertical.ts +++ b/packages/devextreme/js/__internal/scheduler/appointments/rendering_strategies/m_strategy_vertical.ts @@ -4,10 +4,10 @@ import { roundFloatPart } from '@js/core/utils/math'; import { isNumeric } from '@js/core/utils/type'; import { getAppointmentTakesAllDay } from '@js/renovation/ui/scheduler/appointment/utils/getAppointmentTakesAllDay'; import getSkippedHoursInRange from '@js/renovation/ui/scheduler/view_model/appointments/utils/getSkippedHoursInRange'; -import { createAppointmentAdapter } from '@js/ui/scheduler/appointmentAdapter'; -import { ExpressionUtils } from '@js/ui/scheduler/expressionUtils'; import timeZoneUtils from '@js/ui/scheduler/utils.timeZone'; +import { createAppointmentAdapter } from '../../m_appointment_adapter'; +import { ExpressionUtils } from '../../m_expression_utils'; import BaseAppointmentsStrategy from './m_strategy_base'; const ALLDAY_APPOINTMENT_MIN_VERTICAL_OFFSET = 5; diff --git a/packages/devextreme/js/__internal/scheduler/header/m_utils.ts b/packages/devextreme/js/__internal/scheduler/header/m_utils.ts index 3721bea66a2d..11a2a6b0deb7 100644 --- a/packages/devextreme/js/__internal/scheduler/header/m_utils.ts +++ b/packages/devextreme/js/__internal/scheduler/header/m_utils.ts @@ -4,7 +4,8 @@ import { camelize } from '@js/core/utils/inflector'; import { isFunction, isObject } from '@js/core/utils/type'; import dateLocalization from '@js/localization/date'; import messageLocalization from '@js/localization/message'; -import { VIEWS } from '@js/ui/scheduler/constants'; + +import { VIEWS } from '../m_constants'; const DAY_FORMAT = 'd'; diff --git a/packages/devextreme/js/__internal/scheduler/m_appointment_adapter.ts b/packages/devextreme/js/__internal/scheduler/m_appointment_adapter.ts index dd781bb7c0dc..b5b795e42e11 100644 --- a/packages/devextreme/js/__internal/scheduler/m_appointment_adapter.ts +++ b/packages/devextreme/js/__internal/scheduler/m_appointment_adapter.ts @@ -1,179 +1,179 @@ -import { extend } from '../../core/utils/extend'; -import errors from '../widget/ui.errors'; -import { deepExtendArraySafe } from '../../core/utils/object'; -import { getRecurrenceProcessor } from './recurrence'; -import { ExpressionUtils } from './expressionUtils'; +import { extend } from '@js/core/utils/extend'; +import { deepExtendArraySafe } from '@js/core/utils/object'; +import errors from '@js/ui/widget/ui.errors'; + +import { ExpressionUtils } from './m_expression_utils'; +import { getRecurrenceProcessor } from './m_recurrence'; const PROPERTY_NAMES = { - startDate: 'startDate', - endDate: 'endDate', - allDay: 'allDay', - text: 'text', - description: 'description', - startDateTimeZone: 'startDateTimeZone', - endDateTimeZone: 'endDateTimeZone', - recurrenceRule: 'recurrenceRule', - recurrenceException: 'recurrenceException', - disabled: 'disabled' + startDate: 'startDate', + endDate: 'endDate', + allDay: 'allDay', + text: 'text', + description: 'description', + startDateTimeZone: 'startDateTimeZone', + endDateTimeZone: 'endDateTimeZone', + recurrenceRule: 'recurrenceRule', + recurrenceException: 'recurrenceException', + disabled: 'disabled', }; class AppointmentAdapter { - constructor(rawAppointment, dataAccessors, timeZoneCalculator, options) { - this.rawAppointment = rawAppointment; - this.dataAccessors = dataAccessors; - this.timeZoneCalculator = timeZoneCalculator; - this.options = options; - } - - get duration() { - return this.endDate ? this.endDate - this.startDate : 0; - } - - get startDate() { - const result = this.getField(PROPERTY_NAMES.startDate); - return result === undefined ? result : new Date(result); - } - - set startDate(value) { - this.setField(PROPERTY_NAMES.startDate, value); - } + constructor( + public rawAppointment: any, + public dataAccessors: any, + public timeZoneCalculator: any, + public options: any, + ) { + } + + get duration() { + return this.endDate ? this.endDate - this.startDate : 0; + } + + get startDate() { + const result = this.getField(PROPERTY_NAMES.startDate); + return result === undefined ? result : new Date(result); + } + + set startDate(value) { + this.setField(PROPERTY_NAMES.startDate, value); + } + + get endDate() { + const result = this.getField(PROPERTY_NAMES.endDate); + return result === undefined ? result : new Date(result); + } + + set endDate(value) { + this.setField(PROPERTY_NAMES.endDate, value); + } + + get allDay() { + return this.getField(PROPERTY_NAMES.allDay); + } + + set allDay(value) { + this.setField(PROPERTY_NAMES.allDay, value); + } + + get text() { + return this.getField(PROPERTY_NAMES.text); + } + + set text(value) { + this.setField(PROPERTY_NAMES.text, value); + } + + get description() { + return this.getField(PROPERTY_NAMES.description); + } + + set description(value) { + this.setField(PROPERTY_NAMES.description, value); + } + + get startDateTimeZone() { + return this.getField(PROPERTY_NAMES.startDateTimeZone); + } + + get endDateTimeZone() { + return this.getField(PROPERTY_NAMES.endDateTimeZone); + } + + get recurrenceRule() { + return this.getField(PROPERTY_NAMES.recurrenceRule); + } + + set recurrenceRule(value) { + this.setField(PROPERTY_NAMES.recurrenceRule, value); + } + + get recurrenceException() { + return this.getField(PROPERTY_NAMES.recurrenceException); + } + + set recurrenceException(value) { + this.setField(PROPERTY_NAMES.recurrenceException, value); + } + + get disabled() { + return !!this.getField(PROPERTY_NAMES.disabled); + } + + get isRecurrent() { + return getRecurrenceProcessor().isValidRecurrenceRule(this.recurrenceRule); + } + + getField(property) { + return ExpressionUtils.getField( + this.dataAccessors, + property, + this.rawAppointment, + ); + } + + setField(property, value) { + return ExpressionUtils.setField( + this.dataAccessors, + property, + this.rawAppointment, + value, + ); + } + + calculateStartDate(pathTimeZoneConversion) { + if (!this.startDate || isNaN(this.startDate.getTime())) { + throw errors.Error('E1032', this.text); + } - get endDate() { - const result = this.getField(PROPERTY_NAMES.endDate); - return result === undefined ? result : new Date(result); - } + return this.calculateDate(this.startDate, this.startDateTimeZone, pathTimeZoneConversion); + } + + calculateEndDate(pathTimeZoneConversion) { + return this.calculateDate(this.endDate, this.endDateTimeZone, pathTimeZoneConversion); + } + + calculateDate(date, appointmentTimeZone, pathTimeZoneConversion) { + if (!date) { // TODO: E1032 should be thrown only for startDate above + return undefined; + } - set endDate(value) { - this.setField(PROPERTY_NAMES.endDate, value); - } + return this.timeZoneCalculator.createDate(date, { + appointmentTimeZone, + path: pathTimeZoneConversion, + }); + } - get allDay() { - return this.getField(PROPERTY_NAMES.allDay); - } + clone(options: any = undefined) { + const result = new AppointmentAdapter( + deepExtendArraySafe({}, this.rawAppointment), + this.dataAccessors, + this.timeZoneCalculator, + options, + ); - set allDay(value) { - this.setField(PROPERTY_NAMES.allDay, value); + if (options?.pathTimeZone) { + result.startDate = result.calculateStartDate(options.pathTimeZone); + result.endDate = result.calculateEndDate(options.pathTimeZone); } - get text() { - return this.getField(PROPERTY_NAMES.text); - } - - set text(value) { - this.setField(PROPERTY_NAMES.text, value); - } + return result; + } + + source(serializeDate = false) { + if (serializeDate) { + // TODO: hack for use dateSerializationFormat + const clonedAdapter = this.clone(); + clonedAdapter.startDate = this.startDate; + clonedAdapter.endDate = this.endDate; - get description() { - return this.getField(PROPERTY_NAMES.description); - } - - set description(value) { - this.setField(PROPERTY_NAMES.description, value); - } - - get startDateTimeZone() { - return this.getField(PROPERTY_NAMES.startDateTimeZone); - } - - get endDateTimeZone() { - return this.getField(PROPERTY_NAMES.endDateTimeZone); - } - - get recurrenceRule() { - return this.getField(PROPERTY_NAMES.recurrenceRule); - } - - set recurrenceRule(value) { - this.setField(PROPERTY_NAMES.recurrenceRule, value); - } - - get recurrenceException() { - return this.getField(PROPERTY_NAMES.recurrenceException); - } + return clonedAdapter.source(); + } - set recurrenceException(value) { - this.setField(PROPERTY_NAMES.recurrenceException, value); - } - - get disabled() { - return !!this.getField(PROPERTY_NAMES.disabled); - } - - get isRecurrent() { - return getRecurrenceProcessor().isValidRecurrenceRule(this.recurrenceRule); - } - - getField(property) { - return ExpressionUtils.getField( - this.dataAccessors, - property, - this.rawAppointment - ); - } - - setField(property, value) { - return ExpressionUtils.setField( - this.dataAccessors, - property, - this.rawAppointment, - value - ); - } - - calculateStartDate(pathTimeZoneConversion) { - if(!this.startDate || isNaN(this.startDate.getTime())) { - throw errors.Error('E1032', this.text); - } - - return this.calculateDate(this.startDate, this.startDateTimeZone, pathTimeZoneConversion); - } - - calculateEndDate(pathTimeZoneConversion) { - return this.calculateDate(this.endDate, this.endDateTimeZone, pathTimeZoneConversion); - } - - calculateDate(date, appointmentTimeZone, pathTimeZoneConversion) { - if(!date) { // TODO: E1032 should be thrown only for startDate above - return undefined; - } - - return this.timeZoneCalculator.createDate(date, { - appointmentTimeZone: appointmentTimeZone, - path: pathTimeZoneConversion - }); - } - - clone(options = undefined) { - const result = new AppointmentAdapter( - deepExtendArraySafe({}, this.rawAppointment), - this.dataAccessors, - this.timeZoneCalculator, - options - ); - - if(options?.pathTimeZone) { - result.startDate = result.calculateStartDate(options.pathTimeZone); - result.endDate = result.calculateEndDate(options.pathTimeZone); - } - - return result; - } - - source(serializeDate = false) { - if(serializeDate) { - // TODO: hack for use dateSerializationFormat - const clonedAdapter = this.clone(); - clonedAdapter.startDate = this.startDate; - clonedAdapter.endDate = this.endDate; - - return clonedAdapter.source(); - } - - return extend({}, this.rawAppointment); - } + return extend({}, this.rawAppointment); + } } export default AppointmentAdapter; -export const createAppointmentAdapter = (rawAppointment, dataAccessors, timeZoneCalculator, options) => { - return new AppointmentAdapter(rawAppointment, dataAccessors, timeZoneCalculator, options); -}; +export const createAppointmentAdapter = (rawAppointment, dataAccessors, timeZoneCalculator?: any, options?: any) => new AppointmentAdapter(rawAppointment, dataAccessors, timeZoneCalculator, options); diff --git a/packages/devextreme/js/__internal/scheduler/m_appointment_drag_behavior.ts b/packages/devextreme/js/__internal/scheduler/m_appointment_drag_behavior.ts index 02cf842b4eb8..718913429957 100644 --- a/packages/devextreme/js/__internal/scheduler/m_appointment_drag_behavior.ts +++ b/packages/devextreme/js/__internal/scheduler/m_appointment_drag_behavior.ts @@ -1,194 +1,195 @@ -import $ from '../../core/renderer'; -import Draggable from '../draggable'; -import { extend } from '../../core/utils/extend'; -import { LIST_ITEM_DATA_KEY } from './constants'; -import { isSchedulerComponent } from '../../__internal/scheduler/utils/is_scheduler_component'; -import { Deferred } from '../../core/utils/deferred'; +import $ from '@js/core/renderer'; +import { Deferred } from '@js/core/utils/deferred'; +import { extend } from '@js/core/utils/extend'; +import Draggable from '@js/ui/draggable'; + +import { LIST_ITEM_DATA_KEY } from './m_constants'; +import { isSchedulerComponent } from './utils/is_scheduler_component'; const APPOINTMENT_ITEM_CLASS = 'dx-scheduler-appointment'; export default class AppointmentDragBehavior { - constructor(scheduler) { - this.scheduler = scheduler; - this.workspace = scheduler._workSpace; - this.appointments = scheduler._appointments; + workspace = this.scheduler._workSpace; - this.initialPosition = { - left: 0, - top: 0 - }; + appointments = this.scheduler._appointments; - this.appointmentInfo = null; + initialPosition = { + left: 0, + top: 0, + }; - this.dragBetweenComponentsPromise = null; - } + appointmentInfo: any = null; - isAllDay(appointment) { - return appointment.data('dxAppointmentSettings').allDay; - } + dragBetweenComponentsPromise: any = null; - onDragStart(e) { - const { itemSettings, itemData, initialPosition } = e; + constructor(public scheduler) { + } - this.initialPosition = initialPosition; - this.appointmentInfo = { - appointment: itemData, - settings: itemSettings, - }; + isAllDay(appointment) { + return appointment.data('dxAppointmentSettings').allDay; + } - this.appointments.notifyObserver('hideAppointmentTooltip'); - } + onDragStart(e) { + const { itemSettings, itemData, initialPosition } = e; - onDragMove(e) { - if(e.fromComponent !== e.toComponent) { - this.appointments.notifyObserver('removeDroppableCellClass'); - } - } + this.initialPosition = initialPosition; + this.appointmentInfo = { + appointment: itemData, + settings: itemSettings, + }; - getAppointmentElement(e) { - const itemElement = e.event.data && e.event.data.itemElement || e.itemElement; + this.appointments.notifyObserver('hideAppointmentTooltip'); + } - return $(itemElement); + onDragMove(e) { + if (e.fromComponent !== e.toComponent) { + this.appointments.notifyObserver('removeDroppableCellClass'); } + } - onDragEnd(event) { - const element = this.getAppointmentElement(event); + getAppointmentElement(e) { + const itemElement = e.event.data && e.event.data.itemElement || e.itemElement; - const rawAppointment = this.appointments._getItemData(element); - const container = this.appointments._getAppointmentContainer(this.isAllDay(element)); - container.append(element); + return $(itemElement); + } - const newCellIndex = this.workspace.getDroppableCellIndex(); - const oldCellIndex = this.workspace.getCellIndexByCoordinates(this.initialPosition); + onDragEnd(event) { + const element = this.getAppointmentElement(event); - this.appointments.notifyObserver('updateAppointmentAfterDrag', { - event, - element, - rawAppointment, - newCellIndex, - oldCellIndex - }); - } + const rawAppointment = this.appointments._getItemData(element); + const container = this.appointments._getAppointmentContainer(this.isAllDay(element)); + container.append(element); - onDragCancel() { - this.removeDroppableClasses(); - } + const newCellIndex = this.workspace.getDroppableCellIndex(); + const oldCellIndex = this.workspace.getCellIndexByCoordinates(this.initialPosition); - getItemData(appointmentElement) { - const dataFromTooltip = $(appointmentElement).data(LIST_ITEM_DATA_KEY); - const itemDataFromTooltip = dataFromTooltip?.appointment; - const itemDataFromGrid = this.appointments._getItemData(appointmentElement); + this.appointments.notifyObserver('updateAppointmentAfterDrag', { + event, + element, + rawAppointment, + newCellIndex, + oldCellIndex, + }); + } - return itemDataFromTooltip || itemDataFromGrid; - } + onDragCancel() { + this.removeDroppableClasses(); + } - getItemSettings(appointment) { - const itemData = $(appointment).data(LIST_ITEM_DATA_KEY); - return itemData && itemData.settings || []; - } + getItemData(appointmentElement) { + const dataFromTooltip: any = $(appointmentElement).data(LIST_ITEM_DATA_KEY); + const itemDataFromTooltip = dataFromTooltip?.appointment; + const itemDataFromGrid = this.appointments._getItemData(appointmentElement); - createDragStartHandler(options, appointmentDragging) { - return (e) => { - e.itemData = this.getItemData(e.itemElement); - e.itemSettings = this.getItemSettings(e.itemElement); + return itemDataFromTooltip || itemDataFromGrid; + } - appointmentDragging.onDragStart && appointmentDragging.onDragStart(e); + getItemSettings(appointment) { + const itemData: any = $(appointment).data(LIST_ITEM_DATA_KEY); + return itemData && itemData.settings || []; + } - if(!e.cancel) { - options.onDragStart(e); - } - }; - } + createDragStartHandler(options, appointmentDragging) { + return (e) => { + e.itemData = this.getItemData(e.itemElement); + e.itemSettings = this.getItemSettings(e.itemElement); - createDragMoveHandler(options, appointmentDragging) { - return (e) => { - appointmentDragging.onDragMove && appointmentDragging.onDragMove(e); + appointmentDragging.onDragStart && appointmentDragging.onDragStart(e); - if(!e.cancel) { - options.onDragMove(e); - } - }; - } + if (!e.cancel) { + options.onDragStart(e); + } + }; + } - createDragEndHandler(options, appointmentDragging) { - return (e) => { - const updatedData = this.appointments.invoke('getUpdatedData', e.itemData); + createDragMoveHandler(options, appointmentDragging) { + return (e) => { + appointmentDragging.onDragMove && appointmentDragging.onDragMove(e); - this.appointmentInfo = null; - e.toItemData = extend({}, e.itemData, updatedData); + if (!e.cancel) { + options.onDragMove(e); + } + }; + } - appointmentDragging.onDragEnd && appointmentDragging.onDragEnd(e); + createDragEndHandler(options, appointmentDragging) { + return (e) => { + const updatedData = this.appointments.invoke('getUpdatedData', e.itemData); - if(!e.cancel) { - options.onDragEnd(e); - if(e.fromComponent !== e.toComponent) { - appointmentDragging.onRemove && appointmentDragging.onRemove(e); - } - } + this.appointmentInfo = null; + e.toItemData = extend({}, e.itemData, updatedData); - // NOTE: event.cancel may be promise or different type, so we need strict check here. - if(e.cancel === true) { - this.removeDroppableClasses(); - } + appointmentDragging.onDragEnd && appointmentDragging.onDragEnd(e); - if(e.cancel !== true && isSchedulerComponent(e.toComponent)) { - const targetDragBehavior = e.toComponent._getDragBehavior(); - targetDragBehavior.dragBetweenComponentsPromise = new Deferred(); - } - }; - } - - createDropHandler(appointmentDragging) { - return (e) => { - const updatedData = this.appointments.invoke('getUpdatedData', e.itemData); - e.itemData = extend({}, e.itemData, updatedData); - - if(e.fromComponent !== e.toComponent) { - appointmentDragging.onAdd && appointmentDragging.onAdd(e); - } - - if(this.dragBetweenComponentsPromise) { - this.dragBetweenComponentsPromise.resolve(); - } - }; - } - - addTo(container, config) { - const appointmentDragging = this.scheduler.option('appointmentDragging') || {}; - const options = extend({ - component: this.scheduler, - contentTemplate: null, - filter: `.${APPOINTMENT_ITEM_CLASS}`, - immediate: false, - onDragStart: this.onDragStart.bind(this), - onDragMove: this.onDragMove.bind(this), - onDragEnd: this.onDragEnd.bind(this), - onDragCancel: this.onDragCancel.bind(this) - }, config); - - this.appointments._createComponent(container, Draggable, extend({}, options, appointmentDragging, { - onDragStart: this.createDragStartHandler(options, appointmentDragging), - onDragMove: this.createDragMoveHandler(options, appointmentDragging), - onDragEnd: this.createDragEndHandler(options, appointmentDragging), - onDrop: this.createDropHandler(appointmentDragging), - onCancelByEsc: true - })); - } - - updateDragSource(appointment, settings) { - const { appointmentInfo } = this; - if(appointmentInfo || appointment) { - const currentAppointment = appointment || appointmentInfo.appointment; - const currentSettings = settings || appointmentInfo.settings; - - this.appointments._setDragSourceAppointment( - currentAppointment, currentSettings, - ); + if (!e.cancel) { + options.onDragEnd(e); + if (e.fromComponent !== e.toComponent) { + appointmentDragging.onRemove && appointmentDragging.onRemove(e); } - } + } - removeDroppableClasses() { - this.appointments._removeDragSourceClassFromDraggedAppointment(); - this.workspace.removeDroppableCellClass(); - } + // NOTE: event.cancel may be promise or different type, so we need strict check here. + if (e.cancel === true) { + this.removeDroppableClasses(); + } + + if (e.cancel !== true && isSchedulerComponent(e.toComponent)) { + const targetDragBehavior = e.toComponent._getDragBehavior(); + // @ts-expect-error + targetDragBehavior.dragBetweenComponentsPromise = new Deferred(); + } + }; + } + + createDropHandler(appointmentDragging) { + return (e) => { + const updatedData = this.appointments.invoke('getUpdatedData', e.itemData); + e.itemData = extend({}, e.itemData, updatedData); + + if (e.fromComponent !== e.toComponent) { + appointmentDragging.onAdd && appointmentDragging.onAdd(e); + } + + if (this.dragBetweenComponentsPromise) { + this.dragBetweenComponentsPromise.resolve(); + } + }; + } + + addTo(container, config) { + const appointmentDragging = this.scheduler.option('appointmentDragging') || {}; + const options = extend({ + component: this.scheduler, + contentTemplate: null, + filter: `.${APPOINTMENT_ITEM_CLASS}`, + immediate: false, + onDragStart: this.onDragStart.bind(this), + onDragMove: this.onDragMove.bind(this), + onDragEnd: this.onDragEnd.bind(this), + onDragCancel: this.onDragCancel.bind(this), + }, config); + + this.appointments._createComponent(container, Draggable, extend({}, options, appointmentDragging, { + onDragStart: this.createDragStartHandler(options, appointmentDragging), + onDragMove: this.createDragMoveHandler(options, appointmentDragging), + onDragEnd: this.createDragEndHandler(options, appointmentDragging), + onDrop: this.createDropHandler(appointmentDragging), + onCancelByEsc: true, + })); + } + + updateDragSource(appointment, settings) { + const { appointmentInfo } = this; + if (appointmentInfo || appointment) { + const currentAppointment = appointment || appointmentInfo.appointment; + const currentSettings = settings || appointmentInfo.settings; + + this.appointments._setDragSourceAppointment(currentAppointment, currentSettings); + } + } + + removeDroppableClasses() { + this.appointments._removeDragSourceClassFromDraggedAppointment(); + this.workspace.removeDroppableCellClass(); + } } diff --git a/packages/devextreme/js/__internal/scheduler/m_appointments_layout_manager.ts b/packages/devextreme/js/__internal/scheduler/m_appointments_layout_manager.ts index 9df4d32ba6c0..3478d416909b 100644 --- a/packages/devextreme/js/__internal/scheduler/m_appointments_layout_manager.ts +++ b/packages/devextreme/js/__internal/scheduler/m_appointments_layout_manager.ts @@ -1,244 +1,248 @@ -import { equalByValue } from '../../core/utils/common'; -import { AppointmentViewModelGenerator } from '../../__internal/scheduler/appointments/m_view_model_generator'; -import { getGroupCount } from '../../__internal/scheduler/resources/m_utils'; -import { getCellWidth, getCellHeight, getAllDayHeight } from './workspaces/helpers/positionHelper'; -import { getCellDuration } from '../../renovation/ui/scheduler/view_model/to_test/views/utils/base'; -import { getAppointmentRenderingStrategyName } from '../../renovation/ui/scheduler/model/appointments'; +import { equalByValue } from '@js/core/utils/common'; +import { getAppointmentRenderingStrategyName } from '@js/renovation/ui/scheduler/model/utils'; +import { getCellDuration } from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/base'; -class AppointmentLayoutManager { - constructor(instance) { - this.instance = instance; - this.appointmentViewModel = new AppointmentViewModelGenerator(); - } +import { AppointmentViewModelGenerator } from './appointments/m_view_model_generator'; +import { getGroupCount } from './resources/m_utils'; +import { getAllDayHeight, getCellHeight, getCellWidth } from './workspaces/helpers/m_position_helper'; - get appointmentRenderingStrategyName() { - return getAppointmentRenderingStrategyName(this.instance.currentViewType); - } - - getCellDimensions(options) { - if(this.instance._workSpace) { - return { - width: this.instance._workSpace.getCellWidth(), - height: this.instance._workSpace.getCellHeight(), - allDayHeight: this.instance._workSpace.getAllDayHeight() - }; - } - } - - _getRenderingStrategyOptions() { - const workspace = this.instance.getWorkSpace(); - const { virtualScrollingDispatcher } = this.instance.getWorkSpace(); - const { - cellCountInsideLeftVirtualCell, - cellCountInsideTopVirtualRow - } = virtualScrollingDispatcher; - const groupCount = getGroupCount(this.instance.option('loadedResources')); - const DOMMetaData = workspace.getDOMElementsMetaData(); - const allDayHeight = getAllDayHeight( - workspace.option('showAllDayPanel'), - workspace._isVerticalGroupedWorkSpace(), - DOMMetaData - ); - const rowCount = workspace._getRowCount(); - const { positionHelper, viewDataProvider } = workspace; - const visibleDayDuration = viewDataProvider.getVisibleDayDuration( - workspace.option('startDayHour'), - workspace.option('endDayHour'), - workspace.option('hoursInterval') - ); - - const cellDuration = getCellDuration( - workspace.type, - workspace.option('startDayHour'), - workspace.option('endDayHour'), - workspace.option('hoursInterval') - ); - return { - resources: this.instance.option('resources'), - loadedResources: this.instance.option('loadedResources'), - getAppointmentColor: this.instance.createGetAppointmentColor(), - dataAccessors: this.instance._dataAccessors, - isRenovatedAppointments: this.instance.option('isRenovatedAppointments'), - appointmentRenderingStrategyName: this.appointmentRenderingStrategyName, - adaptivityEnabled: this.instance.option('adaptivityEnabled'), - rtlEnabled: this.instance.option('rtlEnabled'), - startDayHour: this.instance._getCurrentViewOption('startDayHour'), - endDayHour: this.instance._getCurrentViewOption('endDayHour'), - maxAppointmentsPerCell: this.instance._getCurrentViewOption('maxAppointmentsPerCell'), - currentDate: this.instance.option('currentDate'), - isVirtualScrolling: this.instance.isVirtualScrolling(), - leftVirtualCellCount: cellCountInsideLeftVirtualCell, - topVirtualCellCount: cellCountInsideTopVirtualRow, - intervalCount: workspace.option('intervalCount'), - hoursInterval: workspace.option('hoursInterval'), - showAllDayPanel: workspace.option('showAllDayPanel'), - isGroupedAllDayPanel: workspace.isGroupedAllDayPanel(), - groups: this.instance._getCurrentViewOption('groups'), - groupCount, - rowCount, - appointmentCountPerCell: this.instance.option('_appointmentCountPerCell'), - appointmentOffset: this.instance.option('_appointmentOffset'), - allowResizing: this.instance._allowResizing(), - allowAllDayResizing: this.instance._allowAllDayResizing(), - startViewDate: workspace.getStartViewDate(), - groupOrientation: workspace._getRealGroupOrientation(), - cellWidth: getCellWidth(DOMMetaData), - cellHeight: getCellHeight(DOMMetaData), - allDayHeight: allDayHeight, - resizableStep: positionHelper.getResizableStep(), - visibleDayDuration, - allDayPanelMode: this.instance._getCurrentViewOption('allDayPanelMode'), - // appointment settings - timeZoneCalculator: this.instance.timeZoneCalculator, - timeZone: this.instance.option('timeZone'), - firstDayOfWeek: this.instance.getFirstDayOfWeek(), - viewStartDayHour: this.instance._getCurrentViewOption('startDayHour'), - viewEndDayHour: this.instance._getCurrentViewOption('endDayHour'), - viewType: workspace.type, - endViewDate: workspace.getEndViewDate(), - positionHelper, - isGroupedByDate: workspace.isGroupedByDate(), - cellDuration, - cellDurationInMinutes: workspace.option('cellDuration'), - viewDataProvider: workspace.viewDataProvider, - supportAllDayRow: workspace.supportAllDayRow(), - dateRange: workspace.getDateRange(), - intervalDuration: workspace.getIntervalDuration(), - allDayIntervalDuration: workspace.getIntervalDuration(true), - isVerticalGroupOrientation: workspace.isVerticalOrientation(), - DOMMetaData, - // agenda only - instance: this.instance, - agendaDuration: workspace.option('agendaDuration'), - }; - } +class AppointmentLayoutManager { + appointmentViewModel = new AppointmentViewModelGenerator(); - createAppointmentsMap(items) { - const renderingStrategyOptions = this._getRenderingStrategyOptions(); + _positionMap: any; - const { - viewModel, - positionMap - } = this.appointmentViewModel.generate(items, renderingStrategyOptions); + constructor(public instance) { + } - this._positionMap = positionMap; // TODO get rid of this after remove old render + get appointmentRenderingStrategyName() { + return getAppointmentRenderingStrategyName(this.instance.currentViewType); + } - return viewModel; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getCellDimensions(options) { + if (this.instance._workSpace) { + return { + width: this.instance._workSpace.getCellWidth(), + height: this.instance._workSpace.getCellHeight(), + allDayHeight: this.instance._workSpace.getAllDayHeight(), + }; } - _isDataChanged(data) { - const appointmentDataProvider = this.instance.appointmentDataProvider; - const updatedData = appointmentDataProvider.getUpdatedAppointment(); - - return updatedData === data || appointmentDataProvider - .getUpdatedAppointmentKeys() - .some((item) => { - return data[item.key] === item.value; - }); + return undefined; + } + + _getRenderingStrategyOptions() { + const workspace = this.instance.getWorkSpace(); + const { virtualScrollingDispatcher } = this.instance.getWorkSpace(); + const { + cellCountInsideLeftVirtualCell, + cellCountInsideTopVirtualRow, + } = virtualScrollingDispatcher; + const groupCount = getGroupCount(this.instance.option('loadedResources')); + const DOMMetaData = workspace.getDOMElementsMetaData(); + const allDayHeight = getAllDayHeight( + workspace.option('showAllDayPanel'), + workspace._isVerticalGroupedWorkSpace(), + DOMMetaData, + ); + const rowCount = workspace._getRowCount(); + const { positionHelper, viewDataProvider } = workspace; + const visibleDayDuration = viewDataProvider.getVisibleDayDuration( + workspace.option('startDayHour'), + workspace.option('endDayHour'), + workspace.option('hoursInterval'), + ); + + const cellDuration = getCellDuration( + workspace.type, + workspace.option('startDayHour'), + workspace.option('endDayHour'), + workspace.option('hoursInterval'), + ); + return { + resources: this.instance.option('resources'), + loadedResources: this.instance.option('loadedResources'), + getAppointmentColor: this.instance.createGetAppointmentColor(), + dataAccessors: this.instance._dataAccessors, + isRenovatedAppointments: this.instance.option('isRenovatedAppointments'), + appointmentRenderingStrategyName: this.appointmentRenderingStrategyName, + adaptivityEnabled: this.instance.option('adaptivityEnabled'), + rtlEnabled: this.instance.option('rtlEnabled'), + startDayHour: this.instance._getCurrentViewOption('startDayHour'), + endDayHour: this.instance._getCurrentViewOption('endDayHour'), + maxAppointmentsPerCell: this.instance._getCurrentViewOption('maxAppointmentsPerCell'), + currentDate: this.instance.option('currentDate'), + isVirtualScrolling: this.instance.isVirtualScrolling(), + leftVirtualCellCount: cellCountInsideLeftVirtualCell, + topVirtualCellCount: cellCountInsideTopVirtualRow, + intervalCount: workspace.option('intervalCount'), + hoursInterval: workspace.option('hoursInterval'), + showAllDayPanel: workspace.option('showAllDayPanel'), + isGroupedAllDayPanel: workspace.isGroupedAllDayPanel(), + groups: this.instance._getCurrentViewOption('groups'), + groupCount, + rowCount, + appointmentCountPerCell: this.instance.option('_appointmentCountPerCell'), + appointmentOffset: this.instance.option('_appointmentOffset'), + allowResizing: this.instance._allowResizing(), + allowAllDayResizing: this.instance._allowAllDayResizing(), + startViewDate: workspace.getStartViewDate(), + groupOrientation: workspace._getRealGroupOrientation(), + cellWidth: getCellWidth(DOMMetaData), + cellHeight: getCellHeight(DOMMetaData), + allDayHeight, + resizableStep: positionHelper.getResizableStep(), + visibleDayDuration, + allDayPanelMode: this.instance._getCurrentViewOption('allDayPanelMode'), + // appointment settings + timeZoneCalculator: this.instance.timeZoneCalculator, + timeZone: this.instance.option('timeZone'), + firstDayOfWeek: this.instance.getFirstDayOfWeek(), + viewStartDayHour: this.instance._getCurrentViewOption('startDayHour'), + viewEndDayHour: this.instance._getCurrentViewOption('endDayHour'), + viewType: workspace.type, + endViewDate: workspace.getEndViewDate(), + positionHelper, + isGroupedByDate: workspace.isGroupedByDate(), + cellDuration, + cellDurationInMinutes: workspace.option('cellDuration'), + viewDataProvider: workspace.viewDataProvider, + supportAllDayRow: workspace.supportAllDayRow(), + dateRange: workspace.getDateRange(), + intervalDuration: workspace.getIntervalDuration(), + allDayIntervalDuration: workspace.getIntervalDuration(true), + isVerticalGroupOrientation: workspace.isVerticalOrientation(), + DOMMetaData, + // agenda only + instance: this.instance, + agendaDuration: workspace.option('agendaDuration'), + }; + } + + createAppointmentsMap(items) { + const renderingStrategyOptions = this._getRenderingStrategyOptions(); + + const { + viewModel, + positionMap, + } = this.appointmentViewModel.generate(items, renderingStrategyOptions) as any; + + this._positionMap = positionMap; // TODO get rid of this after remove old render + + return viewModel; + } + + _isDataChanged(data) { + const { appointmentDataProvider } = this.instance; + const updatedData = appointmentDataProvider.getUpdatedAppointment(); + + return updatedData === data || appointmentDataProvider + .getUpdatedAppointmentKeys() + .some((item) => data[item.key] === item.value); + } + + _isAppointmentShouldAppear(currentAppointment, sourceAppointment) { + return currentAppointment.needRepaint && sourceAppointment.needRemove; + } + + _isSettingChanged(settings, sourceSetting) { + if (settings.length !== sourceSetting.length) { + return true; } - _isAppointmentShouldAppear(currentAppointment, sourceAppointment) { - return currentAppointment.needRepaint && sourceAppointment.needRemove; + const createSettingsToCompare = (settings, index) => { + const currentSetting = settings[index]; + const leftVirtualCellCount = currentSetting.leftVirtualCellCount || 0; + const topVirtualCellCount = currentSetting.topVirtualCellCount || 0; + const columnIndex = currentSetting.columnIndex + leftVirtualCellCount; + const rowIndex = currentSetting.rowIndex + topVirtualCellCount; + const hMax = currentSetting.reduced ? currentSetting.hMax : undefined; + const vMax = currentSetting.reduced ? currentSetting.vMax : undefined; + + return { + ...currentSetting, + columnIndex, + rowIndex, + positionByMap: undefined, + topVirtualCellCount: undefined, + leftVirtualCellCount: undefined, + leftVirtualWidth: undefined, + topVirtualHeight: undefined, + hMax, + vMax, + info: {}, + }; + }; + + for (let i = 0; i < settings.length; i++) { + const newSettings = createSettingsToCompare(settings, i); + const oldSettings = createSettingsToCompare(sourceSetting, i); + + if (oldSettings) { // exclude sortedIndex property for comparison in commonUtils.equalByValue + oldSettings.sortedIndex = newSettings.sortedIndex; + } + + if (!equalByValue(newSettings, oldSettings)) { + return true; + } } - _isSettingChanged(settings, sourceSetting) { - if(settings.length !== sourceSetting.length) { - return true; - } - - const createSettingsToCompare = (settings, index) => { - const currentSetting = settings[index]; - const leftVirtualCellCount = currentSetting.leftVirtualCellCount || 0; - const topVirtualCellCount = currentSetting.topVirtualCellCount || 0; - const columnIndex = currentSetting.columnIndex + leftVirtualCellCount; - const rowIndex = currentSetting.rowIndex + topVirtualCellCount; - const hMax = currentSetting.reduced ? currentSetting.hMax : undefined; - const vMax = currentSetting.reduced ? currentSetting.vMax : undefined; - - return { - ...currentSetting, - columnIndex, - rowIndex, - positionByMap: undefined, - topVirtualCellCount: undefined, - leftVirtualCellCount: undefined, - leftVirtualWidth: undefined, - topVirtualHeight: undefined, - hMax, - vMax, - info: {}, - }; - }; - - for(let i = 0; i < settings.length; i++) { - const newSettings = createSettingsToCompare(settings, i); - const oldSettings = createSettingsToCompare(sourceSetting, i); - - if(oldSettings) { // exclude sortedIndex property for comparison in commonUtils.equalByValue - oldSettings.sortedIndex = newSettings.sortedIndex; - } - - if(!equalByValue(newSettings, oldSettings)) { - return true; - } - } - - return false; - } + return false; + } - _getAssociatedSourceAppointment(currentAppointment, sourceAppointments) { - for(let i = 0; i < sourceAppointments.length; i++) { - const item = sourceAppointments[i]; - if(item.itemData === currentAppointment.itemData) { - return item; - } - } - return null; + _getAssociatedSourceAppointment(currentAppointment, sourceAppointments) { + for (let i = 0; i < sourceAppointments.length; i++) { + const item = sourceAppointments[i]; + if (item.itemData === currentAppointment.itemData) { + return item; + } } - - _getDeletedAppointments(currentAppointments, sourceAppointments) { - const result = []; - - for(let i = 0; i < sourceAppointments.length; i++) { - const sourceAppointment = sourceAppointments[i]; - const currentAppointment = this._getAssociatedSourceAppointment(sourceAppointment, currentAppointments); - if(!currentAppointment) { - sourceAppointment.needRemove = true; - result.push(sourceAppointment); - } - } - - return result; + return null; + } + + _getDeletedAppointments(currentAppointments, sourceAppointments) { + const result: any = []; + + for (let i = 0; i < sourceAppointments.length; i++) { + const sourceAppointment = sourceAppointments[i]; + const currentAppointment = this._getAssociatedSourceAppointment(sourceAppointment, currentAppointments); + if (!currentAppointment) { + sourceAppointment.needRemove = true; + result.push(sourceAppointment); + } } - getRepaintedAppointments(currentAppointments, sourceAppointments) { - if(sourceAppointments.length === 0 || this.appointmentRenderingStrategyName === 'agenda') { - return currentAppointments; - } + return result; + } - currentAppointments.forEach(appointment => { - const sourceAppointment = this._getAssociatedSourceAppointment(appointment, sourceAppointments); - if(sourceAppointment) { - const isDataChanged = this._isDataChanged(appointment.itemData); - const isSettingChanged = this._isSettingChanged(appointment.settings, sourceAppointment.settings); - const isAppointmentShouldAppear = this._isAppointmentShouldAppear(appointment, sourceAppointment); - - appointment.needRepaint = isDataChanged || isSettingChanged || isAppointmentShouldAppear; - } - }); - - return currentAppointments.concat(this._getDeletedAppointments(currentAppointments, sourceAppointments)); + getRepaintedAppointments(currentAppointments, sourceAppointments) { + if (sourceAppointments.length === 0 || this.appointmentRenderingStrategyName === 'agenda') { + return currentAppointments; } - getRenderingStrategyInstance() { - const renderingStrategy = this.appointmentViewModel.getRenderingStrategy(); - if(!renderingStrategy) { - const options = this._getRenderingStrategyOptions(); - this.appointmentViewModel.initRenderingStrategy(options); - } - - return this.appointmentViewModel.getRenderingStrategy(); + currentAppointments.forEach((appointment) => { + const sourceAppointment = this._getAssociatedSourceAppointment(appointment, sourceAppointments); + if (sourceAppointment) { + const isDataChanged = this._isDataChanged(appointment.itemData); + const isSettingChanged = this._isSettingChanged(appointment.settings, sourceAppointment.settings); + const isAppointmentShouldAppear = this._isAppointmentShouldAppear(appointment, sourceAppointment); + + appointment.needRepaint = isDataChanged || isSettingChanged || isAppointmentShouldAppear; + } + }); + + return currentAppointments.concat(this._getDeletedAppointments(currentAppointments, sourceAppointments)); + } + + getRenderingStrategyInstance() { + const renderingStrategy = this.appointmentViewModel.getRenderingStrategy(); + if (!renderingStrategy) { + const options = this._getRenderingStrategyOptions(); + this.appointmentViewModel.initRenderingStrategy(options); } + + return this.appointmentViewModel.getRenderingStrategy(); + } } export default AppointmentLayoutManager; diff --git a/packages/devextreme/js/__internal/scheduler/m_classes.ts b/packages/devextreme/js/__internal/scheduler/m_classes.ts index 7829548621fe..b8ba639c163a 100644 --- a/packages/devextreme/js/__internal/scheduler/m_classes.ts +++ b/packages/devextreme/js/__internal/scheduler/m_classes.ts @@ -5,32 +5,31 @@ export const RECURRENCE_APPOINTMENT_CLASS = 'dx-scheduler-appointment-recurrence export const EMPTY_APPOINTMENT_CLASS = 'dx-scheduler-appointment-empty'; export const ALL_DAY_APPOINTMENT_CLASS = 'dx-scheduler-all-day-appointment'; export const REDUCED_APPOINTMENT_PARTS_CLASSES = { - head: 'dx-scheduler-appointment-head', - body: 'dx-scheduler-appointment-body', - tail: 'dx-scheduler-appointment-tail' + head: 'dx-scheduler-appointment-head', + body: 'dx-scheduler-appointment-body', + tail: 'dx-scheduler-appointment-tail', }; export const DIRECTION_APPOINTMENT_CLASSES = { - horizontal: 'dx-scheduler-appointment-horizontal', - vertical: 'dx-scheduler-appointment-vertical' + horizontal: 'dx-scheduler-appointment-horizontal', + vertical: 'dx-scheduler-appointment-vertical', }; export const APPOINTMENT_DRAG_SOURCE_CLASS = 'dx-scheduler-appointment-drag-source'; export const APPOINTMENT_ITEM_CLASS = 'dx-scheduler-appointment'; export const APPOINTMENT_CONTENT_CLASSES = { - APPOINTMENT_CONTENT_DETAILS: 'dx-scheduler-appointment-content-details', - RECURRING_ICON: 'dx-scheduler-appointment-recurrence-icon', - APPOINTMENT_TITLE: 'dx-scheduler-appointment-title', - APPOINTMENT_DATE: 'dx-scheduler-appointment-content-date', - ALL_DAY_CONTENT: 'dx-scheduler-appointment-content-allday', - ITEM: 'dx-scheduler-appointment', + APPOINTMENT_CONTENT_DETAILS: 'dx-scheduler-appointment-content-details', + RECURRING_ICON: 'dx-scheduler-appointment-recurrence-icon', + APPOINTMENT_TITLE: 'dx-scheduler-appointment-title', + APPOINTMENT_DATE: 'dx-scheduler-appointment-content-date', + ALL_DAY_CONTENT: 'dx-scheduler-appointment-content-allday', + ITEM: 'dx-scheduler-appointment', - AGENDA_MARKER: 'dx-scheduler-agenda-appointment-marker', - AGENDA_RESOURCE_LIST: 'dx-scheduler-appointment-resource-list', - AGENDA_RESOURCE_LIST_ITEM: 'dx-scheduler-appointment-resource-item', - AGENDA_RESOURCE_LIST_ITEM_VALUE: 'dx-scheduler-appointment-resource-item-value' + AGENDA_MARKER: 'dx-scheduler-agenda-appointment-marker', + AGENDA_RESOURCE_LIST: 'dx-scheduler-appointment-resource-list', + AGENDA_RESOURCE_LIST_ITEM: 'dx-scheduler-appointment-resource-item', + AGENDA_RESOURCE_LIST_ITEM_VALUE: 'dx-scheduler-appointment-resource-item-value', }; export const AGENDA_LAST_IN_DATE_APPOINTMENT_CLASS = 'dx-scheduler-last-in-date-agenda-appointment'; - export const HEADER_CURRENT_TIME_CELL_CLASS = 'dx-scheduler-header-panel-current-time-cell'; export const VIRTUAL_CELL_CLASS = 'dx-scheduler-virtual-cell'; export const TIME_PANEL_CLASS = 'dx-scheduler-time-panel'; @@ -42,7 +41,7 @@ export const LAST_GROUP_CELL_CLASS = 'dx-scheduler-last-group-cell'; export const FIRST_GROUP_CELL_CLASS = 'dx-scheduler-first-group-cell'; export const VERTICAL_GROUP_COUNT_CLASSES = [ - 'dx-scheduler-group-column-count-one', - 'dx-scheduler-group-column-count-two', - 'dx-scheduler-group-column-count-three', + 'dx-scheduler-group-column-count-one', + 'dx-scheduler-group-column-count-two', + 'dx-scheduler-group-column-count-three', ]; diff --git a/packages/devextreme/js/__internal/scheduler/m_compact_appointments_helper.ts b/packages/devextreme/js/__internal/scheduler/m_compact_appointments_helper.ts index 5c7d82ffa01a..dc655833f975 100644 --- a/packages/devextreme/js/__internal/scheduler/m_compact_appointments_helper.ts +++ b/packages/devextreme/js/__internal/scheduler/m_compact_appointments_helper.ts @@ -1,208 +1,205 @@ -import $ from '../../core/renderer'; -import Button from '../button'; -import { move, locate } from '../../animation/translator'; -import messageLocalization from '../../localization/message'; -import { FunctionTemplate } from '../../core/templates/function_template'; -import { when } from '../../core/utils/deferred'; -import { getBoundingRect } from '../../core/utils/position'; -import { AppointmentTooltipInfo } from './dataStructures'; -import { LIST_ITEM_DATA_KEY, LIST_ITEM_CLASS } from './constants'; -import { createAppointmentAdapter } from './appointmentAdapter'; -import { getOverflowIndicatorColor } from '../../renovation/ui/scheduler/appointment/overflow_indicator/utils'; +import { locate, move } from '@js/animation/translator'; +import $ from '@js/core/renderer'; +import { FunctionTemplate } from '@js/core/templates/function_template'; +import { when } from '@js/core/utils/deferred'; +import { getBoundingRect } from '@js/core/utils/position'; +import messageLocalization from '@js/localization/message'; +import { getOverflowIndicatorColor } from '@js/renovation/ui/scheduler/appointment/overflow_indicator/utils'; +import Button from '@js/ui/button'; + +import { createAppointmentAdapter } from './m_appointment_adapter'; +import { LIST_ITEM_CLASS, LIST_ITEM_DATA_KEY } from './m_constants'; +import { AppointmentTooltipInfo } from './m_data_structures'; const APPOINTMENT_COLLECTOR_CLASS = 'dx-scheduler-appointment-collector'; -const COMPACT_APPOINTMENT_COLLECTOR_CLASS = APPOINTMENT_COLLECTOR_CLASS + '-compact'; -const APPOINTMENT_COLLECTOR_CONTENT_CLASS = APPOINTMENT_COLLECTOR_CLASS + '-content'; +const COMPACT_APPOINTMENT_COLLECTOR_CLASS = `${APPOINTMENT_COLLECTOR_CLASS}-compact`; +const APPOINTMENT_COLLECTOR_CONTENT_CLASS = `${APPOINTMENT_COLLECTOR_CLASS}-content`; const WEEK_VIEW_COLLECTOR_OFFSET = 5; const COMPACT_THEME_WEEK_VIEW_COLLECTOR_OFFSET = 1; export class CompactAppointmentsHelper { - constructor(instance) { - this.instance = instance; - this.elements = []; - } - - render(options) { - const { isCompact, items, buttonColor } = options; - - const template = this._createTemplate(items.data.length, isCompact); - const button = this._createCompactButton(template, options); - const $button = button.$element(); - - this._makeBackgroundColor($button, items.colors, buttonColor); - this._makeBackgroundDarker($button); - - this.elements.push($button); - $button.data('items', this._createTooltipInfos(items)); - - return $button; - } - - clear() { - this.elements.forEach(button => { - button.detach(); - button.remove(); - }); - this.elements = []; - } - - _createTooltipInfos(items) { - return items.data.map((appointment, index) => { - const targetedAdapter = createAppointmentAdapter( - appointment, - this.instance._dataAccessors, - this.instance.timeZoneCalculator, - ).clone(); - - if(items.settings?.length > 0) { - const { info } = items.settings[index]; - targetedAdapter.startDate = info.sourceAppointment.startDate; - targetedAdapter.endDate = info.sourceAppointment.endDate; - } - - return new AppointmentTooltipInfo(appointment, targetedAdapter.source(), items.colors[index], items.settings[index]); - }); - } - - _onButtonClick(e, options) { - const $button = $(e.element); - this.instance.showAppointmentTooltipCore( - $button, - $button.data('items'), - this._getExtraOptionsForTooltip(options, $button) - ); - } - - _getExtraOptionsForTooltip(options, $appointmentCollector) { - return { - clickEvent: this._clickEvent(options.onAppointmentClick).bind(this), - dragBehavior: options.allowDrag && this._createTooltipDragBehavior($appointmentCollector).bind(this), - dropDownAppointmentTemplate: this.instance.option().dropDownAppointmentTemplate, // TODO deprecated option - isButtonClick: true - }; - } - - _clickEvent(onAppointmentClick) { - return (e) => { - const clickEventArgs = this.instance._createEventArgs(e); - onAppointmentClick(clickEventArgs); - }; - } - - _createTooltipDragBehavior($appointmentCollector) { - return (e) => { - const $element = $(e.element); - const $schedulerElement = $(this.instance.element()); - const workSpace = this.instance.getWorkSpace(); - - const getItemData = (itemElement) => $(itemElement).data(LIST_ITEM_DATA_KEY)?.appointment; - const getItemSettings = (_, event) => { - return event.itemSettings; - }; - const initialPosition = locate($appointmentCollector); - - const options = { - filter: `.${LIST_ITEM_CLASS}`, - isSetCursorOffset: true, - initialPosition, - getItemData, - getItemSettings, - }; - - workSpace._createDragBehaviorBase($element, $schedulerElement, options); - }; - } - - _getCollectorOffset(width, cellWidth) { - return cellWidth - width - this._getCollectorRightOffset(); - } - - _getCollectorRightOffset() { - return this.instance.getRenderingStrategyInstance()._isCompactTheme() - ? COMPACT_THEME_WEEK_VIEW_COLLECTOR_OFFSET - : WEEK_VIEW_COLLECTOR_OFFSET; - } - - _makeBackgroundDarker(button) { - button.css('boxShadow', `inset ${getBoundingRect(button.get(0)).width}px 0 0 0 rgba(0, 0, 0, 0.3)`); - } - - _makeBackgroundColor($button, colors, color) { - when.apply(null, colors).done(function() { - this._makeBackgroundColorCore($button, color, [...arguments]); - }.bind(this)); - } - - _makeBackgroundColorCore($button, color, itemColors) { - color && color.done((color) => { - const backgroundColor = getOverflowIndicatorColor(color, itemColors); - if(backgroundColor) { - $button.css('backgroundColor', backgroundColor); - } - }); - } - - _setPosition(element, position) { - move(element, { - top: position.top, - left: position.left - }); - } - - _createCompactButton(template, options) { - const $button = this._createCompactButtonElement(options); - - return this.instance._createComponent($button, Button, { - type: 'default', - width: options.width, - height: options.height, - onClick: (e) => this._onButtonClick(e, options), - template: this._renderTemplate(template, options.items, options.isCompact) - }); - } - - _createCompactButtonElement({ isCompact, $container, coordinates }) { - const result = $('
') - .addClass(APPOINTMENT_COLLECTOR_CLASS) - .toggleClass(COMPACT_APPOINTMENT_COLLECTOR_CLASS, isCompact) - .appendTo($container); - - this._setPosition(result, coordinates); - - return result; - } - - _renderTemplate(template, items, isCompact) { - return new FunctionTemplate(options => { - return template.render({ - model: { - appointmentCount: items.data.length, - isCompact: isCompact - }, - container: options.container - }); - }); - } - - _createTemplate(count, isCompact) { - this._initButtonTemplate(count, isCompact); - return this.instance._getAppointmentTemplate('appointmentCollectorTemplate'); - } - - _initButtonTemplate(count, isCompact) { - this.instance._templateManager.addDefaultTemplates({ - appointmentCollector: new FunctionTemplate(options => - this._createButtonTemplate(count, $(options.container), isCompact) - ) - }); - } - - _createButtonTemplate(appointmentCount, element, isCompact) { - const text = isCompact ? appointmentCount : messageLocalization.getFormatter('dxScheduler-moreAppointments')(appointmentCount); - - return element - .append($('').text(text)) - .addClass(APPOINTMENT_COLLECTOR_CONTENT_CLASS); - } + elements: any[] = []; + + constructor(public instance) { + } + + render(options) { + const { isCompact, items, buttonColor } = options; + + const template = this._createTemplate(items.data.length, isCompact); + const button = this._createCompactButton(template, options); + const $button = button.$element(); + + this._makeBackgroundColor($button, items.colors, buttonColor); + this._makeBackgroundDarker($button); + + this.elements.push($button); + $button.data('items', this._createTooltipInfos(items)); + + return $button; + } + + clear() { + this.elements.forEach((button) => { + button.detach(); + button.remove(); + }); + this.elements = []; + } + + _createTooltipInfos(items) { + return items.data.map((appointment, index) => { + const targetedAdapter = createAppointmentAdapter( + appointment, + this.instance._dataAccessors, + this.instance.timeZoneCalculator, + ).clone(); + + if (items.settings?.length > 0) { + const { info } = items.settings[index]; + targetedAdapter.startDate = info.sourceAppointment.startDate; + targetedAdapter.endDate = info.sourceAppointment.endDate; + } + + return new AppointmentTooltipInfo(appointment, targetedAdapter.source(), items.colors[index], items.settings[index]); + }); + } + + _onButtonClick(e, options) { + const $button = $(e.element); + this.instance.showAppointmentTooltipCore( + $button, + $button.data('items'), + this._getExtraOptionsForTooltip(options, $button), + ); + } + + _getExtraOptionsForTooltip(options, $appointmentCollector) { + return { + clickEvent: this._clickEvent(options.onAppointmentClick).bind(this), + dragBehavior: options.allowDrag && this._createTooltipDragBehavior($appointmentCollector).bind(this), + dropDownAppointmentTemplate: this.instance.option().dropDownAppointmentTemplate, // TODO deprecated option + isButtonClick: true, + }; + } + + _clickEvent(onAppointmentClick) { + return (e) => { + const clickEventArgs = this.instance._createEventArgs(e); + onAppointmentClick(clickEventArgs); + }; + } + + _createTooltipDragBehavior($appointmentCollector) { + return (e) => { + const $element = $(e.element); + const $schedulerElement = $(this.instance.element()); + const workSpace = this.instance.getWorkSpace(); + + const getItemData = (itemElement) => ($(itemElement) as any).data(LIST_ITEM_DATA_KEY)?.appointment; + const getItemSettings = (_, event) => event.itemSettings; + const initialPosition = locate($appointmentCollector); + + const options = { + filter: `.${LIST_ITEM_CLASS}`, + isSetCursorOffset: true, + initialPosition, + getItemData, + getItemSettings, + }; + + workSpace._createDragBehaviorBase($element, $schedulerElement, options); + }; + } + + _getCollectorOffset(width, cellWidth) { + return cellWidth - width - this._getCollectorRightOffset(); + } + + _getCollectorRightOffset() { + return this.instance.getRenderingStrategyInstance()._isCompactTheme() + ? COMPACT_THEME_WEEK_VIEW_COLLECTOR_OFFSET + : WEEK_VIEW_COLLECTOR_OFFSET; + } + + _makeBackgroundDarker(button) { + button.css('boxShadow', `inset ${getBoundingRect(button.get(0)).width}px 0 0 0 rgba(0, 0, 0, 0.3)`); + } + + _makeBackgroundColor($button, colors, color) { + when.apply(null, colors).done(function () { + this._makeBackgroundColorCore($button, color, [...arguments]); + }.bind(this)); + } + + _makeBackgroundColorCore($button, color, itemColors) { + color && color.done((color) => { + const backgroundColor = getOverflowIndicatorColor(color, itemColors); + if (backgroundColor) { + $button.css('backgroundColor', backgroundColor); + } + }); + } + + _setPosition(element, position) { + move(element, { + top: position.top, + left: position.left, + }); + } + + _createCompactButton(template, options) { + const $button = this._createCompactButtonElement(options); + + return this.instance._createComponent($button, Button, { + type: 'default', + width: options.width, + height: options.height, + onClick: (e) => this._onButtonClick(e, options), + template: this._renderTemplate(template, options.items, options.isCompact), + }); + } + + _createCompactButtonElement({ isCompact, $container, coordinates }) { + const result = $('
') + .addClass(APPOINTMENT_COLLECTOR_CLASS) + .toggleClass(COMPACT_APPOINTMENT_COLLECTOR_CLASS, isCompact) + .appendTo($container); + + this._setPosition(result, coordinates); + + return result; + } + + _renderTemplate(template, items, isCompact) { + return new (FunctionTemplate as any)((options) => template.render({ + model: { + appointmentCount: items.data.length, + isCompact, + }, + container: options.container, + })); + } + + _createTemplate(count, isCompact) { + this._initButtonTemplate(count, isCompact); + return this.instance._getAppointmentTemplate('appointmentCollectorTemplate'); + } + + _initButtonTemplate(count, isCompact) { + this.instance._templateManager.addDefaultTemplates({ + appointmentCollector: new (FunctionTemplate as any)((options) => this._createButtonTemplate(count, $(options.container), isCompact)), + }); + } + + _createButtonTemplate(appointmentCount, element, isCompact) { + const text = isCompact + ? appointmentCount + : (messageLocalization.getFormatter('dxScheduler-moreAppointments') as any)(appointmentCount); + + return element + .append($('').text(text)) + .addClass(APPOINTMENT_COLLECTOR_CONTENT_CLASS); + } } diff --git a/packages/devextreme/js/__internal/scheduler/m_constants.ts b/packages/devextreme/js/__internal/scheduler/m_constants.ts index 04f1793bc5a4..55acbaa01a74 100644 --- a/packages/devextreme/js/__internal/scheduler/m_constants.ts +++ b/packages/devextreme/js/__internal/scheduler/m_constants.ts @@ -5,13 +5,13 @@ export const APPOINTMENT_SETTINGS_KEY = 'dxAppointmentSettings'; export const HORIZONTAL_GROUP_ORIENTATION = 'horizontal'; export const VIEWS = { - DAY: 'day', - WEEK: 'week', - WORK_WEEK: 'workWeek', - MONTH: 'month', - TIMELINE_DAY: 'timelineDay', - TIMELINE_WEEK: 'timelineWeek', - TIMELINE_WORK_WEEK: 'timelineWorkWeek', - TIMELINE_MONTH: 'timelineMonth', - AGENDA: 'agenda', + DAY: 'day', + WEEK: 'week', + WORK_WEEK: 'workWeek', + MONTH: 'month', + TIMELINE_DAY: 'timelineDay', + TIMELINE_WEEK: 'timelineWeek', + TIMELINE_WORK_WEEK: 'timelineWorkWeek', + TIMELINE_MONTH: 'timelineMonth', + AGENDA: 'agenda', }; diff --git a/packages/devextreme/js/__internal/scheduler/m_data_structures.ts b/packages/devextreme/js/__internal/scheduler/m_data_structures.ts index 17b6477d4536..4b62d3d1e767 100644 --- a/packages/devextreme/js/__internal/scheduler/m_data_structures.ts +++ b/packages/devextreme/js/__internal/scheduler/m_data_structures.ts @@ -1,8 +1,10 @@ +/* eslint-disable @typescript-eslint/no-extraneous-class */ export class AppointmentTooltipInfo { - constructor(appointment, targetedAppointment = undefined, color = [], settings = []) { // TODO - this.appointment = appointment; - this.targetedAppointment = targetedAppointment; - this.color = color; - this.settings = settings; - } + constructor( + public appointment: any, + public targetedAppointment: any = undefined, + public color: any[] = [], + public settings: any[] = [], + ) { // TODO + } } diff --git a/packages/devextreme/js/__internal/scheduler/m_date_adapter.ts b/packages/devextreme/js/__internal/scheduler/m_date_adapter.ts index 1676f9cc3e6c..a1da2e6928ea 100644 --- a/packages/devextreme/js/__internal/scheduler/m_date_adapter.ts +++ b/packages/devextreme/js/__internal/scheduler/m_date_adapter.ts @@ -1,57 +1,58 @@ -import dateUtils from '../../core/utils/date'; +import dateUtils from '@js/core/utils/date'; const toMs = dateUtils.dateToMilliseconds; class DateAdapterCore { - constructor(source) { - this._source = new Date(source.getTime ? source.getTime() : source); - } - - get source() { // TODO - return this._source; - } - - result() { - return this._source; - } - - getTimezoneOffset(format = undefined) { - const value = this._source.getTimezoneOffset(); - if(format === 'minute') { - return value * toMs('minute'); - } - return value; - } - - getTime() { - return this._source.getTime(); - } - - setTime(value) { - this._source.setTime(value); - return this; - } - - addTime(value) { - this._source.setTime(this._source.getTime() + value); - return this; - } - - - setMinutes(value) { - this._source.setMinutes(value); - return this; - } - - addMinutes(value) { - this._source.setMinutes(this._source.getMinutes() + value); - return this; - } - - subtractMinutes(value) { - this._source.setMinutes(this._source.getMinutes() - value); - return this; - } + _source: Date; + + constructor(source) { + this._source = new Date(source.getTime ? source.getTime() : source); + } + + get source() { // TODO + return this._source; + } + + result() { + return this._source; + } + + getTimezoneOffset(format: any = undefined) { + const value = this._source.getTimezoneOffset(); + if (format === 'minute') { + return value * toMs('minute'); + } + return value; + } + + getTime() { + return this._source.getTime(); + } + + setTime(value) { + this._source.setTime(value); + return this; + } + + addTime(value) { + this._source.setTime(this._source.getTime() + value); + return this; + } + + setMinutes(value) { + this._source.setMinutes(value); + return this; + } + + addMinutes(value) { + this._source.setMinutes(this._source.getMinutes() + value); + return this; + } + + subtractMinutes(value) { + this._source.setMinutes(this._source.getMinutes() - value); + return this; + } } const DateAdapter = (date) => new DateAdapterCore(date); diff --git a/packages/devextreme/js/__internal/scheduler/m_expression_utils.ts b/packages/devextreme/js/__internal/scheduler/m_expression_utils.ts index 43cc7c8eda70..542fa7610465 100644 --- a/packages/devextreme/js/__internal/scheduler/m_expression_utils.ts +++ b/packages/devextreme/js/__internal/scheduler/m_expression_utils.ts @@ -1,20 +1,20 @@ -import { isDefined } from '../../core/utils/type'; +import { isDefined } from '@js/core/utils/type'; export const ExpressionUtils = { - getField: (dataAccessors, field, obj) => { - if(!isDefined(dataAccessors.getter[field])) { - return; - } + getField: (dataAccessors, field, obj) => { + if (!isDefined(dataAccessors.getter[field])) { + return; + } - return dataAccessors.getter[field](obj); - }, - setField: (dataAccessors, field, obj, value) => { - if(!isDefined(dataAccessors.setter[field])) { - return; - } + return dataAccessors.getter[field](obj); + }, + setField: (dataAccessors, field, obj, value) => { + if (!isDefined(dataAccessors.setter[field])) { + return; + } - dataAccessors.setter[field](obj, value); + dataAccessors.setter[field](obj, value); - return obj; - } + return obj; + }, }; diff --git a/packages/devextreme/js/__internal/scheduler/m_loading.ts b/packages/devextreme/js/__internal/scheduler/m_loading.ts index 2328e3928183..a4210bcf27f0 100644 --- a/packages/devextreme/js/__internal/scheduler/m_loading.ts +++ b/packages/devextreme/js/__internal/scheduler/m_loading.ts @@ -1,38 +1,42 @@ -import $ from '../../core/renderer'; -import { value as viewPort } from '../../core/utils/view_port'; -import LoadPanel from '../load_panel'; -import { Deferred } from '../../core/utils/deferred'; +import $ from '@js/core/renderer'; +import { Deferred } from '@js/core/utils/deferred'; +import { value as viewPort } from '@js/core/utils/view_port'; +import LoadPanel from '@js/ui/load_panel'; -let loading = null; +let loading: any = null; -const createLoadPanel = function(options) { - return new LoadPanel($('
') - .appendTo(options && options.container || viewPort()), - options); +const createLoadPanel = function (options) { + return new LoadPanel( + ($('
') as any) + .appendTo(options && options.container || viewPort()), + options, + ); }; -const removeLoadPanel = function() { - if(!loading) { - return; - } - loading.$element().remove(); - loading = null; +const removeLoadPanel = function () { + if (!loading) { + return; + } + + loading.$element().remove(); + loading = null; }; export function show(options) { - removeLoadPanel(); - loading = createLoadPanel(options); - return loading.show(); + removeLoadPanel(); + loading = createLoadPanel(options); + return loading.show(); } export function hide() { - // todo: hot fix for case without viewport + // todo: hot fix for case without viewport - if(!loading) { - return new Deferred().resolve(); - } - return loading - .hide() - .done(removeLoadPanel) - .promise(); + if (!loading) { + // @ts-expect-error + return new Deferred().resolve(); + } + return loading + .hide() + .done(removeLoadPanel) + .promise(); } diff --git a/packages/devextreme/js/__internal/scheduler/m_publisher_mixin.ts b/packages/devextreme/js/__internal/scheduler/m_publisher_mixin.ts index 7f931071f5b3..10c67212df1b 100644 --- a/packages/devextreme/js/__internal/scheduler/m_publisher_mixin.ts +++ b/packages/devextreme/js/__internal/scheduler/m_publisher_mixin.ts @@ -1,16 +1,16 @@ const publisherMixin = { - notifyObserver: function(subject, args) { - const observer = this.option('observer'); - if(observer) { - observer.fire(subject, args); - } - }, - invoke: function() { - const observer = this.option('observer'); + notifyObserver(subject, args) { + const observer = this.option('observer'); + if (observer) { + observer.fire(subject, args); + } + }, + invoke() { + const observer = this.option('observer'); - if(observer) { - return observer.fire.apply(observer, arguments); - } + if (observer) { + return observer.fire.apply(observer, arguments); } + }, }; export default publisherMixin; diff --git a/packages/devextreme/js/__internal/scheduler/m_recurrence.ts b/packages/devextreme/js/__internal/scheduler/m_recurrence.ts index 6aceaceef562..c226d5229ad7 100644 --- a/packages/devextreme/js/__internal/scheduler/m_recurrence.ts +++ b/packages/devextreme/js/__internal/scheduler/m_recurrence.ts @@ -1,454 +1,483 @@ -import errors from '../../core/errors'; -import { each } from '../../core/utils/iterator'; +/* eslint-disable max-classes-per-file, spellcheck/spell-checker */ +import errors from '@js/core/errors'; +import dateUtils from '@js/core/utils/date'; +import { each } from '@js/core/utils/iterator'; import { RRule, RRuleSet } from 'rrule'; -import dateUtils from '../../core/utils/date'; -import timeZoneUtils from './utils.timeZone'; + +import timeZoneUtils from './m_utils_time_zone'; const toMs = dateUtils.dateToMilliseconds; const ruleNames = ['freq', 'interval', 'byday', 'byweekno', 'byyearday', 'bymonth', 'bymonthday', 'count', 'until', 'byhour', 'byminute', 'bysecond', 'bysetpos', 'wkst']; const freqNames = ['DAILY', 'WEEKLY', 'MONTHLY', 'YEARLY', 'SECONDLY', 'MINUTELY', 'HOURLY']; -const days = { SU: 0, MO: 1, TU: 2, WE: 3, TH: 4, FR: 5, SA: 6 }; -const loggedWarnings = []; +const days = { + SU: 0, MO: 1, TU: 2, WE: 3, TH: 4, FR: 5, SA: 6, +}; +const loggedWarnings: any = []; const MS_IN_HOUR = 1000 * 60 * 60; const MS_IN_DAY = MS_IN_HOUR * 24; -let recurrence = null; +let recurrence: RecurrenceProcessor | null = null; export function getRecurrenceProcessor() { - if(!recurrence) { - recurrence = new RecurrenceProcessor(); - } - return recurrence; + if (!recurrence) { + recurrence = new RecurrenceProcessor(); + } + return recurrence; } class RecurrenceProcessor { - constructor() { - this.rRule = null; - this.rRuleSet = null; - this.validator = new RecurrenceValidator(); - } - - generateDates(options) { - const recurrenceRule = this.evalRecurrenceRule(options.rule); - const rule = recurrenceRule.rule; + rRule: RRule | null = null; - if(!recurrenceRule.isValid || !rule.freq) { - return []; - } + rRuleSet: RRuleSet | null = null; - const rruleIntervalParams = this._createRruleIntervalParams(options); + validator = new RecurrenceValidator(); - this._initializeRRule(options, - rruleIntervalParams.startIntervalDate, - rule.until); + generateDates(options) { + const recurrenceRule = this.evalRecurrenceRule(options.rule); + const { rule } = recurrenceRule; - return this.rRuleSet.between( - rruleIntervalParams.minViewDate, - rruleIntervalParams.maxViewDate, - true - ) - .filter((date) => date.getTime() + rruleIntervalParams.appointmentDuration >= rruleIntervalParams.minViewTime) - .map((date) => this._convertRruleResult(rruleIntervalParams, options, date)); + if (!recurrenceRule.isValid || !rule.freq) { + return []; } - _createRruleIntervalParams(options) { - const { start, min, max, appointmentTimezoneOffset } = options; - // NOTE: Get local timezone offset of each Rrule date params. - const clientOffsets = { - startDate: timeZoneUtils.getClientTimezoneOffset(start), - minViewDate: timeZoneUtils.getClientTimezoneOffset(min), - maxViewDate: timeZoneUtils.getClientTimezoneOffset(max), - }; - const duration = options.end ? options.end.getTime() - options.start.getTime() : 0; - - // NOTE: Remove local timezone offsets from Rrule date params. - const startIntervalDate = timeZoneUtils.setOffsetsToDate(options.start, [-clientOffsets.startDate, appointmentTimezoneOffset]); - const minViewTime = options.min.getTime() - clientOffsets.minViewDate + appointmentTimezoneOffset; - // NOTE: Shift minViewDate, because recurrent appointment may start before start view date. - const minViewDate = new Date(minViewTime - duration); - const maxViewDate = timeZoneUtils.setOffsetsToDate(options.max, [-clientOffsets.maxViewDate, appointmentTimezoneOffset]); - - // NOTE: Check DST after start date without local timezone offset conversion. - const startDateDSTDifferenceMs = timeZoneUtils.getDiffBetweenClientTimezoneOffsets(options.start, startIntervalDate); - const switchToSummerTime = startDateDSTDifferenceMs < 0; - - return { - startIntervalDate, - minViewTime, - minViewDate, - maxViewDate, - startIntervalDateDSTShift: switchToSummerTime ? 0 : startDateDSTDifferenceMs, - appointmentDuration: duration, - }; + const rruleIntervalParams = this._createRruleIntervalParams(options); + + this._initializeRRule( + options, + rruleIntervalParams.startIntervalDate, + rule.until, + ); + + return this.rRuleSet!.between( + rruleIntervalParams.minViewDate, + rruleIntervalParams.maxViewDate, + true, + ) + .filter((date) => date.getTime() + rruleIntervalParams.appointmentDuration >= rruleIntervalParams.minViewTime) + .map((date) => this._convertRruleResult(rruleIntervalParams, options, date)); + } + + _createRruleIntervalParams(options) { + const { + start, min, max, appointmentTimezoneOffset, + } = options; + // NOTE: Get local timezone offset of each Rrule date params. + const clientOffsets = { + startDate: timeZoneUtils.getClientTimezoneOffset(start), + minViewDate: timeZoneUtils.getClientTimezoneOffset(min), + maxViewDate: timeZoneUtils.getClientTimezoneOffset(max), + }; + const duration = options.end ? options.end.getTime() - options.start.getTime() : 0; + + // NOTE: Remove local timezone offsets from Rrule date params. + const startIntervalDate = timeZoneUtils.setOffsetsToDate(options.start, [-clientOffsets.startDate, appointmentTimezoneOffset]); + const minViewTime = options.min.getTime() - clientOffsets.minViewDate + appointmentTimezoneOffset; + // NOTE: Shift minViewDate, because recurrent appointment may start before start view date. + const minViewDate = new Date(minViewTime - duration); + const maxViewDate = timeZoneUtils.setOffsetsToDate(options.max, [-clientOffsets.maxViewDate, appointmentTimezoneOffset]); + + // NOTE: Check DST after start date without local timezone offset conversion. + const startDateDSTDifferenceMs = timeZoneUtils.getDiffBetweenClientTimezoneOffsets(options.start, startIntervalDate); + const switchToSummerTime = startDateDSTDifferenceMs < 0; + + return { + startIntervalDate, + minViewTime, + minViewDate, + maxViewDate, + startIntervalDateDSTShift: switchToSummerTime ? 0 : startDateDSTDifferenceMs, + appointmentDuration: duration, + }; + } + + _convertRruleResult(rruleIntervalParams, options, rruleDate) { + const localTimezoneOffset = timeZoneUtils.getClientTimezoneOffset(rruleDate); + // NOTE: Workaround for the RRule bug with timezones greater than GMT+12 (e.g. Apia Standard Time GMT+13) + // GitHub issue: https://github.com/jakubroztocil/rrule/issues/555 + const additionalWorkaroundOffsetForRrule = localTimezoneOffset / MS_IN_HOUR <= -13 ? -MS_IN_DAY : 0; + const convertedBackDate = timeZoneUtils.setOffsetsToDate(rruleDate, [ + localTimezoneOffset, + additionalWorkaroundOffsetForRrule, + -options.appointmentTimezoneOffset, + rruleIntervalParams.startIntervalDateDSTShift, + ]); + const convertedDateDSTShift = timeZoneUtils.getDiffBetweenClientTimezoneOffsets(convertedBackDate, rruleDate); + const switchToSummerTime = convertedDateDSTShift < 0; + const resultDate = timeZoneUtils.setOffsetsToDate(convertedBackDate, [convertedDateDSTShift]); + const resultDateDSTShift = timeZoneUtils.getDiffBetweenClientTimezoneOffsets(resultDate, convertedBackDate); + + if (resultDateDSTShift && switchToSummerTime) { + return new Date(resultDate.getTime() + resultDateDSTShift); } - _convertRruleResult(rruleIntervalParams, options, rruleDate) { - const localTimezoneOffset = timeZoneUtils.getClientTimezoneOffset(rruleDate); - // NOTE: Workaround for the RRule bug with timezones greater than GMT+12 (e.g. Apia Standard Time GMT+13) - // GitHub issue: https://github.com/jakubroztocil/rrule/issues/555 - const additionalWorkaroundOffsetForRrule = - localTimezoneOffset / MS_IN_HOUR <= -13 ? -MS_IN_DAY : 0; - const convertedBackDate = timeZoneUtils.setOffsetsToDate( - rruleDate, [ - localTimezoneOffset, - additionalWorkaroundOffsetForRrule, - -options.appointmentTimezoneOffset, - rruleIntervalParams.startIntervalDateDSTShift, - ]); - const convertedDateDSTShift = timeZoneUtils.getDiffBetweenClientTimezoneOffsets(convertedBackDate, rruleDate); - const switchToSummerTime = convertedDateDSTShift < 0; - const resultDate = timeZoneUtils.setOffsetsToDate(convertedBackDate, [convertedDateDSTShift]); - const resultDateDSTShift = timeZoneUtils.getDiffBetweenClientTimezoneOffsets(resultDate, convertedBackDate); - - if(resultDateDSTShift && switchToSummerTime) { - return new Date(resultDate.getTime() + resultDateDSTShift); - } + return resultDate; + } - return resultDate; - } + hasRecurrence(options) { + return !!this.generateDates(options).length; + } - hasRecurrence(options) { - return !!this.generateDates(options).length; - } + evalRecurrenceRule(rule) { + const result = { + rule: {} as any, + isValid: false, + }; - evalRecurrenceRule(rule) { - const result = { - rule: {}, - isValid: false - }; - - if(rule) { - result.rule = this._parseRecurrenceRule(rule); - result.isValid = this.validator.validateRRule(result.rule, rule); - } - - return result; + if (rule) { + result.rule = this._parseRecurrenceRule(rule); + result.isValid = this.validator.validateRRule(result.rule, rule); } - isValidRecurrenceRule(rule) { - return this.evalRecurrenceRule(rule).isValid; - } + return result; + } - daysFromByDayRule(rule) { - let result = []; + isValidRecurrenceRule(rule) { + return this.evalRecurrenceRule(rule).isValid; + } - if(rule['byday']) { - if(Array.isArray(rule['byday'])) { - result = rule['byday']; - } else { - result = rule['byday'].split(','); - } - } + daysFromByDayRule(rule: any) { + let result: any[] = []; - return result.map(item => { - const match = item.match(/[A-Za-z]+/); - return !!match && match[0]; - }).filter(item => !!item); + if (rule.byday) { + if (Array.isArray(rule.byday)) { + result = rule.byday; + } else { + result = rule.byday.split(','); + } } - getAsciiStringByDate(date) { - const currentOffset = date.getTimezoneOffset() * toMs('minute'); - const offsetDate = new Date(date.getTime() + currentOffset); - - return offsetDate.getFullYear() + ('0' + (offsetDate.getMonth() + 1)).slice(-2) + ('0' + offsetDate.getDate()).slice(-2) + - 'T' + ('0' + (offsetDate.getHours())).slice(-2) + ('0' + (offsetDate.getMinutes())).slice(-2) + ('0' + (offsetDate.getSeconds())).slice(-2) + 'Z'; - } + return result.map((item) => { + const match = item.match(/[A-Za-z]+/); + return !!match && match[0]; + }).filter((item) => !!item); + } - getRecurrenceString(object) { - if(!object || !object.freq) { - return; - } + getAsciiStringByDate(date) { + const currentOffset = date.getTimezoneOffset() * toMs('minute'); + const offsetDate = new Date(date.getTime() + currentOffset); - let result = ''; - for(const field in object) { - let value = object[field]; + return `${offsetDate.getFullYear() + `0${offsetDate.getMonth() + 1}`.slice(-2) + `0${offsetDate.getDate()}`.slice(-2) + }T${`0${offsetDate.getHours()}`.slice(-2)}${`0${offsetDate.getMinutes()}`.slice(-2)}${`0${offsetDate.getSeconds()}`.slice(-2)}Z`; + } - if(field === 'interval' && value < 2) { - continue; - } + getRecurrenceString(object) { + if (!object || !object.freq) { + return; + } - if(field === 'until') { - value = this.getAsciiStringByDate(value); - } + let result = ''; + // eslint-disable-next-line guard-for-in, no-restricted-syntax + for (const field in object) { + let value = object[field]; - result += field + '=' + value + ';'; - } + if (field === 'interval' && value < 2) { + continue; + } - result = result.substring(0, result.length - 1); + if (field === 'until') { + value = this.getAsciiStringByDate(value); + } - return result.toUpperCase(); + result += `${field}=${value};`; } - _parseExceptionToRawArray(value) { - return value.match(/(\d{4})(\d{2})(\d{2})(T(\d{2})(\d{2})(\d{2}))?(Z)?/); - } + result = result.substring(0, result.length - 1); - getDateByAsciiString(exceptionText) { - if(typeof exceptionText !== 'string') { - return exceptionText; - } + return result.toUpperCase(); + } - const result = this._parseExceptionToRawArray(exceptionText); + _parseExceptionToRawArray(value) { + return value.match(/(\d{4})(\d{2})(\d{2})(T(\d{2})(\d{2})(\d{2}))?(Z)?/); + } - if(!result) { - return null; - } + getDateByAsciiString(exceptionText) { + if (typeof exceptionText !== 'string') { + return exceptionText; + } - const [year, month, date, hours, minutes, seconds, isUtc] = this._createDateTuple(result); - - if(isUtc) { - return new Date(Date.UTC( - year, - month, - date, - hours, - minutes, - seconds) - ); - } + const result = this._parseExceptionToRawArray(exceptionText); - return new Date( - year, - month, - date, - hours, - minutes, - seconds - ); + if (!result) { + return null; } - _dispose() { - if(this.rRuleSet) { - delete this.rRuleSet; - this.rRuleSet = null; - } - if(this.rRule) { - delete this.rRule; - this.rRule = null; - } + const [year, month, date, hours, minutes, seconds, isUtc] = this._createDateTuple(result); + + if (isUtc) { + return new Date(Date.UTC( + year, + month, + date, + hours, + minutes, + seconds, + )); } - _getTimeZoneOffset() { - return new Date().getTimezoneOffset(); + return new Date( + year, + month, + date, + hours, + minutes, + seconds, + ); + } + + _dispose() { + if (this.rRuleSet) { + // @ts-expect-error + delete this.rRuleSet; + this.rRuleSet = null; } + if (this.rRule) { + // @ts-expect-error + delete this.rRule; + this.rRule = null; + } + } - _initializeRRule(options, startDateUtc, until) { - const ruleOptions = RRule.parseString(options.rule); - const firstDayOfWeek = options.firstDayOfWeek; - - ruleOptions.dtstart = startDateUtc; + _getTimeZoneOffset() { + return new Date().getTimezoneOffset(); + } - if(!ruleOptions.wkst && firstDayOfWeek) { - const weekDayNumbers = [6, 0, 1, 2, 3, 4, 5]; - ruleOptions.wkst = weekDayNumbers[firstDayOfWeek]; - } + _initializeRRule(options, startDateUtc, until) { + const ruleOptions = RRule.parseString(options.rule); + const { firstDayOfWeek } = options; - if(until) { - ruleOptions.until = timeZoneUtils.setOffsetsToDate(until, - [-timeZoneUtils.getClientTimezoneOffset(until), options.appointmentTimezoneOffset]); - } + ruleOptions.dtstart = startDateUtc; - this._createRRule(ruleOptions); + if (!ruleOptions.wkst && firstDayOfWeek) { + const weekDayNumbers = [6, 0, 1, 2, 3, 4, 5]; + ruleOptions.wkst = weekDayNumbers[firstDayOfWeek]; + } - if(options.exception) { - const exceptionStrings = options.exception; - const exceptionDates = exceptionStrings - .split(',') - .map(rule => this.getDateByAsciiString(rule)); + if (until) { + ruleOptions.until = timeZoneUtils.setOffsetsToDate( + until, + [-timeZoneUtils.getClientTimezoneOffset(until), options.appointmentTimezoneOffset], + ); + } - exceptionDates.forEach(date => { - if(options.getPostProcessedException) { - date = options.getPostProcessedException(date); - } + this._createRRule(ruleOptions); - const utcDate = timeZoneUtils.setOffsetsToDate(date, - [-timeZoneUtils.getClientTimezoneOffset(date), options.appointmentTimezoneOffset]); + if (options.exception) { + const exceptionStrings = options.exception; + const exceptionDates = exceptionStrings + .split(',') + .map((rule) => this.getDateByAsciiString(rule)); - this.rRuleSet.exdate(utcDate); - }); + exceptionDates.forEach((date) => { + if (options.getPostProcessedException) { + date = options.getPostProcessedException(date); } - } - - _createRRule(ruleOptions) { - this._dispose(); - this.rRuleSet = new RRuleSet(); - this.rRule = new RRule(ruleOptions); + const utcDate = timeZoneUtils.setOffsetsToDate( + date, + [-timeZoneUtils.getClientTimezoneOffset(date), options.appointmentTimezoneOffset], + ); - this.rRuleSet.rrule(this.rRule); + this.rRuleSet!.exdate(utcDate); + }); } + } - _parseRecurrenceRule(recurrence) { - const ruleObject = {}; - const ruleParts = recurrence.split(';'); - - for(let i = 0, len = ruleParts.length; i < len; i++) { + _createRRule(ruleOptions) { + this._dispose(); - const rule = ruleParts[i].split('='); - const ruleName = rule[0].toLowerCase(); - const ruleValue = rule[1]; + this.rRuleSet = new RRuleSet(); + this.rRule = new RRule(ruleOptions); - ruleObject[ruleName] = ruleValue; - } + this.rRuleSet.rrule(this.rRule); + } - const count = parseInt(ruleObject.count); + _parseRecurrenceRule(recurrence) { + const ruleObject: any = {}; + const ruleParts = recurrence.split(';'); - if(!isNaN(count)) { - ruleObject.count = count; - } + for (let i = 0, len = ruleParts.length; i < len; i++) { + const rule = ruleParts[i].split('='); + const ruleName = rule[0].toLowerCase(); + const ruleValue = rule[1]; - if(ruleObject.interval) { - const interval = parseInt(ruleObject.interval); - if(!isNaN(interval)) { - ruleObject.interval = interval; - } - } else { - ruleObject.interval = 1; - } - - if(ruleObject.freq && ruleObject.until) { - ruleObject.until = this.getDateByAsciiString(ruleObject.until); - } - - return ruleObject; + ruleObject[ruleName] = ruleValue; } - _createDateTuple(parseResult) { - const isUtc = parseResult[8] !== undefined; + // eslint-disable-next-line radix + const count = parseInt(ruleObject.count); - parseResult.shift(); - - if(parseResult[3] === undefined) { - parseResult.splice(3); - } else { - parseResult.splice(3, 1); - parseResult.splice(6); - } + if (!isNaN(count)) { + ruleObject.count = count; + } - parseResult[1]--; + if (ruleObject.interval) { + // eslint-disable-next-line radix + const interval = parseInt(ruleObject.interval); + if (!isNaN(interval)) { + ruleObject.interval = interval; + } + } else { + ruleObject.interval = 1; + } - parseResult.unshift(null); + if (ruleObject.freq && ruleObject.until) { + ruleObject.until = this.getDateByAsciiString(ruleObject.until); + } - return [ - parseInt(parseResult[1]), - parseInt(parseResult[2]), - parseInt(parseResult[3]), - parseInt(parseResult[4]) || 0, - parseInt(parseResult[5]) || 0, - parseInt(parseResult[6]) || 0, - isUtc - ]; + return ruleObject; + } + + _createDateTuple(parseResult): [ + number, + number, + number, + number, + number, + number, + boolean, + ] { + const isUtc = parseResult[8] !== undefined; + + parseResult.shift(); + + if (parseResult[3] === undefined) { + parseResult.splice(3); + } else { + parseResult.splice(3, 1); + parseResult.splice(6); } + + parseResult[1]--; + + parseResult.unshift(null); + + /* eslint-disable radix */ + return [ + parseInt(parseResult[1]), + parseInt(parseResult[2]), + parseInt(parseResult[3]), + parseInt(parseResult[4]) || 0, + parseInt(parseResult[5]) || 0, + parseInt(parseResult[6]) || 0, + isUtc, + ]; + /* eslint-enable radix */ + } } class RecurrenceValidator { - validateRRule(rule, recurrence) { - if(this._brokenRuleNameExists(rule) || - !freqNames.includes(rule.freq) || - this._wrongCountRule(rule) || this._wrongIntervalRule(rule) || - this._wrongDayOfWeek(rule) || - this._wrongByMonthDayRule(rule) || this._wrongByMonth(rule) || - this._wrongUntilRule(rule)) { + validateRRule(rule, recurrence) { + if (this._brokenRuleNameExists(rule) + || !freqNames.includes(rule.freq) + || this._wrongCountRule(rule) || this._wrongIntervalRule(rule) + || this._wrongDayOfWeek(rule) + || this._wrongByMonthDayRule(rule) || this._wrongByMonth(rule) + || this._wrongUntilRule(rule)) { + this._logBrokenRule(recurrence); + + return false; + } - this._logBrokenRule(recurrence); + return true; + } - return false; - } + _wrongUntilRule(rule) { + let wrongUntil = false; + const { until } = rule; - return true; + if (until !== undefined && !(until instanceof Date)) { + wrongUntil = true; } - _wrongUntilRule(rule) { - let wrongUntil = false; - const until = rule.until; + return wrongUntil; + } - if(until !== undefined && !(until instanceof Date)) { - wrongUntil = true; - } + _wrongCountRule(rule) { + let wrongCount = false; + const { count } = rule; - return wrongUntil; + if (count && typeof count === 'string') { + wrongCount = true; } - _wrongCountRule(rule) { - let wrongCount = false; - const count = rule.count; + return wrongCount; + } - if(count && typeof count === 'string') { - wrongCount = true; - } + _wrongByMonthDayRule(rule) { + let wrongByMonthDay = false; + const byMonthDay = rule.bymonthday; - return wrongCount; + // eslint-disable-next-line radix + if (byMonthDay && isNaN(parseInt(byMonthDay))) { + wrongByMonthDay = true; } - _wrongByMonthDayRule(rule) { - let wrongByMonthDay = false; - const byMonthDay = rule['bymonthday']; + return wrongByMonthDay; + } - if(byMonthDay && isNaN(parseInt(byMonthDay))) { - wrongByMonthDay = true; - } + _wrongByMonth(rule) { + let wrongByMonth = false; + const byMonth = rule.bymonth; - return wrongByMonthDay; + // eslint-disable-next-line radix + if (byMonth && isNaN(parseInt(byMonth))) { + wrongByMonth = true; } - _wrongByMonth(rule) { - let wrongByMonth = false; - const byMonth = rule['bymonth']; + return wrongByMonth; + } - if(byMonth && isNaN(parseInt(byMonth))) { - wrongByMonth = true; - } + _wrongIntervalRule(rule) { + let wrongInterval = false; + const { interval } = rule; - return wrongByMonth; + if (interval && typeof interval === 'string') { + wrongInterval = true; } - _wrongIntervalRule(rule) { - let wrongInterval = false; - const interval = rule.interval; + return wrongInterval; + } - if(interval && typeof interval === 'string') { - wrongInterval = true; - } + _wrongDayOfWeek(rule) { + const byDay = rule.byday; + const daysByRule = getRecurrenceProcessor().daysFromByDayRule(rule); + let brokenDaysExist = false; - return wrongInterval; + if (byDay === '') { + brokenDaysExist = true; } + each(daysByRule, (_, day) => { + if (!Object.prototype.hasOwnProperty.call(days, day)) { + brokenDaysExist = true; + return false; + } - _wrongDayOfWeek(rule) { - const byDay = rule['byday']; - const daysByRule = getRecurrenceProcessor().daysFromByDayRule(rule); - let brokenDaysExist = false; + return undefined; + }); - if(byDay === '') { - brokenDaysExist = true; - } - each(daysByRule, function(_, day) { - if(!Object.prototype.hasOwnProperty.call(days, day)) { - brokenDaysExist = true; - return false; - } - }); - - return brokenDaysExist; - } + return brokenDaysExist; + } - _brokenRuleNameExists(rule) { - let brokenRuleExists = false; + _brokenRuleNameExists(rule) { + let brokenRuleExists = false; - each(rule, function(ruleName) { - if(!ruleNames.includes(ruleName)) { - brokenRuleExists = true; - return false; - } - }); + each(rule, (ruleName: any) => { + if (!ruleNames.includes(ruleName)) { + brokenRuleExists = true; + return false; + } - return brokenRuleExists; - } + return undefined; + }); - _logBrokenRule(recurrence) { - if(!loggedWarnings.includes(recurrence)) { - errors.log('W0006', recurrence); - loggedWarnings.push(recurrence); - } + return brokenRuleExists; + } + + _logBrokenRule(recurrence: any) { + if (!loggedWarnings.includes(recurrence)) { + errors.log('W0006', recurrence); + loggedWarnings.push(recurrence); } + } } diff --git a/packages/devextreme/js/__internal/scheduler/m_recurrence_editor.ts b/packages/devextreme/js/__internal/scheduler/m_recurrence_editor.ts index b8300f3e5e72..606705b2278b 100644 --- a/packages/devextreme/js/__internal/scheduler/m_recurrence_editor.ts +++ b/packages/devextreme/js/__internal/scheduler/m_recurrence_editor.ts @@ -1,19 +1,22 @@ -import registerComponent from '../../core/component_registrator'; -import Guid from '../../core/guid'; -import $ from '../../core/renderer'; -import dateUtils from '../../core/utils/date'; -import { extend } from '../../core/utils/extend'; -import { isDefined } from '../../core/utils/type'; -import dateLocalization from '../../localization/date'; -import messageLocalization from '../../localization/message'; -import Form from '../form'; -import ButtonGroup from '../button_group'; -import DateBox from '../date_box'; -import Editor from '../editor/editor'; -import NumberBox from '../number_box'; -import { getRecurrenceProcessor } from './recurrence'; -import '../radio_group'; -import { PathTimeZoneConversion } from '../../renovation/ui/scheduler/timeZoneCalculator/types'; +/* eslint-disable max-classes-per-file, spellcheck/spell-checker */ +import '@js/ui/radio_group'; + +import registerComponent from '@js/core/component_registrator'; +import Guid from '@js/core/guid'; +import $ from '@js/core/renderer'; +import dateUtils from '@js/core/utils/date'; +import { extend } from '@js/core/utils/extend'; +import { isDefined } from '@js/core/utils/type'; +import dateLocalization from '@js/localization/date'; +import messageLocalization from '@js/localization/message'; +import { PathTimeZoneConversion } from '@js/renovation/ui/scheduler/timeZoneCalculator/types'; +import ButtonGroup from '@js/ui/button_group'; +import DateBox from '@js/ui/date_box'; +import Editor from '@js/ui/editor/editor'; +import Form from '@js/ui/form'; +import NumberBox from '@js/ui/number_box'; + +import { getRecurrenceProcessor } from './m_recurrence'; const RECURRENCE_EDITOR = 'dx-recurrence-editor'; const LABEL_POSTFIX = '-label'; @@ -35,816 +38,840 @@ const recurrentEditorSelectBoxWidth = 120; const defaultRecurrenceTypeIndex = 1; // TODO default daily recurrence const frequenciesMessages = [ - /* { + /* { // functionality is not removed, but hide the ability to set minute recurrence in the editor. // in the future, if we publish the dxRecurrenceEditor, then we publish the minute recurrence recurrence: 'dxScheduler-recurrenceMinutely', value: 'minutely' - }*/ - { - recurrence: 'dxScheduler-recurrenceHourly', - value: 'hourly' - }, { - recurrence: 'dxScheduler-recurrenceDaily', - value: 'daily' - }, { - recurrence: 'dxScheduler-recurrenceWeekly', - value: 'weekly' - }, { - recurrence: 'dxScheduler-recurrenceMonthly', - value: 'monthly' - }, { - recurrence: 'dxScheduler-recurrenceYearly', - value: 'yearly' - } + } */ + { + recurrence: 'dxScheduler-recurrenceHourly', + value: 'hourly', + }, { + recurrence: 'dxScheduler-recurrenceDaily', + value: 'daily', + }, { + recurrence: 'dxScheduler-recurrenceWeekly', + value: 'weekly', + }, { + recurrence: 'dxScheduler-recurrenceMonthly', + value: 'monthly', + }, { + recurrence: 'dxScheduler-recurrenceYearly', + value: 'yearly', + }, ]; -const frequencies = frequenciesMessages.map((item) => { - return { text() { return messageLocalization.format(item.recurrence); }, value: item.value }; -}); +const frequencies = frequenciesMessages.map((item) => ({ text() { return messageLocalization.format(item.recurrence); }, value: item.value })); const repeatEndTypes = [ - { type: 'never' }, - { type: 'until' }, - { type: 'count' } + { type: 'never' }, + { type: 'until' }, + { type: 'count' }, ]; - const days = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA']; class RecurrenceRule { + _recurrenceProcessor = getRecurrenceProcessor(); - constructor(rule) { - this._recurrenceProcessor = getRecurrenceProcessor(); - this._recurrenceRule = this._recurrenceProcessor.evalRecurrenceRule(rule).rule; - } - - makeRules(string) { - this._recurrenceRule = this._recurrenceProcessor.evalRecurrenceRule(string).rule; - } - - makeRule(field, value) { - if(!value || (Array.isArray(value) && !value.length)) { - delete this._recurrenceRule[field]; - return; - } + _recurrenceRule: any; - if(isDefined(field)) { - if(field === 'until') { - delete this._recurrenceRule.count; - } + constructor(rule) { + this._recurrenceProcessor = getRecurrenceProcessor(); + this._recurrenceRule = this._recurrenceProcessor.evalRecurrenceRule(rule).rule; + } - if(field === 'count') { - delete this._recurrenceRule.until; - } + makeRules(string) { + this._recurrenceRule = this._recurrenceProcessor.evalRecurrenceRule(string).rule; + } - this._recurrenceRule[field] = value; - } + makeRule(field, value) { + if (!value || (Array.isArray(value) && !value.length)) { + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete + delete this._recurrenceRule[field]; + return; } - getRepeatEndRule() { - const rules = this._recurrenceRule; + if (isDefined(field)) { + if (field === 'until') { + delete this._recurrenceRule.count; + } - if('count' in rules) { - return 'count'; - } - - if('until' in rules) { - return 'until'; - } + if (field === 'count') { + delete this._recurrenceRule.until; + } - return 'never'; + this._recurrenceRule[field] = value; } + } - getRecurrenceString() { - return this._recurrenceProcessor.getRecurrenceString(this._recurrenceRule); - } + getRepeatEndRule() { + const rules = this._recurrenceRule; - getRules() { - return this._recurrenceRule; + if ('count' in rules) { + return 'count'; } - getDaysFromByDayRule() { - return this._recurrenceProcessor.daysFromByDayRule(this._recurrenceRule); + if ('until' in rules) { + return 'until'; } -} -class RecurrenceEditor extends Editor { - _getDefaultOptions() { - const defaultOptions = super._getDefaultOptions(); - - return extend(defaultOptions, { - value: null, - - /** - * @name dxRecurrenceEditorOptions.startDate - * @type Date - * @default new Date() - * @hidden - */ - startDate: new Date(), - - /** - * @name dxRecurrenceEditorOptions.firstDayOfWeek - * @type Enums.FirstDayOfWeek - * @default undefined - * @hidden - */ - firstDayOfWeek: undefined - }); - } + return 'never'; + } - _getFirstDayOfWeek() { - const firstDayOfWeek = this.option('firstDayOfWeek'); - return isDefined(firstDayOfWeek) ? firstDayOfWeek : dateLocalization.firstDayOfWeekIndex(); - } + getRecurrenceString() { + return this._recurrenceProcessor.getRecurrenceString(this._recurrenceRule); + } - _createComponent(element, name, config = {}) { - this._extendConfig(config, { - readOnly: this.option('readOnly') - }); - return super._createComponent(element, name, config); - } + getRules() { + return this._recurrenceRule; + } - _init() { - super._init(); - this._recurrenceRule = new RecurrenceRule(this.option('value')); - } + getDaysFromByDayRule() { + return this._recurrenceProcessor.daysFromByDayRule(this._recurrenceRule); + } +} - _render() { - super._render(); +class RecurrenceEditor extends Editor { + _recurrenceRule!: RecurrenceRule; - this.$element().addClass(RECURRENCE_EDITOR); + _$container: any; - this._$container = $('
') - .addClass(RECURRENCE_EDITOR_CONTAINER) - .appendTo(this.$element()); + _weekEditor: any; - this._prepareEditors(); - this._renderEditors(this._$container); - } + _repeatCountEditor: any; - getEditorByField(fieldName) { - let editor = this.getRecurrenceForm().getEditor(fieldName); - - if(!isDefined(editor)) { - switch(fieldName) { - case 'byday': - editor = this._weekEditor; - break; - case 'count': - editor = this._repeatCountEditor; - break; - case 'until': - editor = this._repeatUntilDate; - break; - } - } + _repeatUntilDate: any; - return editor; - } + _editors!: any[]; - _prepareEditors() { - const freq = (this._recurrenceRule.getRules().freq || frequenciesMessages[defaultRecurrenceTypeIndex].value).toLowerCase(); - - this._editors = [ - this._createFreqEditor(freq), - this._createIntervalEditor(freq), - this._createRepeatOnLabel(freq), - { - itemType: 'group', - cssClass: REPEAT_ON_EDITOR, - colCount: 2, - colCountByScreen: { xs: 2 }, - items: this._createRepeatOnEditor(freq) - }, - { - itemType: 'group', - items: this._createRepeatEndEditor() - } - ]; + _$repeatOnWeek: any; - return this._editors; - } + _recurrenceForm: any; - _createFreqEditor(freq) { - return { - dataField: 'freq', - name: 'FREQ', - editorType: 'dxSelectBox', - cssClass: FREQUENCY_EDITOR, - editorOptions: { - items: frequencies, - value: freq, - field: 'freq', - valueExpr: 'value', - displayExpr: 'text', - layout: 'horizontal', - elementAttr: { - class: FREQUENCY_EDITOR - }, - onValueChanged: (args) => this._valueChangedHandler(args) - }, - label: { - text: messageLocalization.format('dxScheduler-editorLabelRecurrence') - } - }; - } + _$repeatCountEditor: any; - _createIntervalEditor(freq) { - const interval = this._recurrenceRule.getRules().interval || 1; - return { - itemType: 'group', - colCount: 2, - cssClass: `${INTERVAL_EDITOR}${WRAPPER_POSTFIX}`, - colCountByScreen: { xs: 2 }, - items: [ - { - dataField: 'interval', - editorType: 'dxNumberBox', - editorOptions: { - format: '#', - width: recurrentEditorNumberBoxWidth, - min: 1, - field: 'interval', - value: interval, - showSpinButtons: true, - useLargeSpinButtons: false, - elementAttr: { - class: INTERVAL_EDITOR - }, - onValueChanged: (args) => this._valueChangedHandler(args) - }, - label: { - text: messageLocalization.format('dxScheduler-recurrenceRepeatEvery') - } - }, - { - name: 'intervalLabel', - cssClass: `${INTERVAL_EDITOR}${LABEL_POSTFIX}`, - template: () => messageLocalization.format(`dxScheduler-recurrenceRepeat${freq.charAt(0).toUpperCase()}${freq.substr(1).toLowerCase()}`) - } - ] - }; - } + _$repeatDateEditor: any; - _createRepeatOnLabel(freq) { - return { - itemType: 'group', - cssClass: `${REPEAT_ON_EDITOR}${LABEL_POSTFIX}`, - items: [ - { - name: 'repeatOnLabel', - colSpan: 2, - template: () => messageLocalization.format('dxScheduler-recurrenceRepeatOn'), - visible: freq && freq !== 'daily' && freq !== 'hourly', - } - ] - }; - } + _freqEditor: any; - _createRepeatOnEditor(freq) { - return [ - this._createByDayEditor(freq), - this._createByMonthEditor(freq), - this._createByMonthDayEditor(freq) - ]; - } - - _createByDayEditor(freq) { - return { - dataField: 'byday', - colSpan: 2, - template: (_, itemElement) =>{ - const firstDayOfWeek = this._getFirstDayOfWeek(); - const byDay = this._daysOfWeekByRules(); - - const localDaysNames = dateLocalization.getDayNames('abbreviated'); - const dayNames = days.slice(firstDayOfWeek).concat(days.slice(0, firstDayOfWeek)); - - const itemsButtonGroup = localDaysNames.slice(firstDayOfWeek).concat(localDaysNames.slice(0, firstDayOfWeek)).map((item, index) => { return { text: item, key: dayNames[index] }; }); - - this._$repeatOnWeek = $('
').addClass(RECURRENCE_BUTTON_GROUP).appendTo(itemElement); - - this._weekEditor = this._createComponent(this._$repeatOnWeek, ButtonGroup, { - items: itemsButtonGroup, - field: 'byday', - selectionMode: 'multiple', - selectedItemKeys: byDay, - keyExpr: 'key', - onSelectionChanged: (e) => { - const selectedItemKeys = e.component.option('selectedItemKeys'); - const selectedKeys = selectedItemKeys?.length - ? selectedItemKeys - : this._getDefaultByDayValue(); - - this._recurrenceRule.makeRule('byday', selectedKeys); - this._changeEditorValue(); - } - }); - }, - visible: freq === 'weekly', - label: { - visible: false - } - }; - } - - _createByMonthEditor(freq) { - const monthsName = dateLocalization.getMonthNames('wide'); - const months = [...Array(12)].map((_, i) => ({ value: `${i + 1}`, text: monthsName[i] })); - - return { - dataField: 'bymonth', - editorType: 'dxSelectBox', - editorOptions: { - field: 'bymonth', - items: months, - value: this._monthOfYearByRules(), - width: recurrentEditorSelectBoxWidth, - displayExpr: 'text', - valueExpr: 'value', - elementAttr: { - class: MONTH_OF_YEAR - }, - onValueChanged: (args) => this._valueChangedHandler(args) - }, - visible: freq === 'yearly', - label: { - visible: false - } - }; - } - - _createByMonthDayEditor(freq) { - return { - dataField: 'bymonthday', - editorType: 'dxNumberBox', - editorOptions: { - min: 1, - max: 31, - format: '#', - width: recurrentEditorNumberBoxWidth, - field: 'bymonthday', - showSpinButtons: true, - useLargeSpinButtons: false, - value: this._dayOfMonthByRules(), - elementAttr: { - class: DAY_OF_MONTH - }, - onValueChanged: (args) => this._valueChangedHandler(args) - }, - visible: freq === 'monthly' || freq === 'yearly', - label: { - visible: false - } - }; - } - - _createRepeatEndEditor() { - const repeatType = this._recurrenceRule.getRepeatEndRule(); - - return [{ - dataField: 'repeatEnd', - editorType: 'dxRadioGroup', - editorOptions: { - items: repeatEndTypes, - value: repeatType, - valueExpr: 'type', - field: 'repeatEnd', - itemTemplate: (itemData) => { - if(itemData.type === 'count') { - return this._renderRepeatCountEditor(); - } - if(itemData.type === 'until') { - return this._renderRepeatUntilEditor(); - } - - return this._renderDefaultRepeatEnd(); - - }, - layout: 'vertical', - elementAttr: { - class: REPEAT_END_TYPE_EDITOR - }, - onValueChanged: (args) => this._repeatEndValueChangedHandler(args) + _switchEditor: any; + + _getDefaultOptions() { + // @ts-expect-error + const defaultOptions = super._getDefaultOptions(); + + return extend(defaultOptions, { + value: null, + + startDate: new Date(), + + firstDayOfWeek: undefined, + }); + } + + _getFirstDayOfWeek() { + const firstDayOfWeek = this.option('firstDayOfWeek'); + return isDefined(firstDayOfWeek) ? firstDayOfWeek : dateLocalization.firstDayOfWeekIndex(); + } + + _createComponent(element, name, config = {}) { + // @ts-expect-error + this._extendConfig(config, { + readOnly: this.option('readOnly'), + }); + // @ts-expect-error + return super._createComponent(element, name, config); + } + + _init() { + // @ts-expect-error + super._init(); + this._recurrenceRule = new RecurrenceRule(this.option('value')); + } + + _render() { + // @ts-expect-error + super._render(); + + (this.$element() as any).addClass(RECURRENCE_EDITOR); + + this._$container = $('
') + .addClass(RECURRENCE_EDITOR_CONTAINER) + .appendTo(this.$element()); + + this._prepareEditors(); + this._renderEditors(this._$container); + } + + getEditorByField(fieldName) { + let editor = this.getRecurrenceForm().getEditor(fieldName); + + if (!isDefined(editor)) { + switch (fieldName) { + case 'byday': + editor = this._weekEditor; + break; + case 'count': + editor = this._repeatCountEditor; + break; + case 'until': + editor = this._repeatUntilDate; + break; + default: + break; + } + } + + return editor; + } + + _prepareEditors() { + const freq = (this._recurrenceRule.getRules().freq || frequenciesMessages[defaultRecurrenceTypeIndex].value).toLowerCase(); + + this._editors = [ + this._createFreqEditor(freq), + this._createIntervalEditor(freq), + this._createRepeatOnLabel(freq), + { + itemType: 'group', + cssClass: REPEAT_ON_EDITOR, + colCount: 2, + colCountByScreen: { xs: 2 }, + items: this._createRepeatOnEditor(freq), + }, + { + itemType: 'group', + items: this._createRepeatEndEditor(), + }, + ]; + + return this._editors; + } + + _createFreqEditor(freq) { + return { + dataField: 'freq', + name: 'FREQ', + editorType: 'dxSelectBox', + cssClass: FREQUENCY_EDITOR, + editorOptions: { + items: frequencies, + value: freq, + field: 'freq', + valueExpr: 'value', + displayExpr: 'text', + layout: 'horizontal', + elementAttr: { + class: FREQUENCY_EDITOR, + }, + onValueChanged: (args) => this._valueChangedHandler(args), + }, + label: { + text: messageLocalization.format('dxScheduler-editorLabelRecurrence'), + }, + }; + } + + _createIntervalEditor(freq) { + const interval = this._recurrenceRule.getRules().interval || 1; + return { + itemType: 'group', + colCount: 2, + cssClass: `${INTERVAL_EDITOR}${WRAPPER_POSTFIX}`, + colCountByScreen: { xs: 2 }, + items: [ + { + dataField: 'interval', + editorType: 'dxNumberBox', + editorOptions: { + format: '#', + width: recurrentEditorNumberBoxWidth, + min: 1, + field: 'interval', + value: interval, + showSpinButtons: true, + useLargeSpinButtons: false, + elementAttr: { + class: INTERVAL_EDITOR, }, - label: { - text: messageLocalization.format('dxScheduler-recurrenceEnd') - } - }]; - } - - _renderEditors($container) { - this._recurrenceForm = this._createComponent($container, Form, { - items: this._editors, - showValidationSummary: false, - scrollingEnabled: true, - showColonAfterLabel: false, - labelLocation: 'top', + onValueChanged: (args) => this._valueChangedHandler(args), + }, + label: { + text: messageLocalization.format('dxScheduler-recurrenceRepeatEvery'), + }, + }, + { + name: 'intervalLabel', + cssClass: `${INTERVAL_EDITOR}${LABEL_POSTFIX}`, + template: () => messageLocalization.format(`dxScheduler-recurrenceRepeat${freq.charAt(0).toUpperCase()}${freq.substr(1).toLowerCase()}`), + }, + ], + }; + } + + _createRepeatOnLabel(freq) { + return { + itemType: 'group', + cssClass: `${REPEAT_ON_EDITOR}${LABEL_POSTFIX}`, + items: [ + { + name: 'repeatOnLabel', + colSpan: 2, + template: () => messageLocalization.format('dxScheduler-recurrenceRepeatOn'), + visible: freq && freq !== 'daily' && freq !== 'hourly', + }, + ], + }; + } + + _createRepeatOnEditor(freq) { + return [ + this._createByDayEditor(freq), + this._createByMonthEditor(freq), + this._createByMonthDayEditor(freq), + ]; + } + + _createByDayEditor(freq) { + return { + dataField: 'byday', + colSpan: 2, + template: (_, itemElement) => { + const firstDayOfWeek = this._getFirstDayOfWeek() as any; + const byDay = this._daysOfWeekByRules(); + + const localDaysNames = dateLocalization.getDayNames('abbreviated'); + const dayNames = days.slice(firstDayOfWeek).concat(days.slice(0, firstDayOfWeek)); + + const itemsButtonGroup = localDaysNames.slice(firstDayOfWeek).concat(localDaysNames.slice(0, firstDayOfWeek)).map((item, index) => ({ text: item, key: dayNames[index] })); + + this._$repeatOnWeek = $('
').addClass(RECURRENCE_BUTTON_GROUP).appendTo(itemElement); + + this._weekEditor = this._createComponent(this._$repeatOnWeek, ButtonGroup, { + items: itemsButtonGroup, + field: 'byday', + selectionMode: 'multiple', + selectedItemKeys: byDay, + keyExpr: 'key', + onSelectionChanged: (e) => { + const selectedItemKeys = e.component.option('selectedItemKeys'); + const selectedKeys = selectedItemKeys?.length + ? selectedItemKeys + : this._getDefaultByDayValue(); + + this._recurrenceRule.makeRule('byday', selectedKeys); + this._changeEditorValue(); + }, }); + }, + visible: freq === 'weekly', + label: { + visible: false, + }, + }; + } + + _createByMonthEditor(freq) { + const monthsName = (dateLocalization.getMonthNames as any)('wide'); + const months = [...Array(12)].map((_, i) => ({ value: `${i + 1}`, text: monthsName[i] })); + + return { + dataField: 'bymonth', + editorType: 'dxSelectBox', + editorOptions: { + field: 'bymonth', + items: months, + value: this._monthOfYearByRules(), + width: recurrentEditorSelectBoxWidth, + displayExpr: 'text', + valueExpr: 'value', + elementAttr: { + class: MONTH_OF_YEAR, + }, + onValueChanged: (args) => this._valueChangedHandler(args), + }, + visible: freq === 'yearly', + label: { + visible: false, + }, + }; + } + + _createByMonthDayEditor(freq) { + return { + dataField: 'bymonthday', + editorType: 'dxNumberBox', + editorOptions: { + min: 1, + max: 31, + format: '#', + width: recurrentEditorNumberBoxWidth, + field: 'bymonthday', + showSpinButtons: true, + useLargeSpinButtons: false, + value: this._dayOfMonthByRules(), + elementAttr: { + class: DAY_OF_MONTH, + }, + onValueChanged: (args) => this._valueChangedHandler(args), + }, + visible: freq === 'monthly' || freq === 'yearly', + label: { + visible: false, + }, + }; + } + + _createRepeatEndEditor() { + const repeatType = this._recurrenceRule.getRepeatEndRule(); + + return [{ + dataField: 'repeatEnd', + editorType: 'dxRadioGroup', + editorOptions: { + items: repeatEndTypes, + value: repeatType, + valueExpr: 'type', + field: 'repeatEnd', + itemTemplate: (itemData) => { + if (itemData.type === 'count') { + return this._renderRepeatCountEditor(); + } + if (itemData.type === 'until') { + return this._renderRepeatUntilEditor(); + } + + return this._renderDefaultRepeatEnd(); + }, + layout: 'vertical', + elementAttr: { + class: REPEAT_END_TYPE_EDITOR, + }, + onValueChanged: (args) => this._repeatEndValueChangedHandler(args), + }, + label: { + text: messageLocalization.format('dxScheduler-recurrenceEnd'), + }, + }]; + } + + _renderEditors($container) { + this._recurrenceForm = this._createComponent($container, Form, { + items: this._editors, + showValidationSummary: false, + scrollingEnabled: true, + showColonAfterLabel: false, + labelLocation: 'top', + }); + + this._disableRepeatEndParts(); + } + + _setAriaDescribedBy(editor, $label) { + const labelId = `label-${new Guid()}`; + + editor.setAria('describedby', labelId); + editor.setAria('id', labelId, $label); + } + + getRecurrenceForm() { + return this._recurrenceForm; + } + + changeValueByVisibility(value) { + if (value) { + if (!this.option('value')) { + this._handleDefaults(); + } + } else { + this._recurrenceRule.makeRules(''); + this.option('value', ''); + } + } + + _handleDefaults() { + this._recurrenceRule.makeRule('freq', frequenciesMessages[defaultRecurrenceTypeIndex].value); + this._changeEditorValue(); + } + + _changeEditorValue() { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + this.option('value', this._recurrenceRule.getRecurrenceString() || ''); + } + + _daysOfWeekByRules() { + let daysByRule = this._recurrenceRule.getDaysFromByDayRule(); + if (!daysByRule.length) { + daysByRule = this._getDefaultByDayValue(); + } + + return daysByRule; + } + + _getDefaultByDayValue() { + const startDate = this.option('startDate') as any; + const startDay = startDate.getDay(); + return [days[startDay]]; + } + + _dayOfMonthByRules() { + let dayByRule = this._recurrenceRule.getRules().bymonthday; + + if (!dayByRule) { + dayByRule = (this.option('startDate') as any).getDate(); + } + + return dayByRule; + } + + _monthOfYearByRules() { + let monthByRule = this._recurrenceRule.getRules().bymonth; + + if (!monthByRule) { + monthByRule = (this.option('startDate') as any).getMonth() + 1; + } + + return String(monthByRule); + } + + _renderDefaultRepeatEnd() { + const $editorTemplate = $('
').addClass(REPEAT_END_EDITOR + WRAPPER_POSTFIX); - this._disableRepeatEndParts(); - } + $('
') + .text(messageLocalization.format('dxScheduler-recurrenceNever')) + .addClass(REPEAT_END_EDITOR + LABEL_POSTFIX) + .appendTo($editorTemplate); - _setAriaDescribedBy(editor, $label) { - const labelId = `label-${new Guid()}`; + return $editorTemplate; + } - editor.setAria('describedby', labelId); - editor.setAria('id', labelId, $label); - } + _repeatEndValueChangedHandler(args) { + const { value } = args; - getRecurrenceForm() { - return this._recurrenceForm; - } + this._disableRepeatEndParts(value); - changeValueByVisibility(value) { - if(value) { - if(!this.option('value')) { - this._handleDefaults(); - } - } else { - this._recurrenceRule.makeRules(''); - this.option('value', ''); - } + if (value === 'until') { + this._recurrenceRule.makeRule(value, this._getUntilValue()); } - - _handleDefaults() { - this._recurrenceRule.makeRule('freq', frequenciesMessages[defaultRecurrenceTypeIndex].value); - this._changeEditorValue(); + if (value === 'count') { + this._recurrenceRule.makeRule(value, this._repeatCountEditor.option('value')); } - - _changeEditorValue() { - this.option('value', this._recurrenceRule.getRecurrenceString() || ''); + if (value === 'never') { + this._recurrenceRule.makeRule('count', ''); + this._recurrenceRule.makeRule('until', ''); } - _daysOfWeekByRules() { - let daysByRule = this._recurrenceRule.getDaysFromByDayRule(); - if(!daysByRule.length) { - daysByRule = this._getDefaultByDayValue(); - } - - return daysByRule; - } + this._changeEditorValue(); + } - _getDefaultByDayValue() { - const startDate = this.option('startDate'); - const startDay = startDate.getDay(); - return [days[startDay]]; + _disableRepeatEndParts(value = this._recurrenceRule.getRepeatEndRule()) { + if (value === 'until') { + this._repeatCountEditor.option('disabled', true); + this._repeatUntilDate.option('disabled', false); } - - _dayOfMonthByRules() { - let dayByRule = this._recurrenceRule.getRules()['bymonthday']; - - if(!dayByRule) { - dayByRule = this.option('startDate').getDate(); - } - - return dayByRule; + if (value === 'count') { + this._repeatCountEditor.option('disabled', false); + this._repeatUntilDate.option('disabled', true); } - - _monthOfYearByRules() { - let monthByRule = this._recurrenceRule.getRules()['bymonth']; - - if(!monthByRule) { - monthByRule = this.option('startDate').getMonth() + 1; - } - - return String(monthByRule); + if (value === 'never') { + this._repeatCountEditor.option('disabled', true); + this._repeatUntilDate.option('disabled', true); } + } - _renderDefaultRepeatEnd() { - const $editorTemplate = $('
').addClass(REPEAT_END_EDITOR + WRAPPER_POSTFIX); + _renderRepeatCountEditor() { + const repeatCount = this._recurrenceRule.getRules().count || 1; + const $editorWrapper = $('
').addClass(REPEAT_END_EDITOR + WRAPPER_POSTFIX); - $('
') - .text(messageLocalization.format('dxScheduler-recurrenceNever')) - .addClass(REPEAT_END_EDITOR + LABEL_POSTFIX) - .appendTo($editorTemplate); + $('
') + .text(messageLocalization.format('dxScheduler-recurrenceAfter')) + .addClass(REPEAT_END_EDITOR + LABEL_POSTFIX) + .appendTo($editorWrapper); - return $editorTemplate; - } + this._$repeatCountEditor = $('
') + .addClass(REPEAT_COUNT_EDITOR) + .appendTo($editorWrapper); - _repeatEndValueChangedHandler(args) { - const value = args.value; + $('
') + .text(messageLocalization.format('dxScheduler-recurrenceRepeatCount')) + .addClass(REPEAT_END_EDITOR + LABEL_POSTFIX) + .appendTo($editorWrapper); - this._disableRepeatEndParts(value); + this._repeatCountEditor = this._createComponent(this._$repeatCountEditor, NumberBox, { + field: 'count', + format: '#', + width: recurrentEditorNumberBoxWidth, + min: 1, + showSpinButtons: true, + useLargeSpinButtons: false, + value: repeatCount, + onValueChanged: this._repeatCountValueChangeHandler.bind(this), + }); - if(value === 'until') { - this._recurrenceRule.makeRule(value, this._getUntilValue()); - } - if(value === 'count') { - this._recurrenceRule.makeRule(value, this._repeatCountEditor.option('value')); - } - if(value === 'never') { - this._recurrenceRule.makeRule('count', ''); - this._recurrenceRule.makeRule('until', ''); - } + return $editorWrapper; + } - this._changeEditorValue(); + _repeatCountValueChangeHandler(args) { + if (this._recurrenceRule.getRepeatEndRule() === 'count') { + const { value } = args; + this._recurrenceRule.makeRule('count', value); + this._changeEditorValue(); } + } - _disableRepeatEndParts(value = this._recurrenceRule.getRepeatEndRule()) { - if(value === 'until') { - this._repeatCountEditor.option('disabled', true); - this._repeatUntilDate.option('disabled', false); - } - if(value === 'count') { - this._repeatCountEditor.option('disabled', false); - this._repeatUntilDate.option('disabled', true); - } - if(value === 'never') { - this._repeatCountEditor.option('disabled', true); - this._repeatUntilDate.option('disabled', true); - } + _formatUntilDate(date) { + if (this._recurrenceRule.getRules().until && dateUtils.sameDate(this._recurrenceRule.getRules().until, date)) { + return date; } - _renderRepeatCountEditor() { - const repeatCount = this._recurrenceRule.getRules().count || 1; - const $editorWrapper = $('
').addClass(REPEAT_END_EDITOR + WRAPPER_POSTFIX); - - $('
') - .text(messageLocalization.format('dxScheduler-recurrenceAfter')) - .addClass(REPEAT_END_EDITOR + LABEL_POSTFIX) - .appendTo($editorWrapper); - - this._$repeatCountEditor = $('
') - .addClass(REPEAT_COUNT_EDITOR) - .appendTo($editorWrapper); - - $('
') - .text(messageLocalization.format('dxScheduler-recurrenceRepeatCount')) - .addClass(REPEAT_END_EDITOR + LABEL_POSTFIX) - .appendTo($editorWrapper); - - this._repeatCountEditor = this._createComponent(this._$repeatCountEditor, NumberBox, { - field: 'count', - format: '#', - width: recurrentEditorNumberBoxWidth, - min: 1, - showSpinButtons: true, - useLargeSpinButtons: false, - value: repeatCount, - onValueChanged: this._repeatCountValueChangeHandler.bind(this) - }); + return dateUtils.setToDayEnd(date); + } - return $editorWrapper; - } + _renderRepeatUntilEditor() { + const repeatUntil = this._getUntilValue(); + const $editorWrapper = $('
').addClass(REPEAT_END_EDITOR + WRAPPER_POSTFIX); + + $('
') + .text(messageLocalization.format('dxScheduler-recurrenceOn')) + .addClass(REPEAT_END_EDITOR + LABEL_POSTFIX) + .appendTo($editorWrapper); + + this._$repeatDateEditor = $('
') + .addClass(REPEAT_UNTIL_DATE_EDITOR) + .appendTo($editorWrapper); + + this._repeatUntilDate = this._createComponent(this._$repeatDateEditor, DateBox, { + field: 'until', + value: repeatUntil, + type: 'date', + onValueChanged: this._repeatUntilValueChangeHandler.bind(this), + calendarOptions: { + firstDayOfWeek: this._getFirstDayOfWeek(), + }, + useMaskBehavior: true, + }); + + return $editorWrapper; + } + + _repeatUntilValueChangeHandler(args) { + if (this._recurrenceRule.getRepeatEndRule() === 'until') { + const dateInTimeZone = this._formatUntilDate(new Date(args.value)); + const getStartDateTimeZone: any = this.option('getStartDateTimeZone'); + const appointmentTimeZone = getStartDateTimeZone(); + + const path = appointmentTimeZone + ? PathTimeZoneConversion.fromAppointmentToSource : PathTimeZoneConversion.fromGridToSource; + + const dateInLocaleTimeZone = (this.option('timeZoneCalculator') as any) + .createDate(dateInTimeZone, { path, appointmentTimeZone }); + + this._recurrenceRule.makeRule('until', dateInLocaleTimeZone); + this._changeEditorValue(); + } + } + + _valueChangedHandler(args) { + const { value, previousValue } = args; + const field = args.component.option('field'); + + if (!this.option('visible')) { + this.option('value', ''); + } else { + this._recurrenceRule.makeRule(field, value); + if (field === 'freq') { + this._makeRepeatOnRule(value); + this._changeRepeatOnVisibility(value, previousValue); + } + this._changeEditorValue(); + } + } + + _makeRepeatOnRule(value) { + if (value === 'daily' || value === 'hourly') { + this._recurrenceRule.makeRule('byday', ''); + this._recurrenceRule.makeRule('bymonth', ''); + this._recurrenceRule.makeRule('bymonthday', ''); + } + if (value === 'weekly') { + this._recurrenceRule.makeRule('byday', this._daysOfWeekByRules()); + this._recurrenceRule.makeRule('bymonth', ''); + this._recurrenceRule.makeRule('bymonthday', ''); + } + + if (value === 'monthly') { + this._recurrenceRule.makeRule('bymonthday', this._dayOfMonthByRules()); + this._recurrenceRule.makeRule('bymonth', ''); + this._recurrenceRule.makeRule('byday', ''); + } + + if (value === 'yearly') { + this._recurrenceRule.makeRule('bymonthday', this._dayOfMonthByRules()); + this._recurrenceRule.makeRule('bymonth', this._monthOfYearByRules()); + this._recurrenceRule.makeRule('byday', ''); + } + } + + _optionChanged(args) { + switch (args.name) { + case 'readOnly': + this._recurrenceForm?.option('readOnly', args.value); + this._repeatCountEditor?.option('readOnly', args.value); + this._weekEditor?.option('readOnly', args.value); + this._repeatUntilDate?.option('readOnly', args.value); + // @ts-expect-error + super._optionChanged(args); + break; + case 'value': + this._recurrenceRule.makeRules(args.value); + + this._changeRepeatIntervalLabel(); + this._disableRepeatEndParts(); + this._changeEditorsValue(this._recurrenceRule.getRules()); - _repeatCountValueChangeHandler(args) { - if(this._recurrenceRule.getRepeatEndRule() === 'count') { - const value = args.value; - this._recurrenceRule.makeRule('count', value); - this._changeEditorValue(); - } - } + // @ts-expect-error + super._optionChanged(args); + break; + case 'startDate': + this._makeRepeatOnRule(this._recurrenceRule.getRules().freq); - _formatUntilDate(date) { - if(this._recurrenceRule.getRules().until && dateUtils.sameDate(this._recurrenceRule.getRules().until, date)) { - return date; + if (isDefined(this._recurrenceRule.getRecurrenceString())) { + this._changeEditorValue(); } - return dateUtils.setToDayEnd(date); - } - - _renderRepeatUntilEditor() { - const repeatUntil = this._getUntilValue(); - const $editorWrapper = $('
').addClass(REPEAT_END_EDITOR + WRAPPER_POSTFIX); - - $('
') - .text(messageLocalization.format('dxScheduler-recurrenceOn')) - .addClass(REPEAT_END_EDITOR + LABEL_POSTFIX) - .appendTo($editorWrapper); - - this._$repeatDateEditor = $('
') - .addClass(REPEAT_UNTIL_DATE_EDITOR) - .appendTo($editorWrapper); - - this._repeatUntilDate = this._createComponent(this._$repeatDateEditor, DateBox, { - field: 'until', - value: repeatUntil, - type: 'date', - onValueChanged: this._repeatUntilValueChangeHandler.bind(this), - calendarOptions: { - firstDayOfWeek: this._getFirstDayOfWeek() - }, - useMaskBehavior: true - }); + break; + case 'firstDayOfWeek': + if (this._weekEditor) { + const localDaysNames = dateLocalization.getDayNames('abbreviated'); + const dayNames = days.slice(args.value).concat(days.slice(0, args.value)); - return $editorWrapper; - } + const itemsButtonGroup = localDaysNames.slice(args.value).concat(localDaysNames.slice(0, args.value)).map((item, index) => ({ text: item, key: dayNames[index] })); - _repeatUntilValueChangeHandler(args) { - if(this._recurrenceRule.getRepeatEndRule() === 'until') { - const dateInTimeZone = this._formatUntilDate(new Date(args.value)); - const getStartDateTimeZone = this.option('getStartDateTimeZone'); - const appointmentTimeZone = getStartDateTimeZone(); - - const path = appointmentTimeZone ? - PathTimeZoneConversion.fromAppointmentToSource : PathTimeZoneConversion.fromGridToSource; - - const dateInLocaleTimeZone = this.option('timeZoneCalculator').createDate(dateInTimeZone, { path, appointmentTimeZone }); - - this._recurrenceRule.makeRule('until', dateInLocaleTimeZone); - this._changeEditorValue(); + this._weekEditor.option('items', itemsButtonGroup); } - } - - _valueChangedHandler(args) { - const { value, previousValue } = args; - const field = args.component.option('field'); - - if(!this.option('visible')) { - this.option('value', ''); - } else { - this._recurrenceRule.makeRule(field, value); - if(field === 'freq') { - this._makeRepeatOnRule(value); - this._changeRepeatOnVisibility(value, previousValue); - } - this._changeEditorValue(); + if (this._$repeatDateEditor) { + this._repeatUntilDate.option('calendarOptions.firstDayOfWeek', this._getFirstDayOfWeek()); } + break; + default: + // @ts-expect-error + super._optionChanged(args); } + } - _makeRepeatOnRule(value) { - if(value === 'daily' || value === 'hourly') { - this._recurrenceRule.makeRule('byday', ''); - this._recurrenceRule.makeRule('bymonth', ''); - this._recurrenceRule.makeRule('bymonthday', ''); - } - if(value === 'weekly') { - this._recurrenceRule.makeRule('byday', this._daysOfWeekByRules()); - this._recurrenceRule.makeRule('bymonth', ''); - this._recurrenceRule.makeRule('bymonthday', ''); - } + _changeRepeatOnVisibility(freq, previousFreq) { + if (freq !== previousFreq) { + this._recurrenceForm.itemOption('byday', 'visible', false); + this._recurrenceForm.itemOption('bymonthday', 'visible', false); + this._recurrenceForm.itemOption('bymonth', 'visible', false); - if(value === 'monthly') { - this._recurrenceRule.makeRule('bymonthday', this._dayOfMonthByRules()); - this._recurrenceRule.makeRule('bymonth', ''); - this._recurrenceRule.makeRule('byday', ''); - } + this._recurrenceForm.itemOption('repeatOnLabel', 'visible', freq && freq !== 'daily' && freq !== 'hourly'); - if(value === 'yearly') { - this._recurrenceRule.makeRule('bymonthday', this._dayOfMonthByRules()); - this._recurrenceRule.makeRule('bymonth', this._monthOfYearByRules()); - this._recurrenceRule.makeRule('byday', ''); - } + if (freq === 'weekly') { + this._recurrenceForm.itemOption('byday', 'visible', true); + } + if (freq === 'monthly') { + this._recurrenceForm.itemOption('bymonthday', 'visible', true); + } + if (freq === 'yearly') { + this._recurrenceForm.itemOption('bymonthday', 'visible', true); + this._recurrenceForm.itemOption('bymonth', 'visible', true); + } } + } - _optionChanged(args) { - switch(args.name) { - case 'readOnly': - this._recurrenceForm?.option('readOnly', args.value); - this._repeatCountEditor?.option('readOnly', args.value); - this._weekEditor?.option('readOnly', args.value); - this._repeatUntilDate?.option('readOnly', args.value); - super._optionChanged(args); - break; - case 'value': - this._recurrenceRule.makeRules(args.value); - - this._changeRepeatIntervalLabel(); - this._disableRepeatEndParts(); - this._changeEditorsValue(this._recurrenceRule.getRules()); - - super._optionChanged(args); - break; - case 'startDate': - this._makeRepeatOnRule(this._recurrenceRule.getRules().freq); - - if(isDefined(this._recurrenceRule.getRecurrenceString())) { - this._changeEditorValue(); - } - - break; - case 'firstDayOfWeek': - if(this._weekEditor) { - const localDaysNames = dateLocalization.getDayNames('abbreviated'); - const dayNames = days.slice(args.value).concat(days.slice(0, args.value)); - - const itemsButtonGroup = localDaysNames.slice(args.value).concat(localDaysNames.slice(0, args.value)).map((item, index) => { return { text: item, key: dayNames[index] }; }); - - this._weekEditor.option('items', itemsButtonGroup); - } - if(this._$repeatDateEditor) { - this._repeatUntilDate.option('calendarOptions.firstDayOfWeek', this._getFirstDayOfWeek()); - } - break; - default: - super._optionChanged(args); - } - } + _changeRepeatIntervalLabel() { + const { freq } = this._recurrenceRule.getRules(); - _changeRepeatOnVisibility(freq, previousFreq) { - if(freq !== previousFreq) { - this._recurrenceForm.itemOption('byday', 'visible', false); - this._recurrenceForm.itemOption('bymonthday', 'visible', false); - this._recurrenceForm.itemOption('bymonth', 'visible', false); - - this._recurrenceForm.itemOption('repeatOnLabel', 'visible', freq && freq !== 'daily' && freq !== 'hourly'); - - if(freq === 'weekly') { - this._recurrenceForm.itemOption('byday', 'visible', true); - } - if(freq === 'monthly') { - this._recurrenceForm.itemOption('bymonthday', 'visible', true); - } - if(freq === 'yearly') { - this._recurrenceForm.itemOption('bymonthday', 'visible', true); - this._recurrenceForm.itemOption('bymonth', 'visible', true); - } - } - } + freq && this._recurrenceForm.itemOption('intervalLabel', 'template', messageLocalization.format(`dxScheduler-recurrenceRepeat${freq.charAt(0).toUpperCase()}${freq.substr(1).toLowerCase()}`)); + } - _changeRepeatIntervalLabel() { - const freq = this._recurrenceRule.getRules().freq; + _changeEditorsValue(rules) { + this._recurrenceForm.getEditor('freq').option('value', (rules.freq || frequenciesMessages[defaultRecurrenceTypeIndex].value).toLowerCase()); - freq && this._recurrenceForm.itemOption('intervalLabel', 'template', messageLocalization.format(`dxScheduler-recurrenceRepeat${freq.charAt(0).toUpperCase()}${freq.substr(1).toLowerCase()}`)); - } + this._changeDayOfWeekValue(); + this._changeDayOfMonthValue(); + this._changeMonthOfYearValue(); - _changeEditorsValue(rules) { - this._recurrenceForm.getEditor('freq').option('value', (rules.freq || frequenciesMessages[defaultRecurrenceTypeIndex].value).toLowerCase()); + this._changeIntervalValue(rules.interval); - this._changeDayOfWeekValue(); - this._changeDayOfMonthValue(); - this._changeMonthOfYearValue(); + this._changeRepeatCountValue(); + this._changeRepeatEndValue(); + this._changeRepeatUntilValue(); + } - this._changeIntervalValue(rules.interval); + _changeIntervalValue(value) { + this._recurrenceForm.getEditor('interval').option('value', value || 1); + } - this._changeRepeatCountValue(); - this._changeRepeatEndValue(); - this._changeRepeatUntilValue(); - } - - _changeIntervalValue(value) { - this._recurrenceForm.getEditor('interval').option('value', value || 1); - } + _changeRepeatEndValue() { + const repeatType = this._recurrenceRule.getRepeatEndRule(); - _changeRepeatEndValue() { - const repeatType = this._recurrenceRule.getRepeatEndRule(); + this._recurrenceForm.getEditor('repeatEnd').option('value', repeatType); + } - this._recurrenceForm.getEditor('repeatEnd').option('value', repeatType); + _changeDayOfWeekValue() { + const isEditorVisible = this._recurrenceForm.itemOption('byday').visible; + if (isEditorVisible) { + const days = this._daysOfWeekByRules(); + this.getEditorByField('byday').option('selectedItemKeys', days); } + } - _changeDayOfWeekValue() { - const isEditorVisible = this._recurrenceForm.itemOption('byday').visible; - if(isEditorVisible) { - const days = this._daysOfWeekByRules(); - this.getEditorByField('byday').option('selectedItemKeys', days); - } + _changeDayOfMonthValue() { + const isEditorVisible = this._recurrenceForm.itemOption('bymonthday').visible; + if (isEditorVisible) { + const day = this._dayOfMonthByRules(); + this._recurrenceForm.getEditor('bymonthday').option('value', day); } + } - _changeDayOfMonthValue() { - const isEditorVisible = this._recurrenceForm.itemOption('bymonthday').visible; - if(isEditorVisible) { - const day = this._dayOfMonthByRules(); - this._recurrenceForm.getEditor('bymonthday').option('value', day); - } + _changeMonthOfYearValue() { + const isEditorVisible = this._recurrenceForm.itemOption('bymonth').visible; + if (isEditorVisible) { + const month = this._monthOfYearByRules(); + this._recurrenceForm.getEditor('bymonth').option('value', month); } + } - _changeMonthOfYearValue() { - const isEditorVisible = this._recurrenceForm.itemOption('bymonth').visible; - if(isEditorVisible) { - const month = this._monthOfYearByRules(); - this._recurrenceForm.getEditor('bymonth').option('value', month); - } - } + _changeRepeatCountValue() { + const count = this._recurrenceRule.getRules().count || 1; + this._repeatCountEditor.option('value', count); + } - _changeRepeatCountValue() { - const count = this._recurrenceRule.getRules().count || 1; - this._repeatCountEditor.option('value', count); - } + _changeRepeatUntilValue() { + this._repeatUntilDate.option('value', this._getUntilValue()); + } - _changeRepeatUntilValue() { - this._repeatUntilDate.option('value', this._getUntilValue()); - } + _getUntilValue() { + const untilDate = this._recurrenceRule.getRules().until; - _getUntilValue() { - const untilDate = this._recurrenceRule.getRules().until; + if (!untilDate) { + return this._formatUntilDate(new Date()); + } - if(!untilDate) { - return this._formatUntilDate(new Date()); - } + const getStartDateTimeZone: any = this.option('getStartDateTimeZone'); + const appointmentTimeZone = getStartDateTimeZone(); - const getStartDateTimeZone = this.option('getStartDateTimeZone'); - const appointmentTimeZone = getStartDateTimeZone(); + const path = appointmentTimeZone + ? PathTimeZoneConversion.fromSourceToAppointment : PathTimeZoneConversion.fromSourceToGrid; - const path = appointmentTimeZone ? - PathTimeZoneConversion.fromSourceToAppointment : PathTimeZoneConversion.fromSourceToGrid; + return (this.option('timeZoneCalculator') as any).createDate(untilDate, { path, appointmentTimeZone }); + } - return this.option('timeZoneCalculator').createDate(untilDate, { path, appointmentTimeZone }); - } + toggle() { + this._freqEditor.focus(); + } - toggle() { - this._freqEditor.focus(); - } - - setAria(...args) { - if(this._switchEditor) { - this._switchEditor.setAria(args[0], args[1]); - } + setAria(...args) { + if (this._switchEditor) { + this._switchEditor.setAria(args[0], args[1]); } + } } -registerComponent('dxRecurrenceEditor', RecurrenceEditor); +registerComponent('dxRecurrenceEditor', RecurrenceEditor as any); export default RecurrenceEditor; diff --git a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts index 7fdec2d46e1d..398974b86b49 100644 --- a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts +++ b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts @@ -1,84 +1,85 @@ -import registerComponent from '../../core/component_registrator'; -import config from '../../core/config'; -import devices from '../../core/devices'; -import $ from '../../core/renderer'; -import { BindableTemplate } from '../../core/templates/bindable_template'; -import { EmptyTemplate } from '../../core/templates/empty_template'; -import Callbacks from '../../core/utils/callbacks'; -import { noop } from '../../core/utils/common'; -import { compileGetter } from '../../core/utils/data'; -import { getBoundingRect } from '../../core/utils/position'; -import dateUtils from '../../core/utils/date'; -import dateSerialization from '../../core/utils/date_serialization'; -import { Deferred, when, fromPromise } from '../../core/utils/deferred'; -import { extend } from '../../core/utils/extend'; -import { each } from '../../core/utils/iterator'; +import registerComponent from '@js/core/component_registrator'; +import config from '@js/core/config'; +import devices from '@js/core/devices'; +import $ from '@js/core/renderer'; +import { BindableTemplate } from '@js/core/templates/bindable_template'; +import { EmptyTemplate } from '@js/core/templates/empty_template'; +import Callbacks from '@js/core/utils/callbacks'; +import { noop } from '@js/core/utils/common'; +import { compileGetter } from '@js/core/utils/data'; +import dateUtils from '@js/core/utils/date'; +import dateSerialization from '@js/core/utils/date_serialization'; +// @ts-expect-error +import { Deferred, fromPromise, when } from '@js/core/utils/deferred'; +import { extend } from '@js/core/utils/extend'; +import { each } from '@js/core/utils/iterator'; +import { getBoundingRect } from '@js/core/utils/position'; import { - isDefined, - isString, - isObject, - isFunction, - isEmptyObject, - isDeferred, - isPromise, -} from '../../core/utils/type'; -import { hasWindow } from '../../core/utils/window'; -import DataHelperMixin from '../../data_helper'; -import { triggerResizeEvent } from '../../events/visibility_change'; -import dateLocalization from '../../localization/date'; -import messageLocalization from '../../localization/message'; -import { custom as customDialog } from '../dialog'; -import { isMaterial } from '../themes'; -import errors from '../widget/ui.errors'; -import Widget from '../widget/ui.widget'; -import { AppointmentPopup, ACTION_TO_APPOINTMENT } from '../../__internal/scheduler/appointment_popup/m_popup'; -import { AppointmentForm } from '../../__internal/scheduler/appointment_popup/m_form'; -import { CompactAppointmentsHelper } from './compactAppointmentsHelper'; -import { DesktopTooltipStrategy } from '../../__internal/scheduler/tooltip_strategies/m_desktop_tooltip_strategy'; -import { MobileTooltipStrategy } from '../../__internal/scheduler/tooltip_strategies/m_mobile_tooltip_strategy'; -import { hide as hideLoading, show as showLoading } from './loading'; -import AppointmentCollection from '../../__internal/scheduler/appointments/m_appointment_collection'; -import AppointmentLayoutManager from './appointments.layout_manager'; -import { SchedulerHeader } from '../../__internal/scheduler/header/m_header'; -import subscribes from './subscribes'; -import { getRecurrenceProcessor } from './recurrence'; -import timeZoneUtils from './utils.timeZone'; -import SchedulerAgenda from './workspaces/ui.scheduler.agenda'; -import SchedulerTimelineDay from './workspaces/ui.scheduler.timeline_day'; -import SchedulerTimelineMonth from './workspaces/ui.scheduler.timeline_month'; -import SchedulerTimelineWeek from './workspaces/ui.scheduler.timeline_week'; -import SchedulerTimelineWorkWeek from './workspaces/ui.scheduler.timeline_work_week'; -import SchedulerWorkSpaceDay from './workspaces/ui.scheduler.work_space_day'; -import SchedulerWorkSpaceMonth from './workspaces/ui.scheduler.work_space_month'; -import SchedulerWorkSpaceWeek from './workspaces/ui.scheduler.work_space_week'; -import SchedulerWorkSpaceWorkWeek from './workspaces/ui.scheduler.work_space_work_week'; -import { createAppointmentAdapter } from './appointmentAdapter'; -import { AppointmentTooltipInfo } from './dataStructures'; -import { utils } from './utils'; + isDeferred, + isDefined, + isEmptyObject, + isFunction, + isObject, + isPromise, + isString, +} from '@js/core/utils/type'; +import { hasWindow } from '@js/core/utils/window'; +import DataHelperMixin from '@js/data_helper'; +import { triggerResizeEvent } from '@js/events/visibility_change'; +import dateLocalization from '@js/localization/date'; +import messageLocalization from '@js/localization/message'; +import { getAppointmentTakesAllDay } from '@js/renovation/ui/scheduler/appointment/utils/getAppointmentTakesAllDay'; +import { renovationGetCurrentView } from '@js/renovation/ui/scheduler/model/untyped_getCurrentView'; +import { createTimeZoneCalculator } from '@js/renovation/ui/scheduler/timeZoneCalculator/createTimeZoneCalculator'; +import { getPreparedDataItems } from '@js/renovation/ui/scheduler/utils/data'; +import { excludeFromRecurrence } from '@js/renovation/ui/scheduler/utils/recurrence/excludeFromRecurrence'; import { - createExpressions, - createResourceEditorModel, - getAppointmentColor, - getCellGroups, - loadResources, - setResourceToAppointment -} from '../../__internal/scheduler/resources/m_utils'; -import { ExpressionUtils } from './expressionUtils'; + isDateAndTimeView, + isTimelineView, + validateDayHours, +} from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/base'; +import { custom as customDialog } from '@js/ui/dialog'; +import { isMaterial } from '@js/ui/themes'; +import errors from '@js/ui/widget/ui.errors'; +import Widget from '@js/ui/widget/ui.widget'; + +import { AppointmentForm } from './appointment_popup/m_form'; +import { ACTION_TO_APPOINTMENT, AppointmentPopup } from './appointment_popup/m_popup'; +import { AppointmentDataProvider } from './appointments/data_provider/m_appointment_data_provider'; +import AppointmentCollection from './appointments/m_appointment_collection'; +import { renderAppointments } from './appointments/m_render'; +import { SchedulerHeader } from './header/m_header'; +import { createAppointmentAdapter } from './m_appointment_adapter'; +import AppointmentLayoutManager from './m_appointments_layout_manager'; +import { CompactAppointmentsHelper } from './m_compact_appointments_helper'; +import { AppointmentTooltipInfo } from './m_data_structures'; +import { ExpressionUtils } from './m_expression_utils'; +import { hide as hideLoading, show as showLoading } from './m_loading'; +import { getRecurrenceProcessor } from './m_recurrence'; +import subscribes from './m_subscribes'; +import { utils } from './m_utils'; +import timeZoneUtils from './m_utils_time_zone'; +import { AgendaResourceProcessor } from './resources/m_agenda_resource_processor'; import { - validateDayHours, - isDateAndTimeView, - isTimelineView -} from '../../renovation/ui/scheduler/view_model/to_test/views/utils/base'; -import { renderAppointments } from '../../__internal/scheduler/appointments/m_render'; -import { AgendaResourceProcessor } from '../../__internal/scheduler/resources/m_agenda_resource_processor'; -import { AppointmentDataProvider } from '../../__internal/scheduler/appointments/data_provider/m_appointment_data_provider'; -import { getAppointmentTakesAllDay } from '../../renovation/ui/scheduler/appointment/utils/getAppointmentTakesAllDay'; -import { getPreparedDataItems } from '../../renovation/ui/scheduler/utils/data'; -import { getCurrentView } from '../../renovation/ui/scheduler/model/views'; -import { createTimeZoneCalculator } from '../../renovation/ui/scheduler/timeZoneCalculator/createTimeZoneCalculator'; -import { excludeFromRecurrence } from '../../renovation/ui/scheduler/utils/recurrence/excludeFromRecurrence'; - -// STYLE scheduler + createExpressions, + createResourceEditorModel, + getAppointmentColor, + getCellGroups, + loadResources, + setResourceToAppointment, +} from './resources/m_utils'; +import { DesktopTooltipStrategy } from './tooltip_strategies/m_desktop_tooltip_strategy'; +import { MobileTooltipStrategy } from './tooltip_strategies/m_mobile_tooltip_strategy'; +import SchedulerAgenda from './workspaces/m_agenda'; +import SchedulerTimelineDay from './workspaces/m_timeline_day'; +import SchedulerTimelineMonth from './workspaces/m_timeline_month'; +import SchedulerTimelineWeek from './workspaces/m_timeline_week'; +import SchedulerTimelineWorkWeek from './workspaces/m_timeline_work_week'; +import SchedulerWorkSpaceDay from './workspaces/m_work_space_day'; +import SchedulerWorkSpaceMonth from './workspaces/m_work_space_month'; +import SchedulerWorkSpaceWeek from './workspaces/m_work_space_week'; +import SchedulerWorkSpaceWorkWeek from './workspaces/m_work_space_work_week'; + const MINUTES_IN_HOUR = 60; const DEFAULT_AGENDA_DURATION = 7; @@ -89,2444 +90,2513 @@ const WIDGET_READONLY_CLASS = `${WIDGET_CLASS}-readonly`; const WIDGET_SMALL_WIDTH = 400; const FULL_DATE_FORMAT = 'yyyyMMddTHHmmss'; -const UTC_FULL_DATE_FORMAT = FULL_DATE_FORMAT + 'Z'; +const UTC_FULL_DATE_FORMAT = `${FULL_DATE_FORMAT}Z`; const DEFAULT_APPOINTMENT_TEMPLATE_NAME = 'item'; const DEFAULT_APPOINTMENT_COLLECTOR_TEMPLATE_NAME = 'appointmentCollector'; const DEFAULT_DROP_DOWN_APPOINTMENT_TEMPLATE_NAME = 'dropDownAppointment'; const VIEWS_CONFIG = { - day: { - workSpace: SchedulerWorkSpaceDay, - renderingStrategy: 'vertical' - }, - week: { - workSpace: SchedulerWorkSpaceWeek, - renderingStrategy: 'vertical' - }, - workWeek: { - workSpace: SchedulerWorkSpaceWorkWeek, - renderingStrategy: 'vertical' - }, - month: { - workSpace: SchedulerWorkSpaceMonth, - renderingStrategy: 'horizontalMonth' - }, - timelineDay: { - workSpace: SchedulerTimelineDay, - renderingStrategy: 'horizontal' - }, - timelineWeek: { - workSpace: SchedulerTimelineWeek, - renderingStrategy: 'horizontal' - }, - timelineWorkWeek: { - workSpace: SchedulerTimelineWorkWeek, - renderingStrategy: 'horizontal' - }, - timelineMonth: { - workSpace: SchedulerTimelineMonth, - renderingStrategy: 'horizontalMonthLine' - }, - agenda: { - workSpace: SchedulerAgenda, - renderingStrategy: 'agenda' - } + day: { + workSpace: SchedulerWorkSpaceDay, + renderingStrategy: 'vertical', + }, + week: { + workSpace: SchedulerWorkSpaceWeek, + renderingStrategy: 'vertical', + }, + workWeek: { + workSpace: SchedulerWorkSpaceWorkWeek, + renderingStrategy: 'vertical', + }, + month: { + workSpace: SchedulerWorkSpaceMonth, + renderingStrategy: 'horizontalMonth', + }, + timelineDay: { + workSpace: SchedulerTimelineDay, + renderingStrategy: 'horizontal', + }, + timelineWeek: { + workSpace: SchedulerTimelineWeek, + renderingStrategy: 'horizontal', + }, + timelineWorkWeek: { + workSpace: SchedulerTimelineWorkWeek, + renderingStrategy: 'horizontal', + }, + timelineMonth: { + workSpace: SchedulerTimelineMonth, + renderingStrategy: 'horizontalMonthLine', + }, + agenda: { + workSpace: SchedulerAgenda, + renderingStrategy: 'agenda', + }, }; const StoreEventNames = { - ADDING: 'onAppointmentAdding', - ADDED: 'onAppointmentAdded', + ADDING: 'onAppointmentAdding', + ADDED: 'onAppointmentAdded', - DELETING: 'onAppointmentDeleting', - DELETED: 'onAppointmentDeleted', + DELETING: 'onAppointmentDeleting', + DELETED: 'onAppointmentDeleted', - UPDATING: 'onAppointmentUpdating', - UPDATED: 'onAppointmentUpdated' + UPDATING: 'onAppointmentUpdating', + UPDATED: 'onAppointmentUpdated', }; const RECURRENCE_EDITING_MODE = { - SERIES: 'editSeries', - OCCURENCE: 'editOccurence', - CANCEL: 'cancel', + SERIES: 'editSeries', + OCCURENCE: 'editOccurence', + CANCEL: 'cancel', }; -class Scheduler extends Widget { - _getDefaultOptions() { - const defaultOptions = extend(super._getDefaultOptions(), { +class Scheduler extends Widget { + _filteredItems!: any[]; - views: ['day', 'week'], - currentView: 'day', // TODO: should we calculate currentView if views array contains only one item, for example 'month'? - currentDate: dateUtils.trimTime(new Date()), - min: undefined, - max: undefined, - dateSerializationFormat: undefined, - firstDayOfWeek: undefined, + _preparedItems!: any[]; - groups: [], + _timeZoneCalculator!: any; - resources: [], - loadedResources: [], - resourceLoaderMap: new Map(), + postponedOperations: any; - dataSource: null, + _workSpace: any; - customizeDateNavigatorText: undefined, + _header: any; - appointmentTemplate: DEFAULT_APPOINTMENT_TEMPLATE_NAME, + _appointments: any; - dropDownAppointmentTemplate: DEFAULT_DROP_DOWN_APPOINTMENT_TEMPLATE_NAME, + appointmentDataProvider: any; - appointmentCollectorTemplate: DEFAULT_APPOINTMENT_COLLECTOR_TEMPLATE_NAME, + _dataSource: any; - dataCellTemplate: null, + _dataAccessors: any; - timeCellTemplate: null, + agendaResourceProcessor: any; - resourceCellTemplate: null, + _actions: any; - dateCellTemplate: null, + _createActionByOption: any; - startDayHour: 0, + _appointmentTooltip: any; - endDayHour: 24, + _readyToRenderAppointments: any; - editing: { - allowAdding: true, - allowDeleting: true, - allowDragging: true, - allowResizing: true, - allowUpdating: true, - allowTimeZoneEditing: false - }, + _editing: any; - showAllDayPanel: true, + _workSpaceRecalculation: any; - showCurrentTimeIndicator: true, + _appointmentPopup: any; - shadeUntilCurrentTime: false, + _compactAppointmentsHelper: any; - indicatorUpdateInterval: 300000, + _asyncTemplatesTimers!: any[]; - /** - * @hidden - * @name dxSchedulerOptions.indicatorTime - * @type Date - * @default undefined - */ - indicatorTime: undefined, + _dataSourceLoadedCallback: any; - recurrenceEditMode: 'dialog', + _subscribes: any; - cellDuration: 30, + _recurrenceDialog: any; - maxAppointmentsPerCell: 'auto', + _layoutManager: any; - selectedCellData: [], + _appointmentForm: any; - groupByDate: false, + _mainContainer: any; - onAppointmentRendered: null, + _all: any; - onAppointmentClick: null, + _options: any; - onAppointmentDblClick: null, + _editAppointmentData: any; - onAppointmentContextMenu: null, + _getDefaultOptions() { + // @ts-expect-error + const defaultOptions = extend(super._getDefaultOptions(), { - onCellClick: null, + views: ['day', 'week'], + currentView: 'day', // TODO: should we calculate currentView if views array contains only one item, for example 'month'? + currentDate: dateUtils.trimTime(new Date()), + min: undefined, + max: undefined, + dateSerializationFormat: undefined, + firstDayOfWeek: undefined, - onCellContextMenu: null, + groups: [], - onAppointmentAdding: null, + resources: [], + loadedResources: [], + resourceLoaderMap: new Map(), - onAppointmentAdded: null, + dataSource: null, - onAppointmentUpdating: null, + customizeDateNavigatorText: undefined, - onAppointmentUpdated: null, + appointmentTemplate: DEFAULT_APPOINTMENT_TEMPLATE_NAME, - onAppointmentDeleting: null, + dropDownAppointmentTemplate: DEFAULT_DROP_DOWN_APPOINTMENT_TEMPLATE_NAME, - onAppointmentDeleted: null, + appointmentCollectorTemplate: DEFAULT_APPOINTMENT_COLLECTOR_TEMPLATE_NAME, - onAppointmentFormOpening: null, + dataCellTemplate: null, - onAppointmentTooltipShowing: null, + timeCellTemplate: null, - appointmentTooltipTemplate: 'appointmentTooltip', + resourceCellTemplate: null, - /** - * @hidden - * @name dxSchedulerOptions.appointmentPopupTemplate - * @type template|function - * @default "appointmentPopup" - * @type_function_param1 appointmentData:object - * @type_function_param2 contentElement:DxElement - * @type_function_return string|Element|jQuery - */ - appointmentPopupTemplate: 'appointmentPopup', + dateCellTemplate: null, - crossScrollingEnabled: false, + startDayHour: 0, - useDropDownViewSwitcher: false, + endDayHour: 24, - startDateExpr: 'startDate', + editing: { + allowAdding: true, + allowDeleting: true, + allowDragging: true, + allowResizing: true, + allowUpdating: true, + allowTimeZoneEditing: false, + }, - endDateExpr: 'endDate', + showAllDayPanel: true, - textExpr: 'text', + showCurrentTimeIndicator: true, - descriptionExpr: 'description', + shadeUntilCurrentTime: false, - allDayExpr: 'allDay', + indicatorUpdateInterval: 300000, - recurrenceRuleExpr: 'recurrenceRule', + indicatorTime: undefined, - recurrenceExceptionExpr: 'recurrenceException', + recurrenceEditMode: 'dialog', - disabledExpr: 'disabled', + cellDuration: 30, - remoteFiltering: false, + maxAppointmentsPerCell: 'auto', - timeZone: '', + selectedCellData: [], - startDateTimeZoneExpr: 'startDateTimeZone', + groupByDate: false, - endDateTimeZoneExpr: 'endDateTimeZone', + onAppointmentRendered: null, - noDataText: messageLocalization.format('dxCollectionWidget-noDataText'), + onAppointmentClick: null, - adaptivityEnabled: false, + onAppointmentDblClick: null, - allowMultipleCellSelection: true, + onAppointmentContextMenu: null, - scrolling: { - mode: 'standard' - }, + onCellClick: null, - allDayPanelMode: 'all', + onCellContextMenu: null, - renovateRender: true, + onAppointmentAdding: null, - _draggingMode: 'outlook', + onAppointmentAdded: null, - _appointmentTooltipOffset: { x: 0, y: 0 }, - _appointmentTooltipButtonsPosition: 'bottom', - _appointmentTooltipOpenButtonText: messageLocalization.format('dxScheduler-openAppointment'), - _appointmentCountPerCell: 2, - _collectorOffset: 0, - _appointmentOffset: 26, + onAppointmentUpdating: null, - toolbar: [ - { - location: 'before', - defaultElement: 'dateNavigator', - }, - { - location: 'after', - defaultElement: 'viewSwitcher', - } - ] + onAppointmentUpdated: null, - /** - * @name dxSchedulerOptions.activeStateEnabled - * @hidden - */ + onAppointmentDeleting: null, - /** - * @name dxSchedulerOptions.hoverStateEnabled - * @hidden - */ - }); + onAppointmentDeleted: null, - return extend(true, defaultOptions, { - integrationOptions: { - useDeferUpdateForTemplates: false - } - }); - } + onAppointmentFormOpening: null, - get filteredItems() { - if(!this._filteredItems) { - this._filteredItems = []; - } - return this._filteredItems; - } + onAppointmentTooltipShowing: null, - set filteredItems(value) { - this._filteredItems = value; - } + appointmentTooltipTemplate: 'appointmentTooltip', - get preparedItems() { - if(!this._preparedItems) { - this._preparedItems = []; - } - return this._preparedItems; - } - - set preparedItems(value) { - this._preparedItems = value; - } - - get currentView() { - return getCurrentView( - this.option('currentView'), - this.option('views') - ); - } + appointmentPopupTemplate: 'appointmentPopup', - get currentViewType() { - return isObject(this.currentView) - ? this.currentView.type - : this.currentView; - } + crossScrollingEnabled: false, - get timeZoneCalculator() { - if(!this._timeZoneCalculator) { - this._timeZoneCalculator = createTimeZoneCalculator(this.option('timeZone')); - } + useDropDownViewSwitcher: false, - return this._timeZoneCalculator; - } + startDateExpr: 'startDate', - _setDeprecatedOptions() { - super._setDeprecatedOptions(); + endDateExpr: 'endDate', - extend(this._deprecatedOptions, { - dropDownAppointmentTemplate: { since: '19.2', message: 'appointmentTooltipTemplate' }, - }); - } + textExpr: 'text', - _defaultOptionsRules() { - return super._defaultOptionsRules().concat([ - { - device: function() { - return devices.real().deviceType === 'desktop' && !devices.isSimulator(); - }, - options: { - focusStateEnabled: true - } - }, - { - device: function() { - return !devices.current().generic; - }, - options: { - useDropDownViewSwitcher: true, - - - editing: { - allowDragging: false, - allowResizing: false - } - } - }, - { - device: function() { - return isMaterial(); - }, - options: { - useDropDownViewSwitcher: true, - dateCellTemplate: function(data, index, element) { - const text = data.text; - - text.split(' ').forEach(function(text, index) { - const span = $('') - .text(text) - .addClass('dx-scheduler-header-panel-cell-date'); - - $(element).append(span); - if(!index) $(element).append(' '); - }); - }, - - _appointmentTooltipOffset: { x: 0, y: 11 }, - _appointmentTooltipButtonsPosition: 'top', - _appointmentTooltipOpenButtonText: null, - _appointmentCountPerCell: 1, - _collectorOffset: 20, - _appointmentOffset: 30 - } - } - ]); - } + descriptionExpr: 'description', - _postponeDataSourceLoading(promise) { - this.postponedOperations.add('_reloadDataSource', this._reloadDataSource.bind(this), promise); - } + allDayExpr: 'allDay', - _postponeResourceLoading() { - const whenLoaded = this.postponedOperations.add('loadResources', () => { - const groups = this._getCurrentViewOption('groups'); + recurrenceRuleExpr: 'recurrenceRule', - return loadResources(groups, this.option('resources'), this.option('resourceLoaderMap')); - }); + recurrenceExceptionExpr: 'recurrenceException', - const resolveCallbacks = new Deferred(); + disabledExpr: 'disabled', - whenLoaded.done((resources) => { - this.option('loadedResources', resources); - resolveCallbacks.resolve(resources); - }); + remoteFiltering: false, - this._postponeDataSourceLoading(whenLoaded); + timeZone: '', - return resolveCallbacks.promise(); - } + startDateTimeZoneExpr: 'startDateTimeZone', - _optionChanged(args) { - let value = args.value; - const name = args.name; - - switch(args.name) { - case 'customizeDateNavigatorText': - this._updateOption('header', name, value); - break; - case 'firstDayOfWeek': - this._updateOption('workSpace', name, value); - this._updateOption('header', name, value); - break; - case 'currentDate': - value = this._dateOption(name); - value = dateUtils.trimTime(new Date(value)); - this.option('selectedCellData', []); - this._workSpace.option(name, new Date(value)); - this._header?.option(name, new Date(value)); - this._header?.option('startViewDate', this.getStartViewDate()); - this._appointments.option('items', []); - this._filterAppointmentsByDate(); - - this._postponeDataSourceLoading(); - break; - case 'dataSource': - this._initDataSource(); - - this.appointmentDataProvider.setDataSource(this._dataSource); - - this._postponeResourceLoading().done((resources) => { - this._filterAppointmentsByDate(); - this._updateOption('workSpace', 'showAllDayPanel', this.option('showAllDayPanel')); - }); - break; - case 'min': - case 'max': - value = this._dateOption(name); - this._updateOption('header', name, new Date(value)); - this._updateOption('workSpace', name, new Date(value)); - break; - case 'views': - if(this._getCurrentViewOptions()) { - this.repaint(); - } else { - this._header?.option(name, value); - } - break; - case 'useDropDownViewSwitcher': - this._header?.option(name, value); - break; - case 'currentView': - this._validateDayHours(); - - this._validateCellDuration(); - - this._appointments.option({ - items: [], - allowDrag: this._allowDragging(), - allowResize: this._allowResizing(), - itemTemplate: this._getAppointmentTemplate('appointmentTemplate') - }); - - this._postponeResourceLoading().done((resources) => { - this._refreshWorkSpace(resources); - this._header?.option(this._headerConfig()); - this._filterAppointmentsByDate(); - this._appointments.option('allowAllDayResize', value !== 'day'); - }); - // NOTE: - // Calling postponed operations (promises) here, because when we update options with - // usage of the beginUpdate / endUpdate methods, other option changes - // may try to access not initialized values inside the scheduler component. - this.postponedOperations.callPostponedOperations(); - break; - case 'appointmentTemplate': - this._appointments.option('itemTemplate', value); - break; - case 'dateCellTemplate': - case 'resourceCellTemplate': - case 'dataCellTemplate': - case 'timeCellTemplate': - this.repaint(); - break; - case 'groups': - this._postponeResourceLoading().done((resources) => { - this._refreshWorkSpace(resources); - this._filterAppointmentsByDate(); - }); - break; - case 'resources': - this._dataAccessors.resources = createExpressions(this.option('resources')); - this.agendaResourceProcessor.initializeState(this.option('resources')); - this.updateInstances(); - - this._postponeResourceLoading().done((resources) => { - this._appointments.option('items', []); - this._refreshWorkSpace(resources); - this._filterAppointmentsByDate(); - this._createAppointmentPopupForm(); - }); - break; - case 'startDayHour': - case 'endDayHour': - this._validateDayHours(); - - this.updateInstances(); - - this._appointments.option('items', []); - this._updateOption('workSpace', name, value); - this._appointments.repaint(); - this._filterAppointmentsByDate(); - - this._postponeDataSourceLoading(); - break; - case StoreEventNames.ADDING: - case StoreEventNames.ADDED: - case StoreEventNames.UPDATING: - case StoreEventNames.UPDATED: - case StoreEventNames.DELETING: - case StoreEventNames.DELETED: - case 'onAppointmentFormOpening': - case 'onAppointmentTooltipShowing': - this._actions[name] = this._createActionByOption(name); - break; - case 'onAppointmentRendered': - this._appointments.option('onItemRendered', this._getAppointmentRenderedAction()); - break; - case 'onAppointmentClick': - this._appointments.option('onItemClick', this._createActionByOption(name)); - break; - case 'onAppointmentDblClick': - this._appointments.option(name, this._createActionByOption(name)); - break; - case 'onAppointmentContextMenu': - this._appointments.option('onItemContextMenu', this._createActionByOption(name)); - this._appointmentTooltip._options.onItemContextMenu = this._createActionByOption(name); - break; - case 'noDataText': - case 'allowMultipleCellSelection': - case 'selectedCellData': - case 'accessKey': - case 'onCellClick': - this._workSpace.option(name, value); - break; - case 'onCellContextMenu': - this._workSpace.option(name, value); - break; - case 'crossScrollingEnabled': - this._postponeResourceLoading().done((resources) => { - this._appointments.option('items', []); - this._refreshWorkSpace(resources); - if(this._readyToRenderAppointments) { - this._appointments.option('items', this._getAppointmentsToRepaint()); - } - }); - break; - case 'cellDuration': - this._validateCellDuration(); - this._updateOption('workSpace', name, value); - this._appointments.option('items', []); - if(this._readyToRenderAppointments) { - this._updateOption('workSpace', 'hoursInterval', value / 60); - this._appointments.option('items', this._getAppointmentsToRepaint()); - } - break; - case 'tabIndex': - case 'focusStateEnabled': - this._updateOption('header', name, value); - this._updateOption('workSpace', name, value); - this._appointments.option(name, value); - super._optionChanged(args); - break; - case 'width': - // TODO: replace with css - this._updateOption('header', name, value); - if(this.option('crossScrollingEnabled')) { - this._updateOption('workSpace', 'width', value); - } - this._updateOption('workSpace', 'schedulerWidth', value); - super._optionChanged(args); - this._dimensionChanged(null, true); - break; - case 'height': - super._optionChanged(args); - this._dimensionChanged(null, true); - this._updateOption('workSpace', 'schedulerHeight', value); - break; - case 'editing': { - this._initEditing(); - const editing = this._editing; - - this._bringEditingModeToAppointments(editing); - - this.hideAppointmentTooltip(); - this._cleanPopup(); - break; - } - case 'showAllDayPanel': - this.updateInstances(); - this.repaint(); - break; - case 'showCurrentTimeIndicator': - case 'indicatorTime': - case 'indicatorUpdateInterval': - case 'shadeUntilCurrentTime': - case 'groupByDate': - this._updateOption('workSpace', name, value); - this.repaint(); - break; - case 'appointmentDragging': - case 'appointmentTooltipTemplate': - case 'appointmentPopupTemplate': - case 'recurrenceEditMode': - case 'remoteFiltering': - case 'timeZone': - this.updateInstances(); - this.repaint(); - break; - case 'dropDownAppointmentTemplate': - case 'appointmentCollectorTemplate': - case '_appointmentTooltipOffset': - case '_appointmentTooltipButtonsPosition': - case '_appointmentTooltipOpenButtonText': - case '_appointmentCountPerCell': - case '_collectorOffset': - case '_appointmentOffset': - this.repaint(); - break; - case 'dateSerializationFormat': - break; - case 'maxAppointmentsPerCell': - break; - case 'startDateExpr': - case 'endDateExpr': - case 'startDateTimeZoneExpr': - case 'endDateTimeZoneExpr': - case 'textExpr': - case 'descriptionExpr': - case 'allDayExpr': - case 'recurrenceRuleExpr': - case 'recurrenceExceptionExpr': - case 'disabledExpr': - this._updateExpression(name, value); - this.appointmentDataProvider.updateDataAccessors(this._dataAccessors); - - this._initAppointmentTemplate(); - this.repaint(); - break; - case 'adaptivityEnabled': - this._toggleAdaptiveClass(); - this.repaint(); - break; - case 'scrolling': - this.option('crossScrollingEnabled', this._isHorizontalVirtualScrolling() || this.option('crossScrollingEnabled')); - - this._updateOption('workSpace', args.fullName, value); - break; - case 'allDayPanelMode': - this._updateOption('workSpace', args.fullName, value); - break; - case 'renovateRender': - this._updateOption('workSpace', name, value); - break; - case '_draggingMode': - this._workSpace.option('draggingMode', value); - break; - case 'toolbar': - this._header - ? this._header.option('items', value) - : this.repaint(); - break; - case 'loadedResources': - case 'resourceLoaderMap': - break; - default: - super._optionChanged(args); - } - } + endDateTimeZoneExpr: 'endDateTimeZone', - _dateOption(optionName) { - const optionValue = this._getCurrentViewOption(optionName); + noDataText: messageLocalization.format('dxCollectionWidget-noDataText'), - return dateSerialization.deserializeDate(optionValue); - } + adaptivityEnabled: false, - _getSerializationFormat(optionName) { - const value = this._getCurrentViewOption(optionName); + allowMultipleCellSelection: true, - if(typeof value === 'number') { - return 'number'; - } + scrolling: { + mode: 'standard', + }, - if(!isString(value)) { - return; - } - - return dateSerialization.getDateSerializationFormat(value); - } - - _bringEditingModeToAppointments(editing) { - const editingConfig = { - allowDelete: editing.allowUpdating && editing.allowDeleting - }; - - if(!this._isAgenda()) { - editingConfig.allowDrag = editing.allowDragging; - editingConfig.allowResize = editing.allowResizing; - editingConfig.allowAllDayResize = editing.allowResizing && this._supportAllDayResizing(); - } - - this._appointments.option(editingConfig); - this.repaint(); - } - - _isAgenda() { - return this.getLayoutManager().appointmentRenderingStrategyName === 'agenda'; - } - - _allowDragging() { - return this._editing.allowDragging && !this._isAgenda(); - } - - _allowResizing() { - return this._editing.allowResizing && !this._isAgenda(); - } - - _allowAllDayResizing() { - return this._editing.allowResizing && this._supportAllDayResizing(); - } - - _supportAllDayResizing() { - return this.currentViewType !== 'day' || this.currentView.intervalCount > 1; - } - - _isAllDayExpanded() { - return this.option('showAllDayPanel') && this.appointmentDataProvider.hasAllDayAppointments( - this.filteredItems, - this.preparedItems - ); - } - - _getTimezoneOffsetByOption(date) { - return timeZoneUtils.calculateTimezoneByValue(this.option('timeZone'), date); - } - - _filterAppointmentsByDate() { - const dateRange = this._workSpace.getDateRange(); - - const startDate = this.timeZoneCalculator.createDate(dateRange[0], { path: 'fromGrid' }); - const endDate = this.timeZoneCalculator.createDate(dateRange[1], { path: 'fromGrid' }); - - this.appointmentDataProvider.filterByDate( - startDate, - endDate, - this.option('remoteFiltering'), - this.option('dateSerializationFormat') - ); - } + allDayPanelMode: 'all', - _reloadDataSource() { - const result = new Deferred(); + renovateRender: true, - if(this._dataSource) { + _draggingMode: 'outlook', - this._dataSource.load().done((function() { - hideLoading(); + _appointmentTooltipOffset: { x: 0, y: 0 }, + _appointmentTooltipButtonsPosition: 'bottom', + _appointmentTooltipOpenButtonText: messageLocalization.format('dxScheduler-openAppointment'), + _appointmentCountPerCell: 2, + _collectorOffset: 0, + _appointmentOffset: 26, - this._fireContentReadyAction(result); - }).bind(this)).fail(function() { - hideLoading(); - result.reject(); - }); + toolbar: [ + { + location: 'before', + defaultElement: 'dateNavigator', + }, + { + location: 'after', + defaultElement: 'viewSwitcher', + }, + ], + }); + + return extend(true, defaultOptions, { + integrationOptions: { + useDeferUpdateForTemplates: false, + }, + }); + } + + get filteredItems() { + if (!this._filteredItems) { + this._filteredItems = []; + } + return this._filteredItems; + } + + set filteredItems(value) { + this._filteredItems = value; + } + + get preparedItems() { + if (!this._preparedItems) { + this._preparedItems = []; + } + return this._preparedItems; + } + + set preparedItems(value) { + this._preparedItems = value; + } + + get currentView() { + return renovationGetCurrentView( + this.option('currentView'), + this.option('views'), + ); + } + + get currentViewType() { + return isObject(this.currentView) + ? (this.currentView as any).type + : this.currentView; + } + + get timeZoneCalculator() { + if (!this._timeZoneCalculator) { + this._timeZoneCalculator = createTimeZoneCalculator(this.option('timeZone')); + } + + return this._timeZoneCalculator; + } + + _setDeprecatedOptions() { + // @ts-expect-error + super._setDeprecatedOptions(); + + // @ts-expect-error + extend(this._deprecatedOptions, { + dropDownAppointmentTemplate: { since: '19.2', message: 'appointmentTooltipTemplate' }, + }); + } + + _defaultOptionsRules() { + // @ts-expect-error + return super._defaultOptionsRules().concat([ + { + device() { + return devices.real().deviceType === 'desktop' && !devices.isSimulator(); + }, + options: { + focusStateEnabled: true, + }, + }, + { + device() { + return !devices.current().generic; + }, + options: { + useDropDownViewSwitcher: true, - this._dataSource.isLoading() && showLoading({ - container: this.$element(), - position: { - of: this.$element() - } + editing: { + allowDragging: false, + allowResizing: false, + }, + }, + }, + { + device() { + return (isMaterial as any)(); + }, + options: { + useDropDownViewSwitcher: true, + dateCellTemplate(data, index, element) { + const { text } = data; + + text.split(' ').forEach((text, index) => { + const span = $('') + .text(text) + .addClass('dx-scheduler-header-panel-cell-date'); + + $(element).append(span); + if (!index) $(element).append(' ' as any); }); - } else { - this._fireContentReadyAction(result); - } + }, + + _appointmentTooltipOffset: { x: 0, y: 11 }, + _appointmentTooltipButtonsPosition: 'top', + _appointmentTooltipOpenButtonText: null, + _appointmentCountPerCell: 1, + _collectorOffset: 20, + _appointmentOffset: 30, + }, + }, + ]); + } + + _postponeDataSourceLoading(promise?: any) { + this.postponedOperations.add('_reloadDataSource', this._reloadDataSource.bind(this), promise); + } + + _postponeResourceLoading() { + const whenLoaded = this.postponedOperations.add('loadResources', () => { + const groups = this._getCurrentViewOption('groups'); + + return loadResources(groups, this.option('resources'), this.option('resourceLoaderMap')); + }); + + // @ts-expect-error + const resolveCallbacks = new Deferred(); + + whenLoaded.done((resources) => { + this.option('loadedResources', resources); + resolveCallbacks.resolve(resources); + }); + + this._postponeDataSourceLoading(whenLoaded); + + return resolveCallbacks.promise(); + } + + _optionChanged(args) { + let { value } = args; + const { name } = args; + + switch (args.name) { + case 'customizeDateNavigatorText': + this._updateOption('header', name, value); + break; + case 'firstDayOfWeek': + this._updateOption('workSpace', name, value); + this._updateOption('header', name, value); + break; + case 'currentDate': + value = this._dateOption(name); + value = dateUtils.trimTime(new Date(value)); + this.option('selectedCellData', []); + this._workSpace.option(name, new Date(value)); + this._header?.option(name, new Date(value)); + this._header?.option('startViewDate', this.getStartViewDate()); + this._appointments.option('items', []); + this._filterAppointmentsByDate(); - return result.promise(); - } + this._postponeDataSourceLoading(); + break; + case 'dataSource': + // @ts-expect-error + this._initDataSource(); - _fireContentReadyAction(result) { - const contentReadyBase = super._fireContentReadyAction.bind(this); - const fireContentReady = () => { - contentReadyBase(); - result?.resolve(); - }; + this.appointmentDataProvider.setDataSource(this._dataSource); - if(this._workSpaceRecalculation) { - this._workSpaceRecalculation?.done(() => { - fireContentReady(); - }); + this._postponeResourceLoading().done(() => { + this._filterAppointmentsByDate(); + this._updateOption('workSpace', 'showAllDayPanel', this.option('showAllDayPanel')); + }); + break; + case 'min': + case 'max': + value = this._dateOption(name); + this._updateOption('header', name, new Date(value)); + this._updateOption('workSpace', name, new Date(value)); + break; + case 'views': + if (this._getCurrentViewOptions()) { + this.repaint(); } else { - fireContentReady(); - } - } - - _dimensionChanged(value, isForce = false) { - const isFixedHeight = typeof this.option('height') === 'number'; - const isFixedWidth = typeof this.option('width') === 'number'; - - if(!this._isVisible()) { - return; + this._header?.option(name, value); } + break; + case 'useDropDownViewSwitcher': + this._header?.option(name, value); + break; + case 'currentView': + this._validateDayHours(); - this._toggleSmallClass(); - - const workspace = this.getWorkSpace(); - - if(!this._isAgenda() && this.filteredItems && workspace) { - if(isForce || (!isFixedHeight || !isFixedWidth)) { - workspace.option('allDayExpanded', this._isAllDayExpanded()); - workspace._dimensionChanged(); - const appointments = this.getLayoutManager().createAppointmentsMap(this.filteredItems); - - this._appointments.option('items', appointments); - } - } - - this.hideAppointmentTooltip(); - - // TODO popup - this._appointmentPopup.triggerResize(); - this._appointmentPopup.updatePopupFullScreenMode(); - } - - _clean() { - this._cleanPopup(); - super._clean(); - } - - _toggleSmallClass() { - const width = getBoundingRect(this.$element().get(0)).width; - this.$element().toggleClass(WIDGET_SMALL_CLASS, width < WIDGET_SMALL_WIDTH); - } - - _toggleAdaptiveClass() { - this.$element().toggleClass(WIDGET_ADAPTIVE_CLASS, this.option('adaptivityEnabled')); - } - - _visibilityChanged(visible) { - visible && this._dimensionChanged(null, true); - } - - _dataSourceOptions() { - return { paginate: false }; - } - - _initAllDayPanel() { - if(this.option('allDayPanelMode') === 'hidden') { - this.option('showAllDayPanel', false); - } - } - - _init() { - this._initExpressions({ - startDate: this.option('startDateExpr'), - endDate: this.option('endDateExpr'), - startDateTimeZone: this.option('startDateTimeZoneExpr'), - endDateTimeZone: this.option('endDateTimeZoneExpr'), - allDay: this.option('allDayExpr'), - text: this.option('textExpr'), - description: this.option('descriptionExpr'), - recurrenceRule: this.option('recurrenceRuleExpr'), - recurrenceException: this.option('recurrenceExceptionExpr'), - disabled: this.option('disabledExpr') - }); - - super._init(); - - this._initAllDayPanel(); - - this._initDataSource(); - - this._customizeDataSourceLoadOptions(); - - this.$element().addClass(WIDGET_CLASS); - - this._initEditing(); - - this.updateInstances(); - - this._initActions(); - - this._compactAppointmentsHelper = new CompactAppointmentsHelper(this); - - this._asyncTemplatesTimers = []; - - this._dataSourceLoadedCallback = Callbacks(); - - this._subscribes = subscribes; - - this.agendaResourceProcessor = new AgendaResourceProcessor(this.option('resources')); - } + this._validateCellDuration(); - createAppointmentDataProvider() { - this.appointmentDataProvider?.destroy(); - this.appointmentDataProvider = new AppointmentDataProvider({ - dataSource: this._dataSource, - dataAccessors: this._dataAccessors, - timeZoneCalculator: this.timeZoneCalculator, - dateSerializationFormat: this.option('dateSerializationFormat'), - resources: this.option('resources'), - startDayHour: this._getCurrentViewOption('startDayHour'), - endDayHour: this._getCurrentViewOption('endDayHour'), - appointmentDuration: this._getCurrentViewOption('cellDuration'), - allDayPanelMode: this._getCurrentViewOption('allDayPanelMode'), - showAllDayPanel: this.option('showAllDayPanel'), - getLoadedResources: () => this.option('loadedResources'), - getIsVirtualScrolling: () => this.isVirtualScrolling(), - getSupportAllDayRow: () => this._workSpace.supportAllDayRow(), - getViewType: () => this._workSpace.type, - getViewDirection: () => this._workSpace.viewDirection, - getDateRange: () => this._workSpace.getDateRange(), - getGroupCount: () => this._workSpace._getGroupCount(), - getViewDataProvider: () => this._workSpace.viewDataProvider, + this._appointments.option({ + items: [], + allowDrag: this._allowDragging(), + allowResize: this._allowResizing(), + itemTemplate: this._getAppointmentTemplate('appointmentTemplate'), }); - } - updateInstances() { - this._timeZoneCalculator = null; - - if(this.getWorkSpace()) { - this.createAppointmentDataProvider(); - } - } - - _customizeDataSourceLoadOptions() { - this._dataSource?.on('customizeStoreLoadOptions', ({ storeLoadOptions }) => { - storeLoadOptions.startDate = this.getStartViewDate(); - storeLoadOptions.endDate = this.getEndViewDate(); + this._postponeResourceLoading().done((resources) => { + this._refreshWorkSpace(resources); + this._header?.option(this._headerConfig()); + this._filterAppointmentsByDate(); + this._appointments.option('allowAllDayResize', value !== 'day'); }); - } - - _initTemplates() { - this._initAppointmentTemplate(); - - this._templateManager.addDefaultTemplates({ - appointmentTooltip: new EmptyTemplate(), - dropDownAppointment: new EmptyTemplate(), + // NOTE: + // Calling postponed operations (promises) here, because when we update options with + // usage of the beginUpdate / endUpdate methods, other option changes + // may try to access not initialized values inside the scheduler component. + this.postponedOperations.callPostponedOperations(); + break; + case 'appointmentTemplate': + this._appointments.option('itemTemplate', value); + break; + case 'dateCellTemplate': + case 'resourceCellTemplate': + case 'dataCellTemplate': + case 'timeCellTemplate': + this.repaint(); + break; + case 'groups': + this._postponeResourceLoading().done((resources) => { + this._refreshWorkSpace(resources); + this._filterAppointmentsByDate(); }); - super._initTemplates(); - } - - _initAppointmentTemplate() { - const { expr } = this._dataAccessors; - const createGetter = (property) => compileGetter(`appointmentData.${property}`); - - const getDate = getter => { - return (data) => { - const value = getter(data); - if(value instanceof Date) { - return value.valueOf(); - } - return value; - }; - }; + break; + case 'resources': + this._dataAccessors.resources = createExpressions(this.option('resources')); + this.agendaResourceProcessor.initializeState(this.option('resources')); + this.updateInstances(); - this._templateManager.addDefaultTemplates({ - ['item']: new BindableTemplate(($container, data, model) => this.getAppointmentsInstance()._renderAppointmentTemplate($container, data, model) - , [ - 'html', - 'text', - 'startDate', - 'endDate', - 'allDay', - 'description', - 'recurrenceRule', - 'recurrenceException', - 'startDateTimeZone', - 'endDateTimeZone' - ], this.option('integrationOptions.watchMethod'), { - 'text': createGetter(expr.textExpr), - 'startDate': getDate(createGetter(expr.startDateExpr)), - 'endDate': getDate(createGetter(expr.endDateExpr)), - 'startDateTimeZone': createGetter(expr.startDateTimeZoneExpr), - 'endDateTimeZone': createGetter(expr.endDateTimeZoneExpr), - 'allDay': createGetter(expr.allDayExpr), - 'recurrenceRule': createGetter(expr.recurrenceRuleExpr) - }) + this._postponeResourceLoading().done((resources) => { + this._appointments.option('items', []); + this._refreshWorkSpace(resources); + this._filterAppointmentsByDate(); + this._createAppointmentPopupForm(); }); - } - - _renderContent() { - this._renderContentImpl(); - } - - _updatePreparedItems(items) { - this.preparedItems = getPreparedDataItems( - items, - this._dataAccessors, - this._getCurrentViewOption('cellDuration'), - this.timeZoneCalculator - ); - } - - _dataSourceChangedHandler(result) { - if(this._readyToRenderAppointments) { - this._workSpaceRecalculation.done((function() { - this._updatePreparedItems(result); - this._renderAppointments(); - this.getWorkSpace().onDataSourceChanged(this.filteredItems); - }).bind(this)); - } - } - - isVirtualScrolling() { - const workspace = this.getWorkSpace(); - - if(workspace) { - return workspace.isVirtualScrolling(); - } - - const currentViewOptions = this._getCurrentViewOptions(); - const scrolling = this.option('scrolling'); - - return scrolling?.mode === 'virtual' || - currentViewOptions?.scrolling?.mode === 'virtual'; - } - - _filterAppointments() { - this.filteredItems = this.appointmentDataProvider.filter(this.preparedItems); - } - - _renderAppointments() { - const workspace = this.getWorkSpace(); - this._filterAppointments(); + break; + case 'startDayHour': + case 'endDayHour': + this._validateDayHours(); - workspace.option('allDayExpanded', this._isAllDayExpanded()); + this.updateInstances(); - let viewModel = []; - if(this._isVisible()) { - viewModel = this._getAppointmentsToRepaint(); - } + this._appointments.option('items', []); + this._updateOption('workSpace', name, value); + this._appointments.repaint(); + this._filterAppointmentsByDate(); - if(this.option('isRenovatedAppointments')) { - renderAppointments({ - instance: this, - $dateTable: this.getWorkSpace()._getDateTable(), - viewModel - }); - } else { - this._appointments.option('items', viewModel); + this._postponeDataSourceLoading(); + break; + case StoreEventNames.ADDING: + case StoreEventNames.ADDED: + case StoreEventNames.UPDATING: + case StoreEventNames.UPDATED: + case StoreEventNames.DELETING: + case StoreEventNames.DELETED: + case 'onAppointmentFormOpening': + case 'onAppointmentTooltipShowing': + this._actions[name] = this._createActionByOption(name); + break; + case 'onAppointmentRendered': + this._appointments.option('onItemRendered', this._getAppointmentRenderedAction()); + break; + case 'onAppointmentClick': + this._appointments.option('onItemClick', this._createActionByOption(name)); + break; + case 'onAppointmentDblClick': + this._appointments.option(name, this._createActionByOption(name)); + break; + case 'onAppointmentContextMenu': + this._appointments.option('onItemContextMenu', this._createActionByOption(name)); + this._appointmentTooltip._options.onItemContextMenu = this._createActionByOption(name); + break; + case 'noDataText': + case 'allowMultipleCellSelection': + case 'selectedCellData': + case 'accessKey': + case 'onCellClick': + this._workSpace.option(name, value); + break; + case 'onCellContextMenu': + this._workSpace.option(name, value); + break; + case 'crossScrollingEnabled': + this._postponeResourceLoading().done((resources) => { + this._appointments.option('items', []); + this._refreshWorkSpace(resources); + if (this._readyToRenderAppointments) { + this._appointments.option('items', this._getAppointmentsToRepaint()); + } + }); + break; + case 'cellDuration': + this._validateCellDuration(); + this._updateOption('workSpace', name, value); + this._appointments.option('items', []); + if (this._readyToRenderAppointments) { + this._updateOption('workSpace', 'hoursInterval', value / 60); + this._appointments.option('items', this._getAppointmentsToRepaint()); } - - this.appointmentDataProvider.cleanState(); - } - - _getAppointmentsToRepaint() { - const layoutManager = this.getLayoutManager(); - - const appointmentsMap = layoutManager.createAppointmentsMap(this.filteredItems); - if(this.option('isRenovatedAppointments')) { - const appointmentTemplate = this.option('appointmentTemplate') !== DEFAULT_APPOINTMENT_TEMPLATE_NAME - ? this.option('appointmentTemplate') - : undefined; - return { - appointments: appointmentsMap, - appointmentTemplate - }; + break; + case 'tabIndex': + case 'focusStateEnabled': + this._updateOption('header', name, value); + this._updateOption('workSpace', name, value); + this._appointments.option(name, value); + // @ts-expect-error + super._optionChanged(args); + break; + case 'width': + // TODO: replace with css + this._updateOption('header', name, value); + if (this.option('crossScrollingEnabled')) { + this._updateOption('workSpace', 'width', value); } - - return layoutManager.getRepaintedAppointments( - appointmentsMap, - this.getAppointmentsInstance().option('items') - ); - } - - _initExpressions(fields) { - this._dataAccessors = utils.dataAccessors.create( - fields, - this._dataAccessors, - config().forceIsoDateParsing, - this.option('dateSerializationFormat') - ); - - this._dataAccessors.resources = createExpressions(this.option('resources')); - } - - _updateExpression(name, value) { - const exprObj = {}; - exprObj[name.replace('Expr', '')] = value; - this._initExpressions(exprObj); - } - - getResourceDataAccessors() { - return this._dataAccessors.resources; - } - - _initEditing() { - const editing = this.option('editing'); - - this._editing = { - allowAdding: !!editing, - allowUpdating: !!editing, - allowDeleting: !!editing, - allowResizing: !!editing, - allowDragging: !!editing - }; - - if(isObject(editing)) { - this._editing = extend(this._editing, editing); - } - - this._editing.allowDragging = this._editing.allowDragging && this._editing.allowUpdating; - this._editing.allowResizing = this._editing.allowResizing && this._editing.allowUpdating; - - this.$element().toggleClass(WIDGET_READONLY_CLASS, this._isReadOnly()); - } - - _isReadOnly() { - let result = true; + this._updateOption('workSpace', 'schedulerWidth', value); + // @ts-expect-error + super._optionChanged(args); + this._dimensionChanged(null, true); + break; + case 'height': + // @ts-expect-error + super._optionChanged(args); + this._dimensionChanged(null, true); + this._updateOption('workSpace', 'schedulerHeight', value); + break; + case 'editing': { + this._initEditing(); const editing = this._editing; - for(const prop in editing) { - if(Object.prototype.hasOwnProperty.call(editing, prop)) { - result = result && !editing[prop]; - } - } - - return result; - } + this._bringEditingModeToAppointments(editing); - _dispose() { - this._appointmentTooltip && this._appointmentTooltip.dispose(); - this._recurrenceDialog?.hide(RECURRENCE_EDITING_MODE.CANCEL); - - this.hideAppointmentPopup(); this.hideAppointmentTooltip(); + this._cleanPopup(); + break; + } + case 'showAllDayPanel': + this.updateInstances(); + this.repaint(); + break; + case 'showCurrentTimeIndicator': + case 'indicatorTime': + case 'indicatorUpdateInterval': + case 'shadeUntilCurrentTime': + case 'groupByDate': + this._updateOption('workSpace', name, value); + this.repaint(); + break; + case 'appointmentDragging': + case 'appointmentTooltipTemplate': + case 'appointmentPopupTemplate': + case 'recurrenceEditMode': + case 'remoteFiltering': + case 'timeZone': + this.updateInstances(); + this.repaint(); + break; + case 'dropDownAppointmentTemplate': + case 'appointmentCollectorTemplate': + case '_appointmentTooltipOffset': + case '_appointmentTooltipButtonsPosition': + case '_appointmentTooltipOpenButtonText': + case '_appointmentCountPerCell': + case '_collectorOffset': + case '_appointmentOffset': + this.repaint(); + break; + case 'dateSerializationFormat': + break; + case 'maxAppointmentsPerCell': + break; + case 'startDateExpr': + case 'endDateExpr': + case 'startDateTimeZoneExpr': + case 'endDateTimeZoneExpr': + case 'textExpr': + case 'descriptionExpr': + case 'allDayExpr': + case 'recurrenceRuleExpr': + case 'recurrenceExceptionExpr': + case 'disabledExpr': + this._updateExpression(name, value); + this.appointmentDataProvider.updateDataAccessors(this._dataAccessors); - this._asyncTemplatesTimers.forEach(clearTimeout); - this._asyncTemplatesTimers = []; - - super._dispose(); - } + this._initAppointmentTemplate(); + this.repaint(); + break; + case 'adaptivityEnabled': + this._toggleAdaptiveClass(); + this.repaint(); + break; + case 'scrolling': + this.option('crossScrollingEnabled', this._isHorizontalVirtualScrolling() || this.option('crossScrollingEnabled')); + + this._updateOption('workSpace', args.fullName, value); + break; + case 'allDayPanelMode': + this._updateOption('workSpace', args.fullName, value); + break; + case 'renovateRender': + this._updateOption('workSpace', name, value); + break; + case '_draggingMode': + this._workSpace.option('draggingMode', value); + break; + case 'toolbar': + this._header + ? this._header.option('items', value) + : this.repaint(); + break; + case 'loadedResources': + case 'resourceLoaderMap': + break; + default: + // @ts-expect-error + super._optionChanged(args); + } + } + + _dateOption(optionName) { + const optionValue = this._getCurrentViewOption(optionName); + + return dateSerialization.deserializeDate(optionValue); + } + + _getSerializationFormat(optionName) { + const value = this._getCurrentViewOption(optionName); + + if (typeof value === 'number') { + return 'number'; + } + + if (!isString(value)) { + return; + } + + return dateSerialization.getDateSerializationFormat(value); + } + + _bringEditingModeToAppointments(editing) { + const editingConfig: any = { + allowDelete: editing.allowUpdating && editing.allowDeleting, + }; + + if (!this._isAgenda()) { + editingConfig.allowDrag = editing.allowDragging; + editingConfig.allowResize = editing.allowResizing; + editingConfig.allowAllDayResize = editing.allowResizing && this._supportAllDayResizing(); + } + + this._appointments.option(editingConfig); + this.repaint(); + } + + _isAgenda() { + return this.getLayoutManager().appointmentRenderingStrategyName === 'agenda'; + } + + _allowDragging() { + return this._editing.allowDragging && !this._isAgenda(); + } + + _allowResizing() { + return this._editing.allowResizing && !this._isAgenda(); + } + + _allowAllDayResizing() { + return this._editing.allowResizing && this._supportAllDayResizing(); + } + + _supportAllDayResizing() { + return this.currentViewType !== 'day' || this.currentView.intervalCount > 1; + } + + _isAllDayExpanded() { + return this.option('showAllDayPanel') && this.appointmentDataProvider.hasAllDayAppointments( + this.filteredItems, + this.preparedItems, + ); + } + + _getTimezoneOffsetByOption(date) { + return timeZoneUtils.calculateTimezoneByValue(this.option('timeZone'), date); + } + + _filterAppointmentsByDate() { + const dateRange = this._workSpace.getDateRange(); + + const startDate = this.timeZoneCalculator.createDate(dateRange[0], { path: 'fromGrid' }); + const endDate = this.timeZoneCalculator.createDate(dateRange[1], { path: 'fromGrid' }); + + this.appointmentDataProvider.filterByDate( + startDate, + endDate, + this.option('remoteFiltering'), + this.option('dateSerializationFormat'), + ); + } + + _reloadDataSource() { + // @ts-expect-error + const result = new Deferred(); + + if (this._dataSource) { + this._dataSource.load().done(() => { + hideLoading(); - _initActions() { - - this._actions = { - 'onAppointmentAdding': this._createActionByOption(StoreEventNames.ADDING), - 'onAppointmentAdded': this._createActionByOption(StoreEventNames.ADDED), - 'onAppointmentUpdating': this._createActionByOption(StoreEventNames.UPDATING), - 'onAppointmentUpdated': this._createActionByOption(StoreEventNames.UPDATED), - 'onAppointmentDeleting': this._createActionByOption(StoreEventNames.DELETING), - 'onAppointmentDeleted': this._createActionByOption(StoreEventNames.DELETED), - 'onAppointmentFormOpening': this._createActionByOption('onAppointmentFormOpening'), - 'onAppointmentTooltipShowing': this._createActionByOption('onAppointmentTooltipShowing'), - }; - } + this._fireContentReadyAction(result); + }).fail(() => { + hideLoading(); + result.reject(); + }); - _getAppointmentRenderedAction() { - return this._createActionByOption('onAppointmentRendered', { - excludeValidators: ['disabled', 'readOnly'] - }); + this._dataSource.isLoading() && showLoading({ + container: this.$element(), + position: { + of: this.$element(), + }, + }); + } else { + this._fireContentReadyAction(result); } - _renderFocusTarget() { return noop(); } - - _initMarkup() { - super._initMarkup(); - - this._validateDayHours(); - this._validateCellDuration(); - - this._renderMainContainer(); - - this._renderHeader(); - - this._layoutManager = new AppointmentLayoutManager(this); + return result.promise(); + } - this._appointments = this._createComponent('
', AppointmentCollection, this._appointmentsConfig()); - this._appointments.option('itemTemplate', this._getAppointmentTemplate('appointmentTemplate')); + _fireContentReadyAction(result?: any) { + // @ts-expect-error + const contentReadyBase = super._fireContentReadyAction.bind(this); + const fireContentReady = () => { + contentReadyBase(); + result?.resolve(); + }; - this._appointmentTooltip = new (this.option('adaptivityEnabled') - ? MobileTooltipStrategy - : DesktopTooltipStrategy)(this._getAppointmentTooltipOptions()); - - this._createAppointmentPopupForm(); - - if(this._isDataSourceLoaded() || this._isDataSourceLoading()) { - this._initMarkupCore(this.option('loadedResources')); - this._dataSourceChangedHandler(this._dataSource.items()); - this._fireContentReadyAction(); - } else { - const groups = this._getCurrentViewOption('groups'); - loadResources(groups, this.option('resources'), this.option('resourceLoaderMap')).done((resources) => { - this.option('loadedResources', resources); - this._initMarkupCore(resources); - this._reloadDataSource(); - }); - } - } - - _createAppointmentPopupForm() { - if(this._appointmentForm) { - this._appointmentForm.form?.dispose(); - } - this._appointmentForm = this.createAppointmentForm(); - - this._appointmentPopup?.dispose(); - this._appointmentPopup = this.createAppointmentPopup(this._appointmentForm); + if (this._workSpaceRecalculation) { + this._workSpaceRecalculation?.done(() => { + fireContentReady(); + }); + } else { + fireContentReady(); } + } - _renderMainContainer() { - this._mainContainer = $('
').addClass('dx-scheduler-container'); + _dimensionChanged(value, isForce = false) { + const isFixedHeight = typeof this.option('height') === 'number'; + const isFixedWidth = typeof this.option('width') === 'number'; - this.$element().append(this._mainContainer); + // @ts-expect-error + if (!this._isVisible()) { + return; } - createAppointmentForm() { - const scheduler = { - createResourceEditorModel: () => { - return createResourceEditorModel(this.option('resources'), this.option('loadedResources')); - }, - getDataAccessors: () => this._dataAccessors, - createComponent: (element, component, options) => this._createComponent(element, component, options), + this._toggleSmallClass(); - getEditingConfig: () => this._editing, + const workspace = this.getWorkSpace(); - getFirstDayOfWeek: () => this.option('firstDayOfWeek'), - getStartDayHour: () => this.option('startDayHour'), - getCalculatedEndDate: (startDateWithStartHour) => this._workSpace.calculateEndDate(startDateWithStartHour), - getTimeZoneCalculator: () => this.timeZoneCalculator, - }; + if (!this._isAgenda() && this.filteredItems && workspace) { + if (isForce || (!isFixedHeight || !isFixedWidth)) { + workspace.option('allDayExpanded', this._isAllDayExpanded()); + workspace._dimensionChanged(); + const appointments = this.getLayoutManager().createAppointmentsMap(this.filteredItems); - return new AppointmentForm(scheduler); + this._appointments.option('items', appointments); + } } - createAppointmentPopup(form) { - const scheduler = { - getElement: () => this.$element(), - createComponent: (element, component, options) => this._createComponent(element, component, options), - focus: () => this.focus(), + this.hideAppointmentTooltip(); + + // TODO popup + this._appointmentPopup.triggerResize(); + this._appointmentPopup.updatePopupFullScreenMode(); + } - getResources: () => { - return this.option('resources'); - }, + _clean() { + this._cleanPopup(); + // @ts-expect-error + super._clean(); + } - getEditingConfig: () => this._editing, + _toggleSmallClass() { + const { width } = getBoundingRect((this.$element() as any).get(0)); + (this.$element() as any).toggleClass(WIDGET_SMALL_CLASS, width < WIDGET_SMALL_WIDTH); + } - getTimeZoneCalculator: () => this.timeZoneCalculator, - getDataAccessors: () => this._dataAccessors, - getAppointmentFormOpening: () => this._actions['onAppointmentFormOpening'], - processActionResult: (arg, canceled) => this._processActionResult(arg, canceled), - - addAppointment: (appointment) => this.addAppointment(appointment), - updateAppointment: (sourceAppointment, updatedAppointment) => this.updateAppointment(sourceAppointment, updatedAppointment), - - updateScrollPosition: (startDate, resourceItem, inAllDayRow) => { - this._workSpace.updateScrollPosition(startDate, resourceItem, inAllDayRow); - } - }; + _toggleAdaptiveClass() { + (this.$element() as any).toggleClass(WIDGET_ADAPTIVE_CLASS, this.option('adaptivityEnabled')); + } - return new AppointmentPopup(scheduler, form); + _visibilityChanged(visible) { + visible && this._dimensionChanged(null, true); + } + + _dataSourceOptions() { + return { paginate: false }; + } + + _initAllDayPanel() { + if (this.option('allDayPanelMode') === 'hidden') { + this.option('showAllDayPanel', false); + } + } + + _init() { + this._initExpressions({ + startDate: this.option('startDateExpr'), + endDate: this.option('endDateExpr'), + startDateTimeZone: this.option('startDateTimeZoneExpr'), + endDateTimeZone: this.option('endDateTimeZoneExpr'), + allDay: this.option('allDayExpr'), + text: this.option('textExpr'), + description: this.option('descriptionExpr'), + recurrenceRule: this.option('recurrenceRuleExpr'), + recurrenceException: this.option('recurrenceExceptionExpr'), + disabled: this.option('disabledExpr'), + }); + + // @ts-expect-error + super._init(); + + this._initAllDayPanel(); + + // @ts-expect-error + this._initDataSource(); + + this._customizeDataSourceLoadOptions(); + + (this.$element() as any).addClass(WIDGET_CLASS); + + this._initEditing(); + + this.updateInstances(); + + this._initActions(); + + this._compactAppointmentsHelper = new CompactAppointmentsHelper(this); + + this._asyncTemplatesTimers = []; + + this._dataSourceLoadedCallback = Callbacks(); + + this._subscribes = subscribes; + + this.agendaResourceProcessor = new AgendaResourceProcessor(this.option('resources')); + } + + createAppointmentDataProvider() { + this.appointmentDataProvider?.destroy(); + this.appointmentDataProvider = new AppointmentDataProvider({ + dataSource: this._dataSource, + dataAccessors: this._dataAccessors, + timeZoneCalculator: this.timeZoneCalculator, + dateSerializationFormat: this.option('dateSerializationFormat'), + resources: this.option('resources'), + startDayHour: this._getCurrentViewOption('startDayHour'), + endDayHour: this._getCurrentViewOption('endDayHour'), + appointmentDuration: this._getCurrentViewOption('cellDuration'), + allDayPanelMode: this._getCurrentViewOption('allDayPanelMode'), + showAllDayPanel: this.option('showAllDayPanel'), + getLoadedResources: () => this.option('loadedResources'), + getIsVirtualScrolling: () => this.isVirtualScrolling(), + getSupportAllDayRow: () => this._workSpace.supportAllDayRow(), + getViewType: () => this._workSpace.type, + getViewDirection: () => this._workSpace.viewDirection, + getDateRange: () => this._workSpace.getDateRange(), + getGroupCount: () => this._workSpace._getGroupCount(), + getViewDataProvider: () => this._workSpace.viewDataProvider, + }); + } + + updateInstances() { + this._timeZoneCalculator = null; + + if (this.getWorkSpace()) { + this.createAppointmentDataProvider(); + } + } + + _customizeDataSourceLoadOptions() { + this._dataSource?.on('customizeStoreLoadOptions', ({ storeLoadOptions }) => { + storeLoadOptions.startDate = this.getStartViewDate(); + storeLoadOptions.endDate = this.getEndViewDate(); + }); + } + + _initTemplates() { + this._initAppointmentTemplate(); + + this._templateManager.addDefaultTemplates({ + appointmentTooltip: new EmptyTemplate(), + dropDownAppointment: new EmptyTemplate(), + }); + // @ts-expect-error + super._initTemplates(); + } + + _initAppointmentTemplate() { + const { expr } = this._dataAccessors; + const createGetter = (property) => compileGetter(`appointmentData.${property}`); + + const getDate = (getter) => (data) => { + const value = getter(data); + if (value instanceof Date) { + return value.valueOf(); + } + return value; + }; + + this._templateManager.addDefaultTemplates({ + item: new BindableTemplate( + ($container, data, model) => this.getAppointmentsInstance()._renderAppointmentTemplate($container, data, model), + [ + 'html', + 'text', + 'startDate', + 'endDate', + 'allDay', + 'description', + 'recurrenceRule', + 'recurrenceException', + 'startDateTimeZone', + 'endDateTimeZone', + ], + this.option('integrationOptions.watchMethod'), + { + text: createGetter(expr.textExpr), + startDate: getDate(createGetter(expr.startDateExpr)), + endDate: getDate(createGetter(expr.endDateExpr)), + startDateTimeZone: createGetter(expr.startDateTimeZoneExpr), + endDateTimeZone: createGetter(expr.endDateTimeZoneExpr), + allDay: createGetter(expr.allDayExpr), + recurrenceRule: createGetter(expr.recurrenceRuleExpr), + }, + ), + }); + } + + _renderContent() { + // @ts-expect-error + this._renderContentImpl(); + } + + _updatePreparedItems(items) { + this.preparedItems = getPreparedDataItems( + items, + this._dataAccessors, + this._getCurrentViewOption('cellDuration'), + this.timeZoneCalculator, + ); + } + + _dataSourceChangedHandler(result) { + if (this._readyToRenderAppointments) { + this._workSpaceRecalculation.done(() => { + this._updatePreparedItems(result); + this._renderAppointments(); + this.getWorkSpace().onDataSourceChanged(this.filteredItems); + }); + } + } + + isVirtualScrolling() { + const workspace = this.getWorkSpace(); + + if (workspace) { + return workspace.isVirtualScrolling(); + } + + const currentViewOptions: any = this._getCurrentViewOptions(); + const scrolling = this.option('scrolling'); + + return scrolling?.mode === 'virtual' + || currentViewOptions?.scrolling?.mode === 'virtual'; + } + + _filterAppointments() { + this.filteredItems = this.appointmentDataProvider.filter(this.preparedItems); + } + + _renderAppointments() { + const workspace = this.getWorkSpace(); + this._filterAppointments(); + + workspace.option('allDayExpanded', this._isAllDayExpanded()); + + let viewModel = []; + // @ts-expect-error + if (this._isVisible()) { + viewModel = this._getAppointmentsToRepaint(); } - _getAppointmentTooltipOptions() { - const that = this; - return { - createComponent: that._createComponent.bind(that), - container: that.$element(), - getScrollableContainer: that.getWorkSpaceScrollableContainer.bind(that), - addDefaultTemplates: that._templateManager.addDefaultTemplates.bind(that._templateManager), - getAppointmentTemplate: that._getAppointmentTemplate.bind(that), - showAppointmentPopup: that.showAppointmentPopup.bind(that), - checkAndDeleteAppointment: that.checkAndDeleteAppointment.bind(that), - isAppointmentInAllDayPanel: that.isAppointmentInAllDayPanel.bind(that), - - createFormattedDateText: (appointment, targetedAppointment, format) => this.fire('getTextAndFormatDate', appointment, targetedAppointment, format), - getAppointmentDisabled: (appointment) => createAppointmentAdapter( - appointment, - this._dataAccessors, - this.timeZoneCalculator - ).disabled, - onItemContextMenu: that._createActionByOption('onAppointmentContextMenu'), - createEventArgs: that._createEventArgs.bind(that), - }; - } + if (this.option('isRenovatedAppointments')) { + renderAppointments({ + instance: this, + $dateTable: this.getWorkSpace()._getDateTable(), + viewModel, + }); + } else { + this._appointments.option('items', viewModel); + } + + this.appointmentDataProvider.cleanState(); + } + + _getAppointmentsToRepaint() { + const layoutManager = this.getLayoutManager(); + + const appointmentsMap = layoutManager.createAppointmentsMap(this.filteredItems); + if (this.option('isRenovatedAppointments')) { + const appointmentTemplate = this.option('appointmentTemplate') !== DEFAULT_APPOINTMENT_TEMPLATE_NAME + ? this.option('appointmentTemplate') + : undefined; + return { + appointments: appointmentsMap, + appointmentTemplate, + }; + } + + return layoutManager.getRepaintedAppointments( + appointmentsMap, + this.getAppointmentsInstance().option('items'), + ); + } + + _initExpressions(fields) { + this._dataAccessors = utils.dataAccessors.create( + fields, + this._dataAccessors, + config().forceIsoDateParsing, + this.option('dateSerializationFormat'), + ); - _createEventArgs(e) { - const config = { - itemData: e.itemData.appointment, - itemElement: e.itemElement, - targetedAppointment: e.itemData.targetedAppointment, - }; - return extend({}, this.fire('mapAppointmentFields', config), { - component: e.component, - element: e.element, - event: e.event, - model: e.model, - }); - } + this._dataAccessors.resources = createExpressions(this.option('resources')); + } + + _updateExpression(name, value) { + const exprObj = {}; + exprObj[name.replace('Expr', '')] = value; + this._initExpressions(exprObj); + } - checkAndDeleteAppointment(appointment, targetedAppointment) { - const targetedAdapter = createAppointmentAdapter( - targetedAppointment, - this._dataAccessors, - this.timeZoneCalculator - ); + getResourceDataAccessors() { + return this._dataAccessors.resources; + } + + _initEditing() { + const editing = this.option('editing'); + + this._editing = { + allowAdding: !!editing, + allowUpdating: !!editing, + allowDeleting: !!editing, + allowResizing: !!editing, + allowDragging: !!editing, + }; - const deletingOptions = this.fireOnAppointmentDeleting(appointment, targetedAdapter); - this._checkRecurringAppointment( - appointment, - targetedAppointment, - targetedAdapter.startDate, - () => { - this.processDeleteAppointment(appointment, deletingOptions); - }, - true - ); + if (isObject(editing)) { + this._editing = extend(this._editing, editing); } - _getExtraAppointmentTooltipOptions() { - return { - rtlEnabled: this.option('rtlEnabled'), - focusStateEnabled: this.option('focusStateEnabled'), - editing: this.option('editing'), - offset: this.option('_appointmentTooltipOffset'), - }; - } - - isAppointmentInAllDayPanel(appointmentData) { - const workSpace = this._workSpace; - const itTakesAllDay = this.appointmentTakesAllDay(appointmentData); - - return itTakesAllDay && workSpace.supportAllDayRow() && workSpace.option('showAllDayPanel'); - } - - _initMarkupCore(resources) { - this._readyToRenderAppointments = hasWindow(); - - this._workSpace && this._cleanWorkspace(); + this._editing.allowDragging = this._editing.allowDragging && this._editing.allowUpdating; + this._editing.allowResizing = this._editing.allowResizing && this._editing.allowUpdating; - this._renderWorkSpace(resources); - this._appointments.option({ - fixedContainer: this._workSpace.getFixedContainer(), - allDayContainer: this._workSpace.getAllDayContainer() - }); - this._waitAsyncTemplate(() => this._workSpaceRecalculation?.resolve()); + (this.$element() as any).toggleClass(WIDGET_READONLY_CLASS, this._isReadOnly()); + } - this.createAppointmentDataProvider(); - - this._filterAppointmentsByDate(); + _isReadOnly() { + let result = true; + const editing = this._editing; - this._validateKeyFieldIfAgendaExist(); - } + // eslint-disable-next-line no-restricted-syntax + for (const prop in editing) { + if (Object.prototype.hasOwnProperty.call(editing, prop)) { + result = result && !editing[prop]; + } + } + + return result; + } + + _dispose() { + this._appointmentTooltip && this._appointmentTooltip.dispose(); + this._recurrenceDialog?.hide(RECURRENCE_EDITING_MODE.CANCEL); + + this.hideAppointmentPopup(); + this.hideAppointmentTooltip(); + + this._asyncTemplatesTimers.forEach(clearTimeout); + this._asyncTemplatesTimers = []; + + // @ts-expect-error + super._dispose(); + } + + _initActions() { + this._actions = { + onAppointmentAdding: this._createActionByOption(StoreEventNames.ADDING), + onAppointmentAdded: this._createActionByOption(StoreEventNames.ADDED), + onAppointmentUpdating: this._createActionByOption(StoreEventNames.UPDATING), + onAppointmentUpdated: this._createActionByOption(StoreEventNames.UPDATED), + onAppointmentDeleting: this._createActionByOption(StoreEventNames.DELETING), + onAppointmentDeleted: this._createActionByOption(StoreEventNames.DELETED), + onAppointmentFormOpening: this._createActionByOption('onAppointmentFormOpening'), + onAppointmentTooltipShowing: this._createActionByOption('onAppointmentTooltipShowing'), + }; + } + + _getAppointmentRenderedAction() { + return this._createActionByOption('onAppointmentRendered', { + excludeValidators: ['disabled', 'readOnly'], + }); + } + + _renderFocusTarget() { return noop(); } - _isDataSourceLoaded() { // TODO - return this._dataSource && this._dataSource.isLoaded(); - } + _initMarkup() { + // @ts-expect-error + super._initMarkup(); + + this._validateDayHours(); + this._validateCellDuration(); + + this._renderMainContainer(); + + this._renderHeader(); + + this._layoutManager = new AppointmentLayoutManager(this); + + // @ts-expect-error + this._appointments = this._createComponent('
', AppointmentCollection, this._appointmentsConfig()); + this._appointments.option('itemTemplate', this._getAppointmentTemplate('appointmentTemplate')); + + this._appointmentTooltip = new (this.option('adaptivityEnabled') + ? MobileTooltipStrategy + : DesktopTooltipStrategy)(this._getAppointmentTooltipOptions()); + + this._createAppointmentPopupForm(); + + // @ts-expect-error + if (this._isDataSourceLoaded() || this._isDataSourceLoading()) { + this._initMarkupCore(this.option('loadedResources')); + this._dataSourceChangedHandler(this._dataSource.items()); + this._fireContentReadyAction(); + } else { + const groups = this._getCurrentViewOption('groups'); + loadResources(groups, this.option('resources'), this.option('resourceLoaderMap')).done((resources) => { + this.option('loadedResources', resources); + this._initMarkupCore(resources); + this._reloadDataSource(); + }); + } + } + + _createAppointmentPopupForm() { + if (this._appointmentForm) { + this._appointmentForm.form?.dispose(); + } + this._appointmentForm = this.createAppointmentForm(); - _render() { - // NOTE: remove small class applying after adaptivity implementation - this._toggleSmallClass(); + this._appointmentPopup?.dispose(); + this._appointmentPopup = this.createAppointmentPopup(this._appointmentForm); + } + + _renderMainContainer() { + this._mainContainer = $('
').addClass('dx-scheduler-container'); + + this.$element().append(this._mainContainer); + } + + createAppointmentForm() { + const scheduler = { + createResourceEditorModel: () => createResourceEditorModel(this.option('resources'), this.option('loadedResources')), + getDataAccessors: () => this._dataAccessors, + // @ts-expect-error + createComponent: (element, component, options) => this._createComponent(element, component, options), + + getEditingConfig: () => this._editing, + + getFirstDayOfWeek: () => this.option('firstDayOfWeek'), + getStartDayHour: () => this.option('startDayHour'), + getCalculatedEndDate: (startDateWithStartHour) => this._workSpace.calculateEndDate(startDateWithStartHour), + getTimeZoneCalculator: () => this.timeZoneCalculator, + }; + + return new AppointmentForm(scheduler); + } + + createAppointmentPopup(form) { + const scheduler = { + getElement: () => this.$element(), + // @ts-expect-error + createComponent: (element, component, options) => this._createComponent(element, component, options), + focus: () => this.focus(), + + getResources: () => this.option('resources'), + + getEditingConfig: () => this._editing, + + getTimeZoneCalculator: () => this.timeZoneCalculator, + getDataAccessors: () => this._dataAccessors, + getAppointmentFormOpening: () => this._actions.onAppointmentFormOpening, + processActionResult: (arg, canceled) => this._processActionResult(arg, canceled), + + addAppointment: (appointment) => this.addAppointment(appointment), + updateAppointment: (sourceAppointment, updatedAppointment) => this.updateAppointment(sourceAppointment, updatedAppointment), + + updateScrollPosition: (startDate, resourceItem, inAllDayRow) => { + this._workSpace.updateScrollPosition(startDate, resourceItem, inAllDayRow); + }, + }; - this._toggleAdaptiveClass(); + return new AppointmentPopup(scheduler, form); + } + + _getAppointmentTooltipOptions() { + const that = this; + return { + // @ts-expect-error + createComponent: that._createComponent.bind(that), + container: that.$element(), + getScrollableContainer: that.getWorkSpaceScrollableContainer.bind(that), + addDefaultTemplates: that._templateManager.addDefaultTemplates.bind(that._templateManager), + getAppointmentTemplate: that._getAppointmentTemplate.bind(that), + showAppointmentPopup: that.showAppointmentPopup.bind(that), + checkAndDeleteAppointment: that.checkAndDeleteAppointment.bind(that), + isAppointmentInAllDayPanel: that.isAppointmentInAllDayPanel.bind(that), + + createFormattedDateText: (appointment, targetedAppointment, format) => (this.fire as any)('getTextAndFormatDate', appointment, targetedAppointment, format), + getAppointmentDisabled: (appointment) => createAppointmentAdapter( + appointment, + this._dataAccessors, + this.timeZoneCalculator, + ).disabled, + onItemContextMenu: that._createActionByOption('onAppointmentContextMenu'), + createEventArgs: that._createEventArgs.bind(that), + }; + } + + _createEventArgs(e) { + const config = { + itemData: e.itemData.appointment, + itemElement: e.itemElement, + targetedAppointment: e.itemData.targetedAppointment, + }; + return extend({}, (this.fire as any)('mapAppointmentFields', config), { + component: e.component, + element: e.element, + event: e.event, + model: e.model, + }); + } + + checkAndDeleteAppointment(appointment, targetedAppointment) { + const targetedAdapter = createAppointmentAdapter( + targetedAppointment, + this._dataAccessors, + this.timeZoneCalculator, + ); + + const deletingOptions = this.fireOnAppointmentDeleting(appointment, targetedAdapter); + this._checkRecurringAppointment( + appointment, + targetedAppointment, + targetedAdapter.startDate, + () => { + this.processDeleteAppointment(appointment, deletingOptions); + }, + true, + ); + } + + _getExtraAppointmentTooltipOptions() { + return { + rtlEnabled: this.option('rtlEnabled'), + focusStateEnabled: this.option('focusStateEnabled'), + editing: this.option('editing'), + offset: this.option('_appointmentTooltipOffset'), + }; + } + + isAppointmentInAllDayPanel(appointmentData) { + const workSpace = this._workSpace; + const itTakesAllDay = this.appointmentTakesAllDay(appointmentData); + + return itTakesAllDay && workSpace.supportAllDayRow() && workSpace.option('showAllDayPanel'); + } + + _initMarkupCore(resources) { + this._readyToRenderAppointments = hasWindow(); + + this._workSpace && this._cleanWorkspace(); + + this._renderWorkSpace(resources); + this._appointments.option({ + fixedContainer: this._workSpace.getFixedContainer(), + allDayContainer: this._workSpace.getAllDayContainer(), + }); + this._waitAsyncTemplate(() => this._workSpaceRecalculation?.resolve()); + + this.createAppointmentDataProvider(); + + this._filterAppointmentsByDate(); + + this._validateKeyFieldIfAgendaExist(); + } + + _isDataSourceLoaded() { // TODO + return this._dataSource && this._dataSource.isLoaded(); + } + + _render() { + // NOTE: remove small class applying after adaptivity implementation + this._toggleSmallClass(); + + this._toggleAdaptiveClass(); + + this.getWorkSpace()?.updateHeaderEmptyCellWidth(); + + // @ts-expect-error + super._render(); + } + + _renderHeader() { + if (this.option('toolbar').length !== 0) { + const $header = $('
').appendTo(this._mainContainer); + // @ts-expect-error + this._header = this._createComponent($header, SchedulerHeader, this._headerConfig()); + } + } + + _headerConfig() { + const currentViewOptions: any = this._getCurrentViewOptions(); + const countConfig = this._getViewCountConfig(); + + const result = extend({ + firstDayOfWeek: this.getFirstDayOfWeek(), + currentView: this.option('currentView'), + isAdaptive: this.option('adaptivityEnabled'), + tabIndex: this.option('tabIndex'), + focusStateEnabled: this.option('focusStateEnabled'), + rtlEnabled: this.option('rtlEnabled'), + useDropDownViewSwitcher: this.option('useDropDownViewSwitcher'), + customizeDateNavigatorText: this.option('customizeDateNavigatorText'), + agendaDuration: currentViewOptions.agendaDuration || DEFAULT_AGENDA_DURATION, + }, currentViewOptions); + + result.intervalCount = countConfig.intervalCount; + result.views = this.option('views'); + result.min = new Date(this._dateOption('min')); + result.max = new Date(this._dateOption('max')); + result.currentDate = dateUtils.trimTime(new Date(this._dateOption('currentDate'))); + result.onCurrentViewChange = (name) => { + this.option('currentView', name); + }; + result.onCurrentDateChange = (date) => { + this.option('currentDate', date); + }; + result.items = this.option('toolbar'); + result.startViewDate = this.getStartViewDate(); + + result.todayDate = () => { + const result = this.timeZoneCalculator.createDate(new Date(), { path: 'toGrid' }); + return result; + }; + + return result; + } + + _appointmentsConfig() { + const config = { + getResources: () => this.option('resources'), + getResourceDataAccessors: this.getResourceDataAccessors.bind(this), + getAgendaResourceProcessor: () => this.agendaResourceProcessor, + getAppointmentColor: this.createGetAppointmentColor(), + + getAppointmentDataProvider: () => this.appointmentDataProvider, + dataAccessors: this._dataAccessors, + observer: this, + onItemRendered: this._getAppointmentRenderedAction(), + onItemClick: this._createActionByOption('onAppointmentClick'), + onItemContextMenu: this._createActionByOption('onAppointmentContextMenu'), + onAppointmentDblClick: this._createActionByOption('onAppointmentDblClick'), + tabIndex: this.option('tabIndex'), + focusStateEnabled: this.option('focusStateEnabled'), + allowDrag: this._allowDragging(), + allowDelete: this._editing.allowUpdating && this._editing.allowDeleting, + allowResize: this._allowResizing(), + allowAllDayResize: this._allowAllDayResizing(), + rtlEnabled: this.option('rtlEnabled'), + currentView: this.currentView, + groups: this._getCurrentViewOption('groups'), + isRenovatedAppointments: this.option('isRenovatedAppointments'), + timeZoneCalculator: this.timeZoneCalculator, + getResizableStep: () => (this._workSpace ? this._workSpace.positionHelper.getResizableStep() : 0), + getDOMElementsMetaData: () => this._workSpace?.getDOMElementsMetaData(), + getViewDataProvider: () => this._workSpace?.viewDataProvider, + isVerticalViewDirection: () => this.getRenderingStrategyInstance().getDirection() === 'vertical', + isVerticalGroupedWorkSpace: () => this._workSpace._isVerticalGroupedWorkSpace(), + isDateAndTimeView: () => isDateAndTimeView(this._workSpace.type), + onContentReady: () => { + this._workSpace?.option('allDayExpanded', this._isAllDayExpanded()); + }, + }; + + return config; + } + + getCollectorOffset() { + if (this._workSpace.needApplyCollectorOffset() && !this.option('adaptivityEnabled')) { + return this.option('_collectorOffset'); + } + return 0; + } + + getAppointmentDurationInMinutes() { + return this._getCurrentViewOption('cellDuration'); + } + + _getCurrentViewType() { // TODO get rid of mapping + return this.currentViewType; + } + + _renderWorkSpace(groups) { + this._readyToRenderAppointments && this._toggleSmallClass(); + const $workSpace = $('
').appendTo(this._mainContainer); + + const countConfig = this._getViewCountConfig(); + + const workSpaceComponent = VIEWS_CONFIG[this._getCurrentViewType()].workSpace; + const workSpaceConfig = this._workSpaceConfig(groups, countConfig); + // @ts-expect-error + this._workSpace = this._createComponent($workSpace, workSpaceComponent, workSpaceConfig); + + this._allowDragging() && this._workSpace.initDragBehavior(this, this._all); + this._workSpace._attachTablesEvents(); + this._workSpace.getWorkArea().append(this._appointments.$element()); + + this._recalculateWorkspace(); + countConfig.startDate && this._header?.option('currentDate', this._workSpace._getHeaderDate()); + + this._appointments.option('_collectorOffset', this.getCollectorOffset()); + } + + _getViewCountConfig() { + const currentView = this.option('currentView'); + + const view = this._getViewByName(currentView); + const viewCount = view && view.intervalCount || 1; + const startDate = view && view.startDate || null; + + return { + intervalCount: viewCount, + startDate, + }; + } + + _getViewByName(name) { + const views = this.option('views'); + + for (let i = 0; i < views.length; i++) { + if (views[i].name === name || views[i].type === name || views[i] === name) return views[i]; + } + } + + _recalculateWorkspace() { + // @ts-expect-error + this._workSpaceRecalculation = new Deferred(); + this._waitAsyncTemplate(() => { + triggerResizeEvent(this._workSpace.$element()); + this._workSpace._refreshDateTimeIndication(); + }); + } + + _workSpaceConfig(groups, countConfig) { + const currentViewOptions: any = this._getCurrentViewOptions(); + const scrolling = this.option('scrolling'); + const isVirtualScrolling = scrolling.mode === 'virtual' + || currentViewOptions.scrolling?.mode === 'virtual'; + const horizontalVirtualScrollingAllowed = isVirtualScrolling + && ( + !isDefined(scrolling.orientation) + || ['horizontal', 'both'].filter( + (item) => scrolling.orientation === item || currentViewOptions.scrolling?.orientation === item, + ).length > 0 + ); + const crossScrollingEnabled = this.option('crossScrollingEnabled') + || horizontalVirtualScrollingAllowed + || isTimelineView(this.currentViewType); - this.getWorkSpace()?.updateHeaderEmptyCellWidth(); + const result = extend({ + resources: this.option('resources'), + loadedResources: this.option('loadedResources'), + getFilteredItems: () => this.filteredItems, + getResourceDataAccessors: this.getResourceDataAccessors.bind(this), + + noDataText: this.option('noDataText'), + firstDayOfWeek: this.option('firstDayOfWeek'), + startDayHour: this.option('startDayHour'), + endDayHour: this.option('endDayHour'), + tabIndex: this.option('tabIndex'), + accessKey: this.option('accessKey'), + focusStateEnabled: this.option('focusStateEnabled'), + cellDuration: this.option('cellDuration'), + showAllDayPanel: this.option('showAllDayPanel'), + showCurrentTimeIndicator: this.option('showCurrentTimeIndicator'), + indicatorTime: this.option('indicatorTime'), + indicatorUpdateInterval: this.option('indicatorUpdateInterval'), + shadeUntilCurrentTime: this.option('shadeUntilCurrentTime'), + allDayExpanded: this._appointments.option('items'), + crossScrollingEnabled, + dataCellTemplate: this.option('dataCellTemplate'), + timeCellTemplate: this.option('timeCellTemplate'), + resourceCellTemplate: this.option('resourceCellTemplate'), + dateCellTemplate: this.option('dateCellTemplate'), + allowMultipleCellSelection: this.option('allowMultipleCellSelection'), + selectedCellData: this.option('selectedCellData'), + onSelectionChanged: (args) => { + this.option('selectedCellData', args.selectedCellData); + }, + groupByDate: this._getCurrentViewOption('groupByDate'), + scrolling, + draggingMode: this.option('_draggingMode'), + timeZoneCalculator: this.timeZoneCalculator, + schedulerHeight: this.option('height'), + schedulerWidth: this.option('width'), + allDayPanelMode: this.option('allDayPanelMode'), + onSelectedCellsClick: this.showAddAppointmentPopup.bind(this), + onRenderAppointments: this._renderAppointments.bind(this), + onShowAllDayPanel: (value) => this.option('showAllDayPanel', value), + getHeaderHeight: () => utils.DOM.getHeaderHeight(this._header), + onScrollEnd: () => this._appointments.updateResizableArea(), + + // TODO: SSR does not work correctly with renovated render + renovateRender: this._isRenovatedRender(isVirtualScrolling), + isRenovatedAppointments: this.option('isRenovatedAppointments'), + }, currentViewOptions); + + result.observer = this; + result.intervalCount = countConfig.intervalCount; + result.startDate = countConfig.startDate; + result.groups = groups; + result.onCellClick = this._createActionByOption('onCellClick'); + result.onCellContextMenu = this._createActionByOption('onCellContextMenu'); + result.currentDate = dateUtils.trimTime(new Date(this._dateOption('currentDate'))); + result.hoursInterval = result.cellDuration / 60; + result.allDayExpanded = false; + result.dataCellTemplate = result.dataCellTemplate ? this._getTemplate(result.dataCellTemplate) : null; + result.timeCellTemplate = result.timeCellTemplate ? this._getTemplate(result.timeCellTemplate) : null; + result.resourceCellTemplate = result.resourceCellTemplate ? this._getTemplate(result.resourceCellTemplate) : null; + result.dateCellTemplate = result.dateCellTemplate ? this._getTemplate(result.dateCellTemplate) : null; + result.getAppointmentDataProvider = () => this.appointmentDataProvider; + + return result; + } + + _isRenovatedRender(isVirtualScrolling) { + return (this.option('renovateRender') && hasWindow()) || isVirtualScrolling; + } + + _waitAsyncTemplate(callback) { + if (this._options.silent('templatesRenderAsynchronously')) { + const timer = setTimeout(() => { + callback(); + clearTimeout(timer); + }); + this._asyncTemplatesTimers.push(timer); + } else { + callback(); + } + } + + _getCurrentViewOptions() { + return this.currentView; + } + + _getCurrentViewOption(optionName) { + if (this.currentView && this.currentView[optionName] !== undefined) { + return this.currentView[optionName]; + } + + return this.option(optionName); + } + + _getAppointmentTemplate(optionName) { + const currentViewOptions = this._getCurrentViewOptions(); + + if (currentViewOptions && currentViewOptions[optionName]) { + return this._getTemplate(currentViewOptions[optionName]); + } + + // @ts-expect-error + return this._getTemplateByOption(optionName); + } + + _updateOption(viewName, optionName, value) { + const currentViewOptions = this._getCurrentViewOptions(); + + if (!currentViewOptions || !isDefined(currentViewOptions[optionName])) { + this[`_${viewName}`].option(optionName, value); + } + } + + _refreshWorkSpace(groups) { + this._cleanWorkspace(); + + delete this._workSpace; + + this._renderWorkSpace(groups); + + if (this._readyToRenderAppointments) { + this._appointments.option({ + fixedContainer: this._workSpace.getFixedContainer(), + allDayContainer: this._workSpace.getAllDayContainer(), + }); + this._waitAsyncTemplate(() => this._workSpaceRecalculation.resolve()); + } + } + + _cleanWorkspace() { + this._appointments.$element().detach(); + this._workSpace._dispose(); + this._workSpace.$element().remove(); + + this.option('selectedCellData', []); + } + + getWorkSpaceScrollable() { + return this._workSpace.getScrollable(); + } + + getWorkSpaceScrollableContainer() { + return this._workSpace.getScrollableContainer(); + } + + getWorkSpace() { + return this._workSpace; + } + + getHeader() { + return this._header; + } + + _cleanPopup() { + this._appointmentPopup?.dispose(); + } + + _checkRecurringAppointment( + rawAppointment, + singleAppointment, + exceptionDate, + callback, + isDeleted, + isPopupEditing?: any, + dragEvent?: any, + recurrenceEditMode?: any, + ) { + const recurrenceRule = ExpressionUtils.getField(this._dataAccessors, 'recurrenceRule', rawAppointment); - super._render(); + if (!getRecurrenceProcessor().evalRecurrenceRule(recurrenceRule).isValid || !this._editing.allowUpdating) { + callback(); + return; } - _renderHeader() { - if(this.option('toolbar').length !== 0) { - const $header = $('
').appendTo(this._mainContainer); - this._header = this._createComponent($header, SchedulerHeader, this._headerConfig()); + const editMode = recurrenceEditMode || this.option('recurrenceEditMode'); + switch (editMode) { + case 'series': + callback(); + break; + case 'occurrence': + this._excludeAppointmentFromSeries(rawAppointment, singleAppointment, exceptionDate, isDeleted, isPopupEditing, dragEvent); + break; + default: + if (dragEvent) { + // @ts-expect-error + dragEvent.cancel = new Deferred(); } - } - - _headerConfig() { - const currentViewOptions = this._getCurrentViewOptions(); - const countConfig = this._getViewCountConfig(); - - const result = extend({ - firstDayOfWeek: this.getFirstDayOfWeek(), - currentView: this.option('currentView'), - isAdaptive: this.option('adaptivityEnabled'), - tabIndex: this.option('tabIndex'), - focusStateEnabled: this.option('focusStateEnabled'), - rtlEnabled: this.option('rtlEnabled'), - useDropDownViewSwitcher: this.option('useDropDownViewSwitcher'), - customizeDateNavigatorText: this.option('customizeDateNavigatorText'), - agendaDuration: currentViewOptions['agendaDuration'] || DEFAULT_AGENDA_DURATION - }, currentViewOptions); - - result.intervalCount = countConfig.intervalCount; - result.views = this.option('views'); - result.min = new Date(this._dateOption('min')); - result.max = new Date(this._dateOption('max')); - result.currentDate = dateUtils.trimTime(new Date(this._dateOption('currentDate'))); - result.onCurrentViewChange = (name) => { - this.option('currentView', name); + this._showRecurrenceChangeConfirm(isDeleted) + .done((editingMode) => { + editingMode === RECURRENCE_EDITING_MODE.SERIES && callback(); + + editingMode === RECURRENCE_EDITING_MODE.OCCURENCE && this._excludeAppointmentFromSeries( + rawAppointment, + singleAppointment, + exceptionDate, + isDeleted, + isPopupEditing, + dragEvent, + ); + }) + .fail(() => this._appointments.moveAppointmentBack(dragEvent)); + } + } + + _excludeAppointmentFromSeries(rawAppointment, newRawAppointment, exceptionDate, isDeleted, isPopupEditing, dragEvent) { + const appointment = excludeFromRecurrence( + rawAppointment, + exceptionDate, + this._dataAccessors, + this._timeZoneCalculator, + ); + + const singleRawAppointment = { ...newRawAppointment }; + /* eslint-disable @typescript-eslint/no-dynamic-delete */ + delete singleRawAppointment[this._dataAccessors.expr.recurrenceExceptionExpr]; + delete singleRawAppointment[this._dataAccessors.expr.recurrenceRuleExpr]; + + const keyPropertyName = this.appointmentDataProvider.keyName; + delete singleRawAppointment[keyPropertyName]; + /* eslint-enable @typescript-eslint/no-dynamic-delete */ + + const canCreateNewAppointment = !isDeleted && !isPopupEditing; + if (canCreateNewAppointment) { + this.addAppointment(singleRawAppointment); + } + + if (isPopupEditing) { + this._appointmentPopup.show(singleRawAppointment, { + isToolbarVisible: true, + action: ACTION_TO_APPOINTMENT.EXCLUDE_FROM_SERIES, + excludeInfo: { + sourceAppointment: rawAppointment, + updatedAppointment: appointment.source(), }, - result.onCurrentDateChange = (date) => { - this.option('currentDate', date); - }; - result.items = this.option('toolbar'); - result.startViewDate = this.getStartViewDate(); - - result.todayDate = () => { - const result = this.timeZoneCalculator.createDate(new Date(), { path: 'toGrid' }); - return result; - }; - - return result; + }); + this._editAppointmentData = rawAppointment; + } else { + this._updateAppointment(rawAppointment, appointment.source(), () => { + this._appointments.moveAppointmentBack(dragEvent); + }, dragEvent); } + } - _appointmentsConfig() { - const config = { - getResources: () => this.option('resources'), - getResourceDataAccessors: this.getResourceDataAccessors.bind(this), - getAgendaResourceProcessor: () => this.agendaResourceProcessor, - getAppointmentColor: this.createGetAppointmentColor(), - - getAppointmentDataProvider: () => this.appointmentDataProvider, - dataAccessors: this._dataAccessors, - observer: this, - onItemRendered: this._getAppointmentRenderedAction(), - onItemClick: this._createActionByOption('onAppointmentClick'), - onItemContextMenu: this._createActionByOption('onAppointmentContextMenu'), - onAppointmentDblClick: this._createActionByOption('onAppointmentDblClick'), - tabIndex: this.option('tabIndex'), - focusStateEnabled: this.option('focusStateEnabled'), - allowDrag: this._allowDragging(), - allowDelete: this._editing.allowUpdating && this._editing.allowDeleting, - allowResize: this._allowResizing(), - allowAllDayResize: this._allowAllDayResizing(), - rtlEnabled: this.option('rtlEnabled'), - currentView: this.currentView, - groups: this._getCurrentViewOption('groups'), - isRenovatedAppointments: this.option('isRenovatedAppointments'), - timeZoneCalculator: this.timeZoneCalculator, - getResizableStep: () => this._workSpace ? this._workSpace.positionHelper.getResizableStep() : 0, - getDOMElementsMetaData: () => this._workSpace?.getDOMElementsMetaData(), - getViewDataProvider: () => this._workSpace?.viewDataProvider, - isVerticalViewDirection: () => this.getRenderingStrategyInstance().getDirection() === 'vertical', - isVerticalGroupedWorkSpace: () => this._workSpace._isVerticalGroupedWorkSpace(), - isDateAndTimeView: () => isDateAndTimeView(this._workSpace.type), - onContentReady: () => { - this._workSpace?.option('allDayExpanded', this._isAllDayExpanded()); - } - }; + _createRecurrenceException(appointment, exceptionDate) { + const result: any[] = []; - return config; + if (appointment.recurrenceException) { + result.push(appointment.recurrenceException); } + result.push(this._getSerializedDate(exceptionDate, appointment.startDate, appointment.allDay)); - getCollectorOffset() { - if(this._workSpace.needApplyCollectorOffset() && !this.option('adaptivityEnabled')) { - return this.option('_collectorOffset'); - } else { - return 0; - } - } + return result.join(); + } - getAppointmentDurationInMinutes() { - return this._getCurrentViewOption('cellDuration'); - } + _getSerializedDate(date, startDate, isAllDay) { + isAllDay && date.setHours( + startDate.getHours(), + startDate.getMinutes(), + startDate.getSeconds(), + startDate.getMilliseconds(), + ); - _getCurrentViewType() { // TODO get rid of mapping - return this.currentViewType; - } + return dateSerialization.serializeDate(date, UTC_FULL_DATE_FORMAT); + } - _renderWorkSpace(groups) { - this._readyToRenderAppointments && this._toggleSmallClass(); - const $workSpace = $('
').appendTo(this._mainContainer); + _showRecurrenceChangeConfirm(isDeleted) { + const message = messageLocalization.format(isDeleted ? 'dxScheduler-confirmRecurrenceDeleteMessage' : 'dxScheduler-confirmRecurrenceEditMessage'); + const seriesText = messageLocalization.format(isDeleted ? 'dxScheduler-confirmRecurrenceDeleteSeries' : 'dxScheduler-confirmRecurrenceEditSeries'); + const occurrenceText = messageLocalization.format(isDeleted ? 'dxScheduler-confirmRecurrenceDeleteOccurrence' : 'dxScheduler-confirmRecurrenceEditOccurrence'); - const countConfig = this._getViewCountConfig(); + this._recurrenceDialog = customDialog({ + messageHtml: message, + showCloseButton: true, + showTitle: true, + buttons: [ + { text: seriesText, onClick() { return RECURRENCE_EDITING_MODE.SERIES; } }, + { text: occurrenceText, onClick() { return RECURRENCE_EDITING_MODE.OCCURENCE; } }, + ], + popupOptions: { + wrapperAttr: { class: 'dx-dialog' }, + }, + } as any); - const workSpaceComponent = VIEWS_CONFIG[this._getCurrentViewType()].workSpace; - const workSpaceConfig = this._workSpaceConfig(groups, countConfig); - this._workSpace = this._createComponent($workSpace, workSpaceComponent, workSpaceConfig); + return this._recurrenceDialog.show(); + } - this._allowDragging() && this._workSpace.initDragBehavior(this, this._all); - this._workSpace._attachTablesEvents(); - this._workSpace.getWorkArea().append(this._appointments.$element()); + _getUpdatedData(rawAppointment) { + const getConvertedFromGrid = (date) => (date + ? this.timeZoneCalculator.createDate(date, { path: 'fromGrid' }) + : undefined); - this._recalculateWorkspace(); - countConfig.startDate && this._header?.option('currentDate', this._workSpace._getHeaderDate()); + const isValidDate = (date) => !isNaN(new Date(date).getTime()); - this._appointments.option('_collectorOffset', this.getCollectorOffset()); - } + const targetCell = this.getTargetCellData(); + const appointment = createAppointmentAdapter( + rawAppointment, + this._dataAccessors, + this.timeZoneCalculator, + ); - _getViewCountConfig() { - const currentView = this.option('currentView'); + const cellStartDate = getConvertedFromGrid(targetCell.startDate); + const cellEndDate = getConvertedFromGrid(targetCell.endDate); - const view = this._getViewByName(currentView); - const viewCount = view && view.intervalCount || 1; - const startDate = view && view.startDate || null; + let appointmentStartDate = new Date(appointment.startDate); + let appointmentEndDate = new Date(appointment.endDate); + let resultedStartDate = cellStartDate || appointmentStartDate; - return { - intervalCount: viewCount, - startDate: startDate - }; + if (!isValidDate(appointmentStartDate)) { + appointmentStartDate = resultedStartDate; } - _getViewByName(name) { - const views = this.option('views'); - - for(let i = 0; i < views.length; i++) { - if(views[i].name === name || views[i].type === name || views[i] === name) return views[i]; - } + if (!isValidDate(appointmentEndDate)) { + appointmentEndDate = cellEndDate; } - _recalculateWorkspace() { - this._workSpaceRecalculation = new Deferred(); - this._waitAsyncTemplate(() => { - triggerResizeEvent(this._workSpace.$element()); - this._workSpace._refreshDateTimeIndication(); - }); - } + const duration = appointmentEndDate.getTime() - appointmentStartDate.getTime(); - _workSpaceConfig(groups, countConfig) { - const currentViewOptions = this._getCurrentViewOptions(); - const scrolling = this.option('scrolling'); - const isVirtualScrolling = scrolling.mode === 'virtual' || - currentViewOptions.scrolling?.mode === 'virtual'; - const horizontalVirtualScrollingAllowed = isVirtualScrolling && - ( - !isDefined(scrolling.orientation) || - ['horizontal', 'both'].filter( - item => scrolling.orientation === item || currentViewOptions.scrolling?.orientation === item - ).length > 0 - ); - const crossScrollingEnabled = this.option('crossScrollingEnabled') - || horizontalVirtualScrollingAllowed - || isTimelineView(this.currentViewType); + const isKeepAppointmentHours = this._workSpace.keepOriginalHours() + && isValidDate(appointment.startDate) + && isValidDate(cellStartDate); - const result = extend({ - resources: this.option('resources'), - loadedResources: this.option('loadedResources'), - getFilteredItems: () => this.filteredItems, - getResourceDataAccessors: this.getResourceDataAccessors.bind(this), - - noDataText: this.option('noDataText'), - firstDayOfWeek: this.option('firstDayOfWeek'), - startDayHour: this.option('startDayHour'), - endDayHour: this.option('endDayHour'), - tabIndex: this.option('tabIndex'), - accessKey: this.option('accessKey'), - focusStateEnabled: this.option('focusStateEnabled'), - cellDuration: this.option('cellDuration'), - showAllDayPanel: this.option('showAllDayPanel'), - showCurrentTimeIndicator: this.option('showCurrentTimeIndicator'), - indicatorTime: this.option('indicatorTime'), - indicatorUpdateInterval: this.option('indicatorUpdateInterval'), - shadeUntilCurrentTime: this.option('shadeUntilCurrentTime'), - allDayExpanded: this._appointments.option('items'), - crossScrollingEnabled, - dataCellTemplate: this.option('dataCellTemplate'), - timeCellTemplate: this.option('timeCellTemplate'), - resourceCellTemplate: this.option('resourceCellTemplate'), - dateCellTemplate: this.option('dateCellTemplate'), - allowMultipleCellSelection: this.option('allowMultipleCellSelection'), - selectedCellData: this.option('selectedCellData'), - onSelectionChanged: (args) => { - this.option('selectedCellData', args.selectedCellData); - }, - groupByDate: this._getCurrentViewOption('groupByDate'), - scrolling, - draggingMode: this.option('_draggingMode'), - timeZoneCalculator: this.timeZoneCalculator, - schedulerHeight: this.option('height'), - schedulerWidth: this.option('width'), - allDayPanelMode: this.option('allDayPanelMode'), - onSelectedCellsClick: this.showAddAppointmentPopup.bind(this), - onRenderAppointments: this._renderAppointments.bind(this), - onShowAllDayPanel: (value) => this.option('showAllDayPanel', value), - getHeaderHeight: () => utils.DOM.getHeaderHeight(this._header), - onScrollEnd: () => this._appointments.updateResizableArea(), - - // TODO: SSR does not work correctly with renovated render - renovateRender: this._isRenovatedRender(isVirtualScrolling), - isRenovatedAppointments: this.option('isRenovatedAppointments'), - }, currentViewOptions); - - result.observer = this; - result.intervalCount = countConfig.intervalCount; - result.startDate = countConfig.startDate; - result.groups = groups; - result.onCellClick = this._createActionByOption('onCellClick'); - result.onCellContextMenu = this._createActionByOption('onCellContextMenu'); - result.currentDate = dateUtils.trimTime(new Date(this._dateOption('currentDate'))); - result.hoursInterval = result.cellDuration / 60; - result.allDayExpanded = false; - result.dataCellTemplate = result.dataCellTemplate ? this._getTemplate(result.dataCellTemplate) : null; - result.timeCellTemplate = result.timeCellTemplate ? this._getTemplate(result.timeCellTemplate) : null; - result.resourceCellTemplate = result.resourceCellTemplate ? this._getTemplate(result.resourceCellTemplate) : null; - result.dateCellTemplate = result.dateCellTemplate ? this._getTemplate(result.dateCellTemplate) : null; - result.getAppointmentDataProvider = () => this.appointmentDataProvider; + if (isKeepAppointmentHours) { + const { trimTime } = dateUtils; - return result; - } + const startDate = this.timeZoneCalculator.createDate(appointment.startDate, { path: 'toGrid' }); + const timeInMs = startDate.getTime() - trimTime(startDate).getTime(); - _isRenovatedRender(isVirtualScrolling) { - return (this.option('renovateRender') && hasWindow()) || isVirtualScrolling; + resultedStartDate = new Date(trimTime(targetCell.startDate).getTime() + timeInMs); + resultedStartDate = this.timeZoneCalculator.createDate(resultedStartDate, { path: 'fromGrid' }); } - _waitAsyncTemplate(callback) { - if(this._options.silent('templatesRenderAsynchronously')) { - const timer = setTimeout(() => { - callback(); - clearTimeout(timer); - }); - this._asyncTemplatesTimers.push(timer); - } else { - callback(); - } - } + const result = createAppointmentAdapter( + {}, + this._dataAccessors, + this.timeZoneCalculator, + ); - _getCurrentViewOptions() { - return this.currentView; + if (targetCell.allDay !== undefined) { + result.allDay = targetCell.allDay; } + result.startDate = resultedStartDate; - _getCurrentViewOption(optionName) { - if(this.currentView && this.currentView[optionName] !== undefined) { - return this.currentView[optionName]; - } + let resultedEndDate = new Date(resultedStartDate.getTime() + duration); - return this.option(optionName); + if (this.appointmentTakesAllDay(rawAppointment) && !result.allDay && this._workSpace.supportAllDayRow()) { + resultedEndDate = this._workSpace.calculateEndDate(resultedStartDate); } - _getAppointmentTemplate(optionName) { - const currentViewOptions = this._getCurrentViewOptions(); + if (appointment.allDay && !this._workSpace.supportAllDayRow() && !this._workSpace.keepOriginalHours()) { + const dateCopy = new Date(resultedStartDate); + dateCopy.setHours(0); - if(currentViewOptions && currentViewOptions[optionName]) { - return this._getTemplate(currentViewOptions[optionName]); - } + resultedEndDate = new Date(dateCopy.getTime() + duration); - return this._getTemplateByOption(optionName); + if (resultedEndDate.getHours() !== 0) { + resultedEndDate.setHours(this._getCurrentViewOption('endDayHour')); + } } - _updateOption(viewName, optionName, value) { - const currentViewOptions = this._getCurrentViewOptions(); + const timeZoneOffset = timeZoneUtils.getTimezoneOffsetChangeInMs(appointmentStartDate, appointmentEndDate, resultedStartDate, resultedEndDate); + result.endDate = new Date(resultedEndDate.getTime() - timeZoneOffset); - if(!currentViewOptions || !isDefined(currentViewOptions[optionName])) { - this['_' + viewName].option(optionName, value); - } - } + const rawResult = result.source(); - _refreshWorkSpace(groups) { - this._cleanWorkspace(); + setResourceToAppointment(this.option('resources'), this.getResourceDataAccessors(), rawResult, targetCell.groups); - delete this._workSpace; + return rawResult; + } - this._renderWorkSpace(groups); + getTargetedAppointment(appointment, element) { + const settings: any = utils.dataAccessors.getAppointmentSettings(element); + const info = utils.dataAccessors.getAppointmentInfo(element); - if(this._readyToRenderAppointments) { - this._appointments.option({ - fixedContainer: this._workSpace.getFixedContainer(), - allDayContainer: this._workSpace.getAllDayContainer() - }); - this._waitAsyncTemplate(() => this._workSpaceRecalculation.resolve()); - } - } + const appointmentIndex = $(element).data(this._appointments._itemIndexKey()); - _cleanWorkspace() { - this._appointments.$element().detach(); - this._workSpace._dispose(); - this._workSpace.$element().remove(); + const adapter = createAppointmentAdapter( + appointment, + this._dataAccessors, + this.timeZoneCalculator, + ); - this.option('selectedCellData', []); - } + const targetedAdapter = adapter.clone(); - getWorkSpaceScrollable() { - return this._workSpace.getScrollable(); - } + if (this._isAgenda() && adapter.isRecurrent) { + const { agendaSettings } = settings; - getWorkSpaceScrollableContainer() { - return this._workSpace.getScrollableContainer(); + targetedAdapter.startDate = ExpressionUtils.getField(this._dataAccessors, 'startDate', agendaSettings); + targetedAdapter.endDate = ExpressionUtils.getField(this._dataAccessors, 'endDate', agendaSettings); + } else if (settings) { + targetedAdapter.startDate = info ? info.sourceAppointment.startDate : adapter.startDate; // TODO: in agenda we havn't info field + targetedAdapter.endDate = info ? info.sourceAppointment.endDate : adapter.endDate; } - getWorkSpace() { - return this._workSpace; + const rawTargetedAppointment = targetedAdapter.source(); + if (element) { + this.setTargetedAppointmentResources(rawTargetedAppointment, element, appointmentIndex); } - getHeader() { - return this._header; + if (info) { + rawTargetedAppointment.displayStartDate = new Date(info.appointment.startDate); + rawTargetedAppointment.displayEndDate = new Date(info.appointment.endDate); } - _cleanPopup() { - this._appointmentPopup?.dispose(); - } + return rawTargetedAppointment; + } - _checkRecurringAppointment(rawAppointment, singleAppointment, exceptionDate, callback, isDeleted, isPopupEditing, dragEvent, recurrenceEditMode) { - const recurrenceRule = ExpressionUtils.getField(this._dataAccessors, 'recurrenceRule', rawAppointment); + subscribe(subject, action) { + this._subscribes[subject] = subscribes[subject] = action; + } - if(!getRecurrenceProcessor().evalRecurrenceRule(recurrenceRule).isValid || !this._editing.allowUpdating) { - callback(); - return; - } + fire(subject) { + const callback = this._subscribes[subject]; + const args = Array.prototype.slice.call(arguments); - const editMode = recurrenceEditMode || this.option('recurrenceEditMode'); - switch(editMode) { - case 'series': - callback(); - break; - case 'occurrence': - this._excludeAppointmentFromSeries(rawAppointment, singleAppointment, exceptionDate, isDeleted, isPopupEditing, dragEvent); - break; - default: - if(dragEvent) { - dragEvent.cancel = new Deferred(); - } - this._showRecurrenceChangeConfirm(isDeleted) - .done((editingMode) => { - editingMode === RECURRENCE_EDITING_MODE.SERIES && callback(); - - editingMode === RECURRENCE_EDITING_MODE.OCCURENCE && this._excludeAppointmentFromSeries( - rawAppointment, singleAppointment, exceptionDate, - isDeleted, isPopupEditing, dragEvent, - ); - }) - .fail(() => this._appointments.moveAppointmentBack(dragEvent)); - } + if (!isFunction(callback)) { + throw errors.Error('E1031', subject); } - _excludeAppointmentFromSeries(rawAppointment, newRawAppointment, exceptionDate, isDeleted, isPopupEditing, dragEvent) { - const appointment = excludeFromRecurrence( - rawAppointment, - exceptionDate, - this._dataAccessors, - this._timeZoneCalculator, - ); + return callback.apply(this, args.slice(1)); + } - const singleRawAppointment = { ...newRawAppointment }; - delete singleRawAppointment[this._dataAccessors.expr.recurrenceExceptionExpr]; - delete singleRawAppointment[this._dataAccessors.expr.recurrenceRuleExpr]; + getTargetCellData() { + return this._workSpace.getDataByDroppableCell(); + } - const keyPropertyName = this.appointmentDataProvider.keyName; - delete singleRawAppointment[keyPropertyName]; + _updateAppointment(target, rawAppointment, onUpdatePrevented?: any, dragEvent?: any) { + const updatingOptions = { + newData: rawAppointment, + oldData: extend({}, target), + cancel: false, + }; - const canCreateNewAppointment = !isDeleted && !isPopupEditing; - if(canCreateNewAppointment) { - this.addAppointment(singleRawAppointment); - } - - if(isPopupEditing) { - this._appointmentPopup.show(singleRawAppointment, { - isToolbarVisible: true, - action: ACTION_TO_APPOINTMENT.EXCLUDE_FROM_SERIES, - excludeInfo: { - sourceAppointment: rawAppointment, - updatedAppointment: appointment.source() - } - }); - this._editAppointmentData = rawAppointment; - } else { - this._updateAppointment(rawAppointment, appointment.source(), () => { - this._appointments.moveAppointmentBack(dragEvent); - }, dragEvent); - } - } - - _createRecurrenceException(appointment, exceptionDate) { - const result = []; + const performFailAction = function (err?: any) { + if (onUpdatePrevented) { + onUpdatePrevented.call(this); + } - if(appointment.recurrenceException) { - result.push(appointment.recurrenceException); - } - result.push(this._getSerializedDate(exceptionDate, appointment.startDate, appointment.allDay)); - - return result.join(); - } - - _getSerializedDate(date, startDate, isAllDay) { - isAllDay && date.setHours(startDate.getHours(), - startDate.getMinutes(), - startDate.getSeconds(), - startDate.getMilliseconds()); + if (err && err.name === 'Error') { + throw err; + } + }.bind(this); - return dateSerialization.serializeDate(date, UTC_FULL_DATE_FORMAT); - } - - _showRecurrenceChangeConfirm(isDeleted) { - const message = messageLocalization.format(isDeleted ? 'dxScheduler-confirmRecurrenceDeleteMessage' : 'dxScheduler-confirmRecurrenceEditMessage'); - const seriesText = messageLocalization.format(isDeleted ? 'dxScheduler-confirmRecurrenceDeleteSeries' : 'dxScheduler-confirmRecurrenceEditSeries'); - const occurrenceText = messageLocalization.format(isDeleted ? 'dxScheduler-confirmRecurrenceDeleteOccurrence' : 'dxScheduler-confirmRecurrenceEditOccurrence'); - - this._recurrenceDialog = customDialog({ - messageHtml: message, - showCloseButton: true, - showTitle: true, - buttons: [ - { text: seriesText, onClick: function() { return RECURRENCE_EDITING_MODE.SERIES; } }, - { text: occurrenceText, onClick: function() { return RECURRENCE_EDITING_MODE.OCCURENCE; } } - ], - popupOptions: { - wrapperAttr: { class: 'dx-dialog' } - } - }); + this._actions[StoreEventNames.UPDATING](updatingOptions); - return this._recurrenceDialog.show(); + if (dragEvent && !isDeferred(dragEvent.cancel)) { + // @ts-expect-error + dragEvent.cancel = new Deferred(); } - _getUpdatedData(rawAppointment) { - const getConvertedFromGrid = date => date - ? this.timeZoneCalculator.createDate(date, { path: 'fromGrid' }) - : undefined; + return this._processActionResult(updatingOptions, function (canceled) { + // @ts-expect-error + let deferred = new Deferred(); - const isValidDate = date => !isNaN(new Date(date).getTime()); + if (!canceled) { + this._expandAllDayPanel(rawAppointment); - const targetCell = this.getTargetCellData(); - const appointment = createAppointmentAdapter( - rawAppointment, - this._dataAccessors, - this.timeZoneCalculator - ); - - const cellStartDate = getConvertedFromGrid(targetCell.startDate); - const cellEndDate = getConvertedFromGrid(targetCell.endDate); - - let appointmentStartDate = new Date(appointment.startDate); - let appointmentEndDate = new Date(appointment.endDate); - let resultedStartDate = cellStartDate || appointmentStartDate; - - if(!isValidDate(appointmentStartDate)) { - appointmentStartDate = resultedStartDate; + try { + deferred = this.appointmentDataProvider + .update(target, rawAppointment) + .done(() => { + dragEvent && dragEvent.cancel.resolve(false); + }) + .always((storeAppointment) => this._onDataPromiseCompleted(StoreEventNames.UPDATED, storeAppointment)) + .fail(() => performFailAction()); + } catch (err) { + performFailAction(err); + deferred.resolve(); } - - if(!isValidDate(appointmentEndDate)) { - appointmentEndDate = cellEndDate; + } else { + performFailAction(); + deferred.resolve(); + } + + return deferred.promise(); + }); + } + + _processActionResult(actionOptions, callback) { + // @ts-expect-error + const deferred = new Deferred(); + const resolveCallback = (callbackResult) => { + when(fromPromise(callbackResult)) + .always(deferred.resolve); + }; + + if (isPromise(actionOptions.cancel)) { + when(fromPromise(actionOptions.cancel)).always((cancel) => { + if (!isDefined(cancel)) { + cancel = actionOptions.cancel.state() === 'rejected'; } - - const duration = appointmentEndDate.getTime() - appointmentStartDate.getTime(); - - const isKeepAppointmentHours = this._workSpace.keepOriginalHours() - && isValidDate(appointment.startDate) - && isValidDate(cellStartDate); - - if(isKeepAppointmentHours) { - const { trimTime } = dateUtils; - - const startDate = this.timeZoneCalculator.createDate(appointment.startDate, { path: 'toGrid' }); - const timeInMs = startDate.getTime() - trimTime(startDate).getTime(); - - resultedStartDate = new Date(trimTime(targetCell.startDate).getTime() + timeInMs); - resultedStartDate = this.timeZoneCalculator.createDate(resultedStartDate, { path: 'fromGrid' }); - } - - const result = createAppointmentAdapter( - {}, - this._dataAccessors, - this.timeZoneCalculator - ); - - if(targetCell.allDay !== undefined) { - result.allDay = targetCell.allDay; - } - result.startDate = resultedStartDate; - - let resultedEndDate = new Date(resultedStartDate.getTime() + duration); - - if(this.appointmentTakesAllDay(rawAppointment) && !result.allDay && this._workSpace.supportAllDayRow()) { - resultedEndDate = this._workSpace.calculateEndDate(resultedStartDate); - } - - if(appointment.allDay && !this._workSpace.supportAllDayRow() && !this._workSpace.keepOriginalHours()) { - const dateCopy = new Date(resultedStartDate); - dateCopy.setHours(0); - - resultedEndDate = new Date(dateCopy.getTime() + duration); - - if(resultedEndDate.getHours() !== 0) { - resultedEndDate.setHours(this._getCurrentViewOption('endDayHour')); - } - } - - const timeZoneOffset = timeZoneUtils.getTimezoneOffsetChangeInMs(appointmentStartDate, appointmentEndDate, resultedStartDate, resultedEndDate); - result.endDate = new Date(resultedEndDate.getTime() - timeZoneOffset); - - const rawResult = result.source(); - - setResourceToAppointment(this.option('resources'), this.getResourceDataAccessors(), rawResult, targetCell.groups); - - return rawResult; + resolveCallback(callback.call(this, cancel)); + }); + } else { + resolveCallback(callback.call(this, actionOptions.cancel)); } - getTargetedAppointment(appointment, element) { - const settings = utils.dataAccessors.getAppointmentSettings(element); - const info = utils.dataAccessors.getAppointmentInfo(element); + return deferred.promise(); + } - const appointmentIndex = $(element).data(this._appointments._itemIndexKey()); - - const adapter = createAppointmentAdapter( - appointment, - this._dataAccessors, - this.timeZoneCalculator - ); - - const targetedAdapter = adapter.clone(); - - if(this._isAgenda() && adapter.isRecurrent) { - const agendaSettings = settings.agendaSettings; - - targetedAdapter.startDate = ExpressionUtils.getField(this._dataAccessors, 'startDate', agendaSettings); - targetedAdapter.endDate = ExpressionUtils.getField(this._dataAccessors, 'endDate', agendaSettings); - - } else if(settings) { - targetedAdapter.startDate = info ? info.sourceAppointment.startDate : adapter.startDate; // TODO: in agenda we havn't info field - targetedAdapter.endDate = info ? info.sourceAppointment.endDate : adapter.endDate; - } - - const rawTargetedAppointment = targetedAdapter.source(); - if(element) { - this.setTargetedAppointmentResources(rawTargetedAppointment, element, appointmentIndex); - } - - if(info) { - rawTargetedAppointment.displayStartDate = new Date(info.appointment.startDate); - rawTargetedAppointment.displayEndDate = new Date(info.appointment.endDate); - } - - return rawTargetedAppointment; + _expandAllDayPanel(appointment) { + if (!this._isAllDayExpanded() && this.appointmentTakesAllDay(appointment)) { + this._workSpace.option('allDayExpanded', true); } + } - subscribe(subject, action) { - this._subscribes[subject] = subscribes[subject] = action; - } - - fire(subject) { - const callback = this._subscribes[subject]; - const args = Array.prototype.slice.call(arguments); - - if(!isFunction(callback)) { - throw errors.Error('E1031', subject); - } + _onDataPromiseCompleted(handlerName, storeAppointment, appointment?: any) { + const args: any = { appointmentData: appointment || storeAppointment }; - return callback.apply(this, args.slice(1)); + if (storeAppointment instanceof Error) { + args.error = storeAppointment; + } else { + this._appointmentPopup.visible && this._appointmentPopup.hide(); } - getTargetCellData() { - return this._workSpace.getDataByDroppableCell(); - } + this._actions[handlerName](args); + this._fireContentReadyAction(); + } - _updateAppointment(target, rawAppointment, onUpdatePrevented, dragEvent) { - const updatingOptions = { - newData: rawAppointment, - oldData: extend({}, target), - cancel: false - }; + /// #DEBUG + getAppointmentDetailsForm() { // TODO for tests + return this._appointmentForm.form; + } + /// #ENDDEBUG - const performFailAction = function(err) { - if(onUpdatePrevented) { - onUpdatePrevented.call(this); - } + getAppointmentsInstance() { + return this._appointments; + } - if(err && err.name === 'Error') { - throw err; - } - }.bind(this); + getLayoutManager() { + return this._layoutManager; + } - this._actions[StoreEventNames.UPDATING](updatingOptions); + getRenderingStrategyInstance() { + return this.getLayoutManager().getRenderingStrategyInstance(); + } - if(dragEvent && !isDeferred(dragEvent.cancel)) { - dragEvent.cancel = new Deferred(); - } + getActions() { + return this._actions; + } - return this._processActionResult(updatingOptions, function(canceled) { - let deferred = new Deferred(); - - if(!canceled) { - this._expandAllDayPanel(rawAppointment); - - try { - deferred = this.appointmentDataProvider - .update(target, rawAppointment) - .done(() => { - dragEvent && dragEvent.cancel.resolve(false); - }) - .always(storeAppointment => this._onDataPromiseCompleted(StoreEventNames.UPDATED, storeAppointment)) - .fail(() => performFailAction()); - } catch(err) { - performFailAction(err); - deferred.resolve(); - } - } else { - performFailAction(); - deferred.resolve(); - } - - return deferred.promise(); - }); - } + appointmentTakesAllDay(rawAppointment) { + const appointment = createAppointmentAdapter( + rawAppointment, + this._dataAccessors, + this.timeZoneCalculator, + ); - _processActionResult(actionOptions, callback) { - const deferred = new Deferred(); - const resolveCallback = callbackResult => { - when(fromPromise(callbackResult)) - .always(deferred.resolve); - }; + return getAppointmentTakesAllDay( + appointment, + this._getCurrentViewOption('startDayHour'), + this._getCurrentViewOption('endDayHour'), + this._getCurrentViewOption('allDayPanelMode'), + ); + } - if(isPromise(actionOptions.cancel)) { - when(fromPromise(actionOptions.cancel)).always((cancel) => { - if(!isDefined(cancel)) { - cancel = actionOptions.cancel.state() === 'rejected'; - } - resolveCallback(callback.call(this, cancel)); - }); - } else { - resolveCallback(callback.call(this, actionOptions.cancel)); - } + dayHasAppointment(day, rawAppointment, trimTime) { + const getConvertedToTimeZone = (date) => this.timeZoneCalculator.createDate(date, { path: 'toGrid' }); - return deferred.promise(); - } + const appointment = createAppointmentAdapter( + rawAppointment, + this._dataAccessors, + this.timeZoneCalculator, + ); - _expandAllDayPanel(appointment) { - if(!this._isAllDayExpanded() && this.appointmentTakesAllDay(appointment)) { - this._workSpace.option('allDayExpanded', true); - } - } + let startDate = new Date(appointment.startDate); + let endDate = new Date(appointment.endDate); - _onDataPromiseCompleted(handlerName, storeAppointment, appointment) { - const args = { appointmentData: appointment || storeAppointment }; + startDate = getConvertedToTimeZone(startDate); + endDate = getConvertedToTimeZone(endDate); - if(storeAppointment instanceof Error) { - args.error = storeAppointment; - } else { - this._appointmentPopup.visible && this._appointmentPopup.hide(); - } - - this._actions[handlerName](args); - this._fireContentReadyAction(); + if (day.getTime() === endDate.getTime()) { + return startDate.getTime() === endDate.getTime(); } - ///#DEBUG - getAppointmentDetailsForm() { // TODO for tests - return this._appointmentForm.form; + if (trimTime) { + day = dateUtils.trimTime(day); + startDate = dateUtils.trimTime(startDate); + endDate = dateUtils.trimTime(endDate); } - ///#ENDDEBUG - getAppointmentsInstance() { - return this._appointments; - } + const dayTimeStamp = day.getTime(); + const startDateTimeStamp = startDate.getTime(); + const endDateTimeStamp = endDate.getTime(); - getLayoutManager() { - return this._layoutManager; - } + return startDateTimeStamp <= dayTimeStamp && dayTimeStamp <= endDateTimeStamp; + } - getRenderingStrategyInstance() { - return this.getLayoutManager().getRenderingStrategyInstance(); - } + setTargetedAppointmentResources(rawAppointment, element, appointmentIndex) { + const groups = this._getCurrentViewOption('groups'); - getActions() { - return this._actions; - } + if (groups?.length) { + const resourcesSetter = this.getResourceDataAccessors().setter; + const workSpace = this._workSpace; + let getGroups; + let setResourceCallback; - appointmentTakesAllDay(rawAppointment) { - const appointment = createAppointmentAdapter( - rawAppointment, - this._dataAccessors, - this.timeZoneCalculator - ); + if (this._isAgenda()) { + getGroups = function () { + const apptSettings = this.getLayoutManager()._positionMap[appointmentIndex]; - return getAppointmentTakesAllDay( - appointment, - this._getCurrentViewOption('startDayHour'), - this._getCurrentViewOption('endDayHour'), - this._getCurrentViewOption('allDayPanelMode'), - ); - } - - dayHasAppointment(day, rawAppointment, trimTime) { - const getConvertedToTimeZone = date => { - return this.timeZoneCalculator.createDate(date, { path: 'toGrid' }); + return getCellGroups( + apptSettings[0].groupIndex, + this.getWorkSpace().option('groups'), + ); }; - const appointment = createAppointmentAdapter( - rawAppointment, - this._dataAccessors, - this.timeZoneCalculator - ); - - let startDate = new Date(appointment.startDate); - let endDate = new Date(appointment.endDate); - - startDate = getConvertedToTimeZone(startDate); - endDate = getConvertedToTimeZone(endDate); - - if(day.getTime() === endDate.getTime()) { - return startDate.getTime() === endDate.getTime(); - } - - if(trimTime) { - day = dateUtils.trimTime(day); - startDate = dateUtils.trimTime(startDate); - endDate = dateUtils.trimTime(endDate); - } - - const dayTimeStamp = day.getTime(); - const startDateTimeStamp = startDate.getTime(); - const endDateTimeStamp = endDate.getTime(); + setResourceCallback = function (_, group) { + resourcesSetter[group.name](rawAppointment, group.id); + }; + } else { + getGroups = function () { + // TODO: in the future, necessary refactor the engine of determining groups + const setting: any = utils.dataAccessors.getAppointmentSettings(element) || {}; + return workSpace.getCellDataByCoordinates({ left: setting.left, top: setting.top }).groups; + }; + setResourceCallback = function (field, value) { + resourcesSetter[field](rawAppointment, value); + }; + } - return startDateTimeStamp <= dayTimeStamp && dayTimeStamp <= endDateTimeStamp; + each(getGroups.call(this), setResourceCallback); } + } - setTargetedAppointmentResources(rawAppointment, element, appointmentIndex) { - const groups = this._getCurrentViewOption('groups'); - - if(groups?.length) { - const resourcesSetter = this.getResourceDataAccessors().setter; - const workSpace = this._workSpace; - let getGroups; - let setResourceCallback; - - if(this._isAgenda()) { - getGroups = function() { - const apptSettings = this.getLayoutManager()._positionMap[appointmentIndex]; - - return getCellGroups( - apptSettings[0].groupIndex, - this.getWorkSpace().option('groups') - ); - }; - - setResourceCallback = function(_, group) { - resourcesSetter[group.name](rawAppointment, group.id); - }; - } else { - getGroups = function() { - // TODO: in the future, necessary refactor the engine of determining groups - const setting = utils.dataAccessors.getAppointmentSettings(element) || {}; - return workSpace.getCellDataByCoordinates({ left: setting.left, top: setting.top }).groups; - }; - - setResourceCallback = function(field, value) { - resourcesSetter[field](rawAppointment, value); - }; - } - - each(getGroups.call(this), setResourceCallback); - } - } + getStartViewDate() { + return this._workSpace?.getStartViewDate(); + } - getStartViewDate() { - return this._workSpace?.getStartViewDate(); - } + getEndViewDate() { + return this._workSpace.getEndViewDate(); + } - getEndViewDate() { - return this._workSpace.getEndViewDate(); - } + showAddAppointmentPopup(cellData, cellGroups) { + const appointmentAdapter = createAppointmentAdapter( + {}, + this._dataAccessors, + this.timeZoneCalculator, + ); - showAddAppointmentPopup(cellData, cellGroups) { - const appointmentAdapter = createAppointmentAdapter( - {}, - this._dataAccessors, - this.timeZoneCalculator - ); + appointmentAdapter.allDay = cellData.allDay; + appointmentAdapter.startDate = this.timeZoneCalculator.createDate(cellData.startDate, { path: 'fromGrid' }); + appointmentAdapter.endDate = this.timeZoneCalculator.createDate(cellData.endDate, { path: 'fromGrid' }); - appointmentAdapter.allDay = cellData.allDay; - appointmentAdapter.startDate = this.timeZoneCalculator.createDate(cellData.startDate, { path: 'fromGrid' }); - appointmentAdapter.endDate = this.timeZoneCalculator.createDate(cellData.endDate, { path: 'fromGrid' }); + const resultAppointment = extend(appointmentAdapter.source(), cellGroups); + this.showAppointmentPopup(resultAppointment, true); + } - const resultAppointment = extend(appointmentAdapter.source(), cellGroups); - this.showAppointmentPopup(resultAppointment, true); + showAppointmentPopup(rawAppointment, createNewAppointment, rawTargetedAppointment?: any) { + const newRawTargetedAppointment = { ...rawTargetedAppointment }; + if (newRawTargetedAppointment) { + delete newRawTargetedAppointment.displayStartDate; + delete newRawTargetedAppointment.displayEndDate; } - showAppointmentPopup(rawAppointment, createNewAppointment, rawTargetedAppointment) { - const newRawTargetedAppointment = { ...rawTargetedAppointment }; - if(newRawTargetedAppointment) { - delete newRawTargetedAppointment.displayStartDate; - delete newRawTargetedAppointment.displayEndDate; - } + const appointment = createAppointmentAdapter( + newRawTargetedAppointment || rawAppointment, + this._dataAccessors, + this.timeZoneCalculator, + ); - const appointment = createAppointmentAdapter( - (newRawTargetedAppointment || rawAppointment), - this._dataAccessors, - this.timeZoneCalculator - ); + const newTargetedAppointment = extend({}, rawAppointment, newRawTargetedAppointment); - const newTargetedAppointment = extend({}, rawAppointment, newRawTargetedAppointment); + const isCreateAppointment = createNewAppointment ?? isEmptyObject(rawAppointment); - const isCreateAppointment = createNewAppointment ?? isEmptyObject(rawAppointment); + if (isEmptyObject(rawAppointment)) { + rawAppointment = this.createPopupAppointment(); + } - if(isEmptyObject(rawAppointment)) { - rawAppointment = this.createPopupAppointment(); - } + if (isCreateAppointment) { + delete this._editAppointmentData; // TODO + this._editing.allowAdding && this._appointmentPopup.show(rawAppointment, { + isToolbarVisible: true, + action: ACTION_TO_APPOINTMENT.CREATE, + }); + } else { + this._checkRecurringAppointment(rawAppointment, newTargetedAppointment, appointment.startDate, () => { + this._editAppointmentData = rawAppointment; // TODO - if(isCreateAppointment) { - delete this._editAppointmentData; // TODO - this._editing.allowAdding && this._appointmentPopup.show(rawAppointment, { - isToolbarVisible: true, - action: ACTION_TO_APPOINTMENT.CREATE - }); - } else { - this._checkRecurringAppointment(rawAppointment, newTargetedAppointment, appointment.startDate, () => { - this._editAppointmentData = rawAppointment; // TODO - - this._appointmentPopup.show(rawAppointment, { - isToolbarVisible: this._editing.allowUpdating, - action: ACTION_TO_APPOINTMENT.UPDATE, - }); - }, false, true); - } + this._appointmentPopup.show(rawAppointment, { + isToolbarVisible: this._editing.allowUpdating, + action: ACTION_TO_APPOINTMENT.UPDATE, + }); + }, false, true); } + } - createPopupAppointment() { - const result = {}; - const toMs = dateUtils.dateToMilliseconds; + createPopupAppointment() { + const result = {}; + const toMs = dateUtils.dateToMilliseconds; - const startDate = new Date(this.option('currentDate')); - const endDate = new Date(startDate.getTime() + this.option('cellDuration') * toMs('minute')); + const startDate = new Date(this.option('currentDate')); + const endDate = new Date(startDate.getTime() + this.option('cellDuration') * toMs('minute')); - ExpressionUtils.setField(this._dataAccessors, 'startDate', result, startDate); - ExpressionUtils.setField(this._dataAccessors, 'endDate', result, endDate); + ExpressionUtils.setField(this._dataAccessors, 'startDate', result, startDate); + ExpressionUtils.setField(this._dataAccessors, 'endDate', result, endDate); - return result; - } + return result; + } - hideAppointmentPopup(saveChanges) { - if(this._appointmentPopup?.visible) { - saveChanges && this._appointmentPopup.saveChangesAsync(); - this._appointmentPopup.hide(); - } + hideAppointmentPopup(saveChanges?: any) { + if (this._appointmentPopup?.visible) { + saveChanges && this._appointmentPopup.saveChangesAsync(); + this._appointmentPopup.hide(); } + } - showAppointmentTooltip(appointment, element, targetedAppointment) { - if(appointment) { - const settings = utils.dataAccessors.getAppointmentSettings(element); + showAppointmentTooltip(appointment, element, targetedAppointment) { + if (appointment) { + const settings: any = utils.dataAccessors.getAppointmentSettings(element); - const appointmentConfig = { - itemData: targetedAppointment || appointment, - groupIndex: settings?.groupIndex, - groups: this.option('groups') - }; + const appointmentConfig = { + itemData: targetedAppointment || appointment, + groupIndex: settings?.groupIndex, + groups: this.option('groups'), + }; - const getAppointmentColor = this.createGetAppointmentColor(); - const deferredColor = getAppointmentColor(appointmentConfig); + const getAppointmentColor = this.createGetAppointmentColor(); + const deferredColor = getAppointmentColor(appointmentConfig); - const info = new AppointmentTooltipInfo(appointment, targetedAppointment, deferredColor); - this.showAppointmentTooltipCore(element, [info]); - } + const info = new AppointmentTooltipInfo(appointment, targetedAppointment, deferredColor); + this.showAppointmentTooltipCore(element, [info]); } + } - createGetAppointmentColor() { - return (appointmentConfig) => { - const resourceConfig = { - resources: this.option('resources'), - dataAccessors: this.getResourceDataAccessors(), - loadedResources: this.option('loadedResources'), - resourceLoaderMap: this.option('resourceLoaderMap') - }; + createGetAppointmentColor() { + return (appointmentConfig) => { + const resourceConfig = { + resources: this.option('resources'), + dataAccessors: this.getResourceDataAccessors(), + loadedResources: this.option('loadedResources'), + resourceLoaderMap: this.option('resourceLoaderMap'), + }; - return getAppointmentColor(resourceConfig, appointmentConfig); - }; - } + return getAppointmentColor(resourceConfig, appointmentConfig); + }; + } - showAppointmentTooltipCore(target, data, options) { - const arg = { - cancel: false, - appointments: data.map(item => { - const result = { - appointmentData: item.appointment, - currentAppointmentData: { ...item.targetedAppointment }, - color: item.color - }; - - if(item.settings.info) { - const { startDate, endDate } = item.settings.info.appointment; - - result.currentAppointmentData.displayStartDate = startDate; - result.currentAppointmentData.displayEndDate = endDate; - } - - return result; - }), - targetElement: target + showAppointmentTooltipCore(target, data, options?: any) { + const arg = { + cancel: false, + appointments: data.map((item) => { + const result = { + appointmentData: item.appointment, + currentAppointmentData: { ...item.targetedAppointment }, + color: item.color, }; - this._createActionByOption('onAppointmentTooltipShowing')(arg); + if (item.settings.info) { + const { startDate, endDate } = item.settings.info.appointment; - if(this._appointmentTooltip.isAlreadyShown(target)) { - this.hideAppointmentTooltip(); - } else { - this._processActionResult(arg, canceled => { - !canceled && this._appointmentTooltip.show( - target, data, { ...this._getExtraAppointmentTooltipOptions(), ...options } - ); - }); + result.currentAppointmentData.displayStartDate = startDate; + result.currentAppointmentData.displayEndDate = endDate; } - } - hideAppointmentTooltip() { - this._appointmentTooltip && this._appointmentTooltip.hide(); - } - - scrollToTime(hours, minutes, date) { - errors.log('W0002', 'dxScheduler', 'scrollToTime', '21.1', 'Use the "scrollTo" method instead'); - this._workSpace.scrollToTime(hours, minutes, date); - } - - scrollTo(date, groups, allDay) { - this._workSpace.scrollTo(date, groups, allDay); - } - - _isHorizontalVirtualScrolling() { - const scrolling = this.option('scrolling'); - const { orientation, mode } = scrolling; - const isVirtualScrolling = mode === 'virtual'; - - return isVirtualScrolling && - (orientation === 'horizontal' || orientation === 'both'); - } - - addAppointment(rawAppointment) { - const appointment = createAppointmentAdapter( + return result; + }), + targetElement: target, + }; + + this._createActionByOption('onAppointmentTooltipShowing')(arg); + + if (this._appointmentTooltip.isAlreadyShown(target)) { + this.hideAppointmentTooltip(); + } else { + this._processActionResult(arg, (canceled) => { + !canceled && this._appointmentTooltip.show(target, data, { ...this._getExtraAppointmentTooltipOptions(), ...options }); + }); + } + } + + hideAppointmentTooltip() { + this._appointmentTooltip && this._appointmentTooltip.hide(); + } + + scrollToTime(hours, minutes, date) { + errors.log('W0002', 'dxScheduler', 'scrollToTime', '21.1', 'Use the "scrollTo" method instead'); + this._workSpace.scrollToTime(hours, minutes, date); + } + + scrollTo(date, groups, allDay) { + this._workSpace.scrollTo(date, groups, allDay); + } + + _isHorizontalVirtualScrolling() { + const scrolling = this.option('scrolling'); + const { orientation, mode } = scrolling; + const isVirtualScrolling = mode === 'virtual'; + + return isVirtualScrolling + && (orientation === 'horizontal' || orientation === 'both'); + } + + addAppointment(rawAppointment) { + const appointment = createAppointmentAdapter( + rawAppointment, + this._dataAccessors, + this.timeZoneCalculator, + ); + appointment.text = appointment.text || ''; + + const serializedAppointment = appointment.source(true); + + const addingOptions = { + appointmentData: serializedAppointment, + cancel: false, + }; + + this._actions[StoreEventNames.ADDING](addingOptions); + + return this._processActionResult(addingOptions, (canceled) => { + if (canceled) { + // @ts-expect-error + return new Deferred().resolve(); + } + + this._expandAllDayPanel(serializedAppointment); + + return this.appointmentDataProvider + .add(serializedAppointment) + .always((storeAppointment) => this._onDataPromiseCompleted(StoreEventNames.ADDED, storeAppointment)); + }); + } + + updateAppointment(target, appointment) { + return this._updateAppointment(target, appointment); + } + + deleteAppointment(rawAppointment) { + const deletingOptions = this.fireOnAppointmentDeleting(rawAppointment); + this.processDeleteAppointment(rawAppointment, deletingOptions); + } + + fireOnAppointmentDeleting(rawAppointment, targetedAppointmentData?: any) { + const deletingOptions = { + appointmentData: rawAppointment, + targetedAppointmentData, + cancel: false, + }; + + this._actions[StoreEventNames.DELETING](deletingOptions); + + return deletingOptions; + } + + processDeleteAppointment(rawAppointment, deletingOptions) { + this._processActionResult(deletingOptions, function (canceled) { + if (!canceled) { + this.appointmentDataProvider + .remove(rawAppointment) + .always((storeAppointment) => this._onDataPromiseCompleted( + StoreEventNames.DELETED, + storeAppointment, rawAppointment, - this._dataAccessors, - this.timeZoneCalculator + )); + } + }); + } + + deleteRecurrence( + appointment, + date, + recurrenceEditMode, + ) { + this._checkRecurringAppointment( + appointment, + { }, + date, + () => { + this.processDeleteAppointment( + appointment, + { cancel: false }, ); - appointment.text = appointment.text || ''; - - const serializedAppointment = appointment.source(true); - - const addingOptions = { - appointmentData: serializedAppointment, - cancel: false - }; - - this._actions[StoreEventNames.ADDING](addingOptions); + }, + true, + false, + null, + recurrenceEditMode, + ); + } - return this._processActionResult(addingOptions, canceled => { - if(canceled) { - return new Deferred().resolve(); - } - - this._expandAllDayPanel(serializedAppointment); - - return this.appointmentDataProvider - .add(serializedAppointment) - .always(storeAppointment => this._onDataPromiseCompleted(StoreEventNames.ADDED, storeAppointment)); - }); + focus() { + if (this._editAppointmentData) { + this._appointments.focus(); + } else { + this._workSpace.focus(); } + } - updateAppointment(target, appointment) { - return this._updateAppointment(target, appointment); - } + getFirstDayOfWeek() { + return isDefined(this.option('firstDayOfWeek')) + ? this.option('firstDayOfWeek') + : dateLocalization.firstDayOfWeekIndex(); + } - deleteAppointment(rawAppointment) { - const deletingOptions = this.fireOnAppointmentDeleting(rawAppointment); - this.processDeleteAppointment(rawAppointment, deletingOptions); + _validateKeyFieldIfAgendaExist() { + if (!this.appointmentDataProvider.isDataSourceInit) { + return; } - fireOnAppointmentDeleting(rawAppointment, targetedAppointmentData) { - const deletingOptions = { - appointmentData: rawAppointment, - targetedAppointmentData, - cancel: false - }; - - this._actions[StoreEventNames.DELETING](deletingOptions); + const hasAgendaView = !!this._getViewByName('agenda'); + const isKeyExist = !!this.appointmentDataProvider.keyName; - return deletingOptions; + if (hasAgendaView && !isKeyExist) { + errors.log('W1023'); } + } - processDeleteAppointment(rawAppointment, deletingOptions) { - this._processActionResult(deletingOptions, function(canceled) { - if(!canceled) { - this.appointmentDataProvider - .remove(rawAppointment) - .always(storeAppointment => this._onDataPromiseCompleted( - StoreEventNames.DELETED, - storeAppointment, - rawAppointment, - )); - } - }); - } + _validateCellDuration() { + const endDayHour = this._getCurrentViewOption('endDayHour'); + const startDayHour = this._getCurrentViewOption('startDayHour'); + const cellDuration = this._getCurrentViewOption('cellDuration'); - deleteRecurrence( - appointment, - date, - recurrenceEditMode, - ) { - this._checkRecurringAppointment( - appointment, - { }, - date, - () => { - this.processDeleteAppointment( - appointment, - { cancel: false }, - ); - }, - true, - false, - null, - recurrenceEditMode, - ); + if ((endDayHour - startDayHour) * MINUTES_IN_HOUR % cellDuration !== 0) { + errors.log('W1015'); } + } - focus() { - if(this._editAppointmentData) { - this._appointments.focus(); - } else { - this._workSpace.focus(); - } - } - - getFirstDayOfWeek() { - return isDefined(this.option('firstDayOfWeek')) - ? this.option('firstDayOfWeek') - : dateLocalization.firstDayOfWeekIndex(); - } + _validateDayHours() { + const startDayHour = this._getCurrentViewOption('startDayHour'); + const endDayHour = this._getCurrentViewOption('endDayHour'); - _validateKeyFieldIfAgendaExist() { - if(!this.appointmentDataProvider.isDataSourceInit) { - return; - } - - const hasAgendaView = !!this._getViewByName('agenda'); - const isKeyExist = !!this.appointmentDataProvider.keyName; - - if(hasAgendaView && !isKeyExist) { - errors.log('W1023'); - } - } - - _validateCellDuration() { - const endDayHour = this._getCurrentViewOption('endDayHour'); - const startDayHour = this._getCurrentViewOption('startDayHour'); - const cellDuration = this._getCurrentViewOption('cellDuration'); - - if((endDayHour - startDayHour) * MINUTES_IN_HOUR % cellDuration !== 0) { - errors.log('W1015'); - } - } - - _validateDayHours() { - const startDayHour = this._getCurrentViewOption('startDayHour'); - const endDayHour = this._getCurrentViewOption('endDayHour'); - - validateDayHours(startDayHour, endDayHour); - } - - _getDragBehavior() { - return this._workSpace.dragBehavior; - } + validateDayHours(startDayHour, endDayHour); + } - /** - * @name dxScheduler.registerKeyHandler - * @publicName registerKeyHandler(key, handler) - * @hidden - */ + _getDragBehavior() { + return this._workSpace.dragBehavior; + } } -Scheduler.include(DataHelperMixin); +(Scheduler as any).include(DataHelperMixin); registerComponent('dxScheduler', Scheduler); diff --git a/packages/devextreme/js/__internal/scheduler/m_subscribes.ts b/packages/devextreme/js/__internal/scheduler/m_subscribes.ts index eeae42d140b5..f3ae801a6498 100644 --- a/packages/devextreme/js/__internal/scheduler/m_subscribes.ts +++ b/packages/devextreme/js/__internal/scheduler/m_subscribes.ts @@ -1,328 +1,332 @@ -import $ from '../../core/renderer'; -import { isPlainObject } from '../../core/utils/type'; -import dateUtils from '../../core/utils/date'; -import { each } from '../../core/utils/iterator'; -import { extend } from '../../core/utils/extend'; -import { AGENDA_LAST_IN_DATE_APPOINTMENT_CLASS } from './classes'; -import { utils } from './utils'; -import { createAppointmentAdapter } from './appointmentAdapter'; -import { getFormatType, formatDates } from '../../__internal/scheduler/appointments/m_text_utils'; +import $ from '@js/core/renderer'; +import dateUtils from '@js/core/utils/date'; +import { extend } from '@js/core/utils/extend'; +import { each } from '@js/core/utils/iterator'; +import { isPlainObject } from '@js/core/utils/type'; + +import { formatDates, getFormatType } from './appointments/m_text_utils'; +import { createAppointmentAdapter } from './m_appointment_adapter'; +import { AGENDA_LAST_IN_DATE_APPOINTMENT_CLASS } from './m_classes'; +import { utils } from './m_utils'; const toMs = dateUtils.dateToMilliseconds; const subscribes = { - isCurrentViewAgenda: function() { - return this.currentViewType === 'agenda'; - }, - currentViewUpdated: function(currentView) { - this.option('currentView', currentView); - }, - - currentDateUpdated: function(date) { - this.option('currentDate', date); - }, - - getOption: function(name) { - return this.option(name); - }, - - getWorkspaceOption: function(name) { - return this.getWorkSpace().option(name); - }, - - isVirtualScrolling: function() { - return this.isVirtualScrolling(); - }, - - setCellDataCacheAlias: function(appointment, geometry) { - this._workSpace.setCellDataCacheAlias(appointment, geometry); - }, - - isGroupedByDate: function() { - return this.getWorkSpace().isGroupedByDate(); - }, - - showAppointmentTooltip: function(options) { - const targetedAppointment = this.getTargetedAppointment(options.data, options.target); - this.showAppointmentTooltip(options.data, options.target, targetedAppointment); - }, - - hideAppointmentTooltip: function() { - this.hideAppointmentTooltip(); - }, - - showEditAppointmentPopup: function(options) { - const targetedData = this.getTargetedAppointment(options.data, options.target); - this.showAppointmentPopup(options.data, false, targetedData); - }, - - updateAppointmentAfterResize: function(options) { - const info = utils.dataAccessors.getAppointmentInfo(options.$appointment); - const exceptionDate = info.sourceAppointment.exceptionDate; - - this._checkRecurringAppointment(options.target, options.data, exceptionDate, (function() { - this._updateAppointment(options.target, options.data, function() { - this._appointments.moveAppointmentBack(); - }); - }).bind(this)); - }, - - getUpdatedData: function(rawAppointment) { - return this._getUpdatedData(rawAppointment); - }, - - updateAppointmentAfterDrag: function({ event, element, rawAppointment, newCellIndex, oldCellIndex }) { - const info = utils.dataAccessors.getAppointmentInfo(element); - - const appointment = createAppointmentAdapter(rawAppointment, this._dataAccessors, this.timeZoneCalculator); - const targetedAppointment = createAppointmentAdapter( - extend({}, rawAppointment, this._getUpdatedData(rawAppointment)), - this._dataAccessors, - this.timeZoneCalculator, - ); - const targetedRawAppointment = targetedAppointment.source(); - - const becomeAllDay = targetedAppointment.allDay; - const wasAllDay = appointment.allDay; - - const movedBetweenAllDayAndSimple = this._workSpace.supportAllDayRow() && - (wasAllDay && !becomeAllDay || !wasAllDay && becomeAllDay); - - const isDragAndDropBetweenComponents = event.fromComponent !== event.toComponent; - - if(newCellIndex === -1) { - if(!isDragAndDropBetweenComponents) { // TODO dragging inside component - this._appointments.moveAppointmentBack(event); - } - return; - } - - if((newCellIndex !== oldCellIndex) || isDragAndDropBetweenComponents || movedBetweenAllDayAndSimple) { - this._checkRecurringAppointment(rawAppointment, targetedRawAppointment, info.sourceAppointment.exceptionDate, () => { - - this._updateAppointment(rawAppointment, targetedRawAppointment, function() { - this._appointments.moveAppointmentBack(event); - }, event); - }, undefined, undefined, event); - } else { - this._appointments.moveAppointmentBack(event); - } - }, - - onDeleteButtonPress: function(options) { - const targetedData = this.getTargetedAppointment(options.data, $(options.target)); - this.checkAndDeleteAppointment(options.data, targetedData); - - this.hideAppointmentTooltip(); - }, - - getTextAndFormatDate(appointmentRaw, targetedAppointmentRaw, format) { // TODO: rename to createFormattedDateText - const appointmentAdapter = createAppointmentAdapter(appointmentRaw, this._dataAccessors, this.timeZoneCalculator); - const targetedAdapter = createAppointmentAdapter( - (targetedAppointmentRaw || appointmentRaw), - this._dataAccessors, - this.timeZoneCalculator - ); - - // TODO pull out time zone converting from appointment adapter for knockout(T947938) - const startDate = this.timeZoneCalculator.createDate(targetedAdapter.startDate, { path: 'toGrid' }); - const endDate = this.timeZoneCalculator.createDate(targetedAdapter.endDate, { path: 'toGrid' }); - - const formatType = format || getFormatType(startDate, endDate, targetedAdapter.allDay, this.currentViewType !== 'month'); + isCurrentViewAgenda() { + return this.currentViewType === 'agenda'; + }, + currentViewUpdated(currentView) { + this.option('currentView', currentView); + }, + + currentDateUpdated(date) { + this.option('currentDate', date); + }, + + getOption(name) { + return this.option(name); + }, + + getWorkspaceOption(name) { + return this.getWorkSpace().option(name); + }, + + isVirtualScrolling() { + return this.isVirtualScrolling(); + }, + + setCellDataCacheAlias(appointment, geometry) { + this._workSpace.setCellDataCacheAlias(appointment, geometry); + }, + + isGroupedByDate() { + return this.getWorkSpace().isGroupedByDate(); + }, + + showAppointmentTooltip(options) { + const targetedAppointment = this.getTargetedAppointment(options.data, options.target); + this.showAppointmentTooltip(options.data, options.target, targetedAppointment); + }, + + hideAppointmentTooltip() { + this.hideAppointmentTooltip(); + }, + + showEditAppointmentPopup(options) { + const targetedData = this.getTargetedAppointment(options.data, options.target); + this.showAppointmentPopup(options.data, false, targetedData); + }, + + updateAppointmentAfterResize(options) { + const info = utils.dataAccessors.getAppointmentInfo(options.$appointment); + const { exceptionDate } = info.sourceAppointment; + + this._checkRecurringAppointment(options.target, options.data, exceptionDate, () => { + this._updateAppointment(options.target, options.data, function () { + this._appointments.moveAppointmentBack(); + }); + }); + }, + + getUpdatedData(rawAppointment) { + return this._getUpdatedData(rawAppointment); + }, + + updateAppointmentAfterDrag({ + event, element, rawAppointment, newCellIndex, oldCellIndex, + }) { + const info = utils.dataAccessors.getAppointmentInfo(element); + + const appointment = createAppointmentAdapter(rawAppointment, this._dataAccessors, this.timeZoneCalculator); + const targetedAppointment = createAppointmentAdapter( + extend({}, rawAppointment, this._getUpdatedData(rawAppointment)), + this._dataAccessors, + this.timeZoneCalculator, + ); + const targetedRawAppointment = targetedAppointment.source(); + + const becomeAllDay = targetedAppointment.allDay; + const wasAllDay = appointment.allDay; + + const movedBetweenAllDayAndSimple = this._workSpace.supportAllDayRow() + && (wasAllDay && !becomeAllDay || !wasAllDay && becomeAllDay); + + const isDragAndDropBetweenComponents = event.fromComponent !== event.toComponent; + + if (newCellIndex === -1) { + if (!isDragAndDropBetweenComponents) { // TODO dragging inside component + this._appointments.moveAppointmentBack(event); + } + return; + } + + if ((newCellIndex !== oldCellIndex) || isDragAndDropBetweenComponents || movedBetweenAllDayAndSimple) { + this._checkRecurringAppointment(rawAppointment, targetedRawAppointment, info.sourceAppointment.exceptionDate, () => { + this._updateAppointment(rawAppointment, targetedRawAppointment, function () { + this._appointments.moveAppointmentBack(event); + }, event); + }, undefined, undefined, event); + } else { + this._appointments.moveAppointmentBack(event); + } + }, + + onDeleteButtonPress(options) { + const targetedData = this.getTargetedAppointment(options.data, $(options.target)); + this.checkAndDeleteAppointment(options.data, targetedData); + + this.hideAppointmentTooltip(); + }, + + getTextAndFormatDate(appointmentRaw, targetedAppointmentRaw, format) { // TODO: rename to createFormattedDateText + const appointmentAdapter = createAppointmentAdapter(appointmentRaw, this._dataAccessors, this.timeZoneCalculator); + const targetedAdapter = createAppointmentAdapter( + targetedAppointmentRaw || appointmentRaw, + this._dataAccessors, + this.timeZoneCalculator, + ); + + // TODO pull out time zone converting from appointment adapter for knockout(T947938) + const startDate = this.timeZoneCalculator.createDate(targetedAdapter.startDate, { path: 'toGrid' }); + const endDate = this.timeZoneCalculator.createDate(targetedAdapter.endDate, { path: 'toGrid' }); + + const formatType = format || getFormatType(startDate, endDate, targetedAdapter.allDay, this.currentViewType !== 'month'); + + return { + text: targetedAdapter.text || appointmentAdapter.text, + formatDate: formatDates(startDate, endDate, formatType), + }; + }, + + _createAppointmentTitle(data) { + if (isPlainObject(data)) { + return data.text; + } + return String(data); + }, + + getResizableAppointmentArea(options) { + const { allDay } = options; + const groups = this._getCurrentViewOption('groups'); + + if (groups && groups.length) { + if (allDay || this.getLayoutManager().getRenderingStrategyInstance()._needHorizontalGroupBounds()) { + const horizontalGroupBounds = this._workSpace.getGroupBounds(options.coordinates); return { - text: targetedAdapter.text || appointmentAdapter.text, - formatDate: formatDates(startDate, endDate, formatType) + left: horizontalGroupBounds.left, + right: horizontalGroupBounds.right, + top: 0, + bottom: 0, }; - }, - - _createAppointmentTitle(data) { - if(isPlainObject(data)) { - return data.text; - } - - return String(data); - }, - - getResizableAppointmentArea: function(options) { - const allDay = options.allDay; - const groups = this._getCurrentViewOption('groups'); - - if(groups && groups.length) { - if(allDay || this.getLayoutManager().getRenderingStrategyInstance()._needHorizontalGroupBounds()) { - const horizontalGroupBounds = this._workSpace.getGroupBounds(options.coordinates); - return { - left: horizontalGroupBounds.left, - right: horizontalGroupBounds.right, - top: 0, - bottom: 0 - }; - } - - if(this.getLayoutManager().getRenderingStrategyInstance()._needVerticalGroupBounds(allDay) && this._workSpace._isVerticalGroupedWorkSpace()) { - const verticalGroupBounds = this._workSpace.getGroupBounds(options.coordinates); - return { - left: 0, - right: 0, - top: verticalGroupBounds.top, - bottom: verticalGroupBounds.bottom - }; - } - } - }, - - needRecalculateResizableArea: function() { - return this.getWorkSpace().needRecalculateResizableArea(); - }, - - getAppointmentGeometry: function(settings) { - return this.getLayoutManager().getRenderingStrategyInstance().getAppointmentGeometry(settings); - }, - - isAllDay: function(appointmentData) { - return this.getLayoutManager().getRenderingStrategyInstance().isAllDay(appointmentData); - }, - - getDeltaTime: function(e, initialSize, itemData) { - return this.getLayoutManager().getRenderingStrategyInstance().getDeltaTime(e, initialSize, itemData); - }, - - getDropDownAppointmentWidth: function(isAllDay) { - return this.getLayoutManager().getRenderingStrategyInstance().getDropDownAppointmentWidth(this._getViewCountConfig().intervalCount, isAllDay); - }, - - getDropDownAppointmentHeight: function() { - return this.getLayoutManager().getRenderingStrategyInstance().getDropDownAppointmentHeight(); - }, - - getCellWidth: function() { - return this.getWorkSpace().getCellWidth(); - }, - - getCellHeight: function() { - return this.getWorkSpace().getCellHeight(); - }, - - getMaxAppointmentCountPerCellByType: function(isAllDay) { - return this.getRenderingStrategyInstance()._getMaxAppointmentCountPerCellByType(isAllDay); - }, - - needCorrectAppointmentDates: function() { - return this.getRenderingStrategyInstance().needCorrectAppointmentDates(); - }, - - getRenderingStrategyDirection: function() { - return this.getRenderingStrategyInstance().getDirection(); - }, - - updateAppointmentEndDate: function(options) { - const endDate = options.endDate; - const endDayHour = this._getCurrentViewOption('endDayHour'); - const startDayHour = this._getCurrentViewOption('startDayHour'); - - let updatedEndDate = endDate; - - if(endDate.getHours() >= endDayHour) { - updatedEndDate.setHours(endDayHour, 0, 0, 0); - } else if(!options.isSameDate && startDayHour > 0 && (endDate.getHours() * 60 + endDate.getMinutes() < (startDayHour * 60))) { - updatedEndDate = new Date(updatedEndDate.getTime() - toMs('day')); - updatedEndDate.setHours(endDayHour, 0, 0, 0); - } - return updatedEndDate; - }, - - renderCompactAppointments: function(options) { - this._compactAppointmentsHelper.render(options); - }, - - clearCompactAppointments: function() { - this._compactAppointmentsHelper.clear(); - }, - - supportCompactDropDownAppointments: function() { - return this.getLayoutManager().getRenderingStrategyInstance().supportCompactDropDownAppointments(); - }, - - getGroupCount: function() { - return this._workSpace._getGroupCount(); - }, - - mapAppointmentFields: function(config) { - const { itemData, itemElement, targetedAppointment } = config; - const targetedData = targetedAppointment || this.getTargetedAppointment(itemData, itemElement); + } + if (this.getLayoutManager().getRenderingStrategyInstance()._needVerticalGroupBounds(allDay) && this._workSpace._isVerticalGroupedWorkSpace()) { + const verticalGroupBounds = this._workSpace.getGroupBounds(options.coordinates); return { - appointmentData: config.itemData, - appointmentElement: config.itemElement, - targetedAppointmentData: targetedData, + left: 0, + right: 0, + top: verticalGroupBounds.top, + bottom: verticalGroupBounds.bottom, }; - }, + } + } - dayHasAppointment: function(day, appointment, trimTime) { - return this.dayHasAppointment(day, appointment, trimTime); - }, + return undefined; + }, - getLayoutManager: function() { - return this._layoutManager; - }, + needRecalculateResizableArea() { + return this.getWorkSpace().needRecalculateResizableArea(); + }, - getAgendaVerticalStepHeight: function() { - return this.getWorkSpace().getAgendaVerticalStepHeight(); - }, + getAppointmentGeometry(settings) { + return this.getLayoutManager().getRenderingStrategyInstance().getAppointmentGeometry(settings); + }, - getAgendaDuration: function() { - return this._getCurrentViewOption('agendaDuration'); - }, + isAllDay(appointmentData) { + return this.getLayoutManager().getRenderingStrategyInstance().isAllDay(appointmentData); + }, - getStartViewDate: function() { - return this.getStartViewDate(); - }, + getDeltaTime(e, initialSize, itemData) { + return this.getLayoutManager().getRenderingStrategyInstance().getDeltaTime(e, initialSize, itemData); + }, - getEndViewDate: function() { - return this.getEndViewDate(); - }, + getDropDownAppointmentWidth(isAllDay) { + return this.getLayoutManager().getRenderingStrategyInstance().getDropDownAppointmentWidth(this._getViewCountConfig().intervalCount, isAllDay); + }, - forceMaxAppointmentPerCell: function() { - return this.forceMaxAppointmentPerCell(); - }, + getDropDownAppointmentHeight() { + return this.getLayoutManager().getRenderingStrategyInstance().getDropDownAppointmentHeight(); + }, - onAgendaReady: function(rows) { - const $appts = this.getAppointmentsInstance()._itemElements(); - let total = 0; + getCellWidth() { + return this.getWorkSpace().getCellWidth(); + }, - const applyClass = function(_, count) { - const index = count + total - 1; - $appts.eq(index).addClass(AGENDA_LAST_IN_DATE_APPOINTMENT_CLASS); - total += count; - }; + getCellHeight() { + return this.getWorkSpace().getCellHeight(); + }, - for(let i = 0; i < rows.length; i++) { - each(rows[i], applyClass); - } - }, + getMaxAppointmentCountPerCellByType(isAllDay) { + return this.getRenderingStrategyInstance()._getMaxAppointmentCountPerCellByType(isAllDay); + }, - getTimezone: function() { - return this._getTimezoneOffsetByOption(); - }, + needCorrectAppointmentDates() { + return this.getRenderingStrategyInstance().needCorrectAppointmentDates(); + }, - getTargetedAppointmentData: function(appointment, element) { - return this.getTargetedAppointment(appointment, element); - }, + getRenderingStrategyDirection() { + return this.getRenderingStrategyInstance().getDirection(); + }, - getEndDayHour: function() { - return this._workSpace.option('endDayHour') || this.option('endDayHour'); - }, + updateAppointmentEndDate(options) { + const { endDate } = options; + const endDayHour = this._getCurrentViewOption('endDayHour'); + const startDayHour = this._getCurrentViewOption('startDayHour'); - getStartDayHour: function() { - return this._workSpace.option('startDayHour') || this.option('startDayHour'); - }, + let updatedEndDate = endDate; - isAdaptive: function() { - return this.option('adaptivityEnabled'); - }, - - removeDroppableCellClass: function() { - this._workSpace.removeDroppableCellClass(); + if (endDate.getHours() >= endDayHour) { + updatedEndDate.setHours(endDayHour, 0, 0, 0); + } else if (!options.isSameDate && startDayHour > 0 && (endDate.getHours() * 60 + endDate.getMinutes() < (startDayHour * 60))) { + updatedEndDate = new Date(updatedEndDate.getTime() - toMs('day')); + updatedEndDate.setHours(endDayHour, 0, 0, 0); + } + return updatedEndDate; + }, + + renderCompactAppointments(options) { + this._compactAppointmentsHelper.render(options); + }, + + clearCompactAppointments() { + this._compactAppointmentsHelper.clear(); + }, + + supportCompactDropDownAppointments() { + return this.getLayoutManager().getRenderingStrategyInstance().supportCompactDropDownAppointments(); + }, + + getGroupCount() { + return this._workSpace._getGroupCount(); + }, + + mapAppointmentFields(config) { + const { itemData, itemElement, targetedAppointment } = config; + const targetedData = targetedAppointment || this.getTargetedAppointment(itemData, itemElement); + + return { + appointmentData: config.itemData, + appointmentElement: config.itemElement, + targetedAppointmentData: targetedData, + }; + }, + + dayHasAppointment(day, appointment, trimTime) { + return this.dayHasAppointment(day, appointment, trimTime); + }, + + getLayoutManager() { + return this._layoutManager; + }, + + getAgendaVerticalStepHeight() { + return this.getWorkSpace().getAgendaVerticalStepHeight(); + }, + + getAgendaDuration() { + return this._getCurrentViewOption('agendaDuration'); + }, + + getStartViewDate() { + return this.getStartViewDate(); + }, + + getEndViewDate() { + return this.getEndViewDate(); + }, + + forceMaxAppointmentPerCell() { + return this.forceMaxAppointmentPerCell(); + }, + + onAgendaReady(rows) { + const $appts = this.getAppointmentsInstance()._itemElements(); + let total = 0; + + const applyClass = function (_, count) { + const index = count + total - 1; + $appts.eq(index).addClass(AGENDA_LAST_IN_DATE_APPOINTMENT_CLASS); + total += count; + }; + + for (let i = 0; i < rows.length; i++) { + each(rows[i], applyClass); } + }, + + getTimezone() { + return this._getTimezoneOffsetByOption(); + }, + + getTargetedAppointmentData(appointment, element) { + return this.getTargetedAppointment(appointment, element); + }, + + getEndDayHour() { + return this._workSpace.option('endDayHour') || this.option('endDayHour'); + }, + + getStartDayHour() { + return this._workSpace.option('startDayHour') || this.option('startDayHour'); + }, + + isAdaptive() { + return this.option('adaptivityEnabled'); + }, + + removeDroppableCellClass() { + this._workSpace.removeDroppableCellClass(); + }, }; export default subscribes; diff --git a/packages/devextreme/js/__internal/scheduler/m_table_creator.ts b/packages/devextreme/js/__internal/scheduler/m_table_creator.ts index 273a21c4bb61..44953a690dd8 100644 --- a/packages/devextreme/js/__internal/scheduler/m_table_creator.ts +++ b/packages/devextreme/js/__internal/scheduler/m_table_creator.ts @@ -1,435 +1,430 @@ -import $ from '../../core/renderer'; -import domAdapter from '../../core/dom_adapter'; -import { data as elementData } from '../../core/element_data'; -import { isFunction } from '../../core/utils/type'; -import { getPublicElement } from '../../core/element'; +import domAdapter from '@js/core/dom_adapter'; +import { getPublicElement } from '@js/core/element'; +import { data as elementData } from '@js/core/element_data'; +import $ from '@js/core/renderer'; +import { isFunction } from '@js/core/utils/type'; const ROW_SELECTOR = 'tr'; class SchedulerTableCreator { - constructor() { - this.VERTICAL = 'vertical'; - this.HORIZONTAL = 'horizontal'; - } - - insertAllDayRow(allDayElements, tableBody, index) { - if(allDayElements[index]) { - let row = allDayElements[index].find(ROW_SELECTOR); - - if(!row.length) { - row = $(domAdapter.createElement(ROW_SELECTOR)); - row.append(allDayElements[index].get(0)); - } + readonly VERTICAL = 'vertical'; - tableBody.appendChild(row.get ? row.get(0) : row); - } - } - - makeTable(options) { - const tableBody = domAdapter.createElement('tbody'); - const templateCallbacks = []; - let row; - const rowCountInGroup = options.groupCount ? options.rowCount / options.groupCount : options.rowCount; - let allDayElementIndex = 0; - const allDayElements = options.allDayElements; - const groupIndex = options.groupIndex; - const rowCount = options.rowCount; - - $(options.container).append(tableBody); - - if(allDayElements) { - this.insertAllDayRow(allDayElements, tableBody, 0); - allDayElementIndex++; - } + readonly HORIZONTAL = 'horizontal'; - for(let rowIndex = 0; rowIndex < rowCount; rowIndex++) { - row = domAdapter.createElement(ROW_SELECTOR); - tableBody.appendChild(row); + insertAllDayRow(allDayElements, tableBody, index) { + if (allDayElements[index]) { + let row = allDayElements[index].find(ROW_SELECTOR); - const isLastRowInGroup = (rowIndex + 1) % rowCountInGroup === 0; - - if(options.rowClass) { - row.className = options.rowClass; - } - - for(let columnIndex = 0; columnIndex < options.cellCount; columnIndex++) { - const td = domAdapter.createElement('td'); - row.appendChild(td); - - if(options.cellClass) { - if(isFunction(options.cellClass)) { - td.className = options.cellClass(rowIndex, columnIndex); - } else { - td.className = options.cellClass; - } - } - - - let cellDataObject; - let dataKey; - let dataValue; - - if(options.getCellData) { - cellDataObject = options.getCellData(td, rowIndex, columnIndex, groupIndex); - dataKey = cellDataObject.key; - dataValue = cellDataObject.value; - dataKey && elementData(td, dataKey, dataValue); - } - - options.setAdditionalClasses?.($(td), dataValue); - - if(options.cellTemplate && options.cellTemplate.render) { - const additionalTemplateData = options.getTemplateData - ? options.getTemplateData(rowIndex) - : {}; - - const templateOptions = { - model: { - text: options.getCellText ? options.getCellText(rowIndex, columnIndex) : '', - date: options.getCellDate ? options.getCellDate(rowIndex) : undefined, - ...additionalTemplateData, - }, - container: getPublicElement($(td)), - index: rowIndex * options.cellCount + columnIndex, - }; - - if(dataValue) { - if(dataValue.startDate) { - templateOptions.model['startDate'] = dataValue.startDate; - } - - if(dataValue.endDate) { - templateOptions.model['endDate'] = dataValue.endDate; - } - - if(dataValue.groups) { - templateOptions.model['groups'] = dataValue.groups; - } - - if(dataValue.allDay) { - templateOptions.model['allDay'] = dataValue.allDay; - } - } - - templateCallbacks.push(options.cellTemplate.render.bind(options.cellTemplate, templateOptions)); - - } else { - if(options.getCellText) { - $('
') - .text(options.getCellText(rowIndex, columnIndex)) - .addClass(options.getCellTextClass) - .appendTo($(td)); - } - } - } - - if(allDayElements && isLastRowInGroup) { - this.insertAllDayRow(allDayElements, tableBody, allDayElementIndex); - allDayElementIndex++; - } - } + if (!row.length) { + row = $(domAdapter.createElement(ROW_SELECTOR)); + row.append(allDayElements[index].get(0)); + } - return templateCallbacks; + tableBody.appendChild(row.get ? row.get(0) : row); } - - makeGroupedTable(type, groups, cssClasses, cellCount, cellTemplate, rowCount, groupByDate) { - let rows = []; - - if(type === this.VERTICAL) { - rows = this._makeVerticalGroupedRows(groups, cssClasses, cellTemplate, rowCount); - } else { - rows = this._makeHorizontalGroupedRows(groups, cssClasses, cellCount, cellTemplate, groupByDate); - } - - return rows; + } + + makeTable(options) { + const tableBody = domAdapter.createElement('tbody'); + const templateCallbacks: any[] = []; + let row; + const rowCountInGroup = options.groupCount ? options.rowCount / options.groupCount : options.rowCount; + let allDayElementIndex = 0; + const { allDayElements } = options; + const { groupIndex } = options; + const { rowCount } = options; + + $(options.container).append(tableBody); + + if (allDayElements) { + this.insertAllDayRow(allDayElements, tableBody, 0); + allDayElementIndex++; } - makeGroupedTableFromJSON(type, data, config) { - let table; - const cellStorage = []; - let rowIndex = 0; + for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) { + row = domAdapter.createElement(ROW_SELECTOR); + tableBody.appendChild(row); - config = config || {}; + const isLastRowInGroup = (rowIndex + 1) % rowCountInGroup === 0; - const cellTag = config.cellTag || 'td'; - const childrenField = config.childrenField || 'children'; - const titleField = config.titleField || 'title'; - const groupTableClass = config.groupTableClass; - const groupRowClass = config.groupRowClass; - const groupCellClass = config.groupCellClass; - const groupCellCustomContent = config.groupCellCustomContent; + if (options.rowClass) { + row.className = options.rowClass; + } - function createTable() { - table = domAdapter.createElement('table'); + for (let columnIndex = 0; columnIndex < options.cellCount; columnIndex++) { + const td = domAdapter.createElement('td'); + row.appendChild(td); - if(groupTableClass) { - table.className = groupTableClass; - } + if (options.cellClass) { + if (isFunction(options.cellClass)) { + td.className = options.cellClass(rowIndex, columnIndex); + } else { + td.className = options.cellClass; + } } - function getChildCount(item) { - if(item[childrenField]) { - return item[childrenField].length; - } - return 0; - } + let cellDataObject; + let dataKey; + let dataValue; - function createCell(text, childCount, index, data) { - const cell = { - element: domAdapter.createElement(cellTag), - childCount: childCount - }; + if (options.getCellData) { + cellDataObject = options.getCellData(td, rowIndex, columnIndex, groupIndex); + dataKey = cellDataObject.key; + dataValue = cellDataObject.value; + dataKey && elementData(td, dataKey, dataValue); + } - if(groupCellClass) { - cell.element.className = groupCellClass; + options.setAdditionalClasses?.($(td), dataValue); + + if (options.cellTemplate && options.cellTemplate.render) { + const additionalTemplateData = options.getTemplateData + ? options.getTemplateData(rowIndex) + : {}; + + const templateOptions = { + model: { + text: options.getCellText ? options.getCellText(rowIndex, columnIndex) : '', + date: options.getCellDate ? options.getCellDate(rowIndex) : undefined, + ...additionalTemplateData, + }, + container: getPublicElement($(td)), + index: rowIndex * options.cellCount + columnIndex, + }; + + if (dataValue) { + if (dataValue.startDate) { + templateOptions.model.startDate = dataValue.startDate; } - const cellText = domAdapter.createTextNode(text); - if(typeof groupCellCustomContent === 'function') { - groupCellCustomContent(cell.element, cellText, index, data); - } else { - cell.element.appendChild(cellText); + if (dataValue.endDate) { + templateOptions.model.endDate = dataValue.endDate; } - return cell; - } + if (dataValue.groups) { + templateOptions.model.groups = dataValue.groups; + } - function generateCells(data) { - for(let i = 0; i < data.length; i++) { - const childCount = getChildCount(data[i]); - const cell = createCell(data[i][titleField], childCount, i, data[i]); - - if(!cellStorage[rowIndex]) { - cellStorage[rowIndex] = []; - } - cellStorage[rowIndex].push(cell); - - if(childCount) { - generateCells(data[i][childrenField]); - } else { - rowIndex++; - } + if (dataValue.allDay) { + templateOptions.model.allDay = dataValue.allDay; } + } + + templateCallbacks.push(options.cellTemplate.render.bind(options.cellTemplate, templateOptions)); + } else if (options.getCellText) { + $('
') + .text(options.getCellText(rowIndex, columnIndex)) + .addClass(options.getCellTextClass) + .appendTo($(td)); } + } - function putCellsToRows() { - cellStorage.forEach(function(cells) { - const row = domAdapter.createElement(ROW_SELECTOR); - if(groupRowClass) { - row.className = groupRowClass; - } - - const rowspans = []; - - for(let i = cells.length - 1; i >= 0; i--) { - const prev = cells[i + 1]; - let rowspan = cells[i].childCount; - if(prev && prev.childCount) { - rowspan *= prev.childCount; - } - rowspans.push(rowspan); - } - rowspans.reverse(); - - cells.forEach(function(cell, index) { - if(rowspans[index]) { - cell.element.setAttribute('rowSpan', rowspans[index]); - } - row.appendChild(cell.element); - }); - - table.appendChild(row); - }); - } + if (allDayElements && isLastRowInGroup) { + this.insertAllDayRow(allDayElements, tableBody, allDayElementIndex); + allDayElementIndex++; + } + } - createTable(); - generateCells(data); - putCellsToRows(); + return templateCallbacks; + } - return table; + makeGroupedTable(type, groups, cssClasses, cellCount, cellTemplate, rowCount, groupByDate) { + let rows: any = []; + if (type === this.VERTICAL) { + rows = this._makeVerticalGroupedRows(groups, cssClasses, cellTemplate, rowCount); + } else { + rows = this._makeHorizontalGroupedRows(groups, cssClasses, cellCount, cellTemplate, groupByDate); } - _makeFlexGroupedRowCells(group, repeatCount, cssClasses, cellTemplate, repeatByDate = 1) { - const cells = []; - const items = group.items; - const itemCount = items.length; + return rows; + } + + makeGroupedTableFromJSON(type, data, config) { + let table; + const cellStorage: any[] = []; + let rowIndex = 0; + + config = config || {}; - for(let i = 0; i < repeatCount * repeatByDate; i++) { - for(let j = 0; j < itemCount; j++) { - let $container = $('
'); - const cell = {}; + const cellTag = config.cellTag || 'td'; + const childrenField = config.childrenField || 'children'; + const titleField = config.titleField || 'title'; + const { groupTableClass } = config; + const { groupRowClass } = config; + const { groupCellClass } = config; + const { groupCellCustomContent } = config; - if(cellTemplate && cellTemplate.render) { - const templateOptions = { - model: items[j], - container: getPublicElement($container), - index: i * itemCount + j - }; + function createTable() { + table = domAdapter.createElement('table'); - if(group.data) { - templateOptions.model.data = group.data[j]; - } + if (groupTableClass) { + table.className = groupTableClass; + } + } - cell.template = cellTemplate.render.bind(cellTemplate, templateOptions); - } else { - $container.text(items[j].text).attr('title', items[j].text).addClass('dx-scheduler-group-header-content'); - $container = $('
').append($container); - } + function getChildCount(item) { + if (item[childrenField]) { + return item[childrenField].length; + } + return 0; + } - const cssClass = isFunction(cssClasses.groupHeaderClass) ? cssClasses.groupHeaderClass(j) : cssClasses.groupHeaderClass; + function createCell(text, childCount, index, data) { + const cell = { + element: domAdapter.createElement(cellTag), + childCount, + }; - cell.element = $container.addClass(cssClass); + if (groupCellClass) { + cell.element.className = groupCellClass; + } - cells.push(cell); - } - } + const cellText = (domAdapter as any).createTextNode(text); + if (typeof groupCellCustomContent === 'function') { + groupCellCustomContent(cell.element, cellText, index, data); + } else { + cell.element.appendChild(cellText); + } - return cells; + return cell; } - _makeVerticalGroupedRows(groups, cssClasses, cellTemplate) { - const cellTemplates = []; - let repeatCount = 1; - const cellsArray = []; + function generateCells(data) { + for (let i = 0; i < data.length; i++) { + const childCount = getChildCount(data[i]); + const cell = createCell(data[i][titleField], childCount, i, data[i]); - const cellIterator = function(cell) { - if(cell.template) { - cellTemplates.push(cell.template); - } - }; + if (!cellStorage[rowIndex]) { + cellStorage[rowIndex] = []; + } + cellStorage[rowIndex].push(cell); - for(let i = 0; i < groups.length; i++) { - if(i > 0) { - repeatCount = groups[i - 1].items.length * repeatCount; - } + if (childCount) { + generateCells(data[i][childrenField]); + } else { + rowIndex++; + } + } + } - const cells = this._makeFlexGroupedRowCells(groups[i], repeatCount, cssClasses, cellTemplate); - cells.forEach(cellIterator); - cellsArray.push(cells); + function putCellsToRows() { + cellStorage.forEach((cells) => { + const row = domAdapter.createElement(ROW_SELECTOR); + if (groupRowClass) { + row.className = groupRowClass; } - const rows = []; - const groupCount = cellsArray.length; + const rowspans: any[] = []; - for(let i = 0; i < groupCount; i++) { - rows.push($('
').addClass(cssClasses.groupHeaderRowClass)); + for (let i = cells.length - 1; i >= 0; i--) { + const prev = cells[i + 1]; + let rowspan = cells[i].childCount; + if (prev && prev.childCount) { + rowspan *= prev.childCount; + } + rowspans.push(rowspan); } + rowspans.reverse(); - for(let i = groupCount - 1; i >= 0; i--) { - const currentColumnLength = cellsArray[i].length; - for(let j = 0; j < currentColumnLength; j++) { - rows[i].append(cellsArray[i][j].element); - } - } + cells.forEach((cell, index) => { + if (rowspans[index]) { + cell.element.setAttribute('rowSpan', rowspans[index]); + } + row.appendChild(cell.element); + }); - return { - elements: $('
').addClass('dx-scheduler-group-flex-container').append(rows), - cellTemplates: cellTemplates - }; + table.appendChild(row); + }); } - _makeHorizontalGroupedRows(groups, cssClasses, cellCount, cellTemplate, groupByDate) { - let repeatCount = 1; - const groupCount = groups.length; - const rows = []; - const cellTemplates = []; - const repeatByDate = groupByDate ? cellCount : 1; + createTable(); + generateCells(data); + putCellsToRows(); - const cellIterator = function(cell) { - if(cell.template) { - cellTemplates.push(cell.template); - } + return table; + } - return cell.element; - }; + _makeFlexGroupedRowCells(group, repeatCount, cssClasses, cellTemplate, repeatByDate = 1) { + const cells: any[] = []; + const { items } = group; + const itemCount = items.length; + for (let i = 0; i < repeatCount * repeatByDate; i++) { + for (let j = 0; j < itemCount; j++) { + let $container = $('
'); + const cell: any = {}; - for(let i = 0; i < groupCount; i++) { - if(i > 0) { - repeatCount = groups[i - 1].items.length * repeatCount; - } + if (cellTemplate && cellTemplate.render) { + const templateOptions = { + model: items[j], + container: getPublicElement($container), + index: i * itemCount + j, + }; - const cells = this._makeGroupedRowCells(groups[i], repeatCount, cssClasses, cellTemplate, repeatByDate); + if (group.data) { + templateOptions.model.data = group.data[j]; + } - rows.push( - $('') - .addClass(cssClasses.groupRowClass) - .append(cells.map(cellIterator)) - ); + cell.template = cellTemplate.render.bind(cellTemplate, templateOptions); + } else { + $container.text(items[j].text).attr('title', items[j].text).addClass('dx-scheduler-group-header-content'); + $container = $('
').append($container); } - const maxCellCount = rows[groupCount - 1].find('th').length; + const cssClass = isFunction(cssClasses.groupHeaderClass) ? cssClasses.groupHeaderClass(j) : cssClasses.groupHeaderClass; - for(let j = 0; j < groupCount; j++) { - const $cell = rows[j].find('th'); - let colspan = maxCellCount / $cell.length; - - if(!groupByDate) { - colspan = colspan * cellCount; - } - if((colspan > 1 && repeatByDate === 1) || (groupByDate && groupCount > 1)) { - $cell.attr('colSpan', colspan); - } - } + cell.element = $container.addClass(cssClass); + cells.push(cell); + } + } - return { - elements: rows, - cellTemplates: cellTemplates - }; + return cells; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _makeVerticalGroupedRows(groups, cssClasses, cellTemplate, rowCount?: any) { + const cellTemplates: any = []; + let repeatCount = 1; + const cellsArray: any = []; + + const cellIterator = function (cell) { + if (cell.template) { + cellTemplates.push(cell.template); + } + }; + + for (let i = 0; i < groups.length; i++) { + if (i > 0) { + // eslint-disable-next-line operator-assignment + repeatCount = groups[i - 1].items.length * repeatCount; + } + + const cells = this._makeFlexGroupedRowCells(groups[i], repeatCount, cssClasses, cellTemplate); + cells.forEach(cellIterator); + cellsArray.push(cells); } - _makeGroupedRowCells(group, repeatCount, cssClasses, cellTemplate, repeatByDate) { - repeatByDate = repeatByDate || 1; - repeatCount = repeatCount * repeatByDate; + const rows: any = []; + const groupCount = cellsArray.length; - const cells = []; - const items = group.items; - const itemCount = items.length; + for (let i = 0; i < groupCount; i++) { + rows.push($('
').addClass(cssClasses.groupHeaderRowClass)); + } - for(let i = 0; i < repeatCount; i++) { - for(let j = 0; j < itemCount; j++) { - let $container = $('
'); - const cell = {}; + for (let i = groupCount - 1; i >= 0; i--) { + const currentColumnLength = cellsArray[i].length; + for (let j = 0; j < currentColumnLength; j++) { + rows[i].append(cellsArray[i][j].element); + } + } - if(cellTemplate && cellTemplate.render) { - const templateOptions = { - model: items[j], - container: getPublicElement($container), - index: i * itemCount + j - }; + return { + elements: $('
').addClass('dx-scheduler-group-flex-container').append(rows), + cellTemplates, + }; + } + + _makeHorizontalGroupedRows(groups, cssClasses, cellCount, cellTemplate, groupByDate) { + let repeatCount = 1; + const groupCount = groups.length; + const rows: any = []; + const cellTemplates: any = []; + const repeatByDate = groupByDate ? cellCount : 1; + + const cellIterator = function (cell) { + if (cell.template) { + cellTemplates.push(cell.template); + } + + return cell.element; + }; + + for (let i = 0; i < groupCount; i++) { + if (i > 0) { + // eslint-disable-next-line operator-assignment + repeatCount = groups[i - 1].items.length * repeatCount; + } + + const cells: any = this._makeGroupedRowCells(groups[i], repeatCount, cssClasses, cellTemplate, repeatByDate); + + rows.push( + $('') + .addClass(cssClasses.groupRowClass) + .append(cells.map(cellIterator)), + ); + } - if(group.data) { - templateOptions.model.data = group.data[j]; - } + const maxCellCount = rows[groupCount - 1].find('th').length; - cell.template = cellTemplate.render.bind(cellTemplate, templateOptions); - } else { - $container.text(items[j].text); - $container = $('
').append($container); - } + for (let j = 0; j < groupCount; j++) { + const $cell = rows[j].find('th'); + let colspan = maxCellCount / $cell.length; - $container.addClass(cssClasses.groupHeaderContentClass); + if (!groupByDate) { + colspan *= cellCount; + } + if ((colspan > 1 && repeatByDate === 1) || (groupByDate && groupCount > 1)) { + $cell.attr('colSpan', colspan); + } + } - let cssClass; + return { + elements: rows, + cellTemplates, + }; + } + + _makeGroupedRowCells(group, repeatCount, cssClasses, cellTemplate, repeatByDate) { + repeatByDate = repeatByDate || 1; + repeatCount *= repeatByDate; + + const cells: any[] = []; + const { items } = group; + const itemCount = items.length; + + for (let i = 0; i < repeatCount; i++) { + for (let j = 0; j < itemCount; j++) { + let $container = $('
'); + const cell: any = {}; + + if (cellTemplate && cellTemplate.render) { + const templateOptions = { + model: items[j], + container: getPublicElement($container), + index: i * itemCount + j, + }; + + if (group.data) { + templateOptions.model.data = group.data[j]; + } + + cell.template = cellTemplate.render.bind(cellTemplate, templateOptions); + } else { + $container.text(items[j].text); + $container = $('
').append($container); + } - if(isFunction(cssClasses.groupHeaderClass)) { - cssClass = cssClasses.groupHeaderClass(j); - } else { - cssClass = cssClasses.groupHeaderClass; - } + $container.addClass(cssClasses.groupHeaderContentClass); - cell.element = $('').addClass(cssClass).append($container); + let cssClass; - cells.push(cell); - } + if (isFunction(cssClasses.groupHeaderClass)) { + cssClass = cssClasses.groupHeaderClass(j); + } else { + cssClass = cssClasses.groupHeaderClass; } - return cells; + cell.element = $('').addClass(cssClass).append($container); + + cells.push(cell); + } } + + return cells; + } } export default { - tableCreator: new SchedulerTableCreator() + tableCreator: new SchedulerTableCreator(), }; diff --git a/packages/devextreme/js/__internal/scheduler/m_utils.ts b/packages/devextreme/js/__internal/scheduler/m_utils.ts index 471c4cbf9f92..8c4430ab465d 100644 --- a/packages/devextreme/js/__internal/scheduler/m_utils.ts +++ b/packages/devextreme/js/__internal/scheduler/m_utils.ts @@ -1,115 +1,114 @@ -import { getOuterHeight, setHeight, setWidth } from '../../core/utils/size'; -import $ from '../../core/renderer'; -import { each } from '../../core/utils/iterator'; -import { APPOINTMENT_SETTINGS_KEY } from './constants'; -import { getPublicElement } from '../../core/element'; -import { compileGetter, compileSetter } from '../../core/utils/data'; -import dateSerialization from '../../core/utils/date_serialization'; +import { getPublicElement } from '@js/core/element'; +import $ from '@js/core/renderer'; +import { compileGetter, compileSetter } from '@js/core/utils/data'; +import dateSerialization from '@js/core/utils/date_serialization'; +import { each } from '@js/core/utils/iterator'; +import { getOuterHeight, setHeight, setWidth } from '@js/core/utils/size'; -export const utils = { - dataAccessors: { - getAppointmentSettings: element => { - return $(element).data(APPOINTMENT_SETTINGS_KEY); - }, +import { APPOINTMENT_SETTINGS_KEY } from './m_constants'; - getAppointmentInfo: element => { - const settings = utils.dataAccessors.getAppointmentSettings(element); - return settings?.info; - }, +export const utils = { + dataAccessors: { + getAppointmentSettings: (element) => $(element).data(APPOINTMENT_SETTINGS_KEY), - create: ( - fields, - currentDataAccessors, - forceIsoDateParsing, - dateSerializationFormat - ) => { - const isDateField = (field) => field === 'startDate' || field === 'endDate'; - const defaultDataAccessors = { - getter: {}, - setter: {}, - expr: {} - }; - const dataAccessors = currentDataAccessors - ? { ...currentDataAccessors } - : defaultDataAccessors; + getAppointmentInfo: (element) => { + const settings: any = utils.dataAccessors.getAppointmentSettings(element); + return settings?.info; + }, - each(fields, (name, expr) => { - if(expr) { - const getter = compileGetter(expr); - const setter = compileSetter(expr); + create: ( + fields, + currentDataAccessors, + forceIsoDateParsing, + dateSerializationFormat, + ) => { + const isDateField = (field) => field === 'startDate' || field === 'endDate'; + const defaultDataAccessors = { + getter: {}, + setter: {}, + expr: {}, + }; + const dataAccessors = currentDataAccessors + ? { ...currentDataAccessors } + : defaultDataAccessors; - let dateGetter; - let dateSetter; - let serializationFormat; + each(fields, (name: any, expr) => { + if (expr) { + const getter: any = compileGetter(expr); + const setter: any = compileSetter(expr); - if(isDateField(name)) { - dateGetter = (object) => { - let value = getter(object); - if(forceIsoDateParsing) { - value = dateSerialization.deserializeDate(value); - } - return value; - }; - dateSetter = (object, value) => { - if(dateSerializationFormat) { - serializationFormat = dateSerializationFormat; - } else if(forceIsoDateParsing && !serializationFormat) { - const oldValue = getter(object); + let dateGetter; + let dateSetter; + let serializationFormat; - serializationFormat = dateSerialization.getDateSerializationFormat(oldValue); - } + if (isDateField(name)) { + dateGetter = (object) => { + let value = getter(object); + if (forceIsoDateParsing) { + value = dateSerialization.deserializeDate(value); + } + return value; + }; + dateSetter = (object, value) => { + if (dateSerializationFormat) { + serializationFormat = dateSerializationFormat; + } else if (forceIsoDateParsing && !serializationFormat) { + const oldValue = getter(object); - const newValue = dateSerialization.serializeDate( - value, - serializationFormat - ); + serializationFormat = dateSerialization.getDateSerializationFormat(oldValue); + } - setter(object, newValue); - }; - } - dataAccessors.getter[name] = dateGetter || getter; - dataAccessors.setter[name] = dateSetter || setter; - dataAccessors.expr[`${name}Expr`] = expr; - } else { - delete dataAccessors.getter[name]; - delete dataAccessors.setter[name]; - delete dataAccessors.expr[`${name}Expr`]; - } - }); + const newValue = dateSerialization.serializeDate( + value, + serializationFormat, + ); - return dataAccessors; + setter(object, newValue); + }; + } + dataAccessors.getter[name] = dateGetter || getter; + dataAccessors.setter[name] = dateSetter || setter; + dataAccessors.expr[`${name}Expr`] = expr; + } else { + /* eslint-disable @typescript-eslint/no-dynamic-delete */ + delete dataAccessors.getter[name]; + delete dataAccessors.setter[name]; + delete dataAccessors.expr[`${name}Expr`]; + /* eslint-enable @typescript-eslint/no-dynamic-delete */ } + }); + + return dataAccessors; }, - DOM: { - getHeaderHeight: (header) => { - return header - ? header._$element && parseInt(getOuterHeight(header._$element), 10) - : 0; - }, - }, - renovation: { - renderComponent: (widget, parentElement, componentClass, componentName, viewModel) => { - let component = widget[componentName]; - if(!component) { - const container = getPublicElement(parentElement); - component = widget._createComponent(container, componentClass, viewModel); - widget[componentName] = component; - } else { - // TODO: this is a workaround for setTablesSizes. Remove after CSS refactoring - const $element = component.$element(); - const elementStyle = $element.get(0).style; - const height = elementStyle.height; - const width = elementStyle.width; + }, + DOM: { + getHeaderHeight: (header) => (header + ? header._$element && parseInt(getOuterHeight(header._$element), 10) + : 0), + }, + renovation: { + renderComponent: (widget, parentElement, componentClass, componentName, viewModel) => { + let component = widget[componentName]; + if (!component) { + const container = getPublicElement(parentElement); + component = widget._createComponent(container, componentClass, viewModel); + widget[componentName] = component; + } else { + // TODO: this is a workaround for setTablesSizes. Remove after CSS refactoring + const $element = component.$element(); + const elementStyle = $element.get(0).style; + const { height } = elementStyle; + const { width } = elementStyle; - component.option(viewModel); + component.option(viewModel); - if(height) { - setHeight($element, height); - } - if(width) { - setWidth($element, width); - } - } + if (height) { + setHeight($element, height); } - } + if (width) { + setWidth($element, width); + } + } + }, + }, }; diff --git a/packages/devextreme/js/__internal/scheduler/m_utils_time_zone.ts b/packages/devextreme/js/__internal/scheduler/m_utils_time_zone.ts index 838a73f4e360..caa2aac973b2 100644 --- a/packages/devextreme/js/__internal/scheduler/m_utils_time_zone.ts +++ b/packages/devextreme/js/__internal/scheduler/m_utils_time_zone.ts @@ -1,233 +1,217 @@ /* globals Intl */ import dateUtils from '../../core/utils/date'; -import timeZoneDataUtils from '../../__internal/scheduler/timezones/m_utils_timezones_data'; -import DateAdapter from './dateAdapter'; +import DateAdapter from './m_date_adapter'; +import timeZoneDataUtils from './timezones/m_utils_timezones_data'; const toMs = dateUtils.dateToMilliseconds; const MINUTES_IN_HOUR = 60; const MS_IN_MINUTE = 60000; -const createUTCDateWithLocalOffset = date => { - if(!date) { - return null; - } +const createUTCDateWithLocalOffset = (date) => { + if (!date) { + return null; + } - return new Date(Date.UTC( - date.getFullYear(), - date.getMonth(), - date.getDate(), - date.getHours(), - date.getMinutes(), - date.getSeconds() - )); + return new Date(Date.UTC( + date.getFullYear(), + date.getMonth(), + date.getDate(), + date.getHours(), + date.getMinutes(), + date.getSeconds(), + )); }; -const createDateFromUTCWithLocalOffset = date => { - const result = DateAdapter(date); +const createDateFromUTCWithLocalOffset = (date) => { + const result = DateAdapter(date); - const timezoneOffsetBeforeInMin = result.getTimezoneOffset(); - result.addTime(result.getTimezoneOffset('minute')); - result.subtractMinutes(timezoneOffsetBeforeInMin - result.getTimezoneOffset()); + const timezoneOffsetBeforeInMin = result.getTimezoneOffset(); + result.addTime(result.getTimezoneOffset('minute')); + result.subtractMinutes(timezoneOffsetBeforeInMin - result.getTimezoneOffset()); - return result.source; + return result.source; }; const getTimeZones = (date = new Date()) => { - const dateInUTC = createUTCDate(date); - return timeZoneDataUtils.getDisplayedTimeZones(dateInUTC.getTime()); + const dateInUTC = createUTCDate(date); + return timeZoneDataUtils.getDisplayedTimeZones(dateInUTC.getTime()); }; -const createUTCDate = (date) => { - return new Date(Date.UTC( - date.getUTCFullYear(), - date.getUTCMonth(), - date.getUTCDate(), - date.getUTCHours(), - date.getUTCMinutes() - )); -}; +const createUTCDate = (date) => new Date(Date.UTC( + date.getUTCFullYear(), + date.getUTCMonth(), + date.getUTCDate(), + date.getUTCHours(), + date.getUTCMinutes(), +)); -const getTimezoneOffsetChangeInMinutes = (startDate, endDate, updatedStartDate, updatedEndDate) => { - return getDaylightOffset(updatedStartDate, updatedEndDate) - getDaylightOffset(startDate, endDate); -}; +const getTimezoneOffsetChangeInMinutes = (startDate, endDate, updatedStartDate, updatedEndDate) => getDaylightOffset(updatedStartDate, updatedEndDate) - getDaylightOffset(startDate, endDate); -const getTimezoneOffsetChangeInMs = (startDate, endDate, updatedStartDate, updatedEndDate) => { - return getTimezoneOffsetChangeInMinutes(startDate, endDate, updatedStartDate, updatedEndDate) * toMs('minute'); -}; +const getTimezoneOffsetChangeInMs = (startDate, endDate, updatedStartDate, updatedEndDate) => getTimezoneOffsetChangeInMinutes(startDate, endDate, updatedStartDate, updatedEndDate) * toMs('minute'); -const getDaylightOffset = (startDate, endDate) => { - return new Date(startDate).getTimezoneOffset() - new Date(endDate).getTimezoneOffset(); -}; +const getDaylightOffset = (startDate, endDate) => new Date(startDate).getTimezoneOffset() - new Date(endDate).getTimezoneOffset(); -const getDaylightOffsetInMs = (startDate, endDate) => { - return getDaylightOffset(startDate, endDate) * toMs('minute'); -}; +const getDaylightOffsetInMs = (startDate, endDate) => getDaylightOffset(startDate, endDate) * toMs('minute'); const calculateTimezoneByValue = (timezone, date = new Date()) => { - // NOTE: This check could be removed. We don't support numerical timezones - if(typeof timezone === 'string') { - const dateUtc = createUTCDate(date); - return timeZoneDataUtils.getTimeZoneOffsetById(timezone, dateUtc.getTime()); - } - return timezone; + // NOTE: This check could be removed. We don't support numerical timezones + if (typeof timezone === 'string') { + const dateUtc = createUTCDate(date); + return timeZoneDataUtils.getTimeZoneOffsetById(timezone, dateUtc.getTime()); + } + return timezone; }; -const _getDaylightOffsetByTimezone = (startDate, endDate, timeZone) => { - return calculateTimezoneByValue(timeZone, startDate) - calculateTimezoneByValue(timeZone, endDate); -}; +// eslint-disable-next-line @typescript-eslint/naming-convention +const _getDaylightOffsetByTimezone = (startDate, endDate, timeZone) => calculateTimezoneByValue(timeZone, startDate) - calculateTimezoneByValue(timeZone, endDate); const getCorrectedDateByDaylightOffsets = (convertedOriginalStartDate, convertedDate, date, timeZone, startDateTimezone) => { - const daylightOffsetByCommonTimezone = _getDaylightOffsetByTimezone(convertedOriginalStartDate, convertedDate, timeZone); - const daylightOffsetByAppointmentTimezone = _getDaylightOffsetByTimezone(convertedOriginalStartDate, convertedDate, startDateTimezone); - const diff = daylightOffsetByCommonTimezone - daylightOffsetByAppointmentTimezone; + const daylightOffsetByCommonTimezone = _getDaylightOffsetByTimezone(convertedOriginalStartDate, convertedDate, timeZone); + const daylightOffsetByAppointmentTimezone = _getDaylightOffsetByTimezone(convertedOriginalStartDate, convertedDate, startDateTimezone); + const diff = daylightOffsetByCommonTimezone - daylightOffsetByAppointmentTimezone; - return new Date(date.getTime() - diff * toMs('hour')); + return new Date(date.getTime() - diff * toMs('hour')); }; -const correctRecurrenceExceptionByTimezone = (exception, exceptionByStartDate, timeZone, startDateTimeZone, isBackConversion = false) => { - let timezoneOffset = (exception.getTimezoneOffset() - exceptionByStartDate.getTimezoneOffset()) / MINUTES_IN_HOUR; +const correctRecurrenceExceptionByTimezone = (exception, exceptionByStartDate, timeZone, startDateTimeZone?: any, isBackConversion = false) => { + let timezoneOffset = (exception.getTimezoneOffset() - exceptionByStartDate.getTimezoneOffset()) / MINUTES_IN_HOUR; - if(startDateTimeZone) { - timezoneOffset = _getDaylightOffsetByTimezone(exceptionByStartDate, exception, startDateTimeZone); - } else if(timeZone) { - timezoneOffset = _getDaylightOffsetByTimezone(exceptionByStartDate, exception, timeZone); - } + if (startDateTimeZone) { + timezoneOffset = _getDaylightOffsetByTimezone(exceptionByStartDate, exception, startDateTimeZone); + } else if (timeZone) { + timezoneOffset = _getDaylightOffsetByTimezone(exceptionByStartDate, exception, timeZone); + } - return new Date(exception.getTime() + (isBackConversion ? -1 : 1) * timezoneOffset * toMs('hour')); + return new Date(exception.getTime() + (isBackConversion ? -1 : 1) * timezoneOffset * toMs('hour')); }; -const isTimezoneChangeInDate = date => { - const startDayDate = new Date((new Date(date)).setHours(0, 0, 0, 0)); - const endDayDate = new Date((new Date(date)).setHours(23, 59, 59, 0)); - return (startDayDate.getTimezoneOffset() - endDayDate.getTimezoneOffset()) !== 0; +const isTimezoneChangeInDate = (date) => { + const startDayDate = new Date(new Date(date).setHours(0, 0, 0, 0)); + const endDayDate = new Date(new Date(date).setHours(23, 59, 59, 0)); + return (startDayDate.getTimezoneOffset() - endDayDate.getTimezoneOffset()) !== 0; }; -const getDateWithoutTimezoneChange = date => { - const clonedDate = new Date(date); - if(isTimezoneChangeInDate(clonedDate)) { - const result = new Date(clonedDate); - return new Date(result.setDate(result.getDate() + 1)); - } - return clonedDate; +const getDateWithoutTimezoneChange = (date) => { + const clonedDate = new Date(date); + if (isTimezoneChangeInDate(clonedDate)) { + const result = new Date(clonedDate); + return new Date(result.setDate(result.getDate() + 1)); + } + return clonedDate; }; const isSameAppointmentDates = (startDate, endDate) => { - // NOTE: subtract 1 millisecond to avoid 00.00 time. Method should return 'true' for "2020:10:10 22:00:00" and "2020:10:11 00:00:00", for example. - endDate = new Date(endDate.getTime() - 1); + // NOTE: subtract 1 millisecond to avoid 00.00 time. Method should return 'true' for "2020:10:10 22:00:00" and "2020:10:11 00:00:00", for example. + endDate = new Date(endDate.getTime() - 1); - return dateUtils.sameDate(startDate, endDate); + return dateUtils.sameDate(startDate, endDate); }; -const getClientTimezoneOffset = (date = new Date()) => { - return date.getTimezoneOffset() * MS_IN_MINUTE; -}; +const getClientTimezoneOffset = (date = new Date()) => date.getTimezoneOffset() * MS_IN_MINUTE; -const getDiffBetweenClientTimezoneOffsets = (firstDate = new Date(), secondDate = new Date()) => { - return getClientTimezoneOffset(firstDate) - getClientTimezoneOffset(secondDate); -}; +const getDiffBetweenClientTimezoneOffsets = (firstDate = new Date(), secondDate = new Date()) => getClientTimezoneOffset(firstDate) - getClientTimezoneOffset(secondDate); const isEqualLocalTimeZone = (timeZoneName, date = new Date()) => { - if(Intl) { - const localTimeZoneName = Intl.DateTimeFormat().resolvedOptions().timeZone; - if(localTimeZoneName === timeZoneName) { - return true; - } + if (Intl) { + const localTimeZoneName = Intl.DateTimeFormat().resolvedOptions().timeZone; + if (localTimeZoneName === timeZoneName) { + return true; } + } - return isEqualLocalTimeZoneByDeclaration(timeZoneName, date); + return isEqualLocalTimeZoneByDeclaration(timeZoneName, date); }; // TODO: Not used anywhere, if it isn't use in the future, then it must be removed const hasDSTInLocalTimeZone = () => { - const [startDate, endDate] = getExtremeDates(); - return startDate.getTimezoneOffset() !== endDate.getTimezoneOffset(); + const [startDate, endDate] = getExtremeDates(); + return startDate.getTimezoneOffset() !== endDate.getTimezoneOffset(); }; const isEqualLocalTimeZoneByDeclaration = (timeZoneName, date) => { - const year = date.getFullYear(); - const getOffset = date => -date.getTimezoneOffset() / 60; - const getDateAndMoveHourBack = dateStamp => new Date(dateStamp - 3600000); - - const configTuple = timeZoneDataUtils.getTimeZoneDeclarationTuple(timeZoneName, year); - const [summerTime, winterTime] = configTuple; + const year = date.getFullYear(); + const getOffset = (date) => -date.getTimezoneOffset() / 60; + const getDateAndMoveHourBack = (dateStamp) => new Date(dateStamp - 3600000); - const noDSTInTargetTimeZone = configTuple.length < 2; - if(noDSTInTargetTimeZone) { - const targetTimeZoneOffset = timeZoneDataUtils.getTimeZoneOffsetById(timeZoneName, date); - const localTimeZoneOffset = getOffset(date); + const configTuple = timeZoneDataUtils.getTimeZoneDeclarationTuple(timeZoneName, year); + const [summerTime, winterTime] = configTuple; - if(targetTimeZoneOffset !== localTimeZoneOffset) { - return false; - } + const noDSTInTargetTimeZone = configTuple.length < 2; + if (noDSTInTargetTimeZone) { + const targetTimeZoneOffset = timeZoneDataUtils.getTimeZoneOffsetById(timeZoneName, date); + const localTimeZoneOffset = getOffset(date); - return hasDSTInLocalTimeZone() ? false : true; + if (targetTimeZoneOffset !== localTimeZoneOffset) { + return false; } - const localSummerOffset = getOffset(new Date(summerTime.date)); - const localWinterOffset = getOffset(new Date(winterTime.date)); + return !hasDSTInLocalTimeZone(); + } - if(localSummerOffset !== summerTime.offset) { - return false; - } + const localSummerOffset = getOffset(new Date(summerTime.date)); + const localWinterOffset = getOffset(new Date(winterTime.date)); - if(localSummerOffset === getOffset(getDateAndMoveHourBack(summerTime.date))) { - return false; - } + if (localSummerOffset !== summerTime.offset) { + return false; + } - if(localWinterOffset !== winterTime.offset) { - return false; - } + if (localSummerOffset === getOffset(getDateAndMoveHourBack(summerTime.date))) { + return false; + } - if(localWinterOffset === getOffset(getDateAndMoveHourBack(winterTime.date))) { - return false; - } + if (localWinterOffset !== winterTime.offset) { + return false; + } - return true; -}; + if (localWinterOffset === getOffset(getDateAndMoveHourBack(winterTime.date))) { + return false; + } + return true; +}; // TODO: Getting two dates in january or june is the standard mechanism for determining that an offset has occurred. const getExtremeDates = () => { - const nowDate = new Date(Date.now()); + const nowDate = new Date(Date.now()); - const startDate = new Date(); - const endDate = new Date(); + const startDate = new Date(); + const endDate = new Date(); - startDate.setFullYear(nowDate.getFullYear(), 0, 1); - endDate.setFullYear(nowDate.getFullYear(), 6, 1); + startDate.setFullYear(nowDate.getFullYear(), 0, 1); + endDate.setFullYear(nowDate.getFullYear(), 6, 1); - return [startDate, endDate]; + return [startDate, endDate]; }; const setOffsetsToDate = (targetDate, offsetsArray) => { - const newDateMs = offsetsArray.reduce((result, offset) => result + offset, targetDate.getTime()); - return new Date(newDateMs); + const newDateMs = offsetsArray.reduce((result, offset) => result + offset, targetDate.getTime()); + return new Date(newDateMs); }; const utils = { - getDaylightOffset, - getDaylightOffsetInMs, - getTimezoneOffsetChangeInMinutes, - getTimezoneOffsetChangeInMs, - calculateTimezoneByValue, - getCorrectedDateByDaylightOffsets, - isSameAppointmentDates, - correctRecurrenceExceptionByTimezone, - getClientTimezoneOffset, - getDiffBetweenClientTimezoneOffsets, - - createUTCDateWithLocalOffset, - createDateFromUTCWithLocalOffset, - createUTCDate, - - isTimezoneChangeInDate, - getDateWithoutTimezoneChange, - hasDSTInLocalTimeZone, - isEqualLocalTimeZone, - isEqualLocalTimeZoneByDeclaration, - getTimeZones, - - setOffsetsToDate + getDaylightOffset, + getDaylightOffsetInMs, + getTimezoneOffsetChangeInMinutes, + getTimezoneOffsetChangeInMs, + calculateTimezoneByValue, + getCorrectedDateByDaylightOffsets, + isSameAppointmentDates, + correctRecurrenceExceptionByTimezone, + getClientTimezoneOffset, + getDiffBetweenClientTimezoneOffsets, + + createUTCDateWithLocalOffset, + createDateFromUTCWithLocalOffset, + createUTCDate, + + isTimezoneChangeInDate, + getDateWithoutTimezoneChange, + hasDSTInLocalTimeZone, + isEqualLocalTimeZone, + isEqualLocalTimeZoneByDeclaration, + getTimeZones, + + setOffsetsToDate, }; export default utils; diff --git a/packages/devextreme/js/__internal/scheduler/shaders/m_current_time_shader.ts b/packages/devextreme/js/__internal/scheduler/shaders/m_current_time_shader.ts index eff85bc3b5c7..e231eb4a0e17 100644 --- a/packages/devextreme/js/__internal/scheduler/shaders/m_current_time_shader.ts +++ b/packages/devextreme/js/__internal/scheduler/shaders/m_current_time_shader.ts @@ -1,39 +1,43 @@ - -import $ from '../../../core/renderer'; +import $ from '@js/core/renderer'; const DATE_TIME_SHADER_CLASS = 'dx-scheduler-date-time-shader'; class CurrentTimeShader { - constructor(workSpace) { - this._workSpace = workSpace; - this._$container = this._workSpace._dateTableScrollable.$content(); - } + _$container = this._workSpace._dateTableScrollable.$content(); + + // @ts-expect-error + _shader: any[]; + + _$shader: any; + + constructor(public _workSpace: any) { + } - render() { - this.initShaderElements(); + render() { + this.initShaderElements(); - this.renderShader(); + this.renderShader(); - this._shader.forEach((shader, index) => { - this._$container.append(shader); - }); - } + this._shader.forEach((shader) => { + this._$container.append(shader); + }); + } - initShaderElements() { - this._$shader = this.createShader(); - this._shader = []; - this._shader.push(this._$shader); - } + initShaderElements() { + this._$shader = this.createShader(); + this._shader = []; + this._shader.push(this._$shader); + } - renderShader() {} + renderShader() {} - createShader() { - return $('
').addClass(DATE_TIME_SHADER_CLASS); - } + createShader() { + return $('
').addClass(DATE_TIME_SHADER_CLASS); + } - clean() { - this._$container && this._$container.find('.' + DATE_TIME_SHADER_CLASS).remove(); - } + clean() { + this._$container && this._$container.find(`.${DATE_TIME_SHADER_CLASS}`).remove(); + } } export default CurrentTimeShader; diff --git a/packages/devextreme/js/__internal/scheduler/shaders/m_current_time_shader_horizontal.ts b/packages/devextreme/js/__internal/scheduler/shaders/m_current_time_shader_horizontal.ts index f27112a441d1..2e99b476e147 100644 --- a/packages/devextreme/js/__internal/scheduler/shaders/m_current_time_shader_horizontal.ts +++ b/packages/devextreme/js/__internal/scheduler/shaders/m_current_time_shader_horizontal.ts @@ -1,70 +1,71 @@ -import { setWidth } from '../../../core/utils/size'; -import { getBoundingRect } from '../../../core/utils/position'; -import CurrentTimeShader from '../shaders/ui.scheduler.current_time_shader'; +import { getBoundingRect } from '@js/core/utils/position'; +import { setWidth } from '@js/core/utils/size'; + +import CurrentTimeShader from './m_current_time_shader'; class HorizontalCurrentTimeShader extends CurrentTimeShader { - renderShader() { - const groupCount = this._workSpace._isHorizontalGroupedWorkSpace() ? this._workSpace._getGroupCount() : 1; + renderShader() { + const groupCount = this._workSpace._isHorizontalGroupedWorkSpace() ? this._workSpace._getGroupCount() : 1; - for(let i = 0; i < groupCount; i++) { - const isFirstShader = i === 0; - const $shader = isFirstShader ? this._$shader : this.createShader(); + for (let i = 0; i < groupCount; i += 1) { + const isFirstShader = i === 0; + const $shader = isFirstShader ? this._$shader : this.createShader(); - if(this._workSpace.isGroupedByDate()) { - this._customizeGroupedByDateShader($shader, i); - } else { - this._customizeShader($shader, i); - } + if (this._workSpace.isGroupedByDate()) { + this._customizeGroupedByDateShader($shader, i); + } else { + this._customizeShader($shader, i); + } - !isFirstShader && this._shader.push($shader); - } + !isFirstShader && this._shader.push($shader); } + } - _customizeShader($shader, groupIndex) { - const shaderWidth = this._workSpace.getIndicationWidth(); + _customizeShader($shader, groupIndex) { + const shaderWidth = this._workSpace.getIndicationWidth(); - this._applyShaderWidth($shader, shaderWidth); + this._applyShaderWidth($shader, shaderWidth); - if(groupIndex >= 1) { - const workSpace = this._workSpace; - const indicationWidth = workSpace._getCellCount() * workSpace.getCellWidth(); - $shader.css('left', indicationWidth); - } else { - $shader.css('left', 0); - } + if (groupIndex >= 1) { + const workSpace = this._workSpace; + const indicationWidth = workSpace._getCellCount() * workSpace.getCellWidth(); + $shader.css('left', indicationWidth); + } else { + $shader.css('left', 0); } + } - _applyShaderWidth($shader, width) { - const maxWidth = getBoundingRect(this._$container.get(0)).width; - - if(width > maxWidth) { - width = maxWidth; - } + _applyShaderWidth($shader, width) { + const maxWidth = getBoundingRect(this._$container.get(0)).width; - if(width > 0) { - setWidth($shader, width); - } + if (width > maxWidth) { + width = maxWidth; } - _customizeGroupedByDateShader($shader, groupIndex) { - const cellCount = this._workSpace.getIndicationCellCount(); - const integerPart = Math.floor(cellCount); - const fractionPart = cellCount - integerPart; - const isFirstShaderPart = groupIndex === 0; - const workSpace = this._workSpace; - const shaderWidth = isFirstShaderPart ? workSpace.getIndicationWidth() : fractionPart * workSpace.getCellWidth(); - let shaderLeft; + if (width > 0) { + setWidth($shader, width); + } + } - this._applyShaderWidth($shader, shaderWidth); + _customizeGroupedByDateShader($shader, groupIndex) { + const cellCount = this._workSpace.getIndicationCellCount(); + const integerPart = Math.floor(cellCount); + const fractionPart = cellCount - integerPart; + const isFirstShaderPart = groupIndex === 0; + const workSpace = this._workSpace; + const shaderWidth = isFirstShaderPart ? workSpace.getIndicationWidth() : fractionPart * workSpace.getCellWidth(); + let shaderLeft; - if(isFirstShaderPart) { - shaderLeft = workSpace._getCellCount() * workSpace.getCellWidth() * groupIndex; - } else { - shaderLeft = workSpace.getCellWidth() * integerPart * workSpace._getGroupCount() + groupIndex * workSpace.getCellWidth(); - } + this._applyShaderWidth($shader, shaderWidth); - $shader.css('left', shaderLeft); + if (isFirstShaderPart) { + shaderLeft = workSpace._getCellCount() * workSpace.getCellWidth() * groupIndex; + } else { + shaderLeft = workSpace.getCellWidth() * integerPart * workSpace._getGroupCount() + groupIndex * workSpace.getCellWidth(); } + + $shader.css('left', shaderLeft); + } } export default HorizontalCurrentTimeShader; diff --git a/packages/devextreme/js/__internal/scheduler/shaders/m_current_time_shader_vertical.ts b/packages/devextreme/js/__internal/scheduler/shaders/m_current_time_shader_vertical.ts index cfdb0db26db8..956306d8a26d 100644 --- a/packages/devextreme/js/__internal/scheduler/shaders/m_current_time_shader_vertical.ts +++ b/packages/devextreme/js/__internal/scheduler/shaders/m_current_time_shader_vertical.ts @@ -1,124 +1,130 @@ -import { setHeight, setWidth } from '../../../core/utils/size'; -import $ from '../../../core/renderer'; -import CurrentTimeShader from '../shaders/ui.scheduler.current_time_shader'; +import $ from '@js/core/renderer'; +import { setHeight, setWidth } from '@js/core/utils/size'; + +import CurrentTimeShader from './m_current_time_shader'; const DATE_TIME_SHADER_ALL_DAY_CLASS = 'dx-scheduler-date-time-shader-all-day'; const DATE_TIME_SHADER_TOP_CLASS = 'dx-scheduler-date-time-shader-top'; const DATE_TIME_SHADER_BOTTOM_CLASS = 'dx-scheduler-date-time-shader-bottom'; class VerticalCurrentTimeShader extends CurrentTimeShader { - renderShader() { - let shaderHeight = this._getShaderHeight(); - const maxHeight = this._getShaderMaxHeight(); - const isSolidShader = shaderHeight > maxHeight; - - if(shaderHeight > maxHeight) { - shaderHeight = maxHeight; - } - - setHeight(this._$shader, shaderHeight); - const groupCount = this._workSpace._getGroupCount() || 1; - - if(this._workSpace.isGroupedByDate()) { - this._renderGroupedByDateShaderParts(groupCount, shaderHeight, maxHeight, isSolidShader); - } else { - this._renderShaderParts(groupCount, shaderHeight, maxHeight, isSolidShader); - } - } + _$topShader: any; + + _$bottomShader: any; + + _$allDayIndicator: any; + + renderShader() { + let shaderHeight = this._getShaderHeight(); + const maxHeight = this._getShaderMaxHeight(); + const isSolidShader = shaderHeight > maxHeight; - _renderShaderParts(groupCount, shaderHeight, maxHeight, isSolidShader) { - for(let i = 0; i < groupCount; i++) { - const shaderWidth = this._getShaderWidth(i); - this._renderTopShader(this._$shader, shaderHeight, shaderWidth, i); + if (shaderHeight > maxHeight) { + shaderHeight = maxHeight; + } - !isSolidShader && this._renderBottomShader(this._$shader, maxHeight, shaderHeight, shaderWidth, i); + setHeight(this._$shader, shaderHeight); + const groupCount = this._workSpace._getGroupCount() || 1; - this._renderAllDayShader(shaderWidth, i); - } + if (this._workSpace.isGroupedByDate()) { + this._renderGroupedByDateShaderParts(groupCount, shaderHeight, maxHeight, isSolidShader); + } else { + this._renderShaderParts(groupCount, shaderHeight, maxHeight, isSolidShader); } + } - _renderGroupedByDateShaderParts(groupCount, shaderHeight, maxHeight, isSolidShader) { - const shaderWidth = this._getShaderWidth(0); - let bottomShaderWidth = shaderWidth - this._workSpace.getCellWidth(); + _renderShaderParts(groupCount, shaderHeight, maxHeight, isSolidShader) { + for (let i = 0; i < groupCount; i++) { + const shaderWidth = this._getShaderWidth(i); + this._renderTopShader(this._$shader, shaderHeight, shaderWidth, i); - if(shaderHeight < 0) { - shaderHeight = 0; - bottomShaderWidth = shaderWidth; - } + !isSolidShader && this._renderBottomShader(this._$shader, maxHeight, shaderHeight, shaderWidth, i); - this._renderTopShader(this._$shader, shaderHeight, shaderWidth * groupCount, 0); + this._renderAllDayShader(shaderWidth, i); + } + } - !isSolidShader && this._renderBottomShader(this._$shader, maxHeight, shaderHeight, bottomShaderWidth * groupCount + this._workSpace.getCellWidth(), 0); + _renderGroupedByDateShaderParts(groupCount, shaderHeight, maxHeight, isSolidShader) { + const shaderWidth = this._getShaderWidth(0); + let bottomShaderWidth = shaderWidth - this._workSpace.getCellWidth(); - this._renderAllDayShader(shaderWidth * groupCount, 0); + if (shaderHeight < 0) { + shaderHeight = 0; + bottomShaderWidth = shaderWidth; } - _renderTopShader($shader, height, width, i) { - this._$topShader = $('
').addClass(DATE_TIME_SHADER_TOP_CLASS); - if(width) { - setWidth(this._$topShader, width); - } - if(height) { - setHeight(this._$topShader, height); - } + this._renderTopShader(this._$shader, shaderHeight, shaderWidth * groupCount, 0); + + !isSolidShader && this._renderBottomShader(this._$shader, maxHeight, shaderHeight, bottomShaderWidth * groupCount + this._workSpace.getCellWidth(), 0); - this._$topShader.css('marginTop', this._getShaderTopOffset(i)); - this._$topShader.css('left', this._getShaderOffset(i, width)); + this._renderAllDayShader(shaderWidth * groupCount, 0); + } - $shader.append(this._$topShader); + _renderTopShader($shader, height, width, i) { + this._$topShader = $('
').addClass(DATE_TIME_SHADER_TOP_CLASS); + if (width) { + setWidth(this._$topShader, width); + } + if (height) { + setHeight(this._$topShader, height); } - _renderBottomShader($shader, maxHeight, height, width, i) { - this._$bottomShader = $('
').addClass(DATE_TIME_SHADER_BOTTOM_CLASS); + this._$topShader.css('marginTop', this._getShaderTopOffset(i)); + this._$topShader.css('left', this._getShaderOffset(i, width)); - const shaderWidth = height < 0 ? width : width - this._workSpace.getCellWidth(); - const shaderHeight = height < 0 ? maxHeight : maxHeight - height; + $shader.append(this._$topShader); + } - setWidth(this._$bottomShader, shaderWidth); - setHeight(this._$bottomShader, shaderHeight); + _renderBottomShader($shader, maxHeight, height, width, i) { + this._$bottomShader = $('
').addClass(DATE_TIME_SHADER_BOTTOM_CLASS); - this._$bottomShader.css('left', this._getShaderOffset(i, width - this._workSpace.getCellWidth())); + const shaderWidth = height < 0 ? width : width - this._workSpace.getCellWidth(); + const shaderHeight = height < 0 ? maxHeight : maxHeight - height; - $shader.append(this._$bottomShader); - } + setWidth(this._$bottomShader, shaderWidth); + setHeight(this._$bottomShader, shaderHeight); - _renderAllDayShader(shaderWidth, i) { - if(this._workSpace.option('showAllDayPanel')) { - this._$allDayIndicator = $('
').addClass(DATE_TIME_SHADER_ALL_DAY_CLASS); - setHeight(this._$allDayIndicator, this._workSpace.getAllDayHeight()); - setWidth(this._$allDayIndicator, shaderWidth); - this._$allDayIndicator.css('left', this._getShaderOffset(i, shaderWidth)); + this._$bottomShader.css('left', this._getShaderOffset(i, width - this._workSpace.getCellWidth())); - this._workSpace._$allDayPanel.prepend(this._$allDayIndicator); - } - } + $shader.append(this._$bottomShader); + } - _getShaderOffset(i, width) { - return this._workSpace.getGroupedStrategy().getShaderOffset(i, width); - } + _renderAllDayShader(shaderWidth, i) { + if (this._workSpace.option('showAllDayPanel')) { + this._$allDayIndicator = $('
').addClass(DATE_TIME_SHADER_ALL_DAY_CLASS); + setHeight(this._$allDayIndicator, this._workSpace.getAllDayHeight()); + setWidth(this._$allDayIndicator, shaderWidth); + this._$allDayIndicator.css('left', this._getShaderOffset(i, shaderWidth)); - _getShaderTopOffset(i) { - return this._workSpace.getGroupedStrategy().getShaderTopOffset(i); + this._workSpace._$allDayPanel.prepend(this._$allDayIndicator); } + } - _getShaderHeight(i, width) { - return this._workSpace.getGroupedStrategy().getShaderHeight(); - } + _getShaderOffset(i, width) { + return this._workSpace.getGroupedStrategy().getShaderOffset(i, width); + } - _getShaderMaxHeight(i, width) { - return this._workSpace.getGroupedStrategy().getShaderMaxHeight(); - } + _getShaderTopOffset(i) { + return this._workSpace.getGroupedStrategy().getShaderTopOffset(i); + } - _getShaderWidth(i) { - return this._workSpace.getGroupedStrategy().getShaderWidth(i); - } + _getShaderHeight() { + return this._workSpace.getGroupedStrategy().getShaderHeight(); + } - clean() { - super.clean(); + _getShaderMaxHeight() { + return this._workSpace.getGroupedStrategy().getShaderMaxHeight(); + } - this._workSpace && this._workSpace._$allDayPanel && this._workSpace._$allDayPanel.find('.' + DATE_TIME_SHADER_ALL_DAY_CLASS).remove(); - } -} + _getShaderWidth(i) { + return this._workSpace.getGroupedStrategy().getShaderWidth(i); + } + clean() { + super.clean(); + + this._workSpace && this._workSpace._$allDayPanel && this._workSpace._$allDayPanel.find(`.${DATE_TIME_SHADER_ALL_DAY_CLASS}`).remove(); + } +} export default VerticalCurrentTimeShader; diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/helpers/m_position_helper.ts b/packages/devextreme/js/__internal/scheduler/workspaces/helpers/m_position_helper.ts index b4bfd5ecc487..ceb10873e7ac 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/helpers/m_position_helper.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/helpers/m_position_helper.ts @@ -1,288 +1,289 @@ -const getCellSize = (DOMMetaData) => { - const { dateTableCellsMeta } = DOMMetaData; - const length = dateTableCellsMeta?.length; +/* eslint-disable max-classes-per-file */ - if(!length) { - return { - width: 0, - height: 0 - }; - } - - const cellIndex = (length > 1) ? 1 : 0; - const cellSize = dateTableCellsMeta[cellIndex][0]; +const getCellSize = (DOMMetaData) => { + const { dateTableCellsMeta } = DOMMetaData; + const length = dateTableCellsMeta?.length; + if (!length) { return { - width: cellSize.width, - height: cellSize.height, + width: 0, + height: 0, }; + } + + const cellIndex = length > 1 ? 1 : 0; + const cellSize = dateTableCellsMeta[cellIndex][0]; + + return { + width: cellSize.width, + height: cellSize.height, + }; }; const getMaxAllowedHorizontalPosition = (groupIndex, viewDataProvider, rtlEnabled, DOMMetaData) => { - const { dateTableCellsMeta } = DOMMetaData; - const firstRow = dateTableCellsMeta[0]; + const { dateTableCellsMeta } = DOMMetaData; + const firstRow = dateTableCellsMeta[0]; - if(!firstRow) return 0; + if (!firstRow) return 0; - const { columnIndex } = viewDataProvider.getLastGroupCellPosition(groupIndex); - const cellPosition = firstRow[columnIndex]; + const { columnIndex } = viewDataProvider.getLastGroupCellPosition(groupIndex); + const cellPosition = firstRow[columnIndex]; - if(!cellPosition) return 0; + if (!cellPosition) return 0; - return !rtlEnabled - ? cellPosition.left + cellPosition.width - : cellPosition.left; + return !rtlEnabled + ? cellPosition.left + cellPosition.width + : cellPosition.left; }; -export const getCellHeight = (DOMMetaData) => { - return getCellSize(DOMMetaData).height; -}; +export const getCellHeight = (DOMMetaData) => getCellSize(DOMMetaData).height; -export const getCellWidth = (DOMMetaData) => { - return getCellSize(DOMMetaData).width; -}; +export const getCellWidth = (DOMMetaData) => getCellSize(DOMMetaData).width; export const getAllDayHeight = (showAllDayPanel, isVerticalGrouping, DOMMetaData) => { - if(!showAllDayPanel) { - return 0; - } + if (!showAllDayPanel) { + return 0; + } - if(isVerticalGrouping) { - const { dateTableCellsMeta } = DOMMetaData; - const length = dateTableCellsMeta?.length; + if (isVerticalGrouping) { + const { dateTableCellsMeta } = DOMMetaData; + const length = dateTableCellsMeta?.length; - return length - ? dateTableCellsMeta[0][0].height - : 0; - } + return length + ? dateTableCellsMeta[0][0].height + : 0; + } - const { allDayPanelCellsMeta } = DOMMetaData; + const { allDayPanelCellsMeta } = DOMMetaData; - return allDayPanelCellsMeta?.length - ? allDayPanelCellsMeta[0].height - : 0; + return allDayPanelCellsMeta?.length + ? allDayPanelCellsMeta[0].height + : 0; }; export const getMaxAllowedPosition = (groupIndex, viewDataProvider, rtlEnabled, DOMMetaData) => { - const validGroupIndex = groupIndex || 0; + const validGroupIndex = groupIndex || 0; - return getMaxAllowedHorizontalPosition(validGroupIndex, viewDataProvider, rtlEnabled, DOMMetaData); + return getMaxAllowedHorizontalPosition(validGroupIndex, viewDataProvider, rtlEnabled, DOMMetaData); }; - export const getGroupWidth = (groupIndex, viewDataProvider, options) => { - const { - isVirtualScrolling, - rtlEnabled, - DOMMetaData - } = options; + const { + isVirtualScrolling, + rtlEnabled, + DOMMetaData, + } = options; + + const cellWidth = getCellWidth(DOMMetaData); + let result = viewDataProvider.getCellCount(options) * cellWidth; + // TODO: refactor after deleting old render + if (isVirtualScrolling) { + const groupedData = viewDataProvider.groupedDataMap.dateTableGroupedMap; + const groupLength = groupedData[groupIndex][0].length; + + result = groupLength * cellWidth; + } + + const position = getMaxAllowedPosition( + groupIndex, + viewDataProvider, + rtlEnabled, + DOMMetaData, + ); + + const currentPosition = position[groupIndex]; + + if (currentPosition) { + if (rtlEnabled) { + result = currentPosition - position[groupIndex + 1]; + } else if (groupIndex === 0) { + result = currentPosition; + } else { + result = currentPosition - position[groupIndex - 1]; + } + } - const cellWidth = getCellWidth(DOMMetaData); - let result = viewDataProvider.getCellCount(options) * cellWidth; - // TODO: refactor after deleting old render - if(isVirtualScrolling) { - const groupedData = viewDataProvider.groupedDataMap.dateTableGroupedMap; - const groupLength = groupedData[groupIndex][0].length; + return result; +}; - result = groupLength * cellWidth; - } +export class PositionHelper { + groupStrategy: any; - const position = getMaxAllowedPosition( - groupIndex, - viewDataProvider, - rtlEnabled, - DOMMetaData - ); + get viewDataProvider() { return this.options.viewDataProvider; } - const currentPosition = position[groupIndex]; - - if(currentPosition) { - if(rtlEnabled) { - result = currentPosition - position[groupIndex + 1]; - } else { - if(groupIndex === 0) { - result = currentPosition; - } else { - result = currentPosition - position[groupIndex - 1]; - } - } - } + get rtlEnabled() { return this.options.rtlEnabled; } - return result; -}; + get isGroupedByDate() { return this.options.isGroupedByDate; } -export class PositionHelper { - get viewDataProvider() { return this.options.viewDataProvider; } - get rtlEnabled() { return this.options.rtlEnabled; } - get isGroupedByDate() { return this.options.isGroupedByDate; } - get groupCount() { return this.options.groupCount; } - get DOMMetaData() { return this.options.getDOMMetaDataCallback(); } - - constructor(options) { - this.options = options; - this.groupStrategy = this.options.isVerticalGrouping - ? new GroupStrategyBase(this.options) - : new GroupStrategyHorizontal(this.options); - } + get groupCount() { return this.options.groupCount; } - getHorizontalMax(groupIndex) { - const getMaxPosition = (groupIndex) => { - return getMaxAllowedPosition( - groupIndex, - this.viewDataProvider, - this.rtlEnabled, - this.DOMMetaData - ); - }; - - if(this.isGroupedByDate) { - const viewPortGroupCount = this.viewDataProvider.getViewPortGroupCount(); - return Math.max( - getMaxPosition(groupIndex), - getMaxPosition(viewPortGroupCount - 1), - ); - } - - return getMaxPosition(groupIndex); - } + get DOMMetaData() { return this.options.getDOMMetaDataCallback(); } - getResizableStep() { - const cellWidth = getCellWidth(this.DOMMetaData); + constructor(public options) { + this.groupStrategy = this.options.isVerticalGrouping + ? new GroupStrategyBase(this.options) + : new GroupStrategyHorizontal(this.options); + } - if(this.isGroupedByDate) { - return this.groupCount * cellWidth; - } + getHorizontalMax(groupIndex) { + const getMaxPosition = (groupIndex) => getMaxAllowedPosition( + groupIndex, + this.viewDataProvider, + this.rtlEnabled, + this.DOMMetaData, + ); - return cellWidth; + if (this.isGroupedByDate) { + const viewPortGroupCount = this.viewDataProvider.getViewPortGroupCount(); + return Math.max( + getMaxPosition(groupIndex), + getMaxPosition(viewPortGroupCount - 1), + ); } - getVerticalMax(options) { - return this.groupStrategy.getVerticalMax(options); - } + return getMaxPosition(groupIndex); + } - getOffsetByAllDayPanel(options) { - return this.groupStrategy.getOffsetByAllDayPanel(options); - } + getResizableStep() { + const cellWidth = getCellWidth(this.DOMMetaData); - getGroupTop(options) { - return this.groupStrategy.getGroupTop(options); + if (this.isGroupedByDate) { + return this.groupCount * cellWidth; } -} -class GroupStrategyBase { - constructor(options) { - this.options = options; - } + return cellWidth; + } - get viewDataProvider() { return this.options.viewDataProvider; } - get isGroupedByDate() { return this.options.isGroupedByDate; } - get rtlEnabled() { return this.options.rtlEnabled; } - get groupCount() { return this.options.groupCount; } - get DOMMetaData() { return this.options.getDOMMetaDataCallback(); } - - getOffsetByAllDayPanel({ - groupIndex, - supportAllDayRow, - showAllDayPanel - }) { - let result = 0; - - if(supportAllDayRow && showAllDayPanel) { - const allDayPanelHeight = getAllDayHeight( - showAllDayPanel, - true, - this.DOMMetaData - ); - result = allDayPanelHeight * (groupIndex + 1); - } - - return result; - } + getVerticalMax(options) { + return this.groupStrategy.getVerticalMax(options); + } - getVerticalMax(options) { - let maxAllowedPosition = this._getMaxAllowedVerticalPosition({ - ...options, - viewDataProvider: this.viewDataProvider, - rtlEnabled: this.rtlEnabled, - DOMMetaData: this.DOMMetaData - }); + getOffsetByAllDayPanel(options) { + return this.groupStrategy.getOffsetByAllDayPanel(options); + } - maxAllowedPosition += this.getOffsetByAllDayPanel(options); + getGroupTop(options) { + return this.groupStrategy.getGroupTop(options); + } +} - return maxAllowedPosition; - } +class GroupStrategyBase { + constructor(public options) { + } - getGroupTop({ - groupIndex, - showAllDayPanel, - isGroupedAllDayPanel, - }) { - const rowCount = this.viewDataProvider.getRowCountInGroup(groupIndex); - const maxVerticalPosition = this._getMaxAllowedVerticalPosition({ - groupIndex, - viewDataProvider: this.viewDataProvider, - showAllDayPanel, - isGroupedAllDayPanel, - isVerticalGrouping: true, - DOMMetaData: this.DOMMetaData - }); - - return maxVerticalPosition - getCellHeight(this.DOMMetaData) * rowCount; - } + get viewDataProvider() { return this.options.viewDataProvider; } - _getAllDayHeight(showAllDayPanel) { - return getAllDayHeight(showAllDayPanel, true, this.DOMMetaData); - } + get isGroupedByDate() { return this.options.isGroupedByDate; } - _getMaxAllowedVerticalPosition({ - groupIndex, - showAllDayPanel, - isGroupedAllDayPanel, - }) { - const { rowIndex } = this.viewDataProvider.getLastGroupCellPosition(groupIndex); - const { dateTableCellsMeta } = this.DOMMetaData; - const lastGroupRow = dateTableCellsMeta[rowIndex]; + get rtlEnabled() { return this.options.rtlEnabled; } - if(!lastGroupRow) return 0; + get groupCount() { return this.options.groupCount; } - let result = lastGroupRow[0].top + lastGroupRow[0].height; + get DOMMetaData() { return this.options.getDOMMetaDataCallback(); } - // TODO remove while refactoring dual calculcations. - // Should decrease allDayPanel amount due to the dual calculation corrections. - if(isGroupedAllDayPanel) { - result -= (groupIndex + 1) * this._getAllDayHeight(showAllDayPanel); - } + getOffsetByAllDayPanel({ + groupIndex, + supportAllDayRow, + showAllDayPanel, + }) { + let result = 0; - return result; + if (supportAllDayRow && showAllDayPanel) { + const allDayPanelHeight = getAllDayHeight( + showAllDayPanel, + true, + this.DOMMetaData, + ); + result = allDayPanelHeight * (groupIndex + 1); } + + return result; + } + + getVerticalMax(options) { + let maxAllowedPosition = this._getMaxAllowedVerticalPosition({ + ...options, + viewDataProvider: this.viewDataProvider, + rtlEnabled: this.rtlEnabled, + DOMMetaData: this.DOMMetaData, + }); + + maxAllowedPosition += this.getOffsetByAllDayPanel(options); + + return maxAllowedPosition; + } + + getGroupTop({ + groupIndex, + showAllDayPanel, + isGroupedAllDayPanel, + }) { + const rowCount = this.viewDataProvider.getRowCountInGroup(groupIndex); + const maxVerticalPosition = this._getMaxAllowedVerticalPosition({ + groupIndex, + viewDataProvider: this.viewDataProvider, + showAllDayPanel, + isGroupedAllDayPanel, + isVerticalGrouping: true, + DOMMetaData: this.DOMMetaData, + } as any); + + return maxVerticalPosition - getCellHeight(this.DOMMetaData) * rowCount; + } + + _getAllDayHeight(showAllDayPanel) { + return getAllDayHeight(showAllDayPanel, true, this.DOMMetaData); + } + + _getMaxAllowedVerticalPosition({ + groupIndex, + showAllDayPanel, + isGroupedAllDayPanel, + }) { + const { rowIndex } = this.viewDataProvider.getLastGroupCellPosition(groupIndex); + const { dateTableCellsMeta } = this.DOMMetaData; + const lastGroupRow = dateTableCellsMeta[rowIndex]; + + if (!lastGroupRow) return 0; + + let result = lastGroupRow[0].top + lastGroupRow[0].height; + + // TODO remove while refactoring dual calculcations. + // Should decrease allDayPanel amount due to the dual calculation corrections. + if (isGroupedAllDayPanel) { + result -= (groupIndex + 1) * this._getAllDayHeight(showAllDayPanel); + } + + return result; + } } class GroupStrategyHorizontal extends GroupStrategyBase { - getOffsetByAllDayPanel(options) { - return 0; - } + getOffsetByAllDayPanel() { + return 0; + } - getVerticalMax(options) { - const { - isVirtualScrolling, - groupIndex - } = options; + getVerticalMax(options) { + const { + isVirtualScrolling, + groupIndex, + } = options; - const correctedGroupIndex = isVirtualScrolling - ? groupIndex - : 0; + const correctedGroupIndex = isVirtualScrolling + ? groupIndex + : 0; - return this._getMaxAllowedVerticalPosition({ - ...options, - groupIndex: correctedGroupIndex, - }); - } + return this._getMaxAllowedVerticalPosition({ + ...options, + groupIndex: correctedGroupIndex, + }); + } - getGroupTop(options) { - return 0; - } + getGroupTop() { + return 0; + } - _getAllDayHeight(showAllDayPanel) { - return getAllDayHeight(showAllDayPanel, false, this.DOMMetaData); - } + _getAllDayHeight(showAllDayPanel) { + return getAllDayHeight(showAllDayPanel, false, this.DOMMetaData); + } } diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_agenda.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_agenda.ts index c5784e7cf562..8f25c0ee3e04 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_agenda.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_agenda.ts @@ -1,26 +1,27 @@ -import { setOuterHeight, setHeight } from '../../../core/utils/size'; -import $ from '../../../core/renderer'; -import domAdapter from '../../../core/dom_adapter'; -import { noop } from '../../../core/utils/common'; -import { each } from '../../../core/utils/iterator'; -import { getPublicElement } from '../../../core/element'; -import registerComponent from '../../../core/component_registrator'; -import WorkSpace from './ui.scheduler.work_space'; -import { extend } from '../../../core/utils/extend'; -import dateLocalization from '../../../localization/date'; -import tableCreatorModule from '../table_creator'; +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 { 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 dateLocalization from '@js/localization/date'; +import { calculateStartViewDate } from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/agenda'; +import { formatWeekday, getVerticalGroupCountClass } from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/base'; + import { - TIME_PANEL_CLASS, - DATE_TABLE_CLASS, - DATE_TABLE_ROW_CLASS, - GROUP_ROW_CLASS, - GROUP_HEADER_CONTENT_CLASS, -} from '../classes'; -import { createReducedResourcesTree, getDataAccessors, getPathToLeaf } from '../../../__internal/scheduler/resources/m_utils'; -import { calculateStartViewDate } from '../../../renovation/ui/scheduler/view_model/to_test/views/utils/agenda'; -import { formatWeekday, getVerticalGroupCountClass } from '../../../renovation/ui/scheduler/view_model/to_test/views/utils/base'; -import { VIEWS } from '../constants'; -import dateUtils from '../../../core/utils/date'; + DATE_TABLE_CLASS, + DATE_TABLE_ROW_CLASS, + GROUP_HEADER_CONTENT_CLASS, + GROUP_ROW_CLASS, + TIME_PANEL_CLASS, +} from '../m_classes'; +import { VIEWS } from '../m_constants'; +import tableCreatorModule from '../m_table_creator'; +import { createReducedResourcesTree, getDataAccessors, getPathToLeaf } from '../resources/m_utils'; +import WorkSpace from './m_work_space'; const { tableCreator } = tableCreatorModule; @@ -38,524 +39,533 @@ const INNER_CELL_MARGIN = 5; const OUTER_CELL_MARGIN = 20; class SchedulerAgenda extends WorkSpace { - get type() { return VIEWS.AGENDA; } - - get renderingStrategy() { return this.invoke('getLayoutManager').getRenderingStrategyInstance(); } - - get appointmentDataProvider() { return this.option('getAppointmentDataProvider')(); } - - getStartViewDate() { - return this._startViewDate; - } - - _init() { - super._init(); - this._activeStateUnit = undefined; - } - - _getDefaultOptions() { - return extend(super._getDefaultOptions(), { - // Number | "month" - agendaDuration: 7, - rowHeight: 60, - noDataText: '' - }); - } - - _optionChanged(args) { - const name = args.name; - const value = args.value; - - switch(name) { - case 'agendaDuration': - break; - case 'noDataText': - case 'rowHeight': - this._recalculateAgenda(this._rows); - break; - case 'groups': - if(!value || !value.length) { - if(this._$groupTable) { - this._$groupTable.remove(); - this._$groupTable = null; - this._detachGroupCountClass(); - } - } else { - if(!this._$groupTable) { - this._initGroupTable(); - this._dateTableScrollable.$content().prepend(this._$groupTable); - } - } - super._optionChanged(args); - break; - default: - super._optionChanged(args); + _startViewDate: any; + + _rows: any; + + _$rows: any; + + _$noDataContainer: any; + + get type() { return VIEWS.AGENDA; } + + get renderingStrategy() { return (this.invoke as any)('getLayoutManager').getRenderingStrategyInstance(); } + + get appointmentDataProvider() { return (this.option('getAppointmentDataProvider') as any)(); } + + getStartViewDate() { + return this._startViewDate; + } + + _init() { + super._init(); + this._activeStateUnit = undefined; + } + + _getDefaultOptions() { + return extend(super._getDefaultOptions(), { + // Number | "month" + agendaDuration: 7, + rowHeight: 60, + noDataText: '', + }); + } + + _optionChanged(args) { + const { name } = args; + const { value } = args; + + switch (name) { + case 'agendaDuration': + break; + case 'noDataText': + case 'rowHeight': + this._recalculateAgenda(this._rows); + break; + case 'groups': + if (!value || !value.length) { + if (this._$groupTable) { + this._$groupTable.remove(); + this._$groupTable = null; + this._detachGroupCountClass(); + } + } else if (!this._$groupTable) { + this._initGroupTable(); + this._dateTableScrollable.$content().prepend(this._$groupTable); } + super._optionChanged(args); + break; + default: + super._optionChanged(args); } + } - _renderFocusState() { return noop(); } - _renderFocusTarget() { return noop(); } - _cleanFocusState() { return noop(); } + _renderFocusState() { return noop(); } - supportAllDayRow() { - return false; - } + _renderFocusTarget() { return noop(); } - _isVerticalGroupedWorkSpace() { - return false; - } + _cleanFocusState() { return noop(); } - _getElementClass() { - return AGENDA_CLASS; - } + supportAllDayRow() { + return false; + } - _calculateStartViewDate() { - return calculateStartViewDate(this.option('currentDate'), this.option('startDayHour')); - } + _isVerticalGroupedWorkSpace() { + return false; + } - _getRowCount() { - return this.option('agendaDuration'); - } + _getElementClass() { + return AGENDA_CLASS; + } - _getCellCount() { - return 1; - } + _calculateStartViewDate() { + return calculateStartViewDate(this.option('currentDate') as any, this.option('startDayHour') as any); + } - _getTimePanelRowCount() { - return this.option('agendaDuration'); - } + _getRowCount() { + return this.option('agendaDuration'); + } - _renderAllDayPanel() { return noop(); } + _getCellCount() { + return 1; + } - _toggleAllDayVisibility() { return noop(); } + _getTimePanelRowCount() { + return this.option('agendaDuration'); + } - _initWorkSpaceUnits() { - this._initGroupTable(); - this._$timePanel = $('').addClass(TIME_PANEL_CLASS); - this._$dateTable = $('
').addClass(DATE_TABLE_CLASS); - this._$dateTableScrollableContent = $('
').addClass('dx-scheduler-date-table-scrollable-content'); - this._$dateTableContainer = $('
').addClass('dx-scheduler-date-table-container'); - } + _renderAllDayPanel() { return noop(); } - _initGroupTable() { - const groups = this.option('groups'); - if(groups && groups.length) { - this._$groupTable = $('
').addClass(GROUP_TABLE_CLASS); - } - } + _toggleAllDayVisibility() { return noop(); } + + _initWorkSpaceUnits() { + this._initGroupTable(); + this._$timePanel = $('
').addClass(TIME_PANEL_CLASS); + this._$dateTable = $('
').addClass(DATE_TABLE_CLASS); + this._$dateTableScrollableContent = $('
').addClass('dx-scheduler-date-table-scrollable-content'); + this._$dateTableContainer = $('
').addClass('dx-scheduler-date-table-container'); + } - _renderView() { - this._startViewDate = this._calculateStartViewDate(); - this._rows = []; - this._initPositionHelper(); + _initGroupTable() { + const groups = this.option('groups'); + if (groups && groups.length) { + this._$groupTable = $('
').addClass(GROUP_TABLE_CLASS); } + } - _recalculateAgenda(rows) { - let cellTemplates = []; - this._cleanView(); + _renderView() { + this._startViewDate = this._calculateStartViewDate(); + this._rows = []; + this._initPositionHelper(); + } - if(this._rowsIsEmpty(rows)) { - this._renderNoData(); - return; - } - this._rows = rows; - - if(this._$groupTable) { - cellTemplates = this._renderGroupHeader(); - this._setGroupHeaderCellsHeight(); - } + _recalculateAgenda(rows) { + let cellTemplates = []; + this._cleanView(); - this._renderTimePanel(); - this._renderDateTable(); - this.invoke('onAgendaReady', rows); - this._applyCellTemplates(cellTemplates); - this._dateTableScrollable.update(); + if (this._rowsIsEmpty(rows)) { + this._renderNoData(); + return; } + this._rows = rows; - _renderNoData() { - this._$noDataContainer = $('
').addClass(NODATA_CONTAINER_CLASS) - .html(this.option('noDataText')); - - this._dateTableScrollable.$content().append(this._$noDataContainer); + if (this._$groupTable) { + cellTemplates = this._renderGroupHeader(); + this._setGroupHeaderCellsHeight(); } - _setTableSizes() { return noop(); } - _toggleHorizontalScrollClass() { return noop(); } - _createCrossScrollingConfig() { return noop(); } + this._renderTimePanel(); + this._renderDateTable(); + (this.invoke as any)('onAgendaReady', rows); + this._applyCellTemplates(cellTemplates); + this._dateTableScrollable.update(); + } - _setGroupHeaderCellsHeight() { - const $cells = this._getGroupHeaderCells().filter(function(_, element) { - return !element.getAttribute('rowSpan'); - }); - const rows = this._removeEmptyRows(this._rows); + _renderNoData() { + this._$noDataContainer = $('
').addClass(NODATA_CONTAINER_CLASS) + .html(this.option('noDataText') as any); - if(!rows.length) { - return; - } + this._dateTableScrollable.$content().append(this._$noDataContainer); + } - for(let i = 0; i < $cells.length; i++) { - const $cellContent = $cells.eq(i).find('.dx-scheduler-group-header-content'); - setOuterHeight($cellContent, this._getGroupRowHeight(rows[i])); - } - } + _setTableSizes() { return noop(); } - _rowsIsEmpty(rows) { - let result = true; + _toggleHorizontalScrollClass() { return noop(); } - for(let i = 0; i < rows.length; i++) { - const groupRow = rows[i]; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _createCrossScrollingConfig(argument?: any) { return noop(); } - for(let j = 0; j < groupRow.length; j++) { - if(groupRow[j]) { - result = false; - break; - } - } - } + _setGroupHeaderCellsHeight() { + const $cells = this._getGroupHeaderCells().filter((_, element) => !element.getAttribute('rowSpan')); + const rows = this._removeEmptyRows(this._rows); - return result; + if (!rows.length) { + return; } - _attachGroupCountClass() { - const className = getVerticalGroupCountClass(this.option('groups')); - this.$element().addClass(className); + for (let i = 0; i < $cells.length; i++) { + const $cellContent = $cells.eq(i).find('.dx-scheduler-group-header-content'); + setOuterHeight($cellContent, this._getGroupRowHeight(rows[i])); } + } - _removeEmptyRows(rows) { - const result = []; - const isEmpty = function(data) { - return !data.some(function(value) { - return value > 0; - }); - }; - - for(let i = 0; i < rows.length; i++) { - if(rows[i].length && !isEmpty(rows[i])) { - result.push(rows[i]); - } - } + _rowsIsEmpty(rows) { + let result = true; - return result; - } - - _getGroupHeaderContainer() { - return this._$groupTable; - } + for (let i = 0; i < rows.length; i++) { + const groupRow = rows[i]; - _makeGroupRows() { - const tree = createReducedResourcesTree( - this.option('loadedResources'), - (field, action) => getDataAccessors(this.option('getResourceDataAccessors')(), field, action), - this.option('getFilteredItems')() - ); - - const cellTemplate = this.option('resourceCellTemplate'); - const getGroupHeaderContentClass = GROUP_HEADER_CONTENT_CLASS; - const cellTemplates = []; - - const table = tableCreator.makeGroupedTableFromJSON(tableCreator.VERTICAL, tree, { - cellTag: 'th', - groupTableClass: GROUP_TABLE_CLASS, - groupRowClass: GROUP_ROW_CLASS, - groupCellClass: this._getGroupHeaderClass(), - groupCellCustomContent(cell, cellTextElement, index, data) { - const container = domAdapter.createElement('div'); - container.className = getGroupHeaderContentClass; - - if(cellTemplate && cellTemplate.render) { - cellTemplates.push(cellTemplate.render.bind(cellTemplate, { - model: { - data: data.data, - id: data.value, - color: data.color, - text: cellTextElement.textContent - }, - container: getPublicElement($(container)), - index: index - })); - - } else { - const contentWrapper = domAdapter.createElement('div'); - contentWrapper.appendChild(cellTextElement); - container.appendChild(contentWrapper); - } - - cell.appendChild(container); + for (let j = 0; j < groupRow.length; j++) { + if (groupRow[j]) { + result = false; + break; + } + } + } + + return result; + } + + _attachGroupCountClass() { + const className = getVerticalGroupCountClass(this.option('groups') as any); + (this.$element() as any).addClass(className); + } + + _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]); + } + } + + return result; + } + + _getGroupHeaderContainer() { + return this._$groupTable; + } + + _makeGroupRows() { + const tree = createReducedResourcesTree( + this.option('loadedResources'), + (field, action) => getDataAccessors((this.option('getResourceDataAccessors') as any)(), field, action), + (this.option('getFilteredItems') as any)(), + ); + + const cellTemplate: any = this.option('resourceCellTemplate'); + const getGroupHeaderContentClass = GROUP_HEADER_CONTENT_CLASS; + const cellTemplates: any[] = []; + + const table = tableCreator.makeGroupedTableFromJSON(tableCreator.VERTICAL, tree, { + cellTag: 'th', + groupTableClass: GROUP_TABLE_CLASS, + groupRowClass: GROUP_ROW_CLASS, + groupCellClass: this._getGroupHeaderClass(), + groupCellCustomContent(cell, cellTextElement, index, data) { + const container = domAdapter.createElement('div'); + container.className = getGroupHeaderContentClass; + + if (cellTemplate && cellTemplate.render) { + cellTemplates.push(cellTemplate.render.bind(cellTemplate, { + model: { + data: data.data, + id: data.value, + color: data.color, + text: cellTextElement.textContent, }, - cellTemplate: cellTemplate - }); - - return { - elements: $(table).find(`.${GROUP_ROW_CLASS}`), - cellTemplates: cellTemplates - }; - } - - _cleanView() { - this._$dateTable.empty(); - this._$timePanel.empty(); + container: getPublicElement($(container)), + index, + })); + } else { + const contentWrapper = domAdapter.createElement('div'); + contentWrapper.appendChild(cellTextElement); + container.appendChild(contentWrapper); + } - if(this._$groupTable) { - this._$groupTable.empty(); + cell.appendChild(container); + }, + cellTemplate, + }); + + return { + elements: $(table).find(`.${GROUP_ROW_CLASS}`), + cellTemplates, + }; + } + + _cleanView() { + this._$dateTable.empty(); + this._$timePanel.empty(); + + if (this._$groupTable) { + this._$groupTable.empty(); + } + + if (this._$noDataContainer) { + this._$noDataContainer.empty(); + this._$noDataContainer.remove(); + + delete this._$noDataContainer; + } + } + + _createWorkSpaceElements() { + this._createWorkSpaceStaticElements(); + } + + _createWorkSpaceStaticElements() { + this._$dateTableContainer.append(this._$dateTable); + this._dateTableScrollable.$content().append(this._$dateTableScrollableContent); + + if (this._$groupTable) { + this._$dateTableScrollableContent.prepend(this._$groupTable); + } + + this._$dateTableScrollableContent.append(this._$timePanel, this._$dateTableContainer); + this.$element().append(this._dateTableScrollable.$element()); + } + + _renderDateTable() { + this._renderTableBody({ + container: getPublicElement(this._$dateTable), + rowClass: DATE_TABLE_ROW_CLASS, + cellClass: this._getDateTableCellClass(), + }); + } + + _attachTablesEvents() { return noop(); } + + _attachEvents() { return noop(); } + + _cleanCellDataCache() { return noop(); } + + isIndicationAvailable() { + return false; + } + + _prepareCellTemplateOptions(text, date, rowIndex, $cell) { + const groupsOpt: any[] = this.option('groups')!; + const groups = {}; + const isGroupedView = !!groupsOpt.length; + const path = isGroupedView && getPathToLeaf(rowIndex, groupsOpt) || []; + + path.forEach((resourceValue, resourceIndex) => { + const resourceName = groupsOpt[resourceIndex].name; + groups[resourceName] = resourceValue; + }); + const groupIndex = isGroupedView + ? this._getGroupIndexByResourceId(groups) + : undefined; + + return { + model: { + text, + date, + groups, + groupIndex, + }, + container: getPublicElement($cell), + index: rowIndex, + }; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _renderTableBody(options: any, delayCellTemplateRendering?: any) { + const cellTemplates: any[] = []; + const cellTemplateOpt = options.cellTemplate; + + this._$rows = []; + let i; + + const fillTableBody = function (rowIndex, rowSize) { + if (rowSize) { + let date; + let cellDateNumber; + let cellDayName; + const $row = $('
'); + const $td = $('').append(this._$rows)); + this._applyCellTemplates(cellTemplates); + } - isIndicationAvailable() { - return false; - } + _setLastRowClass() { + if (this._rows.length > 1 && this._$rows.length) { + const $lastRow = this._$rows[this._$rows.length - 1]; - _prepareCellTemplateOptions(text, date, rowIndex, $cell) { - const groupsOpt = this.option('groups'); - const groups = {}; - const isGroupedView = !!groupsOpt.length; - const path = isGroupedView && getPathToLeaf(rowIndex, groupsOpt) || []; - - path.forEach(function(resourceValue, resourceIndex) { - const resourceName = groupsOpt[resourceIndex].name; - groups[resourceName] = resourceValue; - }); - const groupIndex = isGroupedView - ? this._getGroupIndexByResourceId(groups) - : undefined; - - return { - model: { - text, - date, - groups, - groupIndex, - }, - container: getPublicElement($cell), - index: rowIndex - }; + $lastRow.addClass(LAST_ROW_CLASS); } + } - _renderTableBody(options) { - const cellTemplates = []; - const cellTemplateOpt = options.cellTemplate; - - this._$rows = []; - let i; - - const fillTableBody = (function(rowIndex, rowSize) { - if(rowSize) { - let date; - let cellDateNumber; - let cellDayName; - const $row = $(''); - const $td = $('').append(this._$rows)); - this._applyCellTemplates(cellTemplates); - } + _getTimePanelStartDate(rowIndex) { + const current = new Date(this.option('currentDate') as any); + const cellDate = new Date(current.setDate(current.getDate() + rowIndex)); - _setLastRowClass() { - if(this._rows.length > 1 && this._$rows.length) { - const $lastRow = this._$rows[this._$rows.length - 1]; + return cellDate; + } - $lastRow.addClass(LAST_ROW_CLASS); - } - } + _getRowHeight(rowSize) { + const baseHeight = this.option('rowHeight') as any; + const innerOffset = (rowSize - 1) * INNER_CELL_MARGIN; - _renderTimePanel() { - this._renderTableBody({ - container: getPublicElement(this._$timePanel), - rowCount: this._getTimePanelRowCount(), - cellCount: 1, - rowClass: TIME_PANEL_ROW_CLASS, - cellClass: TIME_PANEL_CELL_CLASS, - cellTemplate: this.option('dateCellTemplate'), - getStartDate: this._getTimePanelStartDate.bind(this) - }); - } - - _getTimePanelStartDate(rowIndex) { - const current = new Date(this.option('currentDate')); - const cellDate = new Date(current.setDate(current.getDate() + rowIndex)); + return rowSize ? (baseHeight * rowSize) + innerOffset + OUTER_CELL_MARGIN : 0; + } - return cellDate; + _getGroupRowHeight(groupRows) { + // TODO: hotfix + if (!groupRows) { + return; } - _getRowHeight(rowSize) { - const baseHeight = this.option('rowHeight'); - const innerOffset = (rowSize - 1) * INNER_CELL_MARGIN; + let result = 0; - return rowSize ? (baseHeight * rowSize) + innerOffset + OUTER_CELL_MARGIN : 0; + for (let i = 0; i < groupRows.length; i++) { + result += this._getRowHeight(groupRows[i]); } - _getGroupRowHeight(groupRows) { - // TODO: hotfix - if(!groupRows) { - return; - } + return result; + } - let result = 0; + _calculateRows(appointments) { + return this.renderingStrategy.calculateRows( + appointments, + this.option('agendaDuration'), + this.option('currentDate'), + ); + } - for(let i = 0; i < groupRows.length; i++) { - result += this._getRowHeight(groupRows[i]); - } + onDataSourceChanged(appointments) { + super.onDataSourceChanged(); - return result; - } + this._renderView(); - _calculateRows(appointments) { - return this.renderingStrategy.calculateRows( - appointments, - this.option('agendaDuration'), - this.option('currentDate')); - } + const rows = this._calculateRows(appointments); + this._recalculateAgenda(rows); + } - onDataSourceChanged(appointments) { - super.onDataSourceChanged(); + getAgendaVerticalStepHeight() { + return this.option('rowHeight'); + } - this._renderView(); + getEndViewDate() { + const currentDate = new Date(this.option('currentDate') as any); + const agendaDuration: any = this.option('agendaDuration'); - const rows = this._calculateRows(appointments); - this._recalculateAgenda(rows); - } + currentDate.setHours(this.option('endDayHour') as any); - getAgendaVerticalStepHeight() { - return this.option('rowHeight'); - } + const result = currentDate.setDate(currentDate.getDate() + agendaDuration - 1) - 60000; - getEndViewDate() { - const currentDate = new Date(this.option('currentDate')); - const agendaDuration = this.option('agendaDuration'); + return new Date(result); + } - currentDate.setHours(this.option('endDayHour')); + getEndViewDateByEndDayHour() { + return this.getEndViewDate(); + } - const result = currentDate.setDate(currentDate.getDate() + agendaDuration - 1) - 60000; + getCellDataByCoordinates() { + return { + startDate: null, + endDate: null, + }; + } - return new Date(result); - } + updateScrollPosition(date) { + const newDate = this.timeZoneCalculator.createDate(date, { path: 'toGrid' }); - getEndViewDateByEndDayHour() { - return this.getEndViewDate(); - } + const bounds = this.getVisibleBounds(); + const startDateHour = newDate.getHours(); + const startDateMinutes = newDate.getMinutes(); - getCellDataByCoordinates() { - return { - startDate: null, - endDate: null - }; + if (this.needUpdateScrollPosition(startDateHour, startDateMinutes, bounds, newDate)) { + this.scrollToTime(startDateHour, startDateMinutes, newDate); } + } - updateScrollPosition(date) { - const newDate = this.timeZoneCalculator.createDate(date, { path: 'toGrid' }); - - const bounds = this.getVisibleBounds(); - const startDateHour = newDate.getHours(); - const startDateMinutes = newDate.getMinutes(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + needUpdateScrollPosition(hours, minutes, bounds, newData?: any) { + let isUpdateNeeded = false; - if(this.needUpdateScrollPosition(startDateHour, startDateMinutes, bounds, newDate)) { - this.scrollToTime(startDateHour, startDateMinutes, newDate); - } + if (hours < bounds.top.hours || hours > bounds.bottom.hours) { + isUpdateNeeded = true; } - needUpdateScrollPosition(hours, minutes, bounds) { - let isUpdateNeeded = false; - - if(hours < bounds.top.hours || hours > bounds.bottom.hours) { - isUpdateNeeded = true; - } - - if(hours === bounds.top.hours && minutes < bounds.top.minutes) { - isUpdateNeeded = true; - } - - if(hours === bounds.bottom.hours && minutes > bounds.top.minutes) { - isUpdateNeeded = true; - } + if (hours === bounds.top.hours && minutes < bounds.top.minutes) { + isUpdateNeeded = true; + } - return isUpdateNeeded; + if (hours === bounds.bottom.hours && minutes > bounds.top.minutes) { + isUpdateNeeded = true; } - renovatedRenderSupported() { return false; } + return isUpdateNeeded; + } - _setSelectedCellsByCellData() {} + renovatedRenderSupported() { return false; } - _getIntervalDuration() { - return dateUtils.dateToMilliseconds('day') * this.option('intervalCount'); - } + _setSelectedCellsByCellData() {} - getDOMElementsMetaData() { - return { - dateTableCellsMeta: [[{}]], - allDayPanelCellsMeta: [{}], - }; - } + _getIntervalDuration() { + return dateUtils.dateToMilliseconds('day') * (this.option('intervalCount') as any); + } + + getDOMElementsMetaData() { + return { + dateTableCellsMeta: [[{}]], + allDayPanelCellsMeta: [{}], + }; + } } -registerComponent('dxSchedulerAgenda', SchedulerAgenda); +registerComponent('dxSchedulerAgenda', SchedulerAgenda as any); export default SchedulerAgenda; diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_cache.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_cache.ts index 83d740e4bd2b..8d6febbb2931 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_cache.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_cache.ts @@ -1,25 +1,23 @@ -import { isDefined } from '../../../core/utils/type'; +import { isDefined } from '@js/core/utils/type'; export class Cache { - constructor() { - this._cache = new Map(); - } - - get size() { return this._cache.size; } + _cache = new Map(); - clear() { - this._cache.clear(); - } + get size() { return this._cache.size; } - get(name, callback) { - if(!this._cache.has(name) && callback) { - this.set(name, callback()); - } + clear() { + this._cache.clear(); + } - return this._cache.get(name); + get(name, callback) { + if (!this._cache.has(name) && callback) { + this.set(name, callback()); } - set(name, value) { - isDefined(value) && this._cache.set(name, value); - } + return this._cache.get(name); + } + + set(name, value) { + isDefined(value) && this._cache.set(name, value); + } } diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_cells_selection_controller.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_cells_selection_controller.ts index 3edc27ef11c2..a3ba17db38b3 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_cells_selection_controller.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_cells_selection_controller.ts @@ -1,201 +1,199 @@ -import { isDateAndTimeView } from '../../../renovation/ui/scheduler/view_model/to_test/views/utils/base'; +import { isDateAndTimeView } from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/base'; export class CellsSelectionController { - handleArrowClick(options) { - const { - key, - focusedCellPosition, - edgeIndices, - getCellDataByPosition, - isAllDayPanelCell, - } = options; - - let nextCellIndices; - - switch(key) { - case 'down': - nextCellIndices = this.getCellFromNextRowPosition( - focusedCellPosition, 'next', edgeIndices, - ); - break; - case 'up': - nextCellIndices = this.getCellFromNextRowPosition( - focusedCellPosition, 'prev', edgeIndices, - ); - break; - case 'left': - nextCellIndices = this.getCellFromNextColumnPosition({ - ...options, - direction: 'prev', - }); - break; - case 'right': - nextCellIndices = this.getCellFromNextColumnPosition({ - ...options, - direction: 'next', - }); - break; - } - - const currentCellData = getCellDataByPosition( - nextCellIndices.rowIndex, - nextCellIndices.columnIndex, - isAllDayPanelCell, - ); - - return this.moveToCell({ - ...options, - currentCellData, + handleArrowClick(options) { + const { + key, + focusedCellPosition, + edgeIndices, + getCellDataByPosition, + isAllDayPanelCell, + } = options; + + let nextCellIndices; + + switch (key) { + case 'down': + nextCellIndices = this.getCellFromNextRowPosition(focusedCellPosition, 'next', edgeIndices); + break; + case 'up': + nextCellIndices = this.getCellFromNextRowPosition(focusedCellPosition, 'prev', edgeIndices); + break; + case 'left': + nextCellIndices = this.getCellFromNextColumnPosition({ + ...options, + direction: 'prev', }); + break; + case 'right': + nextCellIndices = this.getCellFromNextColumnPosition({ + ...options, + direction: 'next', + }); + break; + default: + break; } - getCellFromNextRowPosition(focusedCellPosition, direction, edgeIndices) { - const { - columnIndex, - rowIndex, - } = focusedCellPosition; - - const deltaPosition = direction === 'next' ? 1 : -1; - const nextRowIndex = rowIndex + deltaPosition; + const currentCellData = getCellDataByPosition( + nextCellIndices.rowIndex, + nextCellIndices.columnIndex, + isAllDayPanelCell, + ); + + return this.moveToCell({ + ...options, + currentCellData, + }); + } + + getCellFromNextRowPosition(focusedCellPosition, direction, edgeIndices) { + const { + columnIndex, + rowIndex, + } = focusedCellPosition; + + const deltaPosition = direction === 'next' ? 1 : -1; + const nextRowIndex = rowIndex + deltaPosition; + + const validRowIndex = nextRowIndex >= 0 && nextRowIndex <= edgeIndices.lastRowIndex + ? nextRowIndex + : rowIndex; + + return { + columnIndex, + rowIndex: validRowIndex, + }; + } + + getCellFromNextColumnPosition(options) { + const { + focusedCellPosition, + direction, + edgeIndices, + isRTL, + isGroupedByDate, + groupCount, + isMultiSelection, + viewType, + } = options; + const { + columnIndex, + rowIndex, + } = focusedCellPosition; + const { + firstColumnIndex, + lastColumnIndex, + firstRowIndex, + lastRowIndex, + } = edgeIndices; + + const step = isGroupedByDate && isMultiSelection ? groupCount : 1; + const sign = isRTL ? -1 : 1; + const deltaColumnIndex = direction === 'next' ? sign * step : -1 * sign * step; + const nextColumnIndex = columnIndex + deltaColumnIndex; + + const isValidColumnIndex = nextColumnIndex >= firstColumnIndex + && nextColumnIndex <= lastColumnIndex; - const validRowIndex = nextRowIndex >= 0 && nextRowIndex <= edgeIndices.lastRowIndex - ? nextRowIndex - : rowIndex; + if (isValidColumnIndex) { + return { + columnIndex: nextColumnIndex, + rowIndex, + }; + } - return { - columnIndex, - rowIndex: validRowIndex, - }; + return isDateAndTimeView(viewType) ? focusedCellPosition : this._processEdgeCell({ + nextColumnIndex, + rowIndex, + columnIndex, + firstColumnIndex, + lastColumnIndex, + firstRowIndex, + lastRowIndex, + step, + }); + } + + _processEdgeCell(options) { + const { + nextColumnIndex, + rowIndex, + columnIndex, + firstColumnIndex, + lastColumnIndex, + firstRowIndex, + lastRowIndex, + step, + } = options; + + let validColumnIndex = nextColumnIndex; + let validRowIndex = rowIndex; + const isLeftEdgeCell = nextColumnIndex < firstColumnIndex; + const isRightEdgeCell = nextColumnIndex > lastColumnIndex; + + if (isLeftEdgeCell) { + const columnIndexInNextRow = lastColumnIndex - (step - columnIndex % step - 1); + const nextRowIndex = rowIndex - 1; + const isValidRowIndex = nextRowIndex >= firstRowIndex; + + validRowIndex = isValidRowIndex ? nextRowIndex : rowIndex; + validColumnIndex = isValidRowIndex ? columnIndexInNextRow : columnIndex; } - getCellFromNextColumnPosition(options) { - const { - focusedCellPosition, - direction, - edgeIndices, - isRTL, - isGroupedByDate, - groupCount, - isMultiSelection, - viewType, - } = options; - const { - columnIndex, - rowIndex, - } = focusedCellPosition; - const { - firstColumnIndex, - lastColumnIndex, - firstRowIndex, - lastRowIndex, - } = edgeIndices; - - const step = isGroupedByDate && isMultiSelection ? groupCount : 1; - const sign = isRTL ? -1 : 1; - const deltaColumnIndex = direction === 'next' ? sign * step : -1 * sign * step; - const nextColumnIndex = columnIndex + deltaColumnIndex; - - const isValidColumnIndex = nextColumnIndex >= firstColumnIndex - && nextColumnIndex <= lastColumnIndex; + if (isRightEdgeCell) { + const columnIndexInNextRow = firstColumnIndex + columnIndex % step; + const nextRowIndex = rowIndex + 1; + const isValidRowIndex = nextRowIndex <= lastRowIndex; - if(isValidColumnIndex) { - return { - columnIndex: nextColumnIndex, - rowIndex, - }; - } - - return isDateAndTimeView(viewType) ? focusedCellPosition : this._processEdgeCell({ - nextColumnIndex, - rowIndex, - columnIndex, - firstColumnIndex, - lastColumnIndex, - firstRowIndex, - lastRowIndex, - step, - }); + validRowIndex = isValidRowIndex ? nextRowIndex : rowIndex; + validColumnIndex = isValidRowIndex ? columnIndexInNextRow : columnIndex; } - _processEdgeCell(options) { - const { - nextColumnIndex, - rowIndex, - columnIndex, - firstColumnIndex, - lastColumnIndex, - firstRowIndex, - lastRowIndex, - step, - } = options; - - let validColumnIndex = nextColumnIndex; - let validRowIndex = rowIndex; - const isLeftEdgeCell = nextColumnIndex < firstColumnIndex; - const isRightEdgeCell = nextColumnIndex > lastColumnIndex; - - if(isLeftEdgeCell) { - const columnIndexInNextRow = lastColumnIndex - (step - columnIndex % step - 1); - const nextRowIndex = rowIndex - 1; - const isValidRowIndex = nextRowIndex >= firstRowIndex; - - validRowIndex = isValidRowIndex ? nextRowIndex : rowIndex; - validColumnIndex = isValidRowIndex ? columnIndexInNextRow : columnIndex; - } - - if(isRightEdgeCell) { - const columnIndexInNextRow = firstColumnIndex + columnIndex % step; - const nextRowIndex = rowIndex + 1; - const isValidRowIndex = nextRowIndex <= lastRowIndex; - - validRowIndex = isValidRowIndex ? nextRowIndex : rowIndex; - validColumnIndex = isValidRowIndex ? columnIndexInNextRow : columnIndex; - } - - return { - columnIndex: validColumnIndex, - rowIndex: validRowIndex, - }; - } + return { + columnIndex: validColumnIndex, + rowIndex: validRowIndex, + }; + } + + moveToCell(options) { + const { + isMultiSelection, + isMultiSelectionAllowed, + focusedCellData, + currentCellData, + } = options; - moveToCell(options) { - const { - isMultiSelection, - isMultiSelectionAllowed, - focusedCellData, - currentCellData, - } = options; + const isValidMultiSelection = isMultiSelection && isMultiSelectionAllowed; - const isValidMultiSelection = isMultiSelection && isMultiSelectionAllowed; + const nextFocusedCellData = isValidMultiSelection + ? this._getNextCellData(currentCellData, focusedCellData) + : currentCellData; - const nextFocusedCellData = isValidMultiSelection - ? this._getNextCellData(currentCellData, focusedCellData) - : currentCellData; + return nextFocusedCellData; + } - return nextFocusedCellData; + _getNextCellData(nextFocusedCellData, focusedCellData, isVirtualCell?: any) { + if (isVirtualCell) { + return focusedCellData; } - _getNextCellData(nextFocusedCellData, focusedCellData, isVirtualCell) { - if(isVirtualCell) { - return focusedCellData; - } + const isValidNextFocusedCell = this._isValidNextFocusedCell(nextFocusedCellData, focusedCellData); - const isValidNextFocusedCell = this._isValidNextFocusedCell(nextFocusedCellData, focusedCellData); + return isValidNextFocusedCell ? nextFocusedCellData : focusedCellData; + } - return isValidNextFocusedCell ? nextFocusedCellData : focusedCellData; + _isValidNextFocusedCell(nextFocusedCellData, focusedCellData) { + if (!focusedCellData) { + return true; } - _isValidNextFocusedCell(nextFocusedCellData, focusedCellData) { - if(!focusedCellData) { - return true; - } + const { groupIndex, allDay } = focusedCellData; + const { + groupIndex: nextGroupIndex, + allDay: nextAllDay, + } = nextFocusedCellData; - const { groupIndex, allDay } = focusedCellData; - const { - groupIndex: nextGroupIndex, - allDay: nextAllDay, - } = nextFocusedCellData; - - return groupIndex === nextGroupIndex && allDay === nextAllDay; - } + return groupIndex === nextGroupIndex && allDay === nextAllDay; + } } diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_cells_selection_state.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_cells_selection_state.ts index a1a4d362ea5f..971eb207832d 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_cells_selection_state.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_cells_selection_state.ts @@ -1,108 +1,111 @@ -import { getSelectedCells } from '../../../renovation/ui/scheduler/workspaces/base/utils'; +import { getSelectedCells } from '@js/renovation/ui/scheduler/workspaces/base/utils'; export default class CellsSelectionState { - constructor(viewDataProvider) { - this._viewDataProvider = viewDataProvider; + _focusedCell: any = null; - this._focusedCell = null; - this._selectedCells = null; + _selectedCells: any = null; - this._firstSelectedCell = null; + _firstSelectedCell: any = null; - this._prevFocusedCell = null; - this._prevSelectedCells = null; - } + _prevFocusedCell: any = null; - get viewDataProvider() { return this._viewDataProvider; } + _prevFirstSelectedCell: any; - get focusedCell() { - const focusedCell = this._focusedCell; + _prevSelectedCells: any = null; - if(!focusedCell) { - return undefined; - } + constructor(public _viewDataProvider) { + } - const { groupIndex, startDate, allDay } = focusedCell; - const cellInfo = { groupIndex, startDate, isAllDay: allDay, index: focusedCell.index }; - const cellPosition = this.viewDataProvider.findCellPositionInMap(cellInfo); + get viewDataProvider() { return this._viewDataProvider; } - return { coordinates: cellPosition, cellData: focusedCell }; - } + get focusedCell() { + const focusedCell = this._focusedCell; - setFocusedCell(rowIndex, columnIndex, isAllDay) { - if(rowIndex >= 0) { - const cell = this._viewDataProvider.getCellData(rowIndex, columnIndex, isAllDay); - this._focusedCell = cell; - } + if (!focusedCell) { + return undefined; } - setSelectedCells(lastCellCoordinates, firstCellCoordinates = undefined) { - const viewDataProvider = this._viewDataProvider; - const { - rowIndex: lastRowIndex, columnIndex: lastColumnIndex, allDay: isLastCellAllDay, - } = lastCellCoordinates; - - if(lastRowIndex < 0) { - return; - } - - const firstCell = firstCellCoordinates - ? viewDataProvider.getCellData( - firstCellCoordinates.rowIndex, - firstCellCoordinates.columnIndex, - firstCellCoordinates.allDay, - ) - : this._firstSelectedCell; - const lastCell = viewDataProvider.getCellData(lastRowIndex, lastColumnIndex, isLastCellAllDay); - - this._firstSelectedCell = firstCell; - - this._selectedCells = getSelectedCells( - this._viewDataProvider, firstCell, lastCell, isLastCellAllDay, - ); - } + const { groupIndex, startDate, allDay } = focusedCell; + const cellInfo = { + groupIndex, startDate, isAllDay: allDay, index: focusedCell.index, + }; + const cellPosition = this.viewDataProvider.findCellPositionInMap(cellInfo); - setSelectedCellsByData(selectedCellsData) { - this._selectedCells = selectedCellsData; - } + return { coordinates: cellPosition, cellData: focusedCell }; + } - getSelectedCells() { - return this._selectedCells; + setFocusedCell(rowIndex, columnIndex, isAllDay) { + if (rowIndex >= 0) { + const cell = this._viewDataProvider.getCellData(rowIndex, columnIndex, isAllDay); + this._focusedCell = cell; } + } - releaseSelectedAndFocusedCells() { - this.releaseSelectedCells(); - this.releaseFocusedCell(); - } - - releaseSelectedCells() { - this._prevSelectedCells = this._selectedCells; - this._prevFirstSelectedCell = this._firstSelectedCell; + setSelectedCells(lastCellCoordinates, firstCellCoordinates: any = undefined) { + const viewDataProvider = this._viewDataProvider; + const { + rowIndex: lastRowIndex, columnIndex: lastColumnIndex, allDay: isLastCellAllDay, + } = lastCellCoordinates; - this._selectedCells = null; - this._firstSelectedCell = null; + if (lastRowIndex < 0) { + return; } - releaseFocusedCell() { - this._prevFocusedCell = this._focusedCell; - this._focusedCell = null; - } - - restoreSelectedAndFocusedCells() { - this._selectedCells = this._selectedCells || this._prevSelectedCells; - this._focusedCell = this._focusedCell || this._prevFocusedCell; - this._firstSelectedCell = this._firstSelectedCell || this._prevFirstSelectedCell; - - this._prevSelectedCells = null; - this._prevFirstSelectedCell = null; - this._prevFocusedCell = null; - } - - clearSelectedAndFocusedCells() { - this._prevSelectedCells = null; - this._selectedCells = null; - - this._prevFocusedCell = null; - this._focusedCell = null; - } + const firstCell = firstCellCoordinates + ? viewDataProvider.getCellData( + firstCellCoordinates.rowIndex, + firstCellCoordinates.columnIndex, + firstCellCoordinates.allDay, + ) + : this._firstSelectedCell; + const lastCell = viewDataProvider.getCellData(lastRowIndex, lastColumnIndex, isLastCellAllDay); + + this._firstSelectedCell = firstCell; + + this._selectedCells = getSelectedCells(this._viewDataProvider, firstCell, lastCell, isLastCellAllDay); + } + + setSelectedCellsByData(selectedCellsData) { + this._selectedCells = selectedCellsData; + } + + getSelectedCells() { + return this._selectedCells; + } + + releaseSelectedAndFocusedCells() { + this.releaseSelectedCells(); + this.releaseFocusedCell(); + } + + releaseSelectedCells() { + this._prevSelectedCells = this._selectedCells; + this._prevFirstSelectedCell = this._firstSelectedCell; + + this._selectedCells = null; + this._firstSelectedCell = null; + } + + releaseFocusedCell() { + this._prevFocusedCell = this._focusedCell; + this._focusedCell = null; + } + + restoreSelectedAndFocusedCells() { + this._selectedCells = this._selectedCells || this._prevSelectedCells; + this._focusedCell = this._focusedCell || this._prevFocusedCell; + this._firstSelectedCell = this._firstSelectedCell || this._prevFirstSelectedCell; + + this._prevSelectedCells = null; + this._prevFirstSelectedCell = null; + this._prevFocusedCell = null; + } + + clearSelectedAndFocusedCells() { + this._prevSelectedCells = null; + this._selectedCells = null; + + this._prevFocusedCell = null; + this._focusedCell = null; + } } diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline.ts index 1f6dbc036847..f0d41b39b23c 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline.ts @@ -1,25 +1,28 @@ -import { getOuterWidth, setHeight, getOuterHeight } from '../../../core/utils/size'; -import $ from '../../../core/renderer'; -import { noop } from '../../../core/utils/common'; -import { extend } from '../../../core/utils/extend'; -import { getBoundingRect } from '../../../core/utils/position'; -import registerComponent from '../../../core/component_registrator'; -import SchedulerWorkSpace from './ui.scheduler.work_space.indicator'; -import dateUtils from '../../../core/utils/date'; -import tableCreatorModule from '../table_creator'; -const { tableCreator } = tableCreatorModule; -import HorizontalShader from '../shaders/ui.scheduler.current_time_shader.horizontal'; +import registerComponent from '@js/core/component_registrator'; +import $ 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 { formatWeekdayAndDay } from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/base'; +import { getDateForHeaderText } from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/timeline_week'; + +// NOTE: Renovation component import. +// @ts-expect-error +import dxrTimelineDateHeader from '../../../renovation/ui/scheduler/workspaces/timeline/header_panel/layout.j'; import { - HEADER_CURRENT_TIME_CELL_CLASS, - GROUP_ROW_CLASS, - GROUP_HEADER_CONTENT_CLASS, -} from '../classes'; -import { getDateForHeaderText } from '../../../renovation/ui/scheduler/view_model/to_test/views/utils/timeline_week'; -import timezoneUtils from '../utils.timeZone'; + GROUP_HEADER_CONTENT_CLASS, + GROUP_ROW_CLASS, + HEADER_CURRENT_TIME_CELL_CLASS, +} from '../m_classes'; +import tableCreatorModule from '../m_table_creator'; +import timezoneUtils from '../m_utils_time_zone'; +import HorizontalShader from '../shaders/m_current_time_shader_horizontal'; +import SchedulerWorkSpace from './m_work_space_indicator'; -import dxrTimelineDateHeader from '../../../renovation/ui/scheduler/workspaces/timeline/header_panel/layout.j'; -import { formatWeekdayAndDay } from '../../../renovation/ui/scheduler/view_model/to_test/views/utils/base'; -import { hasWindow } from '../../../core/utils/window'; +const { tableCreator } = tableCreatorModule; const TIMELINE_CLASS = 'dx-scheduler-timeline'; const GROUP_TABLE_CLASS = 'dx-scheduler-group-table'; @@ -34,477 +37,483 @@ const HORIZONTAL = 'horizontal'; const toMs = dateUtils.dateToMilliseconds; class SchedulerTimeline extends SchedulerWorkSpace { - get verticalGroupTableClass() { return GROUP_TABLE_CLASS; } + _$sidebarTable: any; - get viewDirection() { return 'horizontal'; } + get verticalGroupTableClass() { return GROUP_TABLE_CLASS; } - get renovatedHeaderPanelComponent() { return dxrTimelineDateHeader; } + readonly viewDirection = 'horizontal'; - getGroupTableWidth() { - return this._$sidebarTable ? getOuterWidth(this._$sidebarTable) : 0; - } + get renovatedHeaderPanelComponent() { return dxrTimelineDateHeader; } - _getTotalRowCount(groupCount) { - if(this._isHorizontalGroupedWorkSpace()) { - return this._getRowCount(); - } else { - groupCount = groupCount || 1; - return this._getRowCount() * groupCount; - } - } + getGroupTableWidth() { + return this._$sidebarTable ? getOuterWidth(this._$sidebarTable) : 0; + } - _getFormat() { - return 'shorttime'; + _getTotalRowCount(groupCount) { + if (this._isHorizontalGroupedWorkSpace()) { + return this._getRowCount(); } + groupCount = groupCount || 1; + return this._getRowCount() * groupCount; + } - _getWorkSpaceHeight() { - if(this.option('crossScrollingEnabled') && hasWindow()) { - return getBoundingRect(this._$dateTable.get(0)).height; - } + _getFormat(): any { + return 'shorttime'; + } - return getBoundingRect(this.$element().get(0)).height; + _getWorkSpaceHeight() { + if (this.option('crossScrollingEnabled') && hasWindow()) { + return getBoundingRect(this._$dateTable.get(0)).height; } - _dateTableScrollableConfig() { - const config = super._dateTableScrollableConfig(); - const timelineConfig = { - direction: HORIZONTAL - }; + return getBoundingRect((this.$element() as any).get(0)).height; + } - return this.option('crossScrollingEnabled') ? config : extend(config, timelineConfig); - } + _dateTableScrollableConfig() { + const config = super._dateTableScrollableConfig(); + const timelineConfig = { + direction: HORIZONTAL, + }; - _needCreateCrossScrolling() { - return true; - } + return this.option('crossScrollingEnabled') ? config : extend(config, timelineConfig); + } - _headerScrollableConfig() { - const config = super._headerScrollableConfig(); + _needCreateCrossScrolling() { + return true; + } - return extend(config, { - scrollByContent: true - }); - } + _headerScrollableConfig() { + const config = super._headerScrollableConfig(); - supportAllDayRow() { - return false; - } + return extend(config, { + scrollByContent: true, + }); + } - _getGroupHeaderContainer() { - if(this._isHorizontalGroupedWorkSpace()) { - return this._$thead; - } - return this._$sidebarTable; - } + supportAllDayRow() { + return false; + } - _insertAllDayRowsIntoDateTable() { - return false; + _getGroupHeaderContainer() { + if (this._isHorizontalGroupedWorkSpace()) { + return this._$thead; } + return this._$sidebarTable; + } - _needRenderWeekHeader() { - return false; - } + _insertAllDayRowsIntoDateTable() { + return false; + } - _incrementDate(date) { - date.setDate(date.getDate() + 1); - } + _needRenderWeekHeader() { + return false; + } - getIndicationCellCount() { - const timeDiff = this._getTimeDiff(); - return this._calculateDurationInCells(timeDiff); - } + _incrementDate(date) { + date.setDate(date.getDate() + 1); + } - _getTimeDiff() { - let today = this._getToday(); - const date = this._getIndicationFirstViewDate(); + getIndicationCellCount() { + const timeDiff = this._getTimeDiff(); + return this._calculateDurationInCells(timeDiff); + } - const startViewDate = this.getStartViewDate(); - const dayLightOffset = timezoneUtils.getDaylightOffsetInMs(startViewDate, today); + _getTimeDiff() { + let today = this._getToday(); + const date = this._getIndicationFirstViewDate(); - if(dayLightOffset) { - today = new Date(today.getTime() + dayLightOffset); - } + const startViewDate = this.getStartViewDate(); + const dayLightOffset = timezoneUtils.getDaylightOffsetInMs(startViewDate, today); - return today.getTime() - date.getTime(); + if (dayLightOffset) { + today = new Date(today.getTime() + dayLightOffset); } - _calculateDurationInCells(timeDiff) { - const today = this._getToday(); - const differenceInDays = Math.floor(timeDiff / toMs('day')); - let duration = (timeDiff - differenceInDays * toMs('day') - this.option('startDayHour') * toMs('hour')) / this.getCellDuration(); + return today.getTime() - date.getTime(); + } - if(today.getHours() > this.option('endDayHour')) { - duration = this._getCellCountInDay(); - } + _calculateDurationInCells(timeDiff) { + const today = this._getToday(); + const differenceInDays = Math.floor(timeDiff / toMs('day')); + let duration = (timeDiff - differenceInDays * toMs('day') - (this.option('startDayHour') as any) * toMs('hour')) / this.getCellDuration(); - if(duration < 0) { - duration = 0; - } - return differenceInDays * this._getCellCountInDay() + duration; + if (today.getHours() > (this.option('endDayHour') as any)) { + duration = this._getCellCountInDay(); + } + if (duration < 0) { + duration = 0; } + return differenceInDays * this._getCellCountInDay() + duration; + } - getIndicationWidth() { - if(this.isGroupedByDate()) { - const cellCount = this.getIndicationCellCount(); - const integerPart = Math.floor(cellCount); - const fractionPart = cellCount - integerPart; - - return this.getCellWidth() * (integerPart * this._getGroupCount() + fractionPart); - } else { - return this.getIndicationCellCount() * this.getCellWidth(); - } + getIndicationWidth() { + if (this.isGroupedByDate()) { + const cellCount = this.getIndicationCellCount(); + const integerPart = Math.floor(cellCount); + const fractionPart = cellCount - integerPart; + return this.getCellWidth() * (integerPart * this._getGroupCount() + fractionPart); } + return this.getIndicationCellCount() * this.getCellWidth(); + } - _isVerticalShader() { - return false; - } + _isVerticalShader() { + return false; + } - _isCurrentTimeHeaderCell() { - return false; - } + _isCurrentTimeHeaderCell() { + return false; + } - _setTableSizes() { - super._setTableSizes(); + _setTableSizes() { + super._setTableSizes(); - const minHeight = this._getWorkSpaceMinHeight(); - setHeight(this._$sidebarTable, minHeight); - setHeight(this._$dateTable, minHeight); + const minHeight = this._getWorkSpaceMinHeight(); + setHeight(this._$sidebarTable, minHeight); + setHeight(this._$dateTable, minHeight); - this.virtualScrollingDispatcher.updateDimensions(); + this.virtualScrollingDispatcher.updateDimensions(); + } + + _getWorkSpaceMinHeight() { + let minHeight = this._getWorkSpaceHeight(); + + const workspaceContainerHeight = getOuterHeight(this._$flexContainer, true); + + if (minHeight < workspaceContainerHeight) { + minHeight = workspaceContainerHeight; } - _getWorkSpaceMinHeight() { - let minHeight = this._getWorkSpaceHeight(); + return minHeight; + } - const workspaceContainerHeight = getOuterHeight(this._$flexContainer, true); + _getCellCoordinatesByIndex(index) { + return { + columnIndex: index % this._getCellCount(), + rowIndex: 0, + }; + } - if(minHeight < workspaceContainerHeight) { - minHeight = workspaceContainerHeight; - } + _getCellByCoordinates(cellCoordinates, groupIndex) { + const indexes = this._groupedStrategy.prepareCellIndexes(cellCoordinates, groupIndex); - return minHeight; - } + return this._$dateTable + .find('tr') + .eq(indexes.rowIndex) + .find('td') + .eq(indexes.columnIndex); + } - _getCellCoordinatesByIndex(index) { - return { - columnIndex: index % this._getCellCount(), - rowIndex: 0 - }; - } + _getWorkSpaceWidth() { + return getOuterWidth(this._$dateTable, true); + } - _getCellByCoordinates(cellCoordinates, groupIndex) { - const indexes = this._groupedStrategy.prepareCellIndexes(cellCoordinates, groupIndex); + _getIndicationFirstViewDate() { + return dateUtils.trimTime(new Date(this.getStartViewDate())); + } - return this._$dateTable - .find('tr') - .eq(indexes.rowIndex) - .find('td') - .eq(indexes.columnIndex); - } + _getIntervalBetween(currentDate, allDay) { + const startDayHour = this.option('startDayHour')!; + const endDayHour = this.option('endDayHour')!; + const firstViewDate = this.getStartViewDate(); + const firstViewDateTime = firstViewDate.getTime(); + const hiddenInterval = (24 - endDayHour + startDayHour) * toMs('hour'); + const timeZoneOffset = dateUtils.getTimezonesDifference(firstViewDate, currentDate); + const apptStart = currentDate.getTime(); + const fullInterval = apptStart - firstViewDateTime - timeZoneOffset; + const fullDays = Math.floor(fullInterval / toMs('day')); + const tailDuration = fullInterval - (fullDays * toMs('day')); + let tailDelta = 0; + const cellCount = this._getCellCountInDay() * (fullDays - this._getWeekendsCount(fullDays)); + const gapBeforeAppt = apptStart - dateUtils.trimTime(new Date(currentDate)).getTime(); + let result = cellCount * (this.option('hoursInterval') as any) * toMs('hour'); - _getWorkSpaceWidth() { - return getOuterWidth(this._$dateTable, true); - } + if (!allDay) { + if (currentDate.getHours() < startDayHour) { + tailDelta = tailDuration - hiddenInterval + gapBeforeAppt; + } else if (currentDate.getHours() >= startDayHour && currentDate.getHours() < endDayHour) { + tailDelta = tailDuration; + } else if (currentDate.getHours() >= startDayHour && currentDate.getHours() >= endDayHour) { + tailDelta = tailDuration - (gapBeforeAppt - endDayHour * toMs('hour')); + } else if (!fullDays) { + result = fullInterval; + } - _getIndicationFirstViewDate() { - return dateUtils.trimTime(new Date(this.getStartViewDate())); - } + result += tailDelta; + } + + return result; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _getWeekendsCount(argument?: any) { + return 0; + } - _getIntervalBetween(currentDate, allDay) { - const startDayHour = this.option('startDayHour'); - const endDayHour = this.option('endDayHour'); - const firstViewDate = this.getStartViewDate(); - const firstViewDateTime = firstViewDate.getTime(); - const hiddenInterval = (24 - endDayHour + startDayHour) * toMs('hour'); - const timeZoneOffset = dateUtils.getTimezonesDifference(firstViewDate, currentDate); - const apptStart = currentDate.getTime(); - const fullInterval = apptStart - firstViewDateTime - timeZoneOffset; - const fullDays = Math.floor(fullInterval / (toMs('day'))); - const tailDuration = fullInterval - (fullDays * toMs('day')); - let tailDelta = 0; - const cellCount = this._getCellCountInDay() * (fullDays - this._getWeekendsCount(fullDays)); - const gapBeforeAppt = apptStart - dateUtils.trimTime(new Date(currentDate)).getTime(); - let result = cellCount * this.option('hoursInterval') * toMs('hour'); - - if(!allDay) { - if(currentDate.getHours() < startDayHour) { - tailDelta = tailDuration - hiddenInterval + gapBeforeAppt; - } else if(currentDate.getHours() >= startDayHour && currentDate.getHours() < endDayHour) { - tailDelta = tailDuration; - } else if(currentDate.getHours() >= startDayHour && currentDate.getHours() >= endDayHour) { - tailDelta = tailDuration - (gapBeforeAppt - endDayHour * toMs('hour')); - } else if(!fullDays) { - result = fullInterval; - } - - result += tailDelta; - } + getAllDayContainer() { + return null; + } - return result; - } + getTimePanelWidth() { + return 0; + } - _getWeekendsCount() { - return 0; - } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getIntervalDuration(allDay) { + return this.getCellDuration(); + } - getAllDayContainer() { - return null; - } + getCellMinWidth() { + return 0; + } - getTimePanelWidth() { - return 0; - } + getWorkSpaceLeftOffset() { + return 0; + } - getIntervalDuration(allDay) { - return this.getCellDuration(); - } + scrollToTime(hours, minutes, date) { + const coordinates = this._getScrollCoordinates(hours, minutes, date); + const scrollable = this.getScrollable(); + const offset = this.option('rtlEnabled') ? getBoundingRect(this.getScrollableContainer().get(0)).width : 0; - getCellMinWidth() { - return 0; + if (this.option('templatesRenderAsynchronously')) { + setTimeout(() => { + scrollable.scrollBy({ left: coordinates.left - scrollable.scrollLeft() - offset, top: 0 }); + }); + } else { + scrollable.scrollBy({ left: coordinates.left - scrollable.scrollLeft() - offset, top: 0 }); } + } + + renderRAllDayPanel() {} - getWorkSpaceLeftOffset() { - return 0; - } + renderRTimeTable() {} + + _renderGroupAllDayPanel() {} + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + generateRenderOptions(argument?: any) { + const options = super.generateRenderOptions(true); + + return { + ...options, + isGenerateWeekDaysHeaderData: this._needRenderWeekHeader(), + getDateForHeaderText, + }; + } - scrollToTime(hours, minutes, date) { - const coordinates = this._getScrollCoordinates(hours, minutes, date); - const scrollable = this.getScrollable(); - const offset = this.option('rtlEnabled') ? getBoundingRect(this.getScrollableContainer().get(0)).width : 0; + // ------------- + // We need these methods for now but they are useless for renovation + // ------------- - if(this.option('templatesRenderAsynchronously')) { - setTimeout(function() { - scrollable.scrollBy({ left: coordinates.left - scrollable.scrollLeft() - offset, top: 0 }); - }); - } else { - scrollable.scrollBy({ left: coordinates.left - scrollable.scrollLeft() - offset, top: 0 }); - } - } + _init() { + super._init(); - renderRAllDayPanel() {} + (this.$element() as any).addClass(TIMELINE_CLASS); + this._$sidebarTable = $('
') + .addClass(GROUP_TABLE_CLASS); + } - renderRTimeTable() {} + _getDefaultGroupStrategy() { + return 'vertical'; + } - _renderGroupAllDayPanel() {} + _toggleGroupingDirectionClass() { + (this.$element() as any).toggleClass(HORIZONTAL_GROUPED_WORKSPACE_CLASS, this._isHorizontalGroupedWorkSpace()); + } - generateRenderOptions() { - const options = super.generateRenderOptions(true); + _getDefaultOptions() { + return extend(super._getDefaultOptions(), { + groupOrientation: 'vertical', + }); + } - return { - ...options, - isGenerateWeekDaysHeaderData: this._needRenderWeekHeader(), - getDateForHeaderText, - }; - } + _createWorkSpaceElements() { + this._createWorkSpaceScrollableElements(); + } - // ------------- - // We need these methods for now but they are useless for renovation - // ------------- + _toggleAllDayVisibility() { return noop(); } - _init() { - super._init(); + _changeAllDayVisibility() { return noop(); } - this.$element().addClass(TIMELINE_CLASS); - this._$sidebarTable = $('
') - .addClass(GROUP_TABLE_CLASS); - } + _getDateHeaderTemplate() { + return this.option('timeCellTemplate'); + } - _getDefaultGroupStrategy() { - return 'vertical'; + _renderView() { + let groupCellTemplates; + if (!this.isRenovatedRender()) { + groupCellTemplates = this._renderGroupHeader(); } - _toggleGroupingDirectionClass() { - this.$element().toggleClass(HORIZONTAL_GROUPED_WORKSPACE_CLASS, this._isHorizontalGroupedWorkSpace()); - } + this.renderWorkSpace(); - _getDefaultOptions() { - return extend(super._getDefaultOptions(), { - groupOrientation: 'vertical', - }); + if (this.isRenovatedRender()) { + this.virtualScrollingDispatcher.updateDimensions(); } - _createWorkSpaceElements() { - this._createWorkSpaceScrollableElements(); - } + this._shader = new HorizontalShader(this); - _toggleAllDayVisibility() { return noop(); } - _changeAllDayVisibility() { return noop(); } + this._$sidebarTable.appendTo(this._sidebarScrollable.$content()); - _getDateHeaderTemplate() { - return this.option('timeCellTemplate'); + if (this.isRenovatedRender() && this._isVerticalGroupedWorkSpace()) { + this.renderRGroupPanel(); } - _renderView() { - let groupCellTemplates; - if(!this.isRenovatedRender()) { - groupCellTemplates = this._renderGroupHeader(); - } + this.updateHeaderEmptyCellWidth(); - this.renderWorkSpace(); + this._applyCellTemplates(groupCellTemplates); + } - if(this.isRenovatedRender()) { - this.virtualScrollingDispatcher.updateDimensions(); - } + _setHorizontalGroupHeaderCellsHeight() { return noop(); } - this._shader = new HorizontalShader(this); + _setCurrentTimeCells() { + const timePanelCells = this._getTimePanelCells(); + const currentTimeCellIndices = this._getCurrentTimePanelCellIndices(); + currentTimeCellIndices.forEach((timePanelCellIndex) => { + timePanelCells.eq(timePanelCellIndex) + .addClass(HEADER_CURRENT_TIME_CELL_CLASS); + }); + } - this._$sidebarTable.appendTo(this._sidebarScrollable.$content()); + _cleanCurrentTimeCells() { + (this.$element() as any) + .find(`.${HEADER_CURRENT_TIME_CELL_CLASS}`) + .removeClass(HEADER_CURRENT_TIME_CELL_CLASS); + } - if(this.isRenovatedRender() && this._isVerticalGroupedWorkSpace()) { - this.renderRGroupPanel(); - } + _getTimePanelCells() { + return (this.$element() as any) + .find(`.${HEADER_PANEL_CELL_CLASS}:not(.${HEADER_PANEL_WEEK_CELL_CLASS})`); + } - this.updateHeaderEmptyCellWidth(); + _getCurrentTimePanelCellIndices() { + const columnCountPerGroup = this._getCellCount(); + const today = this._getToday(); + const index = this.getCellIndexByDate(today); + const { columnIndex: currentTimeColumnIndex } = this._getCellCoordinatesByIndex(index); - this._applyCellTemplates(groupCellTemplates); + if (currentTimeColumnIndex === undefined) { + return []; } - _setHorizontalGroupHeaderCellsHeight() { return noop(); } + const horizontalGroupCount = this._isHorizontalGroupedWorkSpace() && !this.isGroupedByDate() + ? this._getGroupCount() + : 1; - _setCurrentTimeCells() { - const timePanelCells = this._getTimePanelCells(); - const currentTimeCellIndices = this._getCurrentTimePanelCellIndices(); - currentTimeCellIndices.forEach((timePanelCellIndex) => { - timePanelCells.eq(timePanelCellIndex) - .addClass(HEADER_CURRENT_TIME_CELL_CLASS); - }); - } + return [...new Array(horizontalGroupCount)] + .map((_, groupIndex) => columnCountPerGroup * groupIndex + currentTimeColumnIndex); + } - _cleanCurrentTimeCells() { - this.$element() - .find(`.${HEADER_CURRENT_TIME_CELL_CLASS}`) - .removeClass(HEADER_CURRENT_TIME_CELL_CLASS); - } + // -------------- + // These methods should be deleted when we get rid of old render + // -------------- - _getTimePanelCells() { - return this.$element() - .find(`.${HEADER_PANEL_CELL_CLASS}:not(.${HEADER_PANEL_WEEK_CELL_CLASS})`); - } + _renderTimePanel() { return noop(); } - _getCurrentTimePanelCellIndices() { - const columnCountPerGroup = this._getCellCount(); - const today = this._getToday(); - const index = this.getCellIndexByDate(today); - const { columnIndex: currentTimeColumnIndex } = this._getCellCoordinatesByIndex(index); + _renderAllDayPanel() { return noop(); } - if(currentTimeColumnIndex === undefined) { - return []; - } + _createAllDayPanelElements() { return noop(); } - const horizontalGroupCount = this._isHorizontalGroupedWorkSpace() && !this.isGroupedByDate() - ? this._getGroupCount() - : 1; + _renderDateHeader() { + const $headerRow = super._renderDateHeader(); + if (this._needRenderWeekHeader()) { + const firstViewDate = new Date(this.getStartViewDate()); + let currentDate = new Date(firstViewDate); - return [...(new Array(horizontalGroupCount))] - .map((_, groupIndex) => columnCountPerGroup * groupIndex + currentTimeColumnIndex); - } + const $cells: any[] = []; + const groupCount = this._getGroupCount(); + const cellCountInDay = this._getCellCountInDay(); + const colSpan = this.isGroupedByDate() + ? cellCountInDay * groupCount + : cellCountInDay; + const cellTemplate: any = this.option('dateCellTemplate'); - // -------------- - // These methods should be deleted when we get rid of old render - // -------------- - - _renderTimePanel() { return noop(); } - _renderAllDayPanel() { return noop(); } - - _createAllDayPanelElements() { return noop(); } - - _renderDateHeader() { - const $headerRow = super._renderDateHeader(); - if(this._needRenderWeekHeader()) { - const firstViewDate = new Date(this.getStartViewDate()); - let currentDate = new Date(firstViewDate); - - const $cells = []; - const groupCount = this._getGroupCount(); - const cellCountInDay = this._getCellCountInDay(); - const colSpan = this.isGroupedByDate() - ? cellCountInDay * groupCount - : cellCountInDay; - const cellTemplate = this.option('dateCellTemplate'); - - const horizontalGroupCount = this._isHorizontalGroupedWorkSpace() && !this.isGroupedByDate() - ? groupCount - : 1; - const cellsInGroup = this.viewDataProvider.viewDataGenerator.daysInInterval * this.option('intervalCount'); - - const cellsCount = cellsInGroup * horizontalGroupCount; - - for(let templateIndex = 0; templateIndex < cellsCount; templateIndex++) { - const $th = $('
').addClass(HEADER_ROW_CLASS).append($cells); - $headerRow.before($row); - } - } + const horizontalGroupCount = this._isHorizontalGroupedWorkSpace() && !this.isGroupedByDate() + ? groupCount + : 1; + const cellsInGroup = this.viewDataProvider.viewDataGenerator.daysInInterval * (this.option('intervalCount') as any); - _renderIndicator(height, rtlOffset, $container, groupCount) { - let $indicator; - const width = this.getIndicationWidth(); + const cellsCount = cellsInGroup * horizontalGroupCount; - if(this.option('groupOrientation') === 'vertical') { - $indicator = this._createIndicator($container); - 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; - $indicator = this._createIndicator($container); - setHeight($indicator, getBoundingRect($container.get(0)).height); + for (let templateIndex = 0; templateIndex < cellsCount; templateIndex++) { + const $th = $('').addClass(HEADER_ROW_CLASS).append($cells as any); + $headerRow.before($row); + } + } + + _renderIndicator(height, rtlOffset, $container, groupCount) { + let $indicator; + const width = this.getIndicationWidth(); + + if (this.option('groupOrientation') === 'vertical') { + $indicator = this._createIndicator($container); + 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; + $indicator = this._createIndicator($container); + setHeight($indicator, getBoundingRect($container.get(0)).height); + + $indicator.css('left', rtlOffset ? rtlOffset - width - offset : width + offset); + } + } + } + + _makeGroupRows(groups, groupByDate) { + const tableCreatorStrategy = this.option('groupOrientation') === 'vertical' ? tableCreator.VERTICAL : tableCreator.HORIZONTAL; + + return tableCreator.makeGroupedTable( + tableCreatorStrategy, + groups, + { + groupRowClass: GROUP_ROW_CLASS, + groupHeaderRowClass: GROUP_ROW_CLASS, + groupHeaderClass: this._getGroupHeaderClass.bind(this), + groupHeaderContentClass: GROUP_HEADER_CONTENT_CLASS, + }, + this._getCellCount() || 1, + this.option('resourceCellTemplate'), + this._getTotalRowCount(this._getGroupCount()), + groupByDate, + ); + } } -registerComponent('dxSchedulerTimeline', SchedulerTimeline); +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 69ddb4fea8c2..4ffb0bd5abfd 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_day.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_day.ts @@ -1,21 +1,22 @@ -import registerComponent from '../../../core/component_registrator'; -import { VIEWS } from '../constants'; -import SchedulerTimeline from './ui.scheduler.timeline'; +import registerComponent from '@js/core/component_registrator'; + +import { VIEWS } from '../m_constants'; +import SchedulerTimeline from './m_timeline'; const TIMELINE_CLASS = 'dx-scheduler-timeline-day'; class SchedulerTimelineDay extends SchedulerTimeline { - get type() { return VIEWS.TIMELINE_DAY; } + get type() { return VIEWS.TIMELINE_DAY; } - _getElementClass() { - return TIMELINE_CLASS; - } + _getElementClass() { + return TIMELINE_CLASS; + } - _needRenderWeekHeader() { - return this._isWorkSpaceWithCount(); - } + _needRenderWeekHeader() { + return this._isWorkSpaceWithCount(); + } } -registerComponent('dxSchedulerTimelineDay', SchedulerTimelineDay); +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 3acac0c9c6bb..4796621d4460 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_month.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_month.ts @@ -1,72 +1,74 @@ -import registerComponent from '../../../core/component_registrator'; -import SchedulerTimeline from './ui.scheduler.timeline'; -import dateUtils from '../../../core/utils/date'; +import registerComponent from '@js/core/component_registrator'; +import dateUtils from '@js/core/utils/date'; +import { formatWeekdayAndDay } from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/base'; +import { getViewStartByOptions } from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/month'; +// NOTE: Renovation component import. +// @ts-expect-error import dxrDateHeader from '../../../renovation/ui/scheduler/workspaces/base/header_panel/layout.j'; -import { getViewStartByOptions } from '../../../renovation/ui/scheduler/view_model/to_test/views/utils/month'; -import { formatWeekdayAndDay } from '../../../renovation/ui/scheduler/view_model/to_test/views/utils/base'; -import { VIEWS } from '../constants'; +import { VIEWS } from '../m_constants'; +import SchedulerTimeline from './m_timeline'; const TIMELINE_CLASS = 'dx-scheduler-timeline-month'; class SchedulerTimelineMonth extends SchedulerTimeline { - get type() { return VIEWS.TIMELINE_MONTH; } + get type() { return VIEWS.TIMELINE_MONTH; } - get viewDirection() { return 'horizontal'; } + readonly viewDirection = 'horizontal'; - get renovatedHeaderPanelComponent() { return dxrDateHeader; } + get renovatedHeaderPanelComponent() { return dxrDateHeader; } - _renderView() { - super._renderView(); + _renderView() { + super._renderView(); - this._updateScrollable(); - } + this._updateScrollable(); + } - _getElementClass() { - return TIMELINE_CLASS; - } + _getElementClass() { + return TIMELINE_CLASS; + } - _getDateHeaderTemplate() { - return this.option('dateCellTemplate'); - } + _getDateHeaderTemplate() { + return this.option('dateCellTemplate'); + } - _calculateDurationInCells(timeDiff) { - return timeDiff / this.getCellDuration(); - } + _calculateDurationInCells(timeDiff) { + return timeDiff / this.getCellDuration(); + } - isIndicatorVisible() { - return true; - } + isIndicatorVisible() { + return true; + } - _getFormat() { - return formatWeekdayAndDay; - } + _getFormat() { + return formatWeekdayAndDay; + } - _getIntervalBetween(currentDate) { - const firstViewDate = this.getStartViewDate(); - const timeZoneOffset = dateUtils.getTimezonesDifference(firstViewDate, currentDate); + _getIntervalBetween(currentDate) { + const firstViewDate = this.getStartViewDate(); + const timeZoneOffset = dateUtils.getTimezonesDifference(firstViewDate, currentDate); - return currentDate.getTime() - (firstViewDate.getTime() - this.option('startDayHour') * 3600000) - timeZoneOffset; - } + return currentDate.getTime() - (firstViewDate.getTime() - (this.option('startDayHour') as any) * 3600000) - timeZoneOffset; + } - _getViewStartByOptions() { - return getViewStartByOptions( - this.option('startDate'), - this.option('currentDate'), - this.option('intervalCount'), - dateUtils.getFirstMonthDate(this.option('startDate')), - ); - } + _getViewStartByOptions() { + return 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, + ); + } - generateRenderOptions() { - const options = super.generateRenderOptions(true); - return { - ...options, - getDateForHeaderText: (_, date) => date, - }; - } + generateRenderOptions() { + const options = super.generateRenderOptions(true); + return { + ...options, + getDateForHeaderText: (_, date) => date, + }; + } } -registerComponent('dxSchedulerTimelineMonth', SchedulerTimelineMonth); +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 cc9486a06304..24a1dc37725f 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_week.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_week.ts @@ -1,28 +1,29 @@ -import registerComponent from '../../../core/component_registrator'; -import SchedulerTimeline from './ui.scheduler.timeline'; -import { getBoundingRect } from '../../../core/utils/position'; -import { VIEWS } from '../constants'; +import registerComponent from '@js/core/component_registrator'; +import { getBoundingRect } from '@js/core/utils/position'; + +import { VIEWS } from '../m_constants'; +import SchedulerTimeline from './m_timeline'; const TIMELINE_CLASS = 'dx-scheduler-timeline-week'; export default class SchedulerTimelineWeek extends SchedulerTimeline { - get type() { return VIEWS.TIMELINE_WEEK; } + get type() { return VIEWS.TIMELINE_WEEK; } - _getElementClass() { - return TIMELINE_CLASS; - } + _getElementClass() { + return TIMELINE_CLASS; + } - _getHeaderPanelCellWidth($headerRow) { - return getBoundingRect($headerRow.children().first().get(0)).width; - } + _getHeaderPanelCellWidth($headerRow) { + return getBoundingRect($headerRow.children().first().get(0)).width; + } - _needRenderWeekHeader() { - return true; - } + _needRenderWeekHeader() { + return true; + } - _incrementDate(date) { - date.setDate(date.getDate() + 1); - } + _incrementDate(date) { + date.setDate(date.getDate() + 1); + } } -registerComponent('dxSchedulerTimelineWeek', SchedulerTimelineWeek); +registerComponent('dxSchedulerTimelineWeek', SchedulerTimelineWeek as any); diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_work_week.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_work_week.ts index 93f440817b3a..5f91c8eabd18 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_work_week.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_timeline_work_week.ts @@ -1,35 +1,37 @@ -import registerComponent from '../../../core/component_registrator'; -import { VIEWS } from '../constants'; -import SchedulerTimelineWeek from './ui.scheduler.timeline_week'; +import registerComponent from '@js/core/component_registrator'; import { - getWeekendsCount, -} from '../../../renovation/ui/scheduler/view_model/to_test/views/utils/work_week'; + getWeekendsCount, +} from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/work_week'; + +import { VIEWS } from '../m_constants'; +import SchedulerTimelineWeek from './m_timeline_week'; const TIMELINE_CLASS = 'dx-scheduler-timeline-work-week'; const LAST_DAY_WEEK_INDEX = 5; class SchedulerTimelineWorkWeek extends SchedulerTimelineWeek { - get type() { return VIEWS.TIMELINE_WORK_WEEK; } + get type() { return VIEWS.TIMELINE_WORK_WEEK; } - constructor(...args) { - super(...args); + constructor(...args: any[]) { + // @ts-expect-error + super(...args); - this._getWeekendsCount = getWeekendsCount; - } + this._getWeekendsCount = getWeekendsCount; + } - _getElementClass() { - return TIMELINE_CLASS; - } + _getElementClass() { + return TIMELINE_CLASS; + } - _incrementDate(date) { - const day = date.getDay(); - if(day === LAST_DAY_WEEK_INDEX) { - date.setDate(date.getDate() + 2); - } - super._incrementDate(date); + _incrementDate(date) { + const day = date.getDay(); + if (day === LAST_DAY_WEEK_INDEX) { + date.setDate(date.getDate() + 2); } + super._incrementDate(date); + } } -registerComponent('dxSchedulerTimelineWorkWeek', SchedulerTimelineWorkWeek); +registerComponent('dxSchedulerTimelineWorkWeek', SchedulerTimelineWorkWeek as any); export default SchedulerTimelineWorkWeek; diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_virtual_scrolling.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_virtual_scrolling.ts index 77d246351a7b..488c8f90bd25 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_virtual_scrolling.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_virtual_scrolling.ts @@ -1,8 +1,9 @@ -import domAdapter from '../../../core/dom_adapter'; -import eventsEngine from '../../../events/core/events_engine'; -import { getWindow } from '../../../core/utils/window'; -import { addNamespace } from '../../../events/utils/index'; -import { isDefined } from '../../../core/utils/type'; +/* eslint-disable max-classes-per-file */ +import domAdapter from '@js/core/dom_adapter'; +import { isDefined } from '@js/core/utils/type'; +import { getWindow } from '@js/core/utils/window'; +import eventsEngine from '@js/events/core/events_engine'; +import { addNamespace } from '@js/events/utils/index'; const DEFAULT_CELL_HEIGHT = 50; const MIN_CELL_WIDTH = 1; @@ -11,671 +12,696 @@ const VIRTUAL_APPOINTMENTS_RENDER_TIMEOUT = 15; const DOCUMENT_SCROLL_EVENT_NAMESPACE = addNamespace('scroll', 'dxSchedulerVirtualScrolling'); const scrollingOrientations = { - vertical: 'vertical', - horizontal: 'horizontal', - both: 'both', - none: 'none' + vertical: 'vertical', + horizontal: 'horizontal', + both: 'both', + none: 'none', }; const DefaultScrollingOrientation = scrollingOrientations.both; export class VirtualScrollingDispatcher { - constructor(options) { - this.options = options; - if(options) { - this._rowHeight = this.getCellHeight(); - this._cellWidth = this.getCellWidth(); + _rowHeight: any; - this._createVirtualScrollingBase(); - } + _cellWidth: any; + _verticalVirtualScrolling: any; + + _horizontalVirtualScrolling: any; + + _onScrollHandler: any; + + constructor(public options?: any) { + if (options) { + this._rowHeight = this.getCellHeight(); + this._cellWidth = this.getCellWidth(); + + this._createVirtualScrollingBase(); } + } - get isRTL() { return this.options.isRTL(); } + get isRTL() { return this.options.isRTL(); } - get verticalVirtualScrolling() { return this._verticalVirtualScrolling; } - set verticalVirtualScrolling(value) { this._verticalVirtualScrolling = value; } + get verticalVirtualScrolling() { return this._verticalVirtualScrolling; } - get horizontalVirtualScrolling() { return this._horizontalVirtualScrolling; } - set horizontalVirtualScrolling(value) { this._horizontalVirtualScrolling = value; } + set verticalVirtualScrolling(value) { this._verticalVirtualScrolling = value; } - get document() { return domAdapter.getDocument(); } + get horizontalVirtualScrolling() { return this._horizontalVirtualScrolling; } - get height() { - return this.options.getSchedulerHeight(); - } + set horizontalVirtualScrolling(value) { this._horizontalVirtualScrolling = value; } - get width() { - return this.options.getSchedulerWidth(); - } + get document() { return domAdapter.getDocument(); } - get rowHeight() { return this._rowHeight; } - set rowHeight(value) { this._rowHeight = value; } + get height() { + return this.options.getSchedulerHeight(); + } - get outlineCount() { return this.options.getScrolling().outlineCount; } + get width() { + return this.options.getSchedulerWidth(); + } - get cellWidth() { return this._cellWidth; } - set cellWidth(value) { this._cellWidth = value; } + get rowHeight() { return this._rowHeight; } - get viewportWidth() { - const width = this.width && this.options.getViewWidth(); + set rowHeight(value) { this._rowHeight = value; } - return width > 0 - ? width - : this.options.getWindowWidth(); - } + get outlineCount() { return this.options.getScrolling().outlineCount; } - get viewportHeight() { - const height = this.height && this.options.getViewHeight(); + get cellWidth() { return this._cellWidth; } - return height > 0 - ? height - : this.options.getWindowHeight(); - } + set cellWidth(value) { this._cellWidth = value; } - get cellCountInsideTopVirtualRow() { return this.verticalScrollingState?.virtualItemCountBefore || 0; } - get cellCountInsideLeftVirtualCell() { return this.horizontalScrollingState?.virtualItemCountBefore || 0; } - get cellCountInsideRightVirtualCell() { return this.horizontalScrollingState?.virtualItemCountAfter || 0; } + get viewportWidth() { + const width = this.width && this.options.getViewWidth(); - get topVirtualRowsCount() { - return this.cellCountInsideTopVirtualRow > 0 - ? 1 - : 0; - } + return width > 0 + ? width + : this.options.getWindowWidth(); + } - get leftVirtualCellsCount() { - const virtualItemsCount = !this.isRTL - ? this.cellCountInsideLeftVirtualCell - : this.cellCountInsideRightVirtualCell; + get viewportHeight() { + const height = this.height && this.options.getViewHeight(); - return virtualItemsCount > 0 - ? 1 - : 0; - } + return height > 0 + ? height + : this.options.getWindowHeight(); + } - get virtualRowOffset() { - return this.verticalScrollingState?.virtualItemSizeBefore || 0; - } + get cellCountInsideTopVirtualRow() { return this.verticalScrollingState?.virtualItemCountBefore || 0; } - get virtualCellOffset() { - return this.horizontalScrollingState?.virtualItemSizeBefore || 0; - } + get cellCountInsideLeftVirtualCell() { return this.horizontalScrollingState?.virtualItemCountBefore || 0; } - get scrollingState() { - return { - vertical: this.verticalVirtualScrolling?.state, - horizontal: this.horizontalVirtualScrolling?.state - }; - } - get verticalScrollingState() { return this.scrollingState.vertical; } - get horizontalScrollingState() { return this.scrollingState.horizontal; } + get cellCountInsideRightVirtualCell() { return this.horizontalScrollingState?.virtualItemCountAfter || 0; } - get scrollingOrientation() { - const scrolling = this.options.getScrolling(); + get topVirtualRowsCount() { + return this.cellCountInsideTopVirtualRow > 0 + ? 1 + : 0; + } - if(scrolling.mode === 'standard') { - return scrollingOrientations.none; - } + get leftVirtualCellsCount() { + const virtualItemsCount = !this.isRTL + ? this.cellCountInsideLeftVirtualCell + : this.cellCountInsideRightVirtualCell; - return scrolling.orientation || DefaultScrollingOrientation; - } + return virtualItemsCount > 0 + ? 1 + : 0; + } - get verticalScrollingAllowed() { - return this.scrollingOrientation === scrollingOrientations.vertical || - this.scrollingOrientation === scrollingOrientations.both; - } + get virtualRowOffset() { + return this.verticalScrollingState?.virtualItemSizeBefore || 0; + } - get horizontalScrollingAllowed() { - return this.scrollingOrientation === scrollingOrientations.horizontal || - this.scrollingOrientation === scrollingOrientations.both; - } + get virtualCellOffset() { + return this.horizontalScrollingState?.virtualItemSizeBefore || 0; + } - setViewOptions(options) { - this.options = options; - - if(this.verticalVirtualScrolling) { - this.verticalVirtualScrolling.options = options; - this.verticalVirtualScrolling.itemSize = this.rowHeight; - this.verticalVirtualScrolling.viewportSize = this.viewportHeight; - } - if(this.horizontalVirtualScrolling) { - this.horizontalVirtualScrolling.options = options; - this.verticalVirtualScrolling.itemSize = this.cellWidth; - this.verticalVirtualScrolling.viewportSize = this.viewportWidth; - } - } + get scrollingState() { + return { + vertical: this.verticalVirtualScrolling?.state, + horizontal: this.horizontalVirtualScrolling?.state, + }; + } - getRenderState() { - const verticalRenderState = this.verticalVirtualScrolling?.getRenderState() || {}; - const horizontalRenderState = this.horizontalVirtualScrolling?.getRenderState() || {}; + get verticalScrollingState() { return this.scrollingState.vertical; } - return { - ...verticalRenderState, - ...horizontalRenderState - }; - } + get horizontalScrollingState() { return this.scrollingState.horizontal; } - getCellHeight() { - const cellHeight = this.options.getCellHeight(); - const result = cellHeight > 0 - ? cellHeight - : DEFAULT_CELL_HEIGHT; + get scrollingOrientation() { + const scrolling = this.options.getScrolling(); - return Math.floor(result); + if (scrolling.mode === 'standard') { + return scrollingOrientations.none; } - getCellWidth() { - let cellWidth = this.options.getCellWidth(); - const minCellWidth = this.options.getCellMinWidth(); + return scrolling.orientation || DefaultScrollingOrientation; + } + + get verticalScrollingAllowed() { + return this.scrollingOrientation === scrollingOrientations.vertical + || this.scrollingOrientation === scrollingOrientations.both; + } - // TODO: Remove this after CSS refactoring - if(!cellWidth || cellWidth < minCellWidth) { - cellWidth = minCellWidth; - } + get horizontalScrollingAllowed() { + return this.scrollingOrientation === scrollingOrientations.horizontal + || this.scrollingOrientation === scrollingOrientations.both; + } - const result = cellWidth > 0 - ? cellWidth - : MIN_CELL_WIDTH; + setViewOptions(options) { + this.options = options; - return Math.floor(result); + if (this.verticalVirtualScrolling) { + this.verticalVirtualScrolling.options = options; + this.verticalVirtualScrolling.itemSize = this.rowHeight; + this.verticalVirtualScrolling.viewportSize = this.viewportHeight; } + if (this.horizontalVirtualScrolling) { + this.horizontalVirtualScrolling.options = options; + this.verticalVirtualScrolling.itemSize = this.cellWidth; + this.verticalVirtualScrolling.viewportSize = this.viewportWidth; + } + } - calculateCoordinatesByDataAndPosition(cellData, position, date, isCalculateTime, isVerticalDirectionView) { - const { - rowIndex, columnIndex, - } = position; - const { - startDate, endDate, allDay, - } = cellData; + getRenderState() { + const verticalRenderState = this.verticalVirtualScrolling?.getRenderState() || {}; + const horizontalRenderState = this.horizontalVirtualScrolling?.getRenderState() || {}; - const timeToScroll = date.getTime(); - const cellStartTime = startDate.getTime(); - const cellEndTime = endDate.getTime(); + return { + ...verticalRenderState, + ...horizontalRenderState, + }; + } - const scrollInCell = allDay || !isCalculateTime - ? 0 - : (timeToScroll - cellStartTime) / (cellEndTime - cellStartTime); + getCellHeight() { + const cellHeight = this.options.getCellHeight(); + const result = cellHeight > 0 + ? cellHeight + : DEFAULT_CELL_HEIGHT; - const cellWidth = this.getCellWidth(); - const rowHeight = this.getCellHeight(); + return Math.floor(result); + } - const top = isVerticalDirectionView - ? (rowIndex + scrollInCell) * rowHeight - : rowIndex * rowHeight; + getCellWidth() { + let cellWidth = this.options.getCellWidth(); + const minCellWidth = this.options.getCellMinWidth(); - let left = isVerticalDirectionView - ? columnIndex * cellWidth - : (columnIndex + scrollInCell) * cellWidth; + // TODO: Remove this after CSS refactoring + if (!cellWidth || cellWidth < minCellWidth) { + cellWidth = minCellWidth; + } - if(this.isRTL) { - left = this.options.getScrollableOuterWidth() - left; - } + const result = cellWidth > 0 + ? cellWidth + : MIN_CELL_WIDTH; - return { top, left }; - } + return Math.floor(result); + } - dispose() { - if(this._onScrollHandler) { - eventsEngine.off(this.document, DOCUMENT_SCROLL_EVENT_NAMESPACE, this._onScrollHandler); - } - } + calculateCoordinatesByDataAndPosition(cellData, position, date, isCalculateTime, isVerticalDirectionView) { + const { + rowIndex, columnIndex, + } = position; + const { + startDate, endDate, allDay, + } = cellData; - createVirtualScrolling() { - const isVerticalVirtualScrollingCreated = !!this.verticalVirtualScrolling; - const isHorizontalVirtualScrollingCreated = !!this.horizontalVirtualScrolling; + const timeToScroll = date.getTime(); + const cellStartTime = startDate.getTime(); + const cellEndTime = endDate.getTime(); - if(this.verticalScrollingAllowed !== isVerticalVirtualScrollingCreated - || this.horizontalScrollingAllowed !== isHorizontalVirtualScrollingCreated) { - this._rowHeight = this.getCellHeight(); - this._cellWidth = this.getCellWidth(); - this._createVirtualScrollingBase(); - } - } + const scrollInCell = allDay || !isCalculateTime + ? 0 + : (timeToScroll - cellStartTime) / (cellEndTime - cellStartTime); - _createVirtualScrollingBase() { - if(this.verticalScrollingAllowed) { - this.verticalVirtualScrolling = new VerticalVirtualScrolling({ - ...this.options, - viewportHeight: this.viewportHeight, - rowHeight: this.rowHeight, - outlineCount: this.outlineCount - }); - } - - if(this.horizontalScrollingAllowed) { - this.horizontalVirtualScrolling = new HorizontalVirtualScrolling({ - ...this.options, - viewportWidth: this.viewportWidth, - cellWidth: this.cellWidth, - outlineCount: this.outlineCount - }); - } - } + const cellWidth = this.getCellWidth(); + const rowHeight = this.getCellHeight(); + + const top = isVerticalDirectionView + ? (rowIndex + scrollInCell) * rowHeight + : rowIndex * rowHeight; - isAttachWindowScrollEvent() { - return (this.horizontalScrollingAllowed || this.verticalScrollingAllowed) && !this.height; + let left = isVerticalDirectionView + ? columnIndex * cellWidth + : (columnIndex + scrollInCell) * cellWidth; + + if (this.isRTL) { + left = this.options.getScrollableOuterWidth() - left; } - attachScrollableEvents() { - if(this.isAttachWindowScrollEvent()) { - this._attachWindowScroll(); - } + return { top, left }; + } + + dispose() { + if (this._onScrollHandler) { + eventsEngine.off(this.document, DOCUMENT_SCROLL_EVENT_NAMESPACE, this._onScrollHandler); } + } - _attachWindowScroll() { - const window = getWindow(); - - this._onScrollHandler = this.options.createAction(() => { - const { - scrollX, - scrollY - } = window; - - if(scrollX >= MIN_SCROLL_OFFSET || scrollY >= MIN_SCROLL_OFFSET) { - this.handleOnScrollEvent({ - left: scrollX, - top: scrollY, - }); - } + createVirtualScrolling() { + const isVerticalVirtualScrollingCreated = !!this.verticalVirtualScrolling; + const isHorizontalVirtualScrollingCreated = !!this.horizontalVirtualScrolling; + + if (this.verticalScrollingAllowed !== isVerticalVirtualScrollingCreated + || this.horizontalScrollingAllowed !== isHorizontalVirtualScrollingCreated) { + this._rowHeight = this.getCellHeight(); + this._cellWidth = this.getCellWidth(); + this._createVirtualScrollingBase(); + } + } + + _createVirtualScrollingBase() { + if (this.verticalScrollingAllowed) { + this.verticalVirtualScrolling = new VerticalVirtualScrolling({ + ...this.options, + viewportHeight: this.viewportHeight, + rowHeight: this.rowHeight, + outlineCount: this.outlineCount, + }); + } + + if (this.horizontalScrollingAllowed) { + this.horizontalVirtualScrolling = new HorizontalVirtualScrolling({ + ...this.options, + viewportWidth: this.viewportWidth, + cellWidth: this.cellWidth, + outlineCount: this.outlineCount, + }); + } + } + + isAttachWindowScrollEvent() { + return (this.horizontalScrollingAllowed || this.verticalScrollingAllowed) && !this.height; + } + + attachScrollableEvents() { + if (this.isAttachWindowScrollEvent()) { + this._attachWindowScroll(); + } + } + + _attachWindowScroll() { + const window = getWindow(); + + this._onScrollHandler = this.options.createAction(() => { + const { + scrollX, + scrollY, + } = window; + + if (scrollX >= MIN_SCROLL_OFFSET || scrollY >= MIN_SCROLL_OFFSET) { + this.handleOnScrollEvent({ + left: scrollX, + top: scrollY, }); + } + }); - eventsEngine.on(this.document, DOCUMENT_SCROLL_EVENT_NAMESPACE, this._onScrollHandler); - } + eventsEngine.on(this.document, DOCUMENT_SCROLL_EVENT_NAMESPACE, this._onScrollHandler); + } - handleOnScrollEvent(scrollPosition) { - if(scrollPosition) { - const { - left, - top - } = scrollPosition; + handleOnScrollEvent(scrollPosition) { + if (scrollPosition) { + const { + left, + top, + } = scrollPosition; - const verticalStateChanged = isDefined(top) && this.verticalVirtualScrolling?.updateState(top); - const horizontalStateChanged = isDefined(left) && this.horizontalVirtualScrolling?.updateState(left); + const verticalStateChanged = isDefined(top) && this.verticalVirtualScrolling?.updateState(top); + const horizontalStateChanged = isDefined(left) && this.horizontalVirtualScrolling?.updateState(left); - if(verticalStateChanged || horizontalStateChanged) { - this.options.updateRender?.(); - } - } + if (verticalStateChanged || horizontalStateChanged) { + this.options.updateRender?.(); + } } + } - updateDimensions(isForce) { - const cellHeight = this.getCellHeight(); - const needUpdateVertical = this.verticalScrollingAllowed && cellHeight !== this.rowHeight; - if((needUpdateVertical || isForce) && this.verticalVirtualScrolling) { - this.rowHeight = cellHeight; + updateDimensions(isForce) { + const cellHeight = this.getCellHeight(); + const needUpdateVertical = this.verticalScrollingAllowed && cellHeight !== this.rowHeight; + if ((needUpdateVertical || isForce) && this.verticalVirtualScrolling) { + this.rowHeight = cellHeight; - this.verticalVirtualScrolling.viewportSize = this.viewportHeight; - this.verticalVirtualScrolling.reinitState(cellHeight, isForce); - } + this.verticalVirtualScrolling.viewportSize = this.viewportHeight; + this.verticalVirtualScrolling.reinitState(cellHeight, isForce); + } - const cellWidth = this.getCellWidth(); - const needUpdateHorizontal = this.horizontalScrollingAllowed && cellWidth !== this.cellWidth; - if((needUpdateHorizontal || isForce) && this.horizontalVirtualScrolling) { - this.cellWidth = cellWidth; + const cellWidth = this.getCellWidth(); + const needUpdateHorizontal = this.horizontalScrollingAllowed && cellWidth !== this.cellWidth; + if ((needUpdateHorizontal || isForce) && this.horizontalVirtualScrolling) { + this.cellWidth = cellWidth; - this.horizontalVirtualScrolling.viewportSize = this.viewportWidth; - this.horizontalVirtualScrolling.reinitState(cellWidth, isForce); - } + this.horizontalVirtualScrolling.viewportSize = this.viewportWidth; + this.horizontalVirtualScrolling.reinitState(cellWidth, isForce); + } - if(needUpdateVertical || needUpdateHorizontal) { - this.options.updateGrid?.(); - } + if (needUpdateVertical || needUpdateHorizontal) { + this.options.updateGrid?.(); } + } } class VirtualScrollingBase { - constructor(options) { - this.options = options; - this._state = this.defaultState; - this.viewportSize = options.viewportSize; - this._itemSize = options.itemSize; + _state = this.defaultState; - this._position = -1; - this._itemSizeChanged = false; + viewportSize = this.options.viewportSize; - this.updateState(0); - } + _itemSize = this.options.itemSize; - get itemSize() { return this._itemSize; } - set itemSize(value) { - this._itemSizeChanged = this._itemSize !== value; - this._itemSize = value; - } - get state() { return this._state; } - set state(value) { this._state = value; } + _position = -1; - get startIndex() { return this.state.startIndex; } + _itemSizeChanged = false; - get pageSize() { - return Math.ceil(this.viewportSize / this.itemSize); - } + constructor(public options: any) { + this.updateState(0); + } - get outlineCount() { - return isDefined(this.options.outlineCount) - ? this.options.outlineCount - : Math.floor(this.pageSize / 2); - } + get itemSize() { return this._itemSize; } - get groupCount() { return this.options.getGroupCount(); } - get isVerticalGrouping() { return this.options.isVerticalGrouping(); } - - get defaultState() { - return { - prevPosition: 0, - startIndex: -1, - itemCount: 0, - virtualItemCountBefore: 0, - virtualItemCountAfter: 0, - outlineCountBefore: 0, - outlineCountAfter: 0, - virtualItemSizeBefore: 0, - virtualItemSizeAfter: 0, - outlineSizeBefore: 0, - outlineSizeAfter: 0 - }; - } + set itemSize(value) { + this._itemSizeChanged = this._itemSize !== value; + this._itemSize = value; + } - get maxScrollPosition() { - return this.getTotalItemCount() * this.itemSize - this.viewportSize; - } + get state() { return this._state; } - get position() { return this._position; } - set position(value) { this._position = value; } + set state(value) { this._state = value; } - needUpdateState(position) { - const { - prevPosition, - startIndex - } = this.state; - const isFirstInitialization = startIndex < 0; + get startIndex() { return this.state.startIndex; } - if(isFirstInitialization) { - return true; - } + get pageSize() { + return Math.ceil(this.viewportSize / this.itemSize); + } - let isStartIndexChanged = false; + get outlineCount() { + return isDefined(this.options.outlineCount) + ? this.options.outlineCount + : Math.floor(this.pageSize / 2); + } - if(this._validateAndSavePosition(position)) { + get groupCount() { return this.options.getGroupCount(); } - if(position === 0 || position === this.maxScrollPosition) { - return true; - } + get isVerticalGrouping() { return this.options.isVerticalGrouping(); } - const currentPosition = prevPosition; - const currentItemsCount = Math.floor(currentPosition / this.itemSize); - const itemsCount = Math.floor(position / this.itemSize); + get defaultState() { + return { + prevPosition: 0, + startIndex: -1, + itemCount: 0, + virtualItemCountBefore: 0, + virtualItemCountAfter: 0, + outlineCountBefore: 0, + outlineCountAfter: 0, + virtualItemSizeBefore: 0, + virtualItemSizeAfter: 0, + outlineSizeBefore: 0, + outlineSizeAfter: 0, + }; + } - isStartIndexChanged = Math.abs(currentItemsCount - itemsCount) >= this.outlineCount; - } + get maxScrollPosition() { + return this.getTotalItemCount() * this.itemSize - this.viewportSize; + } - return isStartIndexChanged; - } + get position() { return this._position; } - _validateAndSavePosition(position) { - if(!isDefined(position)) { - return false; - } + set position(value) { this._position = value; } - const result = this.position !== position; + needUpdateState(position) { + const { + prevPosition, + startIndex, + } = this.state; + const isFirstInitialization = startIndex < 0; - this.position = position; - - return result; + if (isFirstInitialization) { + return true; } - _correctPosition(position) { - return position >= 0 - ? Math.min(position, this.maxScrollPosition) - : -1; - } + let isStartIndexChanged = false; - updateState(position, isForce) { - position = this._correctPosition(position); + if (this._validateAndSavePosition(position)) { + if (position === 0 || position === this.maxScrollPosition) { + return true; + } - if(!this.needUpdateState(position) && !isForce) { - return false; - } + const currentPosition = prevPosition; + const currentItemsCount = Math.floor(currentPosition / this.itemSize); + const itemsCount = Math.floor(position / this.itemSize); - const itemsInfoBefore = this._calcItemInfoBefore(position); - const itemsDeltaBefore = this._calcItemDeltaBefore(itemsInfoBefore); + isStartIndexChanged = Math.abs(currentItemsCount - itemsCount) >= this.outlineCount; + } - const { - outlineCountAfter, - virtualItemCountAfter, - itemCountWithAfter - } = this._calcItemInfoAfter(itemsDeltaBefore); + return isStartIndexChanged; + } - const { - virtualItemCountBefore, - outlineCountBefore - } = itemsInfoBefore; + _validateAndSavePosition(position) { + if (!isDefined(position)) { + return false; + } - const itemCount = outlineCountBefore + itemCountWithAfter + outlineCountAfter; + const result = this.position !== position; - const itemCountBefore = Math.floor(position / this.itemSize); + this.position = position; - this.state.prevPosition = itemCountBefore * this.itemSize; - this.state.startIndex = itemCountBefore - outlineCountBefore; - this.state.virtualItemCountBefore = virtualItemCountBefore; - this.state.outlineCountBefore = outlineCountBefore; - this.state.itemCount = itemCount; - this.state.outlineCountAfter = outlineCountAfter; - this.state.virtualItemCountAfter = virtualItemCountAfter; + return result; + } - this._updateStateCore(); + _correctPosition(position) { + return position >= 0 + ? Math.min(position, this.maxScrollPosition) + : -1; + } - return true; + updateState(position, isForce?: any) { + position = this._correctPosition(position); + + if (!this.needUpdateState(position) && !isForce) { + return false; } - reinitState(itemSize, isForceUpdate) { - const { position } = this; + const itemsInfoBefore = this._calcItemInfoBefore(position); + const itemsDeltaBefore = this._calcItemDeltaBefore(itemsInfoBefore); - this.itemSize = itemSize; + const { + outlineCountAfter, + virtualItemCountAfter, + itemCountWithAfter, + } = this._calcItemInfoAfter(itemsDeltaBefore); - this.updateState(0, isForceUpdate); - if(position > 0) { - this.updateState(position, isForceUpdate); - } - } + const { + virtualItemCountBefore, + outlineCountBefore, + } = itemsInfoBefore; - _calcItemInfoBefore(position) { - let virtualItemCountBefore = Math.floor(position / this.itemSize); + const itemCount = outlineCountBefore + itemCountWithAfter + outlineCountAfter; - const outlineCountBefore = Math.min(virtualItemCountBefore, this.outlineCount); + const itemCountBefore = Math.floor(position / this.itemSize); - virtualItemCountBefore -= outlineCountBefore; + this.state.prevPosition = itemCountBefore * this.itemSize; + this.state.startIndex = itemCountBefore - outlineCountBefore; + this.state.virtualItemCountBefore = virtualItemCountBefore; + this.state.outlineCountBefore = outlineCountBefore; + this.state.itemCount = itemCount; + this.state.outlineCountAfter = outlineCountAfter; + this.state.virtualItemCountAfter = virtualItemCountAfter; - return { - virtualItemCountBefore, - outlineCountBefore - }; - } + this._updateStateCore(); - _calcItemDeltaBefore(itemInfoBefore) { - const { - virtualItemCountBefore, - outlineCountBefore - } = itemInfoBefore; + return true; + } - const totalItemCount = this.getTotalItemCount(); + reinitState(itemSize, isForceUpdate) { + const { position } = this; - return totalItemCount - virtualItemCountBefore - outlineCountBefore; - } + this.itemSize = itemSize; - getTotalItemCount() { - throw 'getTotalItemCount method should be implemented'; + this.updateState(0, isForceUpdate); + if (position > 0) { + this.updateState(position, isForceUpdate); } + } - getRenderState() { - throw 'getRenderState method should be implemented'; - } + _calcItemInfoBefore(position) { + let virtualItemCountBefore = Math.floor(position / this.itemSize); - _calcItemInfoAfter(itemsDeltaBefore) { - const itemCountWithAfter = itemsDeltaBefore >= this.pageSize - ? this.pageSize - : itemsDeltaBefore; + const outlineCountBefore = Math.min(virtualItemCountBefore, this.outlineCount); - let virtualItemCountAfter = itemsDeltaBefore - itemCountWithAfter; + virtualItemCountBefore -= outlineCountBefore; - const outlineCountAfter = virtualItemCountAfter > 0 - ? Math.min(virtualItemCountAfter, this.outlineCount) - : 0; + return { + virtualItemCountBefore, + outlineCountBefore, + }; + } - if(virtualItemCountAfter > 0) { - virtualItemCountAfter -= outlineCountAfter; - } + _calcItemDeltaBefore(itemInfoBefore) { + const { + virtualItemCountBefore, + outlineCountBefore, + } = itemInfoBefore; - return { - virtualItemCountAfter, - outlineCountAfter, - itemCountWithAfter - }; - } + const totalItemCount = this.getTotalItemCount(); - _updateStateCore() { - const { state } = this; + return totalItemCount - virtualItemCountBefore - outlineCountBefore; + } - const virtualItemCountBefore = state.virtualItemCountBefore; - const virtualItemCountAfter = state.virtualItemCountAfter; - const outlineCountBefore = state.outlineCountBefore; - const outlineCountAfter = state.outlineCountAfter; + getTotalItemCount(): any { + // eslint-disable-next-line @typescript-eslint/no-throw-literal + throw 'getTotalItemCount method should be implemented'; + } - const prevVirtualItemSizeBefore = state.virtualItemSizeBefore; - const prevVirtualItemSizeAfter = state.virtualItemSizeAfter; - const prevOutlineSizeBefore = state.outlineSizeBefore; - const prevOutlineSizeAfter = state.outlineSizeAfter; + getRenderState(): any { + // eslint-disable-next-line @typescript-eslint/no-throw-literal + throw 'getRenderState method should be implemented'; + } - const virtualItemSizeBefore = this.itemSize * virtualItemCountBefore; - const virtualItemSizeAfter = this.itemSize * virtualItemCountAfter; - const outlineSizeBefore = this.itemSize * outlineCountBefore; - const outlineSizeAfter = this.itemSize * outlineCountAfter; + _calcItemInfoAfter(itemsDeltaBefore) { + const itemCountWithAfter = itemsDeltaBefore >= this.pageSize + ? this.pageSize + : itemsDeltaBefore; - const prevVirtualSizeBefore = prevVirtualItemSizeBefore + prevOutlineSizeBefore; - const virtualSizeBefore = virtualItemSizeBefore + outlineSizeBefore; - const prevVirtualSizeAfter = prevVirtualItemSizeAfter + prevOutlineSizeAfter; - const virtualSizeAfter = virtualItemSizeAfter + outlineSizeAfter; + let virtualItemCountAfter = itemsDeltaBefore - itemCountWithAfter; - const isAppend = prevVirtualSizeBefore < virtualSizeBefore; - const isPrepend = prevVirtualSizeAfter < virtualSizeAfter; + const outlineCountAfter = virtualItemCountAfter > 0 + ? Math.min(virtualItemCountAfter, this.outlineCount) + : 0; - const needAddItems = this._itemSizeChanged || isAppend || isPrepend; - if(needAddItems) { - this._updateStateVirtualItems(virtualItemSizeBefore, virtualItemSizeAfter); - } + if (virtualItemCountAfter > 0) { + virtualItemCountAfter -= outlineCountAfter; } - _updateStateVirtualItems(virtualItemSizeBefore, virtualItemSizeAfter) { - const { state } = this; + return { + virtualItemCountAfter, + outlineCountAfter, + itemCountWithAfter, + }; + } - state.virtualItemSizeBefore = virtualItemSizeBefore; - state.virtualItemSizeAfter = virtualItemSizeAfter; - } -} + _updateStateCore() { + const { state } = this; -class VerticalVirtualScrolling extends VirtualScrollingBase { - constructor(options) { - super({ - ...options, - itemSize: options.rowHeight, - viewportSize: options.viewportHeight, - }); - } + const { virtualItemCountBefore } = state; + const { virtualItemCountAfter } = state; + const { outlineCountBefore } = state; + const { outlineCountAfter } = state; - get prevTopPosition() { return this.state.prevPosition; } - get rowCount() { return this.state.itemCount; } - get topVirtualRowCount() { return this.state.virtualItemCountBefore; } - get bottomVirtualRowCount() { return this.state.virtualItemCountAfter; } + const prevVirtualItemSizeBefore = state.virtualItemSizeBefore; + const prevVirtualItemSizeAfter = state.virtualItemSizeAfter; + const prevOutlineSizeBefore = state.outlineSizeBefore; + const prevOutlineSizeAfter = state.outlineSizeAfter; - getTotalItemCount() { - return this.options.getTotalRowCount(this.groupCount, this.isVerticalGrouping); - } + const virtualItemSizeBefore = this.itemSize * virtualItemCountBefore; + const virtualItemSizeAfter = this.itemSize * virtualItemCountAfter; + const outlineSizeBefore = this.itemSize * outlineCountBefore; + const outlineSizeAfter = this.itemSize * outlineCountAfter; - getRenderState() { - return { - topVirtualRowHeight: this.state.virtualItemSizeBefore, - bottomVirtualRowHeight: this.state.virtualItemSizeAfter, - startRowIndex: this.state.startIndex, - rowCount: this.state.itemCount, - startIndex: this.state.startIndex, - }; - } -} + const prevVirtualSizeBefore = prevVirtualItemSizeBefore + prevOutlineSizeBefore; + const virtualSizeBefore = virtualItemSizeBefore + outlineSizeBefore; + const prevVirtualSizeAfter = prevVirtualItemSizeAfter + prevOutlineSizeAfter; + const virtualSizeAfter = virtualItemSizeAfter + outlineSizeAfter; -class HorizontalVirtualScrolling extends VirtualScrollingBase { - constructor(options) { - super({ - ...options, - itemSize: options.cellWidth, - viewportSize: options.viewportWidth, - }); - } + const isAppend = prevVirtualSizeBefore < virtualSizeBefore; + const isPrepend = prevVirtualSizeAfter < virtualSizeAfter; - get isRTL() { return this.options.isRTL(); } - - getTotalItemCount() { - return this.options.getTotalCellCount(this.groupCount, this.isVerticalGrouping); + const needAddItems = this._itemSizeChanged || isAppend || isPrepend; + if (needAddItems) { + this._updateStateVirtualItems(virtualItemSizeBefore, virtualItemSizeAfter); } + } - getRenderState() { - return { - leftVirtualCellWidth: this.state.virtualItemSizeBefore, - rightVirtualCellWidth: this.state.virtualItemSizeAfter, - startCellIndex: this.state.startIndex, - cellCount: this.state.itemCount, - cellWidth: this.itemSize - }; - } + _updateStateVirtualItems(virtualItemSizeBefore, virtualItemSizeAfter) { + const { state } = this; - _updateStateVirtualItems(virtualItemSizeBefore, virtualItemSizeAfter) { - if(!this.isRTL) { - super._updateStateVirtualItems(virtualItemSizeBefore, virtualItemSizeAfter); - } else { - const { state } = this; + state.virtualItemSizeBefore = virtualItemSizeBefore; + state.virtualItemSizeAfter = virtualItemSizeAfter; + } +} - state.virtualItemSizeAfter = virtualItemSizeBefore; - state.virtualItemSizeBefore = virtualItemSizeAfter; - state.startIndex = this.getTotalItemCount() - this.startIndex - this.state.itemCount; - } - } +class VerticalVirtualScrolling extends VirtualScrollingBase { + constructor(options) { + super({ + ...options, + itemSize: options.rowHeight, + viewportSize: options.viewportHeight, + }); + } + + get prevTopPosition() { return this.state.prevPosition; } + + get rowCount() { return this.state.itemCount; } + + get topVirtualRowCount() { return this.state.virtualItemCountBefore; } + + get bottomVirtualRowCount() { return this.state.virtualItemCountAfter; } + + getTotalItemCount() { + return this.options.getTotalRowCount(this.groupCount, this.isVerticalGrouping); + } + + getRenderState() { + return { + topVirtualRowHeight: this.state.virtualItemSizeBefore, + bottomVirtualRowHeight: this.state.virtualItemSizeAfter, + startRowIndex: this.state.startIndex, + rowCount: this.state.itemCount, + startIndex: this.state.startIndex, + }; + } } +class HorizontalVirtualScrolling extends VirtualScrollingBase { + constructor(options) { + super({ + ...options, + itemSize: options.cellWidth, + viewportSize: options.viewportWidth, + }); + } + + get isRTL() { return this.options.isRTL(); } + + getTotalItemCount() { + return this.options.getTotalCellCount(this.groupCount, this.isVerticalGrouping); + } + + getRenderState() { + return { + leftVirtualCellWidth: this.state.virtualItemSizeBefore, + rightVirtualCellWidth: this.state.virtualItemSizeAfter, + startCellIndex: this.state.startIndex, + cellCount: this.state.itemCount, + cellWidth: this.itemSize, + }; + } + + _updateStateVirtualItems(virtualItemSizeBefore, virtualItemSizeAfter) { + if (!this.isRTL) { + super._updateStateVirtualItems(virtualItemSizeBefore, virtualItemSizeAfter); + } else { + const { state } = this; + + state.virtualItemSizeAfter = virtualItemSizeBefore; + state.virtualItemSizeBefore = virtualItemSizeAfter; + state.startIndex = this.getTotalItemCount() - this.startIndex - this.state.itemCount; + } + } +} // We do not need this class in renovation export class VirtualScrollingRenderer { - constructor(workspace) { - this._workspace = workspace; - this._renderAppointmentTimeoutID = null; - } + _renderAppointmentTimeoutID: any = null; - getRenderTimeout() { - return this._workspace.option('isRenovatedAppointments') - ? -1 - : VIRTUAL_APPOINTMENTS_RENDER_TIMEOUT; - } + constructor(public _workspace: any) { + } - get workspace() { return this._workspace; } + getRenderTimeout() { + return this._workspace.option('isRenovatedAppointments') + ? -1 + : VIRTUAL_APPOINTMENTS_RENDER_TIMEOUT; + } - updateRender() { - this._renderGrid(); - this._renderAppointments(); - } + get workspace() { return this._workspace; } - _renderGrid() { - this.workspace.renderWorkSpace(false); - } + updateRender() { + this._renderGrid(); + this._renderAppointments(); + } - _renderAppointments() { - const renderTimeout = this.getRenderTimeout(); + _renderGrid() { + this.workspace.renderWorkSpace(false); + } - if(renderTimeout >= 0) { + _renderAppointments() { + const renderTimeout = this.getRenderTimeout(); - clearTimeout(this._renderAppointmentTimeoutID); + if (renderTimeout >= 0) { + clearTimeout(this._renderAppointmentTimeoutID); - this._renderAppointmentTimeoutID = setTimeout( - () => this.workspace.updateAppointments(), - renderTimeout - ); - } else { - this.workspace.updateAppointments(); - } + this._renderAppointmentTimeoutID = setTimeout( + () => this.workspace.updateAppointments(), + renderTimeout, + ); + } else { + this.workspace.updateAppointments(); } + } } 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 0b619c463dd3..2040da03a137 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space.ts @@ -1,94 +1,100 @@ +import { locate, resetPosition } from '@js/animation/translator'; +import domAdapter from '@js/core/dom_adapter'; +import { getPublicElement } from '@js/core/element'; +import $ from '@js/core/renderer'; +import { noop } from '@js/core/utils/common'; +import { compileGetter } from '@js/core/utils/data'; +import dateUtils from '@js/core/utils/date'; +import { extend } from '@js/core/utils/extend'; +import { getBoundingRect } from '@js/core/utils/position'; import { - setWidth, - getOuterHeight, - getOuterWidth, - setOuterHeight, - getHeight, - getWidth, -} from '../../../core/utils/size'; - -import $ from '../../../core/renderer'; -import domAdapter from '../../../core/dom_adapter'; -import eventsEngine from '../../../events/core/events_engine'; -import dateUtils from '../../../core/utils/date'; -import { getWindow, hasWindow } from '../../../core/utils/window'; -import { getPublicElement } from '../../../core/element'; -import { extend } from '../../../core/utils/extend'; -import { getBoundingRect } from '../../../core/utils/position'; -import messageLocalization from '../../../localization/message'; -import { noop } from '../../../core/utils/common'; -import { isDefined } from '../../../core/utils/type'; -import { addNamespace, isMouseEvent } from '../../../events/utils/index'; -import pointerEvents from '../../../events/pointer'; -import errors from '../../widget/ui.errors'; -import { name as clickEventName } from '../../../events/click'; -import { name as contextMenuEventName } from '../../../events/contextmenu'; + getHeight, + getOuterHeight, + getOuterWidth, + getWidth, + setOuterHeight, + setWidth, +} from '@js/core/utils/size'; +import { isDefined } from '@js/core/utils/type'; +import { getWindow, hasWindow } from '@js/core/utils/window'; +import { name as clickEventName } from '@js/events/click'; +import { name as contextMenuEventName } from '@js/events/contextmenu'; +import eventsEngine from '@js/events/core/events_engine'; import { - enter as dragEventEnter, - leave as dragEventLeave, - drop as dragEventDrop -} from '../../../events/drag'; -import Scrollable from '../../scroll_view/ui.scrollable'; -import HorizontalGroupedStrategy from './ui.scheduler.work_space.grouped.strategy.horizontal'; -import VerticalGroupedStrategy from './ui.scheduler.work_space.grouped.strategy.vertical'; -import tableCreatorModule from '../table_creator'; -const { tableCreator } = tableCreatorModule; -import VerticalShader from '../shaders/ui.scheduler.current_time_shader.vertical'; -import AppointmentDragBehavior from '../appointmentDragBehavior'; -import { APPOINTMENT_SETTINGS_KEY } from '../constants'; + drop as dragEventDrop, + enter as dragEventEnter, + leave as dragEventLeave, +} from '@js/events/drag'; +import pointerEvents from '@js/events/pointer'; +import { addNamespace, isMouseEvent } from '@js/events/utils/index'; +import messageLocalization from '@js/localization/message'; +import { getMemoizeScrollTo } from '@js/renovation/ui/common/utils/scroll/getMemoizeScrollTo'; import { - FIXED_CONTAINER_CLASS, - VIRTUAL_CELL_CLASS, - TIME_PANEL_CLASS, - DATE_TABLE_CLASS, - DATE_TABLE_ROW_CLASS, - GROUP_ROW_CLASS, - GROUP_HEADER_CONTENT_CLASS, - VERTICAL_GROUP_COUNT_CLASSES, -} from '../classes'; -import WidgetObserver from '../../../__internal/scheduler/base/m_widget_observer'; -import { resetPosition, locate } from '../../../animation/translator'; - -import { VirtualScrollingDispatcher, VirtualScrollingRenderer } from './ui.scheduler.virtual_scrolling'; -import ViewDataProvider from './view_model/view_data_provider'; - -import dxrDateTableLayout from '../../../renovation/ui/scheduler/workspaces/base/date_table/layout.j'; + calculateIsGroupedAllDayPanel, + calculateViewStartDate, + getCellDuration, + getStartViewDateTimeOffset, + getViewStartByOptions, + isDateAndTimeView, + validateDayHours, +} from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/base'; +import Scrollable from '@js/ui/scroll_view/ui.scrollable'; +import errors from '@js/ui/widget/ui.errors'; + +// NOTE: Renovation component section +// @ts-expect-error import dxrAllDayPanelTable from '../../../renovation/ui/scheduler/workspaces/base/date_table/all_day_panel/table.j'; +// @ts-expect-error import dxrAllDayPanelTitle from '../../../renovation/ui/scheduler/workspaces/base/date_table/all_day_panel/title.j'; -import dxrTimePanelTableLayout from '../../../renovation/ui/scheduler/workspaces/base/time_panel/layout.j'; +// @ts-expect-error +import dxrDateTableLayout from '../../../renovation/ui/scheduler/workspaces/base/date_table/layout.j'; +// @ts-expect-error import dxrGroupPanel from '../../../renovation/ui/scheduler/workspaces/base/group_panel/group_panel.j'; +// @ts-expect-error import dxrDateHeader from '../../../renovation/ui/scheduler/workspaces/base/header_panel/layout.j'; - -import CellsSelectionState from './cells_selection_state'; - -import { Cache } from './cache'; -import { CellsSelectionController } from './cells_selection_controller'; +// @ts-expect-error +import dxrTimePanelTableLayout from '../../../renovation/ui/scheduler/workspaces/base/time_panel/layout.j'; +import WidgetObserver from '../base/m_widget_observer'; +import AppointmentDragBehavior from '../m_appointment_drag_behavior'; import { - calculateViewStartDate, - getViewStartByOptions, - validateDayHours, - getStartViewDateTimeOffset, - isDateAndTimeView, - calculateIsGroupedAllDayPanel, - getCellDuration -} from '../../../renovation/ui/scheduler/view_model/to_test/views/utils/base'; -import { createResourcesTree, getCellGroups, getGroupsObjectFromGroupsArray, getGroupCount } from '../../../__internal/scheduler/resources/m_utils'; + DATE_TABLE_CLASS, + DATE_TABLE_ROW_CLASS, + FIXED_CONTAINER_CLASS, + GROUP_HEADER_CONTENT_CLASS, + GROUP_ROW_CLASS, + TIME_PANEL_CLASS, + VERTICAL_GROUP_COUNT_CLASSES, + VIRTUAL_CELL_CLASS, +} from '../m_classes'; +import { APPOINTMENT_SETTINGS_KEY } from '../m_constants'; +import tableCreatorModule from '../m_table_creator'; +import { utils } from '../m_utils'; import { - getCellWidth, - getCellHeight, - getAllDayHeight, - getMaxAllowedPosition, - PositionHelper -} from './helpers/positionHelper'; + createResourcesTree, getCellGroups, getGroupCount, getGroupsObjectFromGroupsArray, +} from '../resources/m_utils'; +import VerticalShader from '../shaders/m_current_time_shader_vertical'; +import { + getAllDayHeight, + getCellHeight, + getCellWidth, + getMaxAllowedPosition, + PositionHelper, +} from './helpers/m_position_helper'; +import { Cache } from './m_cache'; +import { CellsSelectionController } from './m_cells_selection_controller'; +import CellsSelectionState from './m_cells_selection_state'; +import { VirtualScrollingDispatcher, VirtualScrollingRenderer } from './m_virtual_scrolling'; +import HorizontalGroupedStrategy from './m_work_space_grouped_strategy_horizontal'; +import VerticalGroupedStrategy from './m_work_space_grouped_strategy_vertical'; +import ViewDataProvider from './view_model/m_view_data_provider'; -import { utils } from '../utils'; -import { compileGetter } from '../../../core/utils/data'; -import { getMemoizeScrollTo } from '../../../renovation/ui/common/utils/scroll/getMemoizeScrollTo'; +const { tableCreator } = tableCreatorModule; // TODO: The constant is needed so that the dragging is not sharp. To prevent small twitches const DRAGGING_MOUSE_FAULT = 10; -const abstract = WidgetObserver.abstract; +// @ts-expect-error +const { abstract } = WidgetObserver; const toMs = dateUtils.dateToMilliseconds; const COMPONENT_CLASS = 'dx-scheduler-work-space'; @@ -136,9 +142,9 @@ const SCHEDULER_DATE_TABLE_SCROLLABLE_CLASS = 'dx-scheduler-date-table-scrollabl const SCHEDULER_WORKSPACE_DXPOINTERDOWN_EVENT_NAME = addNamespace(pointerEvents.down, 'dxSchedulerWorkSpace'); const DragEventNames = { - ENTER: addNamespace(dragEventEnter, 'dxSchedulerDateTable'), - DROP: addNamespace(dragEventDrop, 'dxSchedulerDateTable'), - LEAVE: addNamespace(dragEventLeave, 'dxSchedulerDateTable') + ENTER: addNamespace(dragEventEnter, 'dxSchedulerDateTable'), + DROP: addNamespace(dragEventDrop, 'dxSchedulerDateTable'), + LEAVE: addNamespace(dragEventLeave, 'dxSchedulerDateTable'), }; const SCHEDULER_CELL_DXCLICK_EVENT_NAME = addNamespace(clickEventName, 'dxSchedulerDateTable'); @@ -161,3071 +167,3191 @@ const CELL_SELECTOR = `.${DATE_TABLE_CELL_CLASS}, .${ALL_DAY_TABLE_CELL_CLASS}`; const CELL_INDEX_CALCULATION_EPSILON = 0.05; class SchedulerWorkSpace extends WidgetObserver { - get viewDataProvider() { - if(!this._viewDataProvider) { - this._viewDataProvider = new ViewDataProvider(this.type); - } - return this._viewDataProvider; - } + _viewDataProvider: any; - get cache() { - if(!this._cache) { - this._cache = new Cache(); - } + _cache: any; - return this._cache; - } + _cellsSelectionState: any; - get cellsSelectionState() { - if(!this._cellsSelectionState) { - this._cellsSelectionState = new CellsSelectionState(this.viewDataProvider); + _cellsSelectionController: any; - const selectedCellsOption = this.option('selectedCellData'); + _dateTableScrollable: any; - if(selectedCellsOption?.length > 0) { - const validSelectedCells = selectedCellsOption.map((selectedCell) => { - const groups = selectedCell.groups; + _selectionChangedAction: any; - if(!groups || this._getGroupCount() === 0) { - return { - ...selectedCell, - groupIndex: 0, - }; - } + _isCellClick: any; - const groupIndex = this._getGroupIndexByResourceId(groups); + _contextMenuHandled: any; - return { - ...selectedCell, - groupIndex, - }; - }); + _disposed: any; - this._cellsSelectionState.setSelectedCellsByData(validSelectedCells); - } - } + _getToday: any; - return this._cellsSelectionState; - } + _$allDayPanel: any; - get cellsSelectionController() { - if(!this._cellsSelectionController) { - this._cellsSelectionController = new CellsSelectionController(); - } + _$allDayTitle: any; - return this._cellsSelectionController; - } + _$headerPanelEmptyCell: any; - get isAllDayPanelVisible() { - return this._isShowAllDayPanel() && this.supportAllDayRow(); - } + _groupedStrategy: any; - get verticalGroupTableClass() { return WORKSPACE_VERTICAL_GROUP_TABLE_CLASS; } + virtualScrollingDispatcher: any; - get viewDirection() { return 'vertical'; } + _scrollSync: any; - get renovatedHeaderPanelComponent() { return dxrDateHeader; } + _$headerPanel: any; - get timeZoneCalculator() { - return this.option('timeZoneCalculator'); - } + _$dateTable: any; - get isDefaultDraggingMode() { - return this.option('draggingMode') === 'default'; - } + _$allDayTable: any; - _supportedKeys() { - const clickHandler = function(e) { - e.preventDefault(); - e.stopPropagation(); + renderer: any; - const selectedCells = this.cellsSelectionState.getSelectedCells(); + _createAction: any; - if(selectedCells?.length) { - const selectedCellsElement = selectedCells.map((cellData) => { - return this._getCellByData(cellData); - }).filter(cell => !!cell); + _cellClickAction: any; - e.target = selectedCellsElement; - this._showPopup = true; + _createActionByOption: any; - this._cellClickAction({ event: e, cellElement: $(selectedCellsElement), cellData: selectedCells[0] }); - } - }; - const onArrowPressed = (e, key) => { - e.preventDefault(); - e.stopPropagation(); - - const focusedCellData = this.cellsSelectionState.focusedCell?.cellData; - - if(focusedCellData) { - const isAllDayPanelCell = focusedCellData.allDay && !this._isVerticalGroupedWorkSpace(); - const isMultiSelection = e.shiftKey; - const isMultiSelectionAllowed = this.option('allowMultipleCellSelection'); - const isRTL = this._isRTL(); - const groupCount = this._getGroupCount(); - const isGroupedByDate = this.isGroupedByDate(); - const isHorizontalGrouping = this._isHorizontalGroupedWorkSpace(); - const focusedCellPosition = this.viewDataProvider.findCellPositionInMap({ - ...focusedCellData, - isAllDay: focusedCellData.allDay, - }); - - const edgeIndices = isHorizontalGrouping && isMultiSelection && !isGroupedByDate - ? this.viewDataProvider.getGroupEdgeIndices(focusedCellData.groupIndex, isAllDayPanelCell) - : this.viewDataProvider.getViewEdgeIndices(isAllDayPanelCell); - - const nextCellData = this.cellsSelectionController.handleArrowClick({ - focusedCellPosition, - edgeIndices, - isRTL, - isGroupedByDate, - groupCount, - isMultiSelection, - isMultiSelectionAllowed, - viewType: this.type, - key, - getCellDataByPosition: this.viewDataProvider.getCellData.bind(this.viewDataProvider), - isAllDayPanelCell, - focusedCellData, - }); - - this._processNextSelectedCell( - nextCellData, - focusedCellData, - isMultiSelection && isMultiSelectionAllowed, - ); - } - }; + _showPopup: any; - return extend(super._supportedKeys(), { - enter: clickHandler, - space: clickHandler, - downArrow: (e) => { - onArrowPressed(e, 'down'); - }, - upArrow: (e) => { - onArrowPressed(e, 'up'); - }, - rightArrow: (e) => { - onArrowPressed(e, 'right'); - }, - leftArrow: (e) => { - onArrowPressed(e, 'left'); - } - }); - } + NAME: any; - _isRTL() { - return this.option('rtlEnabled'); - } + _contextMenuAction: any; - _moveToCell($cell, isMultiSelection) { - if(!isDefined($cell) || !$cell.length) { - return undefined; - } + _$groupTable: any; - const isMultiSelectionAllowed = this.option('allowMultipleCellSelection'); - const currentCellData = this._getFullCellData($cell); - const focusedCellData = this.cellsSelectionState.focusedCell.cellData; - - const nextFocusedCellData = this.cellsSelectionController.moveToCell({ - isMultiSelection, - isMultiSelectionAllowed, - currentCellData, - focusedCellData, - isVirtualCell: $cell.hasClass(VIRTUAL_CELL_CLASS), - }); + _$thead: any; - this._processNextSelectedCell( - nextFocusedCellData, - focusedCellData, - isMultiSelectionAllowed && isMultiSelection, - ); - } + _headerScrollable: any; - _processNextSelectedCell(nextCellData, focusedCellData, isMultiSelection) { - const nextCellPosition = this.viewDataProvider.findCellPositionInMap({ - startDate: nextCellData.startDate, - groupIndex: nextCellData.groupIndex, - isAllDay: nextCellData.allDay, - index: nextCellData.index, - }); + _sidebarScrollable: any; - if(!this.viewDataProvider.isSameCell(focusedCellData, nextCellData)) { - const $cell = nextCellData.allDay && !this._isVerticalGroupedWorkSpace() - ? this._dom_getAllDayPanelCell(nextCellPosition.columnIndex) - : this._dom_getDateCell(nextCellPosition); - const isNextCellAllDay = nextCellData.allDay; + preventDefaultDragging: any; - this._setSelectedCellsStateAndUpdateSelection( - isNextCellAllDay, nextCellPosition, isMultiSelection, $cell, - ); + dragBehavior: any; - this._dateTableScrollable.scrollToElement($cell); - } - } + _$dateTableContainer: any; - _setSelectedCellsStateAndUpdateSelection(isAllDay, cellPosition, isMultiSelection, $nextFocusedCell) { - const nextCellCoordinates = { - rowIndex: cellPosition.rowIndex, - columnIndex: cellPosition.columnIndex, - allDay: isAllDay, - }; + _$timePanel: any; - this.cellsSelectionState.setFocusedCell( - nextCellCoordinates.rowIndex, - nextCellCoordinates.columnIndex, - isAllDay, - ); + _activeStateUnit: any; - if(isMultiSelection) { - this.cellsSelectionState.setSelectedCells(nextCellCoordinates); - } else { - this.cellsSelectionState.setSelectedCells(nextCellCoordinates, nextCellCoordinates); - } + positionHelper!: PositionHelper; - this.updateCellsSelection(); - this._updateSelectedCellDataOption(this.cellsSelectionState.getSelectedCells(), $nextFocusedCell); - } + _$headerPanelContainer: any; - _hasAllDayClass($cell) { - return $cell.hasClass(ALL_DAY_TABLE_CELL_CLASS); - } + _$headerTablesContainer: any; - _focusInHandler(e) { - if($(e.target).is(this._focusTarget()) && this._isCellClick !== false) { - delete this._isCellClick; - delete this._contextMenuHandled; - super._focusInHandler.apply(this, arguments); + _$fixedContainer: any; - this.cellsSelectionState.restoreSelectedAndFocusedCells(); + _$allDayContainer: any; - if(!this.cellsSelectionState.focusedCell) { - const cellCoordinates = { - columnIndex: 0, - rowIndex: 0, - allDay: this._isVerticalGroupedWorkSpace() && this.isAllDayPanelVisible, - }; - this.cellsSelectionState.setFocusedCell( - cellCoordinates.rowIndex, - cellCoordinates.columnIndex, - cellCoordinates.allDay, - ); - this.cellsSelectionState.setSelectedCells(cellCoordinates, cellCoordinates); - } + _$dateTableScrollableContent: any; - this.updateCellsSelection(); - this._updateSelectedCellDataOption(this.cellsSelectionState.getSelectedCells()); - } - } + _$sidebarScrollableContent: any; - _focusOutHandler() { - super._focusOutHandler.apply(this, arguments); + _allDayTitles!: any[]; - if(!this._contextMenuHandled && !this._disposed) { - this.cellsSelectionState.releaseSelectedAndFocusedCells(); + _allDayTables!: any[]; - this.viewDataProvider.updateViewData(this.generateRenderOptions()); - this.updateCellsSelection(); - } - } + _allDayPanels!: any[]; - _focusTarget() { - return this.$element(); - } + _$flexContainer: any; - _isVerticalGroupedWorkSpace() { // TODO move to the Model - return !!this.option('groups').length && this.option('groupOrientation') === 'vertical'; - } + _shader: any; - _isHorizontalGroupedWorkSpace() { - return !!this.option('groups').length && this.option('groupOrientation') === 'horizontal'; - } + _$sidebarTable: any; - _isWorkSpaceWithCount() { - return this.option('intervalCount') > 1; - } + _interval: any; - _isWorkspaceWithOddCells() { - return this.option('hoursInterval') === 0.5 && !this.isVirtualScrolling(); - } + renovatedAllDayPanel: any; - _getRealGroupOrientation() { - return this._isVerticalGroupedWorkSpace() - ? 'vertical' - : 'horizontal'; - } + renovatedDateTable: any; - createRAllDayPanelElements() { - this._$allDayPanel = $('
').addClass(ALL_DAY_PANEL_CLASS); - this._$allDayTitle = $('
').appendTo(this._$headerPanelEmptyCell); - } + renovatedTimePanel: any; - _dateTableScrollableConfig() { - let config = { - useKeyboard: false, - bounceEnabled: false, - updateManually: true, - onScroll: () => { - this._groupedStrategy.cache?.clear(); - }, - }; - if(this._needCreateCrossScrolling()) { - config = extend(config, this._createCrossScrollingConfig(config)); - } - if(this.isVirtualScrolling() - && (this.virtualScrollingDispatcher.horizontalScrollingAllowed - || this.virtualScrollingDispatcher.height)) { - const currentOnScroll = config.onScroll; - config = { - ...config, - onScroll: (e) => { + renovatedGroupPanel: any; - currentOnScroll?.(e); + renovatedHeaderPanel: any; - this.virtualScrollingDispatcher.handleOnScrollEvent(e?.scrollOffset); - }, - }; - } + // eslint-disable-next-line @typescript-eslint/class-literal-property-style + get type(): string { + return ''; + } - return config; + get viewDataProvider() { + if (!this._viewDataProvider) { + this._viewDataProvider = new ViewDataProvider(this.type); } + return this._viewDataProvider; + } - _createCrossScrollingConfig({ onScroll }) { - return { - direction: 'both', - onScroll: (event) => { - onScroll?.(); - - this._scrollSync.sidebar({ top: event.scrollOffset.top }); - this._scrollSync.header({ left: event.scrollOffset.left }); - }, - onEnd: () => { - this.option('onScrollEnd')(); - } - }; + get cache() { + if (!this._cache) { + this._cache = new Cache(); } - _headerScrollableConfig() { - return { - useKeyboard: false, - showScrollbar: 'never', - direction: 'horizontal', - useNative: false, - updateManually: true, - bounceEnabled: false, - onScroll: (event) => { - this._scrollSync.dateTable({ left: event.scrollOffset.left }); - } - }; - } + return this._cache; + } - _visibilityChanged(visible) { - this.cache.clear(); + get cellsSelectionState() { + if (!this._cellsSelectionState) { + this._cellsSelectionState = new CellsSelectionState(this.viewDataProvider); - if(visible) { - this._updateGroupTableHeight(); - } + const selectedCellsOption: any = this.option('selectedCellData'); - if(visible && this._needCreateCrossScrolling()) { - this._setTableSizes(); - } - } + if (selectedCellsOption?.length > 0) { + const validSelectedCells = selectedCellsOption.map((selectedCell) => { + const { groups } = selectedCell; - _setTableSizes() { - this.cache.clear(); - this._attachTableClasses(); + if (!groups || this._getGroupCount() === 0) { + return { + ...selectedCell, + groupIndex: 0, + }; + } - let cellWidth = this.getCellWidth(); + const groupIndex = this._getGroupIndexByResourceId(groups); - if(cellWidth < this.getCellMinWidth()) { - cellWidth = this.getCellMinWidth(); - } + return { + ...selectedCell, + groupIndex, + }; + }); - const minWidth = this.getWorkSpaceMinWidth(); + this._cellsSelectionState.setSelectedCellsByData(validSelectedCells); + } + } - const groupCount = this._getGroupCount(); - const totalCellCount = this._getTotalCellCount(groupCount); + return this._cellsSelectionState; + } - let width = cellWidth * totalCellCount; + get cellsSelectionController() { + if (!this._cellsSelectionController) { + this._cellsSelectionController = new CellsSelectionController(); + } - if(width < minWidth) { - width = minWidth; - } + return this._cellsSelectionController; + } - setWidth(this._$headerPanel, width); - setWidth(this._$dateTable, width); - if(this._$allDayTable) { - setWidth(this._$allDayTable, width); - } + get isAllDayPanelVisible() { + return this._isShowAllDayPanel() && this.supportAllDayRow(); + } - this._attachHeaderTableClasses(); + get verticalGroupTableClass() { return WORKSPACE_VERTICAL_GROUP_TABLE_CLASS; } - this._updateGroupTableHeight(); + readonly viewDirection: 'vertical' | 'horizontal' = 'vertical'; - this._updateScrollable(); - } + get renovatedHeaderPanelComponent() { return dxrDateHeader; } - getWorkSpaceMinWidth() { - return this._groupedStrategy.getWorkSpaceMinWidth(); - } + get timeZoneCalculator(): any { + return this.option('timeZoneCalculator'); + } - _dimensionChanged() { - if(!this._isVisible()) { - return; - } + get isDefaultDraggingMode() { + return this.option('draggingMode') === 'default'; + } - if(this.option('crossScrollingEnabled')) { - this._setTableSizes(); - } + _supportedKeys() { + const clickHandler = function (e) { + e.preventDefault(); + e.stopPropagation(); - this.updateHeaderEmptyCellWidth(); + const selectedCells = this.cellsSelectionState.getSelectedCells(); - this._updateScrollable(); + if (selectedCells?.length) { + const selectedCellsElement = selectedCells.map((cellData) => this._getCellByData(cellData)).filter((cell) => !!cell); - this.cache.clear(); - } + e.target = selectedCellsElement; + this._showPopup = true; - _needCreateCrossScrolling() { - return this.option('crossScrollingEnabled'); - } + this._cellClickAction({ event: e, cellElement: $(selectedCellsElement), cellData: selectedCells[0] }); + } + }; + const onArrowPressed = (e, key) => { + e.preventDefault(); + e.stopPropagation(); - _getElementClass() { return noop(); } + const focusedCellData = this.cellsSelectionState.focusedCell?.cellData; - _getRowCount() { - return this.viewDataProvider.getRowCount({ - intervalCount: this.option('intervalCount'), - currentDate: this.option('currentDate'), - viewType: this.type, - hoursInterval: this.option('hoursInterval'), - startDayHour: this.option('startDayHour'), - endDayHour: this.option('endDayHour'), + if (focusedCellData) { + const isAllDayPanelCell = focusedCellData.allDay && !this._isVerticalGroupedWorkSpace(); + const isMultiSelection = e.shiftKey; + const isMultiSelectionAllowed = this.option('allowMultipleCellSelection'); + const isRTL = this._isRTL(); + const groupCount = this._getGroupCount(); + const isGroupedByDate = this.isGroupedByDate(); + const isHorizontalGrouping = this._isHorizontalGroupedWorkSpace(); + const focusedCellPosition = this.viewDataProvider.findCellPositionInMap({ + ...focusedCellData, + isAllDay: focusedCellData.allDay, }); - } - _getCellCount() { - return this.viewDataProvider.getCellCount({ - intervalCount: this.option('intervalCount'), - currentDate: this.option('currentDate'), - viewType: this.type, - hoursInterval: this.option('hoursInterval'), - startDayHour: this.option('startDayHour'), - endDayHour: this.option('endDayHour'), + const edgeIndices = isHorizontalGrouping && isMultiSelection && !isGroupedByDate + ? this.viewDataProvider.getGroupEdgeIndices(focusedCellData.groupIndex, isAllDayPanelCell) + : this.viewDataProvider.getViewEdgeIndices(isAllDayPanelCell); + + const nextCellData = this.cellsSelectionController.handleArrowClick({ + focusedCellPosition, + edgeIndices, + isRTL, + isGroupedByDate, + groupCount, + isMultiSelection, + isMultiSelectionAllowed, + viewType: this.type, + key, + getCellDataByPosition: this.viewDataProvider.getCellData.bind(this.viewDataProvider), + isAllDayPanelCell, + focusedCellData, }); - } - isRenovatedRender() { - return this.renovatedRenderSupported() && this.option('renovateRender'); - } + this._processNextSelectedCell( + nextCellData, + focusedCellData, + isMultiSelection && isMultiSelectionAllowed, + ); + } + }; - _isVirtualModeOn() { - return this.option('scrolling.mode') === 'virtual'; - } + // @ts-expect-error + return extend(super._supportedKeys(), { + enter: clickHandler, + space: clickHandler, + downArrow: (e) => { + onArrowPressed(e, 'down'); + }, + upArrow: (e) => { + onArrowPressed(e, 'up'); + }, + rightArrow: (e) => { + onArrowPressed(e, 'right'); + }, + leftArrow: (e) => { + onArrowPressed(e, 'left'); + }, + }); + } + + _isRTL() { + return this.option('rtlEnabled'); + } + + _moveToCell($cell, isMultiSelection) { + if (!isDefined($cell) || !$cell.length) { + return; + } + + const isMultiSelectionAllowed = this.option('allowMultipleCellSelection'); + const currentCellData = this._getFullCellData($cell); + const focusedCellData = this.cellsSelectionState.focusedCell.cellData; + + const nextFocusedCellData = this.cellsSelectionController.moveToCell({ + isMultiSelection, + isMultiSelectionAllowed, + currentCellData, + focusedCellData, + isVirtualCell: $cell.hasClass(VIRTUAL_CELL_CLASS), + }); + + this._processNextSelectedCell( + nextFocusedCellData, + focusedCellData, + isMultiSelectionAllowed && isMultiSelection, + ); + } + + _processNextSelectedCell(nextCellData, focusedCellData, isMultiSelection) { + const nextCellPosition = this.viewDataProvider.findCellPositionInMap({ + startDate: nextCellData.startDate, + groupIndex: nextCellData.groupIndex, + isAllDay: nextCellData.allDay, + index: nextCellData.index, + }); + + if (!this.viewDataProvider.isSameCell(focusedCellData, nextCellData)) { + const $cell = nextCellData.allDay && !this._isVerticalGroupedWorkSpace() + ? this._dom_getAllDayPanelCell(nextCellPosition.columnIndex) + : this._dom_getDateCell(nextCellPosition); + const isNextCellAllDay = nextCellData.allDay; + + this._setSelectedCellsStateAndUpdateSelection(isNextCellAllDay, nextCellPosition, isMultiSelection, $cell); + + this._dateTableScrollable.scrollToElement($cell); + } + } + + _setSelectedCellsStateAndUpdateSelection(isAllDay, cellPosition, isMultiSelection, $nextFocusedCell) { + const nextCellCoordinates = { + rowIndex: cellPosition.rowIndex, + columnIndex: cellPosition.columnIndex, + allDay: isAllDay, + }; - isVirtualScrolling() { - return this.isRenovatedRender() && this._isVirtualModeOn(); + this.cellsSelectionState.setFocusedCell( + nextCellCoordinates.rowIndex, + nextCellCoordinates.columnIndex, + isAllDay, + ); + + if (isMultiSelection) { + this.cellsSelectionState.setSelectedCells(nextCellCoordinates); + } else { + this.cellsSelectionState.setSelectedCells(nextCellCoordinates, nextCellCoordinates); } - _initVirtualScrolling() { - if(this.virtualScrollingDispatcher) { - this.virtualScrollingDispatcher.dispose(); - this.virtualScrollingDispatcher = null; - } + this.updateCellsSelection(); + this._updateSelectedCellDataOption(this.cellsSelectionState.getSelectedCells(), $nextFocusedCell); + } - this.virtualScrollingDispatcher = new VirtualScrollingDispatcher(this._getVirtualScrollingDispatcherOptions()); - this.virtualScrollingDispatcher.attachScrollableEvents(); - this.renderer = new VirtualScrollingRenderer(this); - } + _hasAllDayClass($cell) { + return $cell.hasClass(ALL_DAY_TABLE_CELL_CLASS); + } - onDataSourceChanged() { - } + _focusInHandler(e) { + if ($(e.target).is(this._focusTarget() as any) && this._isCellClick !== false) { + delete this._isCellClick; + delete this._contextMenuHandled; + // @ts-expect-error + super._focusInHandler.apply(this, arguments); - isGroupedAllDayPanel() { - return calculateIsGroupedAllDayPanel( - this.option('groups'), - this.option('groupOrientation'), - this.isAllDayPanelVisible, + this.cellsSelectionState.restoreSelectedAndFocusedCells(); + + if (!this.cellsSelectionState.focusedCell) { + const cellCoordinates = { + columnIndex: 0, + rowIndex: 0, + allDay: this._isVerticalGroupedWorkSpace() && this.isAllDayPanelVisible, + }; + this.cellsSelectionState.setFocusedCell( + cellCoordinates.rowIndex, + cellCoordinates.columnIndex, + cellCoordinates.allDay, ); + this.cellsSelectionState.setSelectedCells(cellCoordinates, cellCoordinates); + } + + this.updateCellsSelection(); + this._updateSelectedCellDataOption(this.cellsSelectionState.getSelectedCells()); } + } - generateRenderOptions(isProvideVirtualCellsWidth) { - const groupCount = this._getGroupCount(); + _focusOutHandler() { + // @ts-expect-error + super._focusOutHandler.apply(this, arguments); - const groupOrientation = groupCount > 0 - ? this.option('groupOrientation') - : this._getDefaultGroupStrategy(); - - const options = { - groupByDate: this.option('groupByDate'), - startRowIndex: 0, - startCellIndex: 0, - groupOrientation, - today: this._getToday?.(), - groups: this.option('groups'), - isProvideVirtualCellsWidth, - isAllDayPanelVisible: this.isAllDayPanelVisible, - selectedCells: this.cellsSelectionState.getSelectedCells(), - focusedCell: this.cellsSelectionState.focusedCell, - headerCellTextFormat: this._getFormat(), - getDateForHeaderText: (_, date) => date, - startDayHour: this.option('startDayHour'), - endDayHour: this.option('endDayHour'), - cellDuration: this.getCellDuration(), - viewType: this.type, - intervalCount: this.option('intervalCount'), - hoursInterval: this.option('hoursInterval'), - currentDate: this.option('currentDate'), - startDate: this.option('startDate'), - firstDayOfWeek: this.option('firstDayOfWeek'), - - ...this.virtualScrollingDispatcher.getRenderState(), - }; + if (!this._contextMenuHandled && !this._disposed) { + this.cellsSelectionState.releaseSelectedAndFocusedCells(); - return options; + this.viewDataProvider.updateViewData(this.generateRenderOptions()); + this.updateCellsSelection(); } + } - renovatedRenderSupported() { return true; } + _focusTarget() { + return this.$element(); + } - _updateGroupTableHeight() { - if(this._isVerticalGroupedWorkSpace() && hasWindow()) { - this._setHorizontalGroupHeaderCellsHeight(); - } - } + _isVerticalGroupedWorkSpace() { // TODO move to the Model + return !!this.option('groups')?.length && this.option('groupOrientation') === 'vertical'; + } - updateHeaderEmptyCellWidth() { - if(hasWindow() && this._isRenderHeaderPanelEmptyCell()) { - const timePanelWidth = this.getTimePanelWidth(); - const groupPanelWidth = this.getGroupTableWidth(); + _isHorizontalGroupedWorkSpace() { + return !!this.option('groups')?.length && this.option('groupOrientation') === 'horizontal'; + } - this._$headerPanelEmptyCell.css('width', timePanelWidth + groupPanelWidth); - } - } + _isWorkSpaceWithCount() { + return this.option('intervalCount') as any > 1; + } - _isGroupsSpecified(resources) { - return this.option('groups').length && resources; - } + _isWorkspaceWithOddCells() { + return this.option('hoursInterval') === 0.5 && !this.isVirtualScrolling(); + } - _getGroupIndexByResourceId(id) { - const groups = this.option('groups'); - const resourceTree = createResourcesTree(groups); + _getRealGroupOrientation() { + return this._isVerticalGroupedWorkSpace() + ? 'vertical' + : 'horizontal'; + } - if(!resourceTree.length) return 0; + createRAllDayPanelElements() { + this._$allDayPanel = $('
').addClass(ALL_DAY_PANEL_CLASS); + this._$allDayTitle = $('
').appendTo(this._$headerPanelEmptyCell); + } - return this._getGroupIndexRecursively(resourceTree, id); + _dateTableScrollableConfig() { + let config: any = { + useKeyboard: false, + bounceEnabled: false, + updateManually: true, + onScroll: () => { + this._groupedStrategy.cache?.clear(); + }, + }; + if (this._needCreateCrossScrolling()) { + config = extend(config, this._createCrossScrollingConfig(config)); } + if (this.isVirtualScrolling() + && (this.virtualScrollingDispatcher.horizontalScrollingAllowed + || this.virtualScrollingDispatcher.height)) { + const currentOnScroll = config.onScroll; + config = { + ...config, + onScroll: (e) => { + currentOnScroll?.(e); - _getGroupIndexRecursively(resourceTree, id) { - const currentKey = resourceTree[0].name; - const currentValue = id[currentKey]; + this.virtualScrollingDispatcher.handleOnScrollEvent(e?.scrollOffset); + }, + }; + } - return resourceTree.reduce((prevIndex, { leafIndex, value, children }) => { - const areValuesEqual = currentValue === value; - if(areValuesEqual && leafIndex !== undefined) { - return leafIndex; - } - if(areValuesEqual) { - return this._getGroupIndexRecursively(children, id); - } + return config; + } - return prevIndex; - }, 0); - } + _createCrossScrollingConfig({ onScroll }): any { + return { + direction: 'both', + onScroll: (event) => { + onScroll?.(); + + this._scrollSync.sidebar({ top: event.scrollOffset.top }); + this._scrollSync.header({ left: event.scrollOffset.left }); + }, + onEnd: () => { + (this.option('onScrollEnd') as any)(); + }, + }; + } - _getViewStartByOptions() { - return getViewStartByOptions( - this.option('startDate'), - this.option('currentDate'), - this._getIntervalDuration(), - this.option('startDate') ? this._calculateViewStartDate() : undefined, - ); - } + _headerScrollableConfig() { + return { + useKeyboard: false, + showScrollbar: 'never', + direction: 'horizontal', + useNative: false, + updateManually: true, + bounceEnabled: false, + onScroll: (event) => { + this._scrollSync.dateTable({ left: event.scrollOffset.left }); + }, + }; + } - _getIntervalDuration() { - return this.viewDataProvider.getIntervalDuration(this.option('intervalCount')); - } + _visibilityChanged(visible) { + this.cache.clear(); - _getHeaderDate() { - return this.getStartViewDate(); + if (visible) { + this._updateGroupTableHeight(); } - _calculateViewStartDate() { - return calculateViewStartDate(this.option('startDate')); + if (visible && this._needCreateCrossScrolling()) { + this._setTableSizes(); } + } - _firstDayOfWeek() { - return this.viewDataProvider.getFirstDayOfWeek(this.option('firstDayOfWeek')); - } + _setTableSizes() { + this.cache.clear(); + this._attachTableClasses(); - _attachEvents() { - this._createSelectionChangedAction(); - this._attachClickEvent(); - this._attachContextMenuEvent(); + let cellWidth = this.getCellWidth(); + + if (cellWidth < this.getCellMinWidth()) { + cellWidth = this.getCellMinWidth(); } - _attachClickEvent() { - const that = this; - const pointerDownAction = this._createAction(function(e) { - that._pointerDownHandler(e.event); - }); + const minWidth = this.getWorkSpaceMinWidth(); - this._createCellClickAction(); + const groupCount = this._getGroupCount(); + const totalCellCount = this._getTotalCellCount(groupCount); - const cellSelector = '.' + DATE_TABLE_CELL_CLASS + ',.' + ALL_DAY_TABLE_CELL_CLASS; - const $element = this.$element(); - - eventsEngine.off($element, SCHEDULER_WORKSPACE_DXPOINTERDOWN_EVENT_NAME); - eventsEngine.off($element, SCHEDULER_CELL_DXCLICK_EVENT_NAME); - eventsEngine.on($element, SCHEDULER_WORKSPACE_DXPOINTERDOWN_EVENT_NAME, function(e) { - if(isMouseEvent(e) && e.which > 1) { - e.preventDefault(); - return; - } - pointerDownAction({ event: e }); - }); - eventsEngine.on($element, SCHEDULER_CELL_DXCLICK_EVENT_NAME, cellSelector, function(e) { - const $cell = $(e.target); - that._cellClickAction({ event: e, cellElement: getPublicElement($cell), cellData: that.getCellData($cell) }); - }); + let width = cellWidth * totalCellCount; + + if (width < minWidth) { + width = minWidth; } - _createCellClickAction() { - this._cellClickAction = this._createActionByOption('onCellClick', { - afterExecute: (e) => this._cellClickHandler(e.args[0].event) - }); + setWidth(this._$headerPanel, width); + setWidth(this._$dateTable, width); + if (this._$allDayTable) { + setWidth(this._$allDayTable, width); } - _createSelectionChangedAction() { - this._selectionChangedAction = this._createActionByOption('onSelectionChanged'); + this._attachHeaderTableClasses(); + + this._updateGroupTableHeight(); + + this._updateScrollable(); + } + + getWorkSpaceMinWidth() { + return this._groupedStrategy.getWorkSpaceMinWidth(); + } + + _dimensionChanged() { + // NOTE: It's a base widget method. Be careful :) + // @ts-expect-error + if (!this._isVisible()) { + return; } - _cellClickHandler() { - if(this._showPopup) { - delete this._showPopup; - this._handleSelectedCellsClick(); - } + if (this.option('crossScrollingEnabled')) { + this._setTableSizes(); } - _pointerDownHandler(e) { - const $target = $(e.target); + this.updateHeaderEmptyCellWidth(); - if(!$target.hasClass(DATE_TABLE_CELL_CLASS) && !$target.hasClass(ALL_DAY_TABLE_CELL_CLASS)) { - this._isCellClick = false; - return; - } + this._updateScrollable(); - this._isCellClick = true; - if($target.hasClass(DATE_TABLE_FOCUSED_CELL_CLASS)) { - this._showPopup = true; - } else { - const cellCoordinates = this._getCoordinatesByCell($target); - const isAllDayCell = this._hasAllDayClass($target); - this._setSelectedCellsStateAndUpdateSelection( - isAllDayCell, cellCoordinates, false, $target, - ); - } - } + this.cache.clear(); + } - _handleSelectedCellsClick() { - const selectedCells = this.cellsSelectionState.getSelectedCells(); + _needCreateCrossScrolling() { + return this.option('crossScrollingEnabled'); + } - const firstCellData = selectedCells[0]; - const lastCellData = selectedCells[selectedCells.length - 1]; + _getElementClass() { return noop(); } - const result = { - startDate: firstCellData.startDate, - endDate: lastCellData.endDate - }; + _getRowCount() { + return this.viewDataProvider.getRowCount({ + intervalCount: this.option('intervalCount'), + currentDate: this.option('currentDate'), + viewType: this.type, + hoursInterval: this.option('hoursInterval'), + startDayHour: this.option('startDayHour'), + endDayHour: this.option('endDayHour'), + }); + } - if(lastCellData.allDay !== undefined) { - result.allDay = lastCellData.allDay; - } + _getCellCount() { + return this.viewDataProvider.getCellCount({ + intervalCount: this.option('intervalCount'), + currentDate: this.option('currentDate'), + viewType: this.type, + hoursInterval: this.option('hoursInterval'), + startDayHour: this.option('startDayHour'), + endDayHour: this.option('endDayHour'), + }); + } - this.option('onSelectedCellsClick')(result, lastCellData.groups); - } + isRenovatedRender() { + return this.renovatedRenderSupported() && this.option('renovateRender'); + } - _attachContextMenuEvent() { - this._createContextMenuAction(); + _isVirtualModeOn() { + return this.option('scrolling.mode') === 'virtual'; + } - const cellSelector = '.' + DATE_TABLE_CELL_CLASS + ',.' + ALL_DAY_TABLE_CELL_CLASS; - const $element = this.$element(); - const eventName = addNamespace(contextMenuEventName, this.NAME); + isVirtualScrolling() { + return this.isRenovatedRender() && this._isVirtualModeOn(); + } - eventsEngine.off($element, eventName, cellSelector); - eventsEngine.on($element, eventName, cellSelector, this._contextMenuHandler.bind(this)); + _initVirtualScrolling() { + if (this.virtualScrollingDispatcher) { + this.virtualScrollingDispatcher.dispose(); + this.virtualScrollingDispatcher = null; } - _contextMenuHandler(e) { - const $cell = $(e.target); - this._contextMenuAction({ event: e, cellElement: getPublicElement($cell), cellData: this.getCellData($cell) }); - this._contextMenuHandled = true; - } + this.virtualScrollingDispatcher = new VirtualScrollingDispatcher(this._getVirtualScrollingDispatcherOptions()); + this.virtualScrollingDispatcher.attachScrollableEvents(); + this.renderer = new VirtualScrollingRenderer(this); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + onDataSourceChanged(argument?: any) { + } + + isGroupedAllDayPanel() { + return calculateIsGroupedAllDayPanel( + this.option('groups') as any, + this.option('groupOrientation') as any, + this.isAllDayPanelVisible as any, + ); + } + + generateRenderOptions(isProvideVirtualCellsWidth?: any) { + const groupCount = this._getGroupCount(); + + const groupOrientation = groupCount > 0 + ? this.option('groupOrientation') + : this._getDefaultGroupStrategy(); + + const options = { + groupByDate: this.option('groupByDate'), + startRowIndex: 0, + startCellIndex: 0, + groupOrientation, + today: this._getToday?.(), + groups: this.option('groups'), + isProvideVirtualCellsWidth, + isAllDayPanelVisible: this.isAllDayPanelVisible, + selectedCells: this.cellsSelectionState.getSelectedCells(), + focusedCell: this.cellsSelectionState.focusedCell, + headerCellTextFormat: this._getFormat(), + getDateForHeaderText: (_, date) => date, + startDayHour: this.option('startDayHour'), + endDayHour: this.option('endDayHour'), + cellDuration: this.getCellDuration(), + viewType: this.type, + intervalCount: this.option('intervalCount'), + hoursInterval: this.option('hoursInterval'), + currentDate: this.option('currentDate'), + startDate: this.option('startDate'), + firstDayOfWeek: this.option('firstDayOfWeek'), + + ...this.virtualScrollingDispatcher.getRenderState(), + }; + + return options; + } + + renovatedRenderSupported() { return true; } + + _updateGroupTableHeight() { + if (this._isVerticalGroupedWorkSpace() && hasWindow()) { + this._setHorizontalGroupHeaderCellsHeight(); + } + } + + updateHeaderEmptyCellWidth() { + if (hasWindow() && this._isRenderHeaderPanelEmptyCell()) { + const timePanelWidth = this.getTimePanelWidth(); + const groupPanelWidth = this.getGroupTableWidth(); + + this._$headerPanelEmptyCell.css('width', timePanelWidth + groupPanelWidth); + } + } + + _isGroupsSpecified(resources) { + return this.option('groups')?.length && resources; + } + + _getGroupIndexByResourceId(id) { + const groups = this.option('groups'); + const resourceTree = createResourcesTree(groups); + + if (!resourceTree.length) return 0; + + return this._getGroupIndexRecursively(resourceTree, id); + } + + _getGroupIndexRecursively(resourceTree, id) { + const currentKey = resourceTree[0].name; + const currentValue = id[currentKey]; + + return resourceTree.reduce((prevIndex, { leafIndex, value, children }) => { + const areValuesEqual = currentValue === value; + if (areValuesEqual && leafIndex !== undefined) { + return leafIndex; + } + if (areValuesEqual) { + return this._getGroupIndexRecursively(children, id); + } + + return prevIndex; + }, 0); + } + + _getViewStartByOptions() { + return getViewStartByOptions( + this.option('startDate') as any, + this.option('currentDate') as any, + this._getIntervalDuration(), + this.option('startDate') ? this._calculateViewStartDate() : undefined, + ); + } + + _getIntervalDuration() { + return this.viewDataProvider.getIntervalDuration(this.option('intervalCount')); + } + + _getHeaderDate() { + return this.getStartViewDate(); + } + + _calculateViewStartDate() { + return calculateViewStartDate(this.option('startDate') as any); + } + + _firstDayOfWeek() { + return this.viewDataProvider.getFirstDayOfWeek(this.option('firstDayOfWeek')); + } + + _attachEvents() { + this._createSelectionChangedAction(); + this._attachClickEvent(); + this._attachContextMenuEvent(); + } + + _attachClickEvent() { + const that = this; + const pointerDownAction = this._createAction((e) => { + that._pointerDownHandler(e.event); + }); + + this._createCellClickAction(); + + 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.on($element, SCHEDULER_WORKSPACE_DXPOINTERDOWN_EVENT_NAME, (e) => { + if (isMouseEvent(e) && e.which > 1) { + e.preventDefault(); + return; + } + 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) }); + }); + } + + _createCellClickAction() { + this._cellClickAction = this._createActionByOption('onCellClick', { + afterExecute: (e) => this._cellClickHandler(e.args[0].event), + }); + } + + _createSelectionChangedAction() { + this._selectionChangedAction = this._createActionByOption('onSelectionChanged'); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _cellClickHandler(argument?: any) { + if (this._showPopup) { + delete this._showPopup; + this._handleSelectedCellsClick(); + } + } + + _pointerDownHandler(e) { + const $target = $(e.target); + + if (!$target.hasClass(DATE_TABLE_CELL_CLASS) && !$target.hasClass(ALL_DAY_TABLE_CELL_CLASS)) { + this._isCellClick = false; + return; + } + + this._isCellClick = true; + if ($target.hasClass(DATE_TABLE_FOCUSED_CELL_CLASS)) { + this._showPopup = true; + } else { + const cellCoordinates = this._getCoordinatesByCell($target); + const isAllDayCell = this._hasAllDayClass($target); + this._setSelectedCellsStateAndUpdateSelection(isAllDayCell, cellCoordinates, false, $target); + } + } + + _handleSelectedCellsClick() { + const selectedCells = this.cellsSelectionState.getSelectedCells(); + + const firstCellData = selectedCells[0]; + const lastCellData = selectedCells[selectedCells.length - 1]; + + const result: any = { + startDate: firstCellData.startDate, + endDate: lastCellData.endDate, + }; - _createContextMenuAction() { - this._contextMenuAction = this._createActionByOption('onCellContextMenu'); + if (lastCellData.allDay !== undefined) { + result.allDay = lastCellData.allDay; } - _getGroupHeaderContainer() { - if(this._isVerticalGroupedWorkSpace()) { - return this._$groupTable; - } + (this.option('onSelectedCellsClick') as any)(result, lastCellData.groups); + } - return this._$thead; - } + _attachContextMenuEvent() { + this._createContextMenuAction(); - _getDateHeaderContainer() { - return this._$thead; - } + const cellSelector = `.${DATE_TABLE_CELL_CLASS},.${ALL_DAY_TABLE_CELL_CLASS}`; + const $element = this.$element(); + const eventName = addNamespace(contextMenuEventName, this.NAME); - _getCalculateHeaderCellRepeatCount() { - return this._groupedStrategy.calculateHeaderCellRepeatCount(); - } + eventsEngine.off($element, eventName, cellSelector); + eventsEngine.on($element, eventName, cellSelector, this._contextMenuHandler.bind(this)); + } - _updateScrollable() { - this._dateTableScrollable.update(); + _contextMenuHandler(e) { + const $cell = $(e.target); + this._contextMenuAction({ event: e, cellElement: getPublicElement($cell), cellData: this.getCellData($cell) }); + this._contextMenuHandled = true; + } - this._headerScrollable?.update(); - this._sidebarScrollable?.update(); - } + _createContextMenuAction() { + this._contextMenuAction = this._createActionByOption('onCellContextMenu'); + } - _getTimePanelRowCount() { - return this._getCellCountInDay(); + _getGroupHeaderContainer() { + if (this._isVerticalGroupedWorkSpace()) { + return this._$groupTable; } - _getCellCountInDay() { - const hoursInterval = this.option('hoursInterval'); - const startDayHour = this.option('startDayHour'); - const endDayHour = this.option('endDayHour'); + return this._$thead; + } - return this.viewDataProvider.getCellCountInDay(startDayHour, endDayHour, hoursInterval); - } + _getDateHeaderContainer() { + return this._$thead; + } - _getTotalCellCount(groupCount) { - return this._groupedStrategy.getTotalCellCount(groupCount); - } + _getCalculateHeaderCellRepeatCount() { + return this._groupedStrategy.calculateHeaderCellRepeatCount(); + } - _getTotalRowCount(groupCount, includeAllDayPanelRows) { - let result = this._groupedStrategy.getTotalRowCount(groupCount); + _updateScrollable() { + this._dateTableScrollable.update(); - if(includeAllDayPanelRows && this.isAllDayPanelVisible) { - result += groupCount; - } + this._headerScrollable?.update(); + this._sidebarScrollable?.update(); + } - return result; - } + _getTimePanelRowCount() { + return this._getCellCountInDay(); + } - _getGroupIndex(rowIndex, columnIndex) { - return this._groupedStrategy.getGroupIndex(rowIndex, columnIndex); - } + _getCellCountInDay() { + const hoursInterval = this.option('hoursInterval'); + const startDayHour = this.option('startDayHour'); + const endDayHour = this.option('endDayHour'); - calculateEndDate(startDate) { - const viewDataGenerator = this.viewDataProvider.viewDataGenerator; + return this.viewDataProvider.getCellCountInDay(startDayHour, endDayHour, hoursInterval); + } - return viewDataGenerator.calculateEndDate( - startDate, - viewDataGenerator.getInterval(this.option('hoursInterval')), - this.option('endDayHour'), - ); - } + _getTotalCellCount(groupCount) { + return this._groupedStrategy.getTotalCellCount(groupCount); + } + + _getTotalRowCount(groupCount, includeAllDayPanelRows?: any) { + let result = this._groupedStrategy.getTotalRowCount(groupCount); - _getGroupCount() { - return getGroupCount(this.option('groups')); + if (includeAllDayPanelRows && this.isAllDayPanelVisible) { + result += groupCount; } - _attachTablesEvents() { - const element = this.$element(); + return result; + } - this._attachDragEvents(element); - this._attachPointerEvents(element); - } + _getGroupIndex(rowIndex, columnIndex) { + return this._groupedStrategy.getGroupIndex(rowIndex, columnIndex); + } - _detachDragEvents(element) { - eventsEngine.off(element, DragEventNames.ENTER); - eventsEngine.off(element, DragEventNames.LEAVE); - eventsEngine.off(element, DragEventNames.DROP); - } + calculateEndDate(startDate) { + const { viewDataGenerator } = this.viewDataProvider; - _attachDragEvents(element) { - this._detachDragEvents(element); + return viewDataGenerator.calculateEndDate( + startDate, + viewDataGenerator.getInterval(this.option('hoursInterval')), + this.option('endDayHour'), + ); + } - const onDragEnter = e => { - if(!this.preventDefaultDragging) { - this.removeDroppableCellClass(); - $(e.target).addClass(DATE_TABLE_DROPPABLE_CELL_CLASS); - } - }; + _getGroupCount() { + return getGroupCount(this.option('groups')); + } - const removeClasses = () => { - if(!this.preventDefaultDragging) { - this.removeDroppableCellClass(); - } - }; + _attachTablesEvents() { + const element = this.$element(); - const onCheckDropTarget = (target, event) => { - return !this._isOutsideScrollable(target, event); - }; + this._attachDragEvents(element); + this._attachPointerEvents(element); + } - eventsEngine.on(element, DragEventNames.ENTER, DRAG_AND_DROP_SELECTOR, { checkDropTarget: onCheckDropTarget }, onDragEnter); - eventsEngine.on(element, DragEventNames.LEAVE, removeClasses); - eventsEngine.on(element, DragEventNames.DROP, DRAG_AND_DROP_SELECTOR, () => { - if(!this.dragBehavior) { - return; - } - - if(!this.dragBehavior?.dragBetweenComponentsPromise) { - this.dragBehavior.removeDroppableClasses(); - return; - } - - this.dragBehavior.dragBetweenComponentsPromise?.then(() => { - this.dragBehavior.removeDroppableClasses(); - }); - }); + _detachDragEvents(element) { + (eventsEngine.off as any)(element, DragEventNames.ENTER); + (eventsEngine.off as any)(element, DragEventNames.LEAVE); + (eventsEngine.off as any)(element, DragEventNames.DROP); + } - } + _attachDragEvents(element) { + this._detachDragEvents(element); - _attachPointerEvents(element) { - let isPointerDown = false; + const onDragEnter = (e) => { + if (!this.preventDefaultDragging) { + this.removeDroppableCellClass(); + $(e.target).addClass(DATE_TABLE_DROPPABLE_CELL_CLASS); + } + }; - eventsEngine.off(element, SCHEDULER_CELL_DXPOINTERMOVE_EVENT_NAME); - eventsEngine.off(element, SCHEDULER_CELL_DXPOINTERDOWN_EVENT_NAME); + const removeClasses = () => { + if (!this.preventDefaultDragging) { + this.removeDroppableCellClass(); + } + }; - eventsEngine.on(element, SCHEDULER_CELL_DXPOINTERDOWN_EVENT_NAME, DRAG_AND_DROP_SELECTOR, e => { - if(isMouseEvent(e) && 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); - }); - } + const onCheckDropTarget = (target, event) => !this._isOutsideScrollable(target, event); + + (eventsEngine.on as any)( + element, + DragEventNames.ENTER, + DRAG_AND_DROP_SELECTOR, + { checkDropTarget: onCheckDropTarget }, + onDragEnter, + ); + eventsEngine.on(element, DragEventNames.LEAVE, removeClasses); + eventsEngine.on(element, DragEventNames.DROP, DRAG_AND_DROP_SELECTOR, () => { + if (!this.dragBehavior) { + return; + } + + if (!this.dragBehavior?.dragBetweenComponentsPromise) { + this.dragBehavior.removeDroppableClasses(); + return; + } + + this.dragBehavior.dragBetweenComponentsPromise?.then(() => { + this.dragBehavior.removeDroppableClasses(); + }); + }); + } + + _attachPointerEvents(element) { + 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.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.on(element, SCHEDULER_CELL_DXPOINTERMOVE_EVENT_NAME, DRAG_AND_DROP_SELECTOR, e => { - if(isPointerDown && this._dateTableScrollable && !this._dateTableScrollable.option('scrollByContent')) { - e.preventDefault(); - e.stopPropagation(); - this._moveToCell($(e.target), true); - } - }); - } + eventsEngine.on(element, SCHEDULER_CELL_DXPOINTERMOVE_EVENT_NAME, DRAG_AND_DROP_SELECTOR, (e) => { + if (isPointerDown && this._dateTableScrollable && !this._dateTableScrollable.option('scrollByContent')) { + e.preventDefault(); + e.stopPropagation(); + this._moveToCell($(e.target), true); + } + }); + } - _getFormat() { return abstract(); } + _getFormat() { return abstract(); } - getWorkArea() { - return this._$dateTableContainer; - } + getWorkArea() { + return this._$dateTableContainer; + } - getScrollable() { - return this._dateTableScrollable; - } + getScrollable() { + return this._dateTableScrollable; + } - getScrollableScrollTop() { - return this._dateTableScrollable.scrollTop(); - } + getScrollableScrollTop() { + return this._dateTableScrollable.scrollTop(); + } - getGroupedScrollableScrollTop(allDay) { - return this._groupedStrategy.getScrollableScrollTop(allDay); - } + getGroupedScrollableScrollTop(allDay) { + return this._groupedStrategy.getScrollableScrollTop(allDay); + } - getScrollableScrollLeft() { - return this._dateTableScrollable.scrollLeft(); - } + getScrollableScrollLeft() { + return this._dateTableScrollable.scrollLeft(); + } - getScrollableOuterWidth() { - return this._dateTableScrollable.scrollWidth(); - } + getScrollableOuterWidth() { + return this._dateTableScrollable.scrollWidth(); + } - getScrollableContainer() { - return $(this._dateTableScrollable.container()); - } + getScrollableContainer() { + return $(this._dateTableScrollable.container()); + } - getHeaderPanelHeight() { - return this._$headerPanel && getOuterHeight(this._$headerPanel, true); - } + getHeaderPanelHeight() { + return this._$headerPanel && getOuterHeight(this._$headerPanel, true); + } - getTimePanelWidth() { - return this._$timePanel && getBoundingRect(this._$timePanel.get(0)).width; - } + getTimePanelWidth() { + return this._$timePanel && getBoundingRect(this._$timePanel.get(0)).width; + } - getGroupTableWidth() { - return this._$groupTable ? getOuterWidth(this._$groupTable) : 0; - } + getGroupTableWidth() { + return this._$groupTable ? getOuterWidth(this._$groupTable) : 0; + } - getWorkSpaceLeftOffset() { - return this._groupedStrategy.getLeftOffset(); - } + getWorkSpaceLeftOffset() { + return this._groupedStrategy.getLeftOffset(); + } - _getCellCoordinatesByIndex(index) { - const columnIndex = Math.floor(index / this._getRowCount()); - const rowIndex = index - this._getRowCount() * columnIndex; + _getCellCoordinatesByIndex(index) { + const columnIndex = Math.floor(index / this._getRowCount()); + const rowIndex = index - this._getRowCount() * columnIndex; - return { - columnIndex, - rowIndex - }; - } + return { + columnIndex, + rowIndex, + }; + } - // TODO: necessary for old render - _getDateGenerationOptions(isOldRender = false) { - return { - startDayHour: this.option('startDayHour'), - endDayHour: this.option('endDayHour'), - isWorkView: this.viewDataProvider.viewDataGenerator.isWorkView, - interval: this.viewDataProvider.viewDataGenerator?.getInterval(this.option('hoursInterval')), - startViewDate: this.getStartViewDate(), - firstDayOfWeek: this._firstDayOfWeek(), - }; - } + // TODO: necessary for old render + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _getDateGenerationOptions(isOldRender = false) { + return { + startDayHour: this.option('startDayHour'), + endDayHour: this.option('endDayHour'), + isWorkView: this.viewDataProvider.viewDataGenerator.isWorkView, + interval: this.viewDataProvider.viewDataGenerator?.getInterval(this.option('hoursInterval')), + startViewDate: this.getStartViewDate(), + firstDayOfWeek: this._firstDayOfWeek(), + }; + } + + // TODO: refactor current time indicator + _getIntervalBetween(currentDate, allDay) { + const firstViewDate = this.getStartViewDate(); + + const startDayTime = (this.option('startDayHour') as any) * HOUR_MS; + const timeZoneOffset = dateUtils.getTimezonesDifference(firstViewDate, currentDate); + const fullInterval = currentDate.getTime() - firstViewDate.getTime() - timeZoneOffset; + const days = this._getDaysOfInterval(fullInterval, startDayTime); + const weekendsCount = this._getWeekendsCount(days); + let result = (days - weekendsCount) * DAY_MS; + + if (!allDay) { + const { hiddenInterval } = this.viewDataProvider; + const visibleDayDuration = this.getVisibleDayDuration(); + + result = fullInterval - days * hiddenInterval - weekendsCount * visibleDayDuration; + } + + return result; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _getWeekendsCount(argument?: any) { + return 0; + } + + _getDaysOfInterval(fullInterval, startDayTime) { + return Math.floor((fullInterval + startDayTime) / DAY_MS); + } + + _updateIndex(index) { + return index * this._getRowCount(); + } + + _getDroppableCell() { + return this._getDateTables().find(`.${DATE_TABLE_DROPPABLE_CELL_CLASS}`); + } + + _getWorkSpaceWidth() { + return this.cache.get('workspaceWidth', () => { + if (this._needCreateCrossScrolling()) { + return getBoundingRect(this._$dateTable.get(0)).width; + } + const totalWidth = getBoundingRect((this.$element() as any).get(0)).width; + const timePanelWidth = this.getTimePanelWidth(); + const groupTableWidth = this.getGroupTableWidth(); + + return totalWidth - timePanelWidth - groupTableWidth; + }); + } + + _getCellByCoordinates(cellCoordinates, groupIndex, inAllDayRow) { + const indexes = this._groupedStrategy.prepareCellIndexes(cellCoordinates, groupIndex, inAllDayRow); + return this._dom_getDateCell(indexes); + } + + // TODO DOM adapter + _dom_getDateCell(position) { + return this._$dateTable + .find(`tr:not(.${VIRTUAL_ROW_CLASS})`) + .eq(position.rowIndex) + .find(`td:not(.${VIRTUAL_CELL_CLASS})`) + .eq(position.columnIndex); + } + + _dom_getAllDayPanelCell(columnIndex) { + return this._$allDayPanel + .find('tr').eq(0) + .find('td').eq(columnIndex); + } + + _getCells(allDay?: any, direction?: any) { + 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})`); + if (!cells.length) break; + result = result.concat(cells.toArray()); + } + return $(result); + } + return (this.$element() as any).find(`.${cellClass}`); + } + + _getFirstAndLastDataTableCell() { + 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]]; + } + + _getAllCells(allDay) { + if (this._isVerticalGroupedWorkSpace()) { + return this._$dateTable.find(`td:not(.${VIRTUAL_CELL_CLASS})`); + } + + const cellClass = allDay && this.supportAllDayRow() + ? ALL_DAY_TABLE_CELL_CLASS + : DATE_TABLE_CELL_CLASS; + + return (this.$element() as any).find(`.${cellClass}`); + } + + _setHorizontalGroupHeaderCellsHeight() { + const { height } = getBoundingRect(this._$dateTable.get(0)); + setOuterHeight(this._$groupTable, height); + } + + _getGroupHeaderCells() { + return (this.$element() as any).find(`.${GROUP_HEADER_CLASS}`); + } + + _getScrollCoordinates(hours, minutes, date, groupIndex?: any, allDay?: any) { + const currentDate = date || new Date(this.option('currentDate') as any); + const startDayHour = this.option('startDayHour')!; + const endDayHour = this.option('endDayHour')!; + + if (hours < startDayHour) { + hours = startDayHour; + } + + if (hours >= endDayHour) { + hours = endDayHour - 1; + } + + currentDate.setHours(hours, minutes, 0, 0); + + const cell = this.viewDataProvider.findGlobalCellPosition(currentDate, groupIndex, allDay); + const { position, cellData } = cell; + + return this.virtualScrollingDispatcher.calculateCoordinatesByDataAndPosition( + cellData, + position, + currentDate, + isDateAndTimeView(this.type as any), + this.viewDirection === 'vertical', + ); + } + + _isOutsideScrollable(target, event) { + 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)); + + if (isTargetInAllDayPanel && !isOutsideHorizontalScrollable) { + return false; + } + + return isOutsideVerticalScrollable || isOutsideHorizontalScrollable; + } + + setCellDataCache(cellCoordinates, groupIndex, $cell) { + const key = JSON.stringify({ + rowIndex: cellCoordinates.rowIndex, + columnIndex: cellCoordinates.columnIndex, + groupIndex, + }); + + this.cache.set( + key, + this.getCellData($cell), + ); + } + + setCellDataCacheAlias(appointment, geometry) { + const key = JSON.stringify({ + rowIndex: appointment.rowIndex, + columnIndex: appointment.columnIndex, + groupIndex: appointment.groupIndex, + }); - // TODO: refactor current time indicator - _getIntervalBetween(currentDate, allDay) { - const firstViewDate = this.getStartViewDate(); + const aliasKey = JSON.stringify({ + top: geometry.top, + left: geometry.left, + }); - 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); - const weekendsCount = this._getWeekendsCount(days); - let result = (days - weekendsCount) * DAY_MS; + this.cache.set( + aliasKey, + this.cache.get(key), + ); + } - if(!allDay) { - const hiddenInterval = this.viewDataProvider.hiddenInterval; - const visibleDayDuration = this.getVisibleDayDuration(); + supportAllDayRow() { + return true; + } - result = fullInterval - days * hiddenInterval - weekendsCount * visibleDayDuration; - } + keepOriginalHours() { + return false; + } - return result; - } + _filterCellDataFields(cellData) { + return extend(true, {}, { + startDate: cellData.startDate, + endDate: cellData.endDate, + groups: cellData.groups, + groupIndex: cellData.groupIndex, + allDay: cellData.allDay, + }); + } + getCellData($cell) { + const cellData = this._getFullCellData($cell) || {}; - _getWeekendsCount() { - return 0; + return this._filterCellDataFields(cellData); + } + + _getFullCellData($cell) { + const currentCell = $cell[0]; + if (currentCell) { + return this._getDataByCell($cell); } - _getDaysOfInterval(fullInterval, startDayTime) { - return Math.floor((fullInterval + startDayTime) / DAY_MS); - } + return undefined; + } - _updateIndex(index) { - return index * this._getRowCount(); - } + _getVirtualRowOffset() { + return this.virtualScrollingDispatcher.virtualRowOffset; + } + + _getVirtualCellOffset() { + return this.virtualScrollingDispatcher.virtualCellOffset; + } + + _getDataByCell($cell) { + const rowIndex = $cell.parent().index() - this.virtualScrollingDispatcher.topVirtualRowsCount; + const columnIndex = $cell.index() - this.virtualScrollingDispatcher.leftVirtualCellsCount; + + const { viewDataProvider } = this; + const isAllDayCell = this._hasAllDayClass($cell); + + const cellData = viewDataProvider.getCellData(rowIndex, columnIndex, isAllDayCell); + + return cellData || undefined; + } + + isGroupedByDate() { + return this.option('groupByDate') + && this._isHorizontalGroupedWorkSpace() + && this._getGroupCount() > 0; + } - _getDroppableCell() { - return this._getDateTables().find('.' + DATE_TABLE_DROPPABLE_CELL_CLASS); - } + // TODO: refactor current time indicator + getCellIndexByDate(date, inAllDayRow?: any) { + const { viewDataGenerator } = this.viewDataProvider; - _getWorkSpaceWidth() { - return this.cache.get('workspaceWidth', () => { - if(this._needCreateCrossScrolling()) { - return getBoundingRect(this._$dateTable.get(0)).width; - } - const totalWidth = getBoundingRect(this.$element().get(0)).width; - const timePanelWidth = this.getTimePanelWidth(); - const groupTableWidth = this.getGroupTableWidth(); + 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; - return totalWidth - timePanelWidth - groupTableWidth; - }); - } + let index = Math.floor(dateTimeStamp / timeInterval); - _getCellByCoordinates(cellCoordinates, groupIndex, inAllDayRow) { - const indexes = this._groupedStrategy.prepareCellIndexes(cellCoordinates, groupIndex, inAllDayRow); - return this._dom_getDateCell(indexes); + if (inAllDayRow) { + index = this._updateIndex(index); } - // TODO DOM adapter - _dom_getDateCell(position) { - return this._$dateTable - .find(`tr:not(.${VIRTUAL_ROW_CLASS})`) - .eq(position.rowIndex) - .find(`td:not(.${VIRTUAL_CELL_CLASS})`) - .eq(position.columnIndex); + if (index < 0) { + index = 0; } - _dom_getAllDayPanelCell(columnIndex) { - return this._$allDayPanel - .find('tr').eq(0) - .find('td').eq(columnIndex); - } + return index; + } - _getCells(allDay, direction) { - const cellClass = allDay ? ALL_DAY_TABLE_CELL_CLASS : DATE_TABLE_CELL_CLASS; - if(direction === 'vertical') { - let result = []; - for(let i = 1; ; i++) { - const cells = this.$element().find(`tr .${cellClass}:nth-child(${i})`); - if(!cells.length) break; - result = result.concat(cells.toArray()); - } - return $(result); - } else { - return this.$element().find('.' + cellClass); - } - } + getDroppableCellIndex() { + const $droppableCell = this._getDroppableCell(); + const $row = $droppableCell.parent(); + const rowIndex = $row.index(); - _getFirstAndLastDataTableCell() { - const selector = this.isVirtualScrolling() - ? `.${DATE_TABLE_CELL_CLASS}, .${VIRTUAL_CELL_CLASS}` - : `.${DATE_TABLE_CELL_CLASS}`; + return rowIndex * $row.find('td').length + $droppableCell.index(); + } - const $cells = this.$element().find(selector); - return [$cells[0], $cells[$cells.length - 1]]; - } + getDataByDroppableCell() { + const cellData = this.getCellData($(this._getDroppableCell())); + const { allDay } = cellData; + const { startDate } = cellData; + const { endDate } = cellData; - _getAllCells(allDay) { - if(this._isVerticalGroupedWorkSpace()) { - return this._$dateTable.find(`td:not(.${VIRTUAL_CELL_CLASS})`); - } + return { + startDate, + endDate, + allDay, + groups: cellData.groups, + }; + } - const cellClass = allDay && this.supportAllDayRow() - ? ALL_DAY_TABLE_CELL_CLASS - : DATE_TABLE_CELL_CLASS; + getDateRange() { + return [ + this.getStartViewDate(), + this.getEndViewDateByEndDayHour(), + ]; + } + + getCellMinWidth() { + return DATE_TABLE_MIN_CELL_WIDTH; + } + + getRoundedCellWidth(groupIndex, startIndex, cellCount) { + if (groupIndex < 0 || !hasWindow()) { + return 0; + } + + const $row = (this.$element() as any).find(`.${DATE_TABLE_ROW_CLASS}`).eq(0); + let width = 0; + const $cells = $row.find(`.${DATE_TABLE_CELL_CLASS}`); + const totalCellCount = this._getCellCount() * groupIndex; + + cellCount = cellCount || this._getCellCount(); + + if (!isDefined(startIndex)) { + startIndex = totalCellCount; + } + + for (let i = startIndex; i < totalCellCount + cellCount; i++) { + const element = $($cells).eq(i).get(0); + const elementWidth = element ? getBoundingRect(element).width : 0; + width += elementWidth; + } + + return width / (totalCellCount + cellCount - startIndex); + } + + // Mappings + getCellWidth() { + return getCellWidth(this.getDOMElementsMetaData()); + } + + getCellHeight() { + return getCellHeight(this.getDOMElementsMetaData()); + } + + getAllDayHeight() { + return getAllDayHeight( + this.option('showAllDayPanel'), + this._isVerticalGroupedWorkSpace(), + this.getDOMElementsMetaData(), + ); + } + + getMaxAllowedPosition(groupIndex) { + return getMaxAllowedPosition( + groupIndex, + this.viewDataProvider, + this.option('rtlEnabled'), + this.getDOMElementsMetaData(), + ); + } + + getAllDayOffset() { + return this._groupedStrategy.getAllDayOffset(); + } + + // NOTE: refactor leftIndex calculation + getCellIndexByCoordinates(coordinates, allDay) { + const cellCount = this._getTotalCellCount(this._getGroupCount()); + const cellWidth = this.getCellWidth(); + const cellHeight = allDay ? this.getAllDayHeight() : this.getCellHeight(); + + const topIndex = Math.floor(Math.floor(coordinates.top) / Math.floor(cellHeight)); + let leftIndex = coordinates.left / cellWidth; + leftIndex = Math.floor(leftIndex + CELL_INDEX_CALCULATION_EPSILON); + + if (this._isRTL()) { + leftIndex = cellCount - leftIndex - 1; + } + + return cellCount * topIndex + leftIndex; + } + + getStartViewDate() { + return this.viewDataProvider.getStartViewDate(); + } + + getEndViewDate() { + return this.viewDataProvider.getLastCellEndDate(); + } + + getEndViewDateByEndDayHour() { + return this.viewDataProvider.getLastViewDateByEndDayHour(this.option('endDayHour')); + } + + getCellDuration() { + return getCellDuration( + this.type as any, + this.option('startDayHour') as any, + this.option('endDayHour') as any, + this.option('hoursInterval') as any, + ); + } + + getIntervalDuration(allDay) { + return allDay + ? toMs('day') + : this.getCellDuration(); + } + + getVisibleDayDuration() { + const startDayHour = this.option('startDayHour'); + const endDayHour = this.option('endDayHour'); + const hoursInterval = this.option('hoursInterval'); + + return this.viewDataProvider.getVisibleDayDuration(startDayHour, endDayHour, hoursInterval); + } + + getGroupBounds(coordinates) { + const groupBounds = this._groupedStrategy instanceof VerticalGroupedStrategy + ? this.getGroupBoundsVertical(coordinates.groupIndex) + : this.getGroupBoundsHorizontal(coordinates); + + return this._isRTL() + ? this.getGroupBoundsRtlCorrection(groupBounds) + : groupBounds; + } + + getGroupBoundsVertical(groupIndex) { + const $firstAndLastCells = this._getFirstAndLastDataTableCell(); + return this._groupedStrategy.getGroupBoundsOffset(groupIndex, $firstAndLastCells); + } + + getGroupBoundsHorizontal(coordinates) { + 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); + } + + getGroupBoundsRtlCorrection(groupBounds) { + const cellWidth = this.getCellWidth(); - return this.$element().find(`.${cellClass}`); - } + return { + ...groupBounds, + left: groupBounds.right - cellWidth * 2, + right: groupBounds.left + cellWidth * 2, + }; + } + + needRecalculateResizableArea() { + return this._isVerticalGroupedWorkSpace() && this.getScrollable().scrollTop() !== 0; + } + + getCellDataByCoordinates(coordinates, allDay) { + const key = JSON.stringify({ top: coordinates.top, left: coordinates.left }); + return this.cache.get(key, () => { + const $cells = this._getCells(allDay); + const cellIndex = this.getCellIndexByCoordinates(coordinates, allDay); + const $cell = $cells.eq(cellIndex); + + return this.getCellData($cell); + }); + } + + getVisibleBounds() { // TODO - this method is only used by the Agenda + const result: any = {}; + 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, + }; - _setHorizontalGroupHeaderCellsHeight() { - const height = getBoundingRect(this._$dateTable.get(0)).height; - setOuterHeight(this._$groupTable, height); - } + result.bottom = { + hours: Math.floor(totalCellCount * (this.option('hoursInterval') as any)) + (this.option('startDayHour') as any), + minutes: Math.floor(totalCellCount) % 2 ? 30 : 0, + }; - _getGroupHeaderCells() { - return this.$element().find('.' + GROUP_HEADER_CLASS); - } + return result; + } - _getScrollCoordinates(hours, minutes, date, groupIndex, allDay) { - const currentDate = date || new Date(this.option('currentDate')); - const startDayHour = this.option('startDayHour'); - const endDayHour = this.option('endDayHour'); + updateScrollPosition(date, groups, allDay = false) { + const newDate = this.timeZoneCalculator.createDate(date, { path: 'toGrid' }); + const inAllDayRow = allDay && this.isAllDayPanelVisible; - if(hours < startDayHour) { - hours = startDayHour; - } + if (this.needUpdateScrollPosition(newDate, groups, inAllDayRow)) { + this.scrollTo(newDate, groups, inAllDayRow, false); + } + } - if(hours >= endDayHour) { - hours = endDayHour - 1; - } + needUpdateScrollPosition(date, groups, inAllDayRow) { + const cells = this._getCellsInViewport(inAllDayRow); + const groupIndex = this._isGroupsSpecified(groups) + ? this._getGroupIndexByResourceId(groups) + : 0; + const time = date.getTime(); + const trimmedTime = dateUtils.trimTime(date).getTime(); - currentDate.setHours(hours, minutes, 0, 0); + return cells.reduce((currentResult, cell) => { + const { + startDate: cellStartDate, + endDate: cellEndDate, + groupIndex: cellGroupIndex, + } = this.getCellData(cell); - const cell = this.viewDataProvider.findGlobalCellPosition( - currentDate, groupIndex, allDay, - ); - const { position, cellData } = cell; - - return this.virtualScrollingDispatcher.calculateCoordinatesByDataAndPosition( - cellData, - position, - currentDate, - isDateAndTimeView(this.type), - this.viewDirection === 'vertical', - ); - } + const cellStartTime = cellStartDate.getTime(); + const cellEndTime = cellEndDate.getTime(); - _isOutsideScrollable(target, event) { - 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)); + if (((!inAllDayRow && cellStartTime <= time + && time < cellEndTime) + || (inAllDayRow && trimmedTime === cellStartTime)) + && groupIndex === cellGroupIndex) { + return false; + } + return currentResult; + }, true); + } - if(isTargetInAllDayPanel && !isOutsideHorizontalScrollable) { - return false; - } + _getCellsInViewport(inAllDayRow) { + const $scrollable = this.getScrollable().$element(); + const cellHeight = this.getCellHeight(); + const cellWidth = this.getCellWidth(); + const totalColumnCount = this._getTotalCellCount(this._getGroupCount()); + const scrollableScrollTop = this.getScrollableScrollTop(); + const scrollableScrollLeft = this.getScrollableScrollLeft(); - return isOutsideVerticalScrollable || isOutsideHorizontalScrollable; - } + const fullScrolledRowCount = scrollableScrollTop / cellHeight - this.virtualScrollingDispatcher.topVirtualRowsCount; - setCellDataCache(cellCoordinates, groupIndex, $cell) { - const key = JSON.stringify({ - rowIndex: cellCoordinates.rowIndex, - columnIndex: cellCoordinates.columnIndex, - groupIndex: groupIndex - }); + let scrolledRowCount = Math.floor(fullScrolledRowCount); + if (scrollableScrollTop % cellHeight !== 0) { + scrolledRowCount += 1; + } - this.cache.set( - key, - this.getCellData($cell) - ); + // TODO horizontal v-scrolling + const fullScrolledColumnCount = scrollableScrollLeft / cellWidth; + let scrolledColumnCount = Math.floor(fullScrolledColumnCount); + if (scrollableScrollLeft % cellWidth !== 0) { + scrolledColumnCount += 1; } - setCellDataCacheAlias(appointment, geometry) { - const key = JSON.stringify({ - rowIndex: appointment.rowIndex, - columnIndex: appointment.columnIndex, - groupIndex: appointment.groupIndex - }); + const rowCount = Math.floor(fullScrolledRowCount + getHeight($scrollable) / cellHeight); + const columnCount = Math.floor(fullScrolledColumnCount + getWidth($scrollable) / cellWidth); - const aliasKey = JSON.stringify({ - top: geometry.top, - left: geometry.left - }); + const $cells = this._getAllCells(inAllDayRow); + const result: any = []; - this.cache.set( - aliasKey, - this.cache.get(key) - ); - } + $cells.each(function (index) { + const $cell = $(this); + const columnIndex = index % totalColumnCount; + const rowIndex = index / totalColumnCount; - supportAllDayRow() { - return true; - } + if (scrolledColumnCount <= columnIndex + && columnIndex < columnCount + && scrolledRowCount <= rowIndex + && rowIndex < rowCount) { + result.push($cell); + } + }); - keepOriginalHours() { - return false; - } + return result; + } - _filterCellDataFields(cellData) { - return extend(true, {}, { - startDate: cellData.startDate, - endDate: cellData.endDate, - groups: cellData.groups, - groupIndex: cellData.groupIndex, - allDay: cellData.allDay, - }); + scrollToTime(hours, minutes, date) { + if (!this._isValidScrollDate(date)) { + return; } - getCellData($cell) { - const cellData = this._getFullCellData($cell) || {}; + const coordinates = this._getScrollCoordinates(hours, minutes, date); - return this._filterCellDataFields(cellData); - } + const scrollable = this.getScrollable(); - _getFullCellData($cell) { - const currentCell = $cell[0]; - if(currentCell) { - return this._getDataByCell($cell); - } + scrollable.scrollBy({ + top: coordinates.top - scrollable.scrollTop(), + left: 0, + }); + } - return undefined; + scrollTo(date, groups, allDay = false, throwWarning = true) { + if (!this._isValidScrollDate(date, throwWarning)) { + return; } - _getVirtualRowOffset() { - return this.virtualScrollingDispatcher.virtualRowOffset; - } + const groupIndex = this._getGroupCount() && groups + ? this._getGroupIndexByResourceId(groups) + : 0; + const isScrollToAllDay = allDay && this.isAllDayPanelVisible; - _getVirtualCellOffset() { - return this.virtualScrollingDispatcher.virtualCellOffset; - } + const coordinates = this._getScrollCoordinates(date.getHours(), date.getMinutes(), date, groupIndex, isScrollToAllDay); - _getDataByCell($cell) { - const rowIndex = $cell.parent().index() - this.virtualScrollingDispatcher.topVirtualRowsCount; - const columnIndex = $cell.index() - this.virtualScrollingDispatcher.leftVirtualCellsCount; + const scrollable = this.getScrollable(); + const $scrollable = scrollable.$element(); - const { viewDataProvider } = this; - const isAllDayCell = this._hasAllDayClass($cell); + const cellWidth = this.getCellWidth(); + const offset = this.option('rtlEnabled') + ? cellWidth + : 0; + const scrollableHeight = getHeight($scrollable); + const scrollableWidth = getWidth($scrollable); + const cellHeight = this.getCellHeight(); - const cellData = viewDataProvider.getCellData(rowIndex, columnIndex, isAllDayCell); + const xShift = (scrollableWidth - cellWidth) / 2; + const yShift = (scrollableHeight - cellHeight) / 2; - return cellData ? cellData : undefined; + const left = coordinates.left - scrollable.scrollLeft() - xShift - offset; + let top = coordinates.top - scrollable.scrollTop() - yShift; + if (isScrollToAllDay && !this._isVerticalGroupedWorkSpace()) { + top = 0; } - isGroupedByDate() { - return this.option('groupByDate') - && this._isHorizontalGroupedWorkSpace() - && this._getGroupCount() > 0; + if (this.option('templatesRenderAsynchronously')) { + setTimeout(() => { + scrollable.scrollBy({ left, top }); + }); + } else { + scrollable.scrollBy({ left, top }); } + } - // TODO: refactor current time indicator - getCellIndexByDate(date, inAllDayRow) { - const viewDataGenerator = this.viewDataProvider.viewDataGenerator; + _isValidScrollDate(date, throwWarning = true) { + const min = this.getStartViewDate(); + const max = this.getEndViewDate(); - const timeInterval = inAllDayRow - ? 24 * 60 * 60 * 1000 - : viewDataGenerator.getInterval(this.option('hoursInterval')); - const startViewDateOffset = getStartViewDateTimeOffset(this.getStartViewDate(), this.option('startDayHour')); - const dateTimeStamp = this._getIntervalBetween(date, inAllDayRow) + startViewDateOffset; + if (date < min || date > max) { + throwWarning && errors.log('W1008', date); + return false; + } - let index = Math.floor(dateTimeStamp / timeInterval); + return true; + } - if(inAllDayRow) { - index = this._updateIndex(index); - } + needApplyCollectorOffset() { + return false; + } - if(index < 0) { - index = 0; - } + removeDroppableCellClass($cellElement?: any) { + const $cell = $cellElement || this._getDroppableCell(); + $cell?.removeClass(DATE_TABLE_DROPPABLE_CELL_CLASS); + } + + _getCoordinatesByCell($cell) { + const columnIndex = $cell.index() - this.virtualScrollingDispatcher.leftVirtualCellsCount; + let rowIndex = $cell.parent().index(); + const isAllDayCell = this._hasAllDayClass($cell); + const isVerticalGrouping = this._isVerticalGroupedWorkSpace(); - return index; + if (!(isAllDayCell && !isVerticalGrouping)) { + rowIndex -= this.virtualScrollingDispatcher.topVirtualRowsCount; } - getDroppableCellIndex() { - const $droppableCell = this._getDroppableCell(); - const $row = $droppableCell.parent(); - const rowIndex = $row.index(); + return { rowIndex, columnIndex }; + } - return rowIndex * $row.find('td').length + $droppableCell.index(); - } - - getDataByDroppableCell() { - const cellData = this.getCellData($(this._getDroppableCell())); - const allDay = cellData.allDay; - const startDate = cellData.startDate; - const endDate = cellData.endDate; - - return { - startDate, - endDate, - allDay, - groups: cellData.groups - }; - } - - getDateRange() { - return [ - this.getStartViewDate(), - this.getEndViewDateByEndDayHour() - ]; - } - - getCellMinWidth() { - return DATE_TABLE_MIN_CELL_WIDTH; - } - - getRoundedCellWidth(groupIndex, startIndex, cellCount) { - if(groupIndex < 0 || !hasWindow()) { - return 0; - } - - const $row = this.$element().find(`.${DATE_TABLE_ROW_CLASS}`).eq(0); - let width = 0; - const $cells = $row.find('.' + DATE_TABLE_CELL_CLASS); - const totalCellCount = this._getCellCount() * groupIndex; - - cellCount = cellCount || this._getCellCount(); - - if(!isDefined(startIndex)) { - startIndex = totalCellCount; - } - - for(let i = startIndex; i < totalCellCount + cellCount; i++) { - const element = $($cells).eq(i).get(0); - const elementWidth = element ? getBoundingRect(element).width : 0; - width = width + elementWidth; - } - - return width / (totalCellCount + cellCount - startIndex); - } - - // Mappings - getCellWidth() { - return getCellWidth(this.getDOMElementsMetaData()); - } - - getCellHeight() { - return getCellHeight(this.getDOMElementsMetaData()); - } - - getAllDayHeight() { - return getAllDayHeight( - this.option('showAllDayPanel'), - this._isVerticalGroupedWorkSpace(), - this.getDOMElementsMetaData() - ); - } - - getMaxAllowedPosition(groupIndex) { - return getMaxAllowedPosition( - groupIndex, - this.viewDataProvider, - this.option('rtlEnabled'), - this.getDOMElementsMetaData() - ); - } - - getAllDayOffset() { - return this._groupedStrategy.getAllDayOffset(); - } - - // NOTE: refactor leftIndex calculation - getCellIndexByCoordinates(coordinates, allDay) { - const cellCount = this._getTotalCellCount(this._getGroupCount()); - const cellWidth = this.getCellWidth(); - const cellHeight = allDay ? this.getAllDayHeight() : this.getCellHeight(); - - const topIndex = Math.floor(Math.floor(coordinates.top) / Math.floor(cellHeight)); - let leftIndex = coordinates.left / cellWidth; - leftIndex = Math.floor(leftIndex + CELL_INDEX_CALCULATION_EPSILON); - - if(this._isRTL()) { - leftIndex = cellCount - leftIndex - 1; - } - - return cellCount * topIndex + leftIndex; - } - - getStartViewDate() { - return this.viewDataProvider.getStartViewDate(); - } - - getEndViewDate() { - return this.viewDataProvider.getLastCellEndDate(); - } - - getEndViewDateByEndDayHour() { - return this.viewDataProvider.getLastViewDateByEndDayHour(this.option('endDayHour')); - } - - getCellDuration() { - return getCellDuration( - this.type, - this.option('startDayHour'), - this.option('endDayHour'), - this.option('hoursInterval') - ); - } - - getIntervalDuration(allDay) { - return allDay - ? toMs('day') - : this.getCellDuration(); - } - - getVisibleDayDuration() { - const startDayHour = this.option('startDayHour'); - const endDayHour = this.option('endDayHour'); - const hoursInterval = this.option('hoursInterval'); - - return this.viewDataProvider.getVisibleDayDuration(startDayHour, endDayHour, hoursInterval); - } - - getGroupBounds(coordinates) { - const groupBounds = this._groupedStrategy instanceof VerticalGroupedStrategy - ? this.getGroupBoundsVertical(coordinates.groupIndex) - : this.getGroupBoundsHorizontal(coordinates); - - return this._isRTL() - ? this.getGroupBoundsRtlCorrection(groupBounds) - : groupBounds; - } - - getGroupBoundsVertical(groupIndex) { - const $firstAndLastCells = this._getFirstAndLastDataTableCell(); - return this._groupedStrategy.getGroupBoundsOffset(groupIndex, $firstAndLastCells); - } - - getGroupBoundsHorizontal(coordinates) { - const cellCount = this._getCellCount(); - const $cells = this._getCells(); - const cellWidth = this.getCellWidth(); - - const groupedDataMap = this.viewDataProvider.groupedDataMap; - return this._groupedStrategy - .getGroupBoundsOffset(cellCount, $cells, cellWidth, coordinates, groupedDataMap); - } - - getGroupBoundsRtlCorrection(groupBounds) { - const cellWidth = this.getCellWidth(); - - return { - ...groupBounds, - left: groupBounds.right - cellWidth * 2, - right: groupBounds.left + cellWidth * 2, - }; - } - - needRecalculateResizableArea() { - return this._isVerticalGroupedWorkSpace() && this.getScrollable().scrollTop() !== 0; - } - - getCellDataByCoordinates(coordinates, allDay) { - const key = JSON.stringify({ top: coordinates.top, left: coordinates.left }); - return this.cache.get(key, () => { - const $cells = this._getCells(allDay); - const cellIndex = this.getCellIndexByCoordinates(coordinates, allDay); - const $cell = $cells.eq(cellIndex); - - return this.getCellData($cell); - }); - } - - getVisibleBounds() { // TODO - this method is only used by the Agenda - const result = {}; - 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')) + this.option('startDayHour'), - minutes: scrolledCellCount % 2 ? 30 : 0 - }; - - result.bottom = { - hours: Math.floor(totalCellCount * this.option('hoursInterval')) + this.option('startDayHour'), - minutes: Math.floor(totalCellCount) % 2 ? 30 : 0 - }; - - return result; - } - - updateScrollPosition(date, groups, allDay = false) { - const newDate = this.timeZoneCalculator.createDate(date, { path: 'toGrid' }); - const inAllDayRow = allDay && this.isAllDayPanelVisible; - - if(this.needUpdateScrollPosition(newDate, groups, inAllDayRow)) { - this.scrollTo(newDate, groups, inAllDayRow, false); - } - } - - needUpdateScrollPosition(date, groups, inAllDayRow) { - const cells = this._getCellsInViewport(inAllDayRow); - const groupIndex = this._isGroupsSpecified(groups) - ? this._getGroupIndexByResourceId(groups) - : 0; - const time = date.getTime(); - const trimmedTime = dateUtils.trimTime(date).getTime(); - - return cells.reduce((currentResult, cell) => { - const { - startDate: cellStartDate, - endDate: cellEndDate, - groupIndex: cellGroupIndex, - } = this.getCellData(cell); - - const cellStartTime = cellStartDate.getTime(); - const cellEndTime = cellEndDate.getTime(); - - if(((!inAllDayRow && cellStartTime <= time - && time < cellEndTime) - || (inAllDayRow && trimmedTime === cellStartTime)) - && groupIndex === cellGroupIndex) { - return false; - } - return currentResult; - }, true); - } - - _getCellsInViewport(inAllDayRow) { - const $scrollable = this.getScrollable().$element(); - const cellHeight = this.getCellHeight(); - const cellWidth = this.getCellWidth(); - const totalColumnCount = this._getTotalCellCount(this._getGroupCount()); - const scrollableScrollTop = this.getScrollableScrollTop(); - const scrollableScrollLeft = this.getScrollableScrollLeft(); - - const fullScrolledRowCount = scrollableScrollTop / cellHeight - this.virtualScrollingDispatcher.topVirtualRowsCount; - - let scrolledRowCount = Math.floor(fullScrolledRowCount); - if(scrollableScrollTop % cellHeight !== 0) { - scrolledRowCount += 1; - } - - // TODO horizontal v-scrolling - const fullScrolledColumnCount = scrollableScrollLeft / cellWidth; - let scrolledColumnCount = Math.floor(fullScrolledColumnCount); - if(scrollableScrollLeft % cellWidth !== 0) { - scrolledColumnCount += 1; - } - - const rowCount = Math.floor(fullScrolledRowCount + getHeight($scrollable) / cellHeight); - const columnCount = Math.floor(fullScrolledColumnCount + getWidth($scrollable) / cellWidth); - - const $cells = this._getAllCells(inAllDayRow); - const result = []; - - $cells.each(function(index) { - const $cell = $(this); - const columnIndex = index % totalColumnCount; - const rowIndex = index / totalColumnCount; - - if(scrolledColumnCount <= columnIndex - && columnIndex < columnCount - && scrolledRowCount <= rowIndex - && rowIndex < rowCount) { - result.push($cell); - } - }); - - return result; - } - - scrollToTime(hours, minutes, date) { - if(!this._isValidScrollDate(date)) { - return; - } - - const coordinates = this._getScrollCoordinates(hours, minutes, date); - - const scrollable = this.getScrollable(); - - scrollable.scrollBy({ - top: coordinates.top - scrollable.scrollTop(), - left: 0, - }); - } - - scrollTo(date, groups, allDay = false, throwWarning = true) { - if(!this._isValidScrollDate(date, throwWarning)) { - return; - } + _isShowAllDayPanel() { + return this.option('showAllDayPanel'); + } - const groupIndex = this._getGroupCount() && groups - ? this._getGroupIndexByResourceId(groups) - : 0; - const isScrollToAllDay = allDay && this.isAllDayPanelVisible; + _getTimePanelCells() { + return (this.$element() as any).find(`.${TIME_PANEL_CELL_CLASS}`); + } - const coordinates = this._getScrollCoordinates( - date.getHours(), date.getMinutes(), date, groupIndex, isScrollToAllDay, - ); - - const scrollable = this.getScrollable(); - const $scrollable = scrollable.$element(); - - const cellWidth = this.getCellWidth(); - const offset = this.option('rtlEnabled') - ? cellWidth - : 0; - const scrollableHeight = getHeight($scrollable); - const scrollableWidth = getWidth($scrollable); - const cellHeight = this.getCellHeight(); - - const xShift = (scrollableWidth - cellWidth) / 2; - const yShift = (scrollableHeight - cellHeight) / 2; - - const left = coordinates.left - scrollable.scrollLeft() - xShift - offset; - let top = coordinates.top - scrollable.scrollTop() - yShift; - if(isScrollToAllDay && !this._isVerticalGroupedWorkSpace()) { - top = 0; - } - - if(this.option('templatesRenderAsynchronously')) { - setTimeout(() => { - scrollable.scrollBy({ left, top }); - }); - } else { - scrollable.scrollBy({ left, top }); - } - } - - _isValidScrollDate(date, throwWarning = true) { - const min = this.getStartViewDate(); - const max = this.getEndViewDate(); - - if(date < min || date > max) { - throwWarning && errors.log('W1008', date); - return false; - } - - return true; - } - - needApplyCollectorOffset() { - return false; - } - - removeDroppableCellClass($cellElement) { - const $cell = ($cellElement || this._getDroppableCell()); - $cell?.removeClass(DATE_TABLE_DROPPABLE_CELL_CLASS); - } - - _getCoordinatesByCell($cell) { - const columnIndex = $cell.index() - this.virtualScrollingDispatcher.leftVirtualCellsCount; - let rowIndex = $cell.parent().index(); - const isAllDayCell = this._hasAllDayClass($cell); - const isVerticalGrouping = this._isVerticalGroupedWorkSpace(); - - if(!(isAllDayCell && !isVerticalGrouping)) { - rowIndex -= this.virtualScrollingDispatcher.topVirtualRowsCount; - } - - return { rowIndex, columnIndex }; - } - - _isShowAllDayPanel() { - return this.option('showAllDayPanel'); - } - - _getTimePanelCells() { - return this.$element().find(`.${TIME_PANEL_CELL_CLASS}`); - } - - _getRDateTableProps() { - return ({ - viewData: this.viewDataProvider.viewData, - dataCellTemplate: this.option('dataCellTemplate'), - addDateTableClass: !this.option('crossScrollingEnabled') || this.isVirtualScrolling(), - groupOrientation: this.option('groupOrientation'), - addVerticalSizesClassToRows: false, - }); - } - - _updateSelectedCellDataOption(selectedCellData) { - const correctedSelectedCellData = selectedCellData.map(({ - startDate, - endDate, - allDay, - groupIndex, - groups, - }) => ({ - startDate, - endDate, - allDay, - groupIndex, - groups, - })); - - this.option('selectedCellData', correctedSelectedCellData); - this._selectionChangedAction({ selectedCellData: correctedSelectedCellData }); - } - - _getCellByData(cellData) { - const { - startDate, groupIndex, allDay, index, - } = cellData; - - const position = this.viewDataProvider.findCellPositionInMap({ - startDate, - groupIndex, - isAllDay: allDay, - index, - }); - - if(!position) { - return undefined; - } - - return allDay && !this._isVerticalGroupedWorkSpace() - ? this._dom_getAllDayPanelCell(position.columnIndex) - : this._dom_getDateCell(position); - } - - // Must replace all DOM manipulations - getDOMElementsMetaData() { - return this.cache.get('cellElementsMeta', () => { - return { - dateTableCellsMeta: this._getDateTableDOMElementsInfo(), - allDayPanelCellsMeta: this._getAllDayPanelDOMElementsInfo(), - }; - }); - } - _getDateTableDOMElementsInfo() { - const dateTableCells = this._getAllCells(false); - if(!dateTableCells.length || !hasWindow()) { - return [[{}]]; - } - - const dateTable = this._getDateTable(); - // We should use getBoundingClientRect in renovation - const dateTableRect = getBoundingRect(dateTable.get(0)); - - const columnsCount = this.viewDataProvider.getColumnsCount(); - - const result = []; - - dateTableCells.each((index, cell) => { - const rowIndex = Math.floor(index / columnsCount); - - if(result.length === rowIndex) { - result.push([]); - } - - this._addCellMetaData(result[rowIndex], cell, dateTableRect); - }); - - return result; - } - _getAllDayPanelDOMElementsInfo() { - const result = []; - - if(this.isAllDayPanelVisible && !this._isVerticalGroupedWorkSpace() && hasWindow()) { - const allDayCells = this._getAllCells(true); - - if(!allDayCells.length) { - return [{}]; - } - - const allDayAppointmentContainer = this._$allDayPanel; - const allDayPanelRect = getBoundingRect(allDayAppointmentContainer.get(0)); - - allDayCells.each((_, cell) => { - this._addCellMetaData(result, cell, allDayPanelRect); - }); - } - - return result; - } - _addCellMetaData(cellMetaDataArray, cell, parentRect) { - const cellRect = getBoundingRect(cell); - - cellMetaDataArray.push({ - left: cellRect.left - parentRect.left, - top: cellRect.top - parentRect.top, - width: cellRect.width, - height: cellRect.height, - }); - } - - // TODO: remove along with old render - _oldRender_getAllDayCellData(groupIndex) { - return (cell, rowIndex, columnIndex) => { - const validColumnIndex = columnIndex % this._getCellCount(); - const options = this._getDateGenerationOptions(true); - let startDate = this.viewDataProvider.viewDataGenerator.getDateByCellIndices( - options, rowIndex, validColumnIndex, this._getCellCountInDay(), - ); - - startDate = dateUtils.trimTime(startDate); - - let validGroupIndex = groupIndex || 0; - - if(this.isGroupedByDate()) { - validGroupIndex = Math.floor(columnIndex % this._getGroupCount()); - } else if(this._isHorizontalGroupedWorkSpace()) { - validGroupIndex = Math.floor(columnIndex / this._getCellCount()); - } - - const data = { - startDate: startDate, - endDate: startDate, - allDay: true, - groupIndex: validGroupIndex, - }; - - const groupsArray = getCellGroups(validGroupIndex, this.option('groups')); - - if(groupsArray.length) { - data.groups = getGroupsObjectFromGroupsArray(groupsArray); - } - - return { - key: CELL_DATA, - value: data - }; - }; - } - - // ------------ - // Methods that render renovated components. Useless in renovation - // ------------ - - renderRWorkSpace(componentsToRender) { - const allComponents = { header: true, timePanel: true, dateTable: true, allDayPanel: true }; - const components = componentsToRender ?? allComponents; - - components.header && this.renderRHeaderPanel(); - components.timePanel && this.renderRTimeTable(); - components.dateTable && this.renderRDateTable(); - components.allDayPanel && this.renderRAllDayPanel(); - } - - renderRDateTable() { - utils.renovation.renderComponent( - this, - this._$dateTable, - dxrDateTableLayout, - 'renovatedDateTable', - this._getRDateTableProps(), - ); - } - - renderRGroupPanel() { - const options = { - groups: this.option('groups'), - groupOrientation: this.option('groupOrientation'), - groupByDate: this.isGroupedByDate(), - resourceCellTemplate: this.option('resourceCellTemplate'), - className: this.verticalGroupTableClass, - groupPanelData: this.viewDataProvider.getGroupPanelData( - this.generateRenderOptions(), - ), - }; - - if(this.option('groups').length) { - this._attachGroupCountClass(); - utils.renovation.renderComponent( - this, - this._getGroupHeaderContainer(), - dxrGroupPanel, - 'renovatedGroupPanel', - options, - ); - } else { - this._detachGroupCountClass(); - } - } - - renderRAllDayPanel() { - const visible = this.isAllDayPanelVisible && !this.isGroupedAllDayPanel(); - - if(visible) { - this._toggleAllDayVisibility(false); - - const options = { - viewData: this.viewDataProvider.viewData, - dataCellTemplate: this.option('dataCellTemplate'), - startCellIndex: 0, - ...(this.virtualScrollingDispatcher.horizontalVirtualScrolling?.getRenderState() || {}), - }; - - utils.renovation.renderComponent(this, this._$allDayTable, dxrAllDayPanelTable, 'renovatedAllDayPanel', options); - utils.renovation.renderComponent(this, this._$allDayTitle, dxrAllDayPanelTitle, 'renovatedAllDayPanelTitle', {}); - } + _getRDateTableProps() { + return { + viewData: this.viewDataProvider.viewData, + dataCellTemplate: this.option('dataCellTemplate'), + addDateTableClass: !this.option('crossScrollingEnabled') || this.isVirtualScrolling(), + groupOrientation: this.option('groupOrientation'), + addVerticalSizesClassToRows: false, + }; + } - this._toggleAllDayVisibility(true); - } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _updateSelectedCellDataOption(selectedCellData, $nextFocusedCell?: any) { + const correctedSelectedCellData = selectedCellData.map(({ + startDate, + endDate, + allDay, + groupIndex, + groups, + }) => ({ + startDate, + endDate, + allDay, + groupIndex, + groups, + })); + + this.option('selectedCellData', correctedSelectedCellData); + this._selectionChangedAction({ selectedCellData: correctedSelectedCellData }); + } - renderRTimeTable() { - utils.renovation.renderComponent( - this, - this._$timePanel, - dxrTimePanelTableLayout, - 'renovatedTimePanel', - { - timePanelData: this.viewDataProvider.timePanelData, - timeCellTemplate: this.option('timeCellTemplate'), - groupOrientation: this.option('groupOrientation'), - } - ); - } + _getCellByData(cellData) { + const { + startDate, groupIndex, allDay, index, + } = cellData; - renderRHeaderPanel(isRenderDateHeader = true) { - if(this.option('groups').length) { - this._attachGroupCountClass(); - } else { - this._detachGroupCountClass(); - } + const position = this.viewDataProvider.findCellPositionInMap({ + startDate, + groupIndex, + isAllDay: allDay, + index, + }); + + if (!position) { + return undefined; + } + + return allDay && !this._isVerticalGroupedWorkSpace() + ? this._dom_getAllDayPanelCell(position.columnIndex) + : this._dom_getDateCell(position); + } + + // Must replace all DOM manipulations + getDOMElementsMetaData() { + return this.cache.get('cellElementsMeta', () => ({ + dateTableCellsMeta: this._getDateTableDOMElementsInfo(), + allDayPanelCellsMeta: this._getAllDayPanelDOMElementsInfo(), + })); + } + + _getDateTableDOMElementsInfo() { + const dateTableCells = this._getAllCells(false); + if (!dateTableCells.length || !hasWindow()) { + return [[{}]]; + } + + const dateTable = this._getDateTable(); + // We should use getBoundingClientRect in renovation + const dateTableRect = getBoundingRect(dateTable.get(0)); + + const columnsCount = this.viewDataProvider.getColumnsCount(); + + const result: any = []; + + dateTableCells.each((index, cell) => { + const rowIndex = Math.floor(index / columnsCount); + + if (result.length === rowIndex) { + result.push([]); + } + + this._addCellMetaData(result[rowIndex], cell, dateTableRect); + }); + + return result; + } + + _getAllDayPanelDOMElementsInfo() { + const result = []; + + if (this.isAllDayPanelVisible && !this._isVerticalGroupedWorkSpace() && hasWindow()) { + const allDayCells = this._getAllCells(true); + + if (!allDayCells.length) { + return [{}]; + } + + const allDayAppointmentContainer = this._$allDayPanel; + const allDayPanelRect = getBoundingRect(allDayAppointmentContainer.get(0)); + + allDayCells.each((_, cell) => { + this._addCellMetaData(result, cell, allDayPanelRect); + }); + } + + return result; + } + + _addCellMetaData(cellMetaDataArray, cell, parentRect) { + const cellRect = getBoundingRect(cell); + + cellMetaDataArray.push({ + left: cellRect.left - parentRect.left, + top: cellRect.top - parentRect.top, + width: cellRect.width, + height: cellRect.height, + }); + } - utils.renovation.renderComponent( - this, - this._$thead, - this.renovatedHeaderPanelComponent, - 'renovatedHeaderPanel', - { - dateHeaderData: this.viewDataProvider.dateHeaderData, - groupPanelData: this.viewDataProvider.getGroupPanelData( - this.generateRenderOptions(), - ), - dateCellTemplate: this.option('dateCellTemplate'), - timeCellTemplate: this.option('timeCellTemplate'), - groups: this.option('groups'), - groupByDate: this.isGroupedByDate(), - groupOrientation: this.option('groupOrientation'), - resourceCellTemplate: this.option('resourceCellTemplate'), - isRenderDateHeader, - } - ); - } + // TODO: remove along with old render + _oldRender_getAllDayCellData(groupIndex) { + return (cell, rowIndex, columnIndex) => { + const validColumnIndex = columnIndex % this._getCellCount(); + const options = this._getDateGenerationOptions(true); + let startDate = this.viewDataProvider.viewDataGenerator.getDateByCellIndices(options, rowIndex, validColumnIndex, this._getCellCountInDay()); - // ------------ - // DnD should be removed from work-space - // ------------ + startDate = dateUtils.trimTime(startDate); - initDragBehavior(scheduler) { - if(!this.dragBehavior && scheduler) { - this.dragBehavior = new AppointmentDragBehavior(scheduler); + let validGroupIndex = groupIndex || 0; - const $rootElement = $(scheduler.element()); + if (this.isGroupedByDate()) { + validGroupIndex = Math.floor(columnIndex % this._getGroupCount()); + } else if (this._isHorizontalGroupedWorkSpace()) { + validGroupIndex = Math.floor(columnIndex / this._getCellCount()); + } - this._createDragBehavior(this.getWorkArea(), $rootElement); - this._createDragBehavior(this._$allDayPanel, $rootElement); - } - } + const data: any = { + startDate, + endDate: startDate, + allDay: true, + groupIndex: validGroupIndex, + }; - _createDragBehavior($targetElement, $rootElement) { - const getItemData = (itemElement, appointments) => appointments._getItemData(itemElement); - const getItemSettings = ($itemElement) => $itemElement.data(APPOINTMENT_SETTINGS_KEY); + const groupsArray = getCellGroups(validGroupIndex, this.option('groups')); - const options = { - getItemData, - getItemSettings, - }; + if (groupsArray.length) { + data.groups = getGroupsObjectFromGroupsArray(groupsArray); + } - this._createDragBehaviorBase($targetElement, $rootElement, options); - } - - _createDragBehaviorBase(targetElement, rootElement, options) { - const container = this.$element().find(`.${FIXED_CONTAINER_CLASS}`); - - const disableDefaultDragging = () => { - if(!this.isDefaultDraggingMode) { - this.preventDefaultDragging = true; - } - }; + return { + key: CELL_DATA, + value: data, + }; + }; + } - const enableDefaultDragging = () => { - if(!this.isDefaultDraggingMode) { - this.preventDefaultDragging = false; - } - }; + // ------------ + // Methods that render renovated components. Useless in renovation + // ------------ + renderRWorkSpace(componentsToRender?: any) { + const allComponents = { + header: true, timePanel: true, dateTable: true, allDayPanel: true, + }; + const components = componentsToRender ?? allComponents; + + components.header && this.renderRHeaderPanel(); + components.timePanel && this.renderRTimeTable(); + components.dateTable && this.renderRDateTable(); + components.allDayPanel && this.renderRAllDayPanel(); + } + + renderRDateTable() { + utils.renovation.renderComponent( + this, + this._$dateTable, + dxrDateTableLayout, + 'renovatedDateTable', + this._getRDateTableProps(), + ); + } + + renderRGroupPanel() { + const options = { + groups: this.option('groups'), + groupOrientation: this.option('groupOrientation'), + groupByDate: this.isGroupedByDate(), + resourceCellTemplate: this.option('resourceCellTemplate'), + className: this.verticalGroupTableClass, + groupPanelData: this.viewDataProvider.getGroupPanelData( + this.generateRenderOptions(), + ), + }; - this.dragBehavior.addTo(targetElement, createDragBehaviorConfig( - container, - rootElement, - this.isDefaultDraggingMode, - this.dragBehavior, - enableDefaultDragging, - disableDefaultDragging, - () => this._getDroppableCell(), - () => this._getDateTables(), - () => this.removeDroppableCellClass(), - () => this.getCellWidth(), - options) - ); - } + if (this.option('groups')?.length) { + this._attachGroupCountClass(); + utils.renovation.renderComponent( + this, + this._getGroupHeaderContainer(), + dxrGroupPanel, + 'renovatedGroupPanel', + options, + ); + } else { + this._detachGroupCountClass(); + } + } + + renderRAllDayPanel() { + const visible = this.isAllDayPanelVisible && !this.isGroupedAllDayPanel(); + + if (visible) { + this._toggleAllDayVisibility(false); + + const options = { + viewData: this.viewDataProvider.viewData, + dataCellTemplate: this.option('dataCellTemplate'), + startCellIndex: 0, + ...this.virtualScrollingDispatcher.horizontalVirtualScrolling?.getRenderState() || {}, + }; + + utils.renovation.renderComponent(this, this._$allDayTable, dxrAllDayPanelTable, 'renovatedAllDayPanel', options); + utils.renovation.renderComponent(this, this._$allDayTitle, dxrAllDayPanelTitle, 'renovatedAllDayPanelTitle', {}); + } + + this._toggleAllDayVisibility(true); + } + + renderRTimeTable() { + utils.renovation.renderComponent( + this, + this._$timePanel, + dxrTimePanelTableLayout, + 'renovatedTimePanel', + { + timePanelData: this.viewDataProvider.timePanelData, + timeCellTemplate: this.option('timeCellTemplate'), + groupOrientation: this.option('groupOrientation'), + }, + ); + } + + renderRHeaderPanel(isRenderDateHeader = true) { + if (this.option('groups')?.length) { + this._attachGroupCountClass(); + } else { + this._detachGroupCountClass(); + } + + utils.renovation.renderComponent( + this, + this._$thead, + this.renovatedHeaderPanelComponent, + 'renovatedHeaderPanel', + { + dateHeaderData: this.viewDataProvider.dateHeaderData, + groupPanelData: this.viewDataProvider.getGroupPanelData( + this.generateRenderOptions(), + ), + dateCellTemplate: this.option('dateCellTemplate'), + timeCellTemplate: this.option('timeCellTemplate'), + groups: this.option('groups'), + groupByDate: this.isGroupedByDate(), + groupOrientation: this.option('groupOrientation'), + resourceCellTemplate: this.option('resourceCellTemplate'), + isRenderDateHeader, + }, + ); + } + + // ------------ + // DnD should be removed from work-space + // ------------ + + initDragBehavior(scheduler) { + if (!this.dragBehavior && scheduler) { + this.dragBehavior = new AppointmentDragBehavior(scheduler); + + const $rootElement = $(scheduler.element()); + + this._createDragBehavior(this.getWorkArea(), $rootElement); + this._createDragBehavior(this._$allDayPanel, $rootElement); + } + } + + _createDragBehavior($targetElement, $rootElement) { + const getItemData = (itemElement, appointments) => appointments._getItemData(itemElement); + const getItemSettings = ($itemElement) => $itemElement.data(APPOINTMENT_SETTINGS_KEY); + + const options = { + getItemData, + getItemSettings, + }; - // -------------- - // We do not need these methods in renovation - // -------------- - - _isRenderHeaderPanelEmptyCell() { - return this._isVerticalGroupedWorkSpace(); - } - - _dispose() { - super._dispose(); - - this.virtualScrollingDispatcher.dispose(); - } - - _getDefaultOptions() { - return extend(super._getDefaultOptions(), { - currentDate: new Date(), - intervalCount: 1, - startDate: null, - firstDayOfWeek: undefined, - startDayHour: 0, - endDayHour: 24, - hoursInterval: 0.5, - activeStateEnabled: true, - hoverStateEnabled: true, - groups: [], - showAllDayPanel: true, - allDayExpanded: false, - onCellClick: null, - crossScrollingEnabled: false, - dataCellTemplate: null, - timeCellTemplate: null, - resourceCellTemplate: null, - dateCellTemplate: null, - allowMultipleCellSelection: true, - indicatorTime: new Date(), - indicatorUpdateInterval: 5 * toMs('minute'), - shadeUntilCurrentTime: true, - groupOrientation: 'horizontal', - selectedCellData: [], - groupByDate: false, - scrolling: { - mode: 'standard', - }, - allDayPanelMode: 'all', - renovateRender: true, - height: undefined, - draggingMode: 'outlook', - onScrollEnd: () => {}, - getHeaderHeight: undefined, - onRenderAppointments: () => {}, - onShowAllDayPanel: () => {}, - onSelectedCellsClick: () => {}, - timeZoneCalculator: undefined, - schedulerHeight: undefined, - schedulerWidth: undefined, - }); - } + this._createDragBehaviorBase($targetElement, $rootElement, options); + } - _optionChanged(args) { - switch(args.name) { - case 'startDayHour': - validateDayHours(args.value, this.option('endDayHour')); - this._cleanWorkSpace(); - break; - case 'endDayHour': - validateDayHours(this.option('startDayHour'), args.value); - this._cleanWorkSpace(); - break; - case 'dateCellTemplate': - case 'resourceCellTemplate': - case 'dataCellTemplate': - case 'timeCellTemplate': - case 'hoursInterval': - case 'firstDayOfWeek': - case 'currentDate': - case 'startDate': - this._cleanWorkSpace(); - break; - case 'groups': - this._cleanView(); - this._removeAllDayElements(); - this._initGrouping(); - this.repaint(); - break; - case 'groupOrientation': - this._initGroupedStrategy(); - this._createAllDayPanelElements(); - this._removeAllDayElements(); - this._cleanWorkSpace(); - this._toggleGroupByDateClass(); - break; - case 'showAllDayPanel': - if(this._isVerticalGroupedWorkSpace()) { - this._cleanView(); - this._removeAllDayElements(); - this._initGrouping(); - this.repaint(); - } else { - if(!this.isRenovatedRender()) { - this._toggleAllDayVisibility(true); - } else { - this.renderWorkSpace(); - } - } - break; - case 'allDayExpanded': - this._changeAllDayVisibility(); - this._attachTablesEvents(); - this._updateScrollable(); - break; - case 'onSelectionChanged': - this._createSelectionChangedAction(); - break; - case 'onCellClick': - this._createCellClickAction(); - break; - case 'onCellContextMenu': - this._attachContextMenuEvent(); - break; - case 'intervalCount': - this._cleanWorkSpace(); - this._toggleWorkSpaceCountClass(); - break; - case 'groupByDate': - this._cleanWorkSpace(); - this._toggleGroupByDateClass(); - break; - case 'crossScrollingEnabled': - this._toggleHorizontalScrollClass(); - this._dateTableScrollable.option(this._dateTableScrollableConfig()); - break; - case 'allDayPanelMode': - this.updateShowAllDayPanel(); - this.updateAppointments(); - break; - case 'width': - super._optionChanged(args); - this._dimensionChanged(); - break; - case 'timeZoneCalculator': - case 'allowMultipleCellSelection': - break; - case 'selectedCellData': - break; - case 'renovateRender': - case 'scrolling': - this.repaint(); - break; - case 'schedulerHeight': - case 'schedulerWidth': - this.virtualScrollingDispatcher.updateDimensions(true); - break; - default: - super._optionChanged(args); - } - } + _createDragBehaviorBase(targetElement, rootElement, options) { + const container = (this.$element() as any).find(`.${FIXED_CONTAINER_CLASS}`); - updateShowAllDayPanel() { - const isHiddenAllDayPanel = this.option('allDayPanelMode') === 'hidden'; - this.option('onShowAllDayPanel')(!isHiddenAllDayPanel); - } + const disableDefaultDragging = () => { + if (!this.isDefaultDraggingMode) { + this.preventDefaultDragging = true; + } + }; - _getVirtualScrollingDispatcherOptions() { - return { - getCellHeight: this.getCellHeight.bind(this), - getCellWidth: this.getCellWidth.bind(this), - getCellMinWidth: this.getCellMinWidth.bind(this), - isRTL: this._isRTL.bind(this), - getSchedulerHeight: () => this.option('schedulerHeight'), - getSchedulerWidth: () => this.option('schedulerWidth'), - getViewHeight: () => this.$element().height ? this.$element().height() : getHeight(this.$element()), - getViewWidth: () => this.$element().width ? this.$element().width() : getWidth(this.$element()), - getWindowHeight: () => getWindow().innerHeight, - getWindowWidth: () => getWindow().innerWidth, - getScrolling: () => 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), - getGroupCount: this._getGroupCount.bind(this), - isVerticalGrouping: this._isVerticalGroupedWorkSpace.bind(this), - getTotalRowCount: this._getTotalRowCount.bind(this), - getTotalCellCount: this._getTotalCellCount.bind(this), - }; - } + const enableDefaultDragging = () => { + if (!this.isDefaultDraggingMode) { + this.preventDefaultDragging = false; + } + }; - _cleanWorkSpace() { + this.dragBehavior.addTo(targetElement, createDragBehaviorConfig( + container, + rootElement, + this.isDefaultDraggingMode, + this.dragBehavior, + enableDefaultDragging, + disableDefaultDragging, + () => this._getDroppableCell(), + () => this._getDateTables(), + () => this.removeDroppableCellClass(), + () => this.getCellWidth(), + options, + )); + } + + // -------------- + // We do not need these methods in renovation + // -------------- + + _isRenderHeaderPanelEmptyCell() { + return this._isVerticalGroupedWorkSpace(); + } + + _dispose() { + // @ts-expect-error + super._dispose(); + + this.virtualScrollingDispatcher.dispose(); + } + + _getDefaultOptions() { + // @ts-expect-error + return extend(super._getDefaultOptions(), { + currentDate: new Date(), + intervalCount: 1, + startDate: null, + firstDayOfWeek: undefined, + startDayHour: 0, + endDayHour: 24, + hoursInterval: 0.5, + activeStateEnabled: true, + hoverStateEnabled: true, + groups: [], + showAllDayPanel: true, + allDayExpanded: false, + onCellClick: null, + crossScrollingEnabled: false, + dataCellTemplate: null, + timeCellTemplate: null, + resourceCellTemplate: null, + dateCellTemplate: null, + allowMultipleCellSelection: true, + indicatorTime: new Date(), + indicatorUpdateInterval: 5 * toMs('minute'), + shadeUntilCurrentTime: true, + groupOrientation: 'horizontal', + selectedCellData: [], + groupByDate: false, + scrolling: { + mode: 'standard', + }, + allDayPanelMode: 'all', + renovateRender: true, + height: undefined, + draggingMode: 'outlook', + onScrollEnd: () => {}, + getHeaderHeight: undefined, + onRenderAppointments: () => {}, + onShowAllDayPanel: () => {}, + onSelectedCellsClick: () => {}, + timeZoneCalculator: undefined, + schedulerHeight: undefined, + schedulerWidth: undefined, + }); + } + + _optionChanged(args) { + switch (args.name) { + case 'startDayHour': + validateDayHours(args.value, this.option('endDayHour')!); + this._cleanWorkSpace(); + break; + case 'endDayHour': + validateDayHours(this.option('startDayHour')!, args.value); + this._cleanWorkSpace(); + break; + case 'dateCellTemplate': + case 'resourceCellTemplate': + case 'dataCellTemplate': + case 'timeCellTemplate': + case 'hoursInterval': + case 'firstDayOfWeek': + case 'currentDate': + case 'startDate': + this._cleanWorkSpace(); + break; + case 'groups': this._cleanView(); - this._toggleGroupedClass(); - this._toggleWorkSpaceWithOddCells(); - - this.virtualScrollingDispatcher.updateDimensions(true); - this._renderView(); - this.option('crossScrollingEnabled') && this._setTableSizes(); - this.cache.clear(); - } - - _init() { - this._scrollSync = {}; - this._viewDataProvider = null; - this._cellsSelectionState = null; - this._activeStateUnit = CELL_SELECTOR; - - super._init(); - + this._removeAllDayElements(); this._initGrouping(); - - this._toggleHorizontalScrollClass(); - this._toggleWorkSpaceCountClass(); - this._toggleGroupByDateClass(); - this._toggleWorkSpaceWithOddCells(); - - this.$element() - .addClass(COMPONENT_CLASS) - .addClass(this._getElementClass()); - } - - _initPositionHelper() { - this.positionHelper = new PositionHelper({ - key: this.option('key'), - viewDataProvider: this.viewDataProvider, - viewStartDayHour: this.option('startDayHour'), - viewEndDayHour: this.option('endDayHour'), - cellDuration: this.getCellDuration(), - groupedStrategy: this._groupedStrategy, - isGroupedByDate: this.isGroupedByDate(), - rtlEnabled: this.option('rtlEnabled'), - startViewDate: this.getStartViewDate(), - isVerticalGrouping: this._isVerticalGroupedWorkSpace(), - groupCount: this._getGroupCount(), - isVirtualScrolling: this.isVirtualScrolling(), - getDOMMetaDataCallback: this.getDOMElementsMetaData.bind(this), - }); - } - - _initGrouping() { + this.repaint(); + break; + case 'groupOrientation': this._initGroupedStrategy(); - this._toggleGroupingDirectionClass(); + this._createAllDayPanelElements(); + this._removeAllDayElements(); + this._cleanWorkSpace(); this._toggleGroupByDateClass(); + break; + case 'showAllDayPanel': + if (this._isVerticalGroupedWorkSpace()) { + this._cleanView(); + this._removeAllDayElements(); + this._initGrouping(); + this.repaint(); + } else if (!this.isRenovatedRender()) { + this._toggleAllDayVisibility(true); + } else { + this.renderWorkSpace(); + } + break; + case 'allDayExpanded': + this._changeAllDayVisibility(); + this._attachTablesEvents(); + this._updateScrollable(); + break; + case 'onSelectionChanged': + this._createSelectionChangedAction(); + break; + case 'onCellClick': + this._createCellClickAction(); + break; + case 'onCellContextMenu': + this._attachContextMenuEvent(); + break; + case 'intervalCount': + this._cleanWorkSpace(); + this._toggleWorkSpaceCountClass(); + break; + case 'groupByDate': + this._cleanWorkSpace(); + this._toggleGroupByDateClass(); + break; + case 'crossScrollingEnabled': + this._toggleHorizontalScrollClass(); + this._dateTableScrollable.option(this._dateTableScrollableConfig()); + break; + case 'allDayPanelMode': + this.updateShowAllDayPanel(); + this.updateAppointments(); + break; + case 'width': + // @ts-expect-error + super._optionChanged(args); + this._dimensionChanged(); + break; + case 'timeZoneCalculator': + case 'allowMultipleCellSelection': + break; + case 'selectedCellData': + break; + case 'renovateRender': + case 'scrolling': + this.repaint(); + break; + case 'schedulerHeight': + case 'schedulerWidth': + this.virtualScrollingDispatcher.updateDimensions(true); + break; + default: + // @ts-expect-error + super._optionChanged(args); } + } - isVerticalOrientation() { - const orientation = this.option('groups').length - ? this.option('groupOrientation') - : this._getDefaultGroupStrategy(); + updateShowAllDayPanel() { + const isHiddenAllDayPanel = this.option('allDayPanelMode') === 'hidden'; + (this.option('onShowAllDayPanel') as any)(!isHiddenAllDayPanel); + } - return orientation === 'vertical'; - } + _getVirtualScrollingDispatcherOptions() { + return { + getCellHeight: this.getCellHeight.bind(this), + getCellWidth: this.getCellWidth.bind(this), + getCellMinWidth: this.getCellMinWidth.bind(this), + 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())), + getWindowHeight: () => getWindow().innerHeight, + getWindowWidth: () => getWindow().innerWidth, + getScrolling: () => 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), + getGroupCount: this._getGroupCount.bind(this), + isVerticalGrouping: this._isVerticalGroupedWorkSpace.bind(this), + getTotalRowCount: this._getTotalRowCount.bind(this), + getTotalCellCount: this._getTotalCellCount.bind(this), + }; + } + + _cleanWorkSpace() { + this._cleanView(); + this._toggleGroupedClass(); + this._toggleWorkSpaceWithOddCells(); + + this.virtualScrollingDispatcher.updateDimensions(true); + this._renderView(); + this.option('crossScrollingEnabled') && this._setTableSizes(); + this.cache.clear(); + } + + _init() { + this._scrollSync = {}; + this._viewDataProvider = null; + this._cellsSelectionState = null; + this._activeStateUnit = CELL_SELECTOR; + + // @ts-expect-error + super._init(); + + this._initGrouping(); + + this._toggleHorizontalScrollClass(); + this._toggleWorkSpaceCountClass(); + this._toggleGroupByDateClass(); + this._toggleWorkSpaceWithOddCells(); + + (this.$element() as any) + .addClass(COMPONENT_CLASS) + .addClass(this._getElementClass()); + } + + _initPositionHelper() { + this.positionHelper = new PositionHelper({ + key: this.option('key'), + viewDataProvider: this.viewDataProvider, + viewStartDayHour: this.option('startDayHour'), + viewEndDayHour: this.option('endDayHour'), + cellDuration: this.getCellDuration(), + groupedStrategy: this._groupedStrategy, + isGroupedByDate: this.isGroupedByDate(), + rtlEnabled: this.option('rtlEnabled'), + startViewDate: this.getStartViewDate(), + isVerticalGrouping: this._isVerticalGroupedWorkSpace(), + groupCount: this._getGroupCount(), + isVirtualScrolling: this.isVirtualScrolling(), + getDOMMetaDataCallback: this.getDOMElementsMetaData.bind(this), + }); + } + + _initGrouping() { + this._initGroupedStrategy(); + this._toggleGroupingDirectionClass(); + this._toggleGroupByDateClass(); + } + + isVerticalOrientation() { + const orientation = this.option('groups')?.length + ? this.option('groupOrientation') + : this._getDefaultGroupStrategy(); + + return orientation === 'vertical'; + } + + _initGroupedStrategy() { + const Strategy = this.isVerticalOrientation() + ? VerticalGroupedStrategy + : HorizontalGroupedStrategy; + + this._groupedStrategy = new Strategy(this); + } + + _getDefaultGroupStrategy() { + return 'horizontal'; + } + + _toggleHorizontalScrollClass() { + (this.$element() as any).toggleClass(WORKSPACE_WITH_BOTH_SCROLLS_CLASS, this.option('crossScrollingEnabled')); + } + + _toggleGroupByDateClass() { + (this.$element() as any).toggleClass(WORKSPACE_WITH_GROUP_BY_DATE_CLASS, this.isGroupedByDate()); + } + + _toggleWorkSpaceCountClass() { + (this.$element() as any).toggleClass(WORKSPACE_WITH_COUNT_CLASS, this._isWorkSpaceWithCount()); + } + + _toggleWorkSpaceWithOddCells() { + (this.$element() as any).toggleClass(WORKSPACE_WITH_ODD_CELLS_CLASS, this._isWorkspaceWithOddCells()); + } + + _toggleGroupingDirectionClass() { + (this.$element() as any).toggleClass(VERTICAL_GROUPED_WORKSPACE_CLASS, this._isVerticalGroupedWorkSpace()); + } + + _getDateTableCellClass(rowIndex?: any, columnIndex?: any) { + const cellClass = `${DATE_TABLE_CELL_CLASS} ${HORIZONTAL_SIZES_CLASS} ${VERTICAL_SIZES_CLASS}`; + + return this._groupedStrategy + .addAdditionalGroupCellClasses(cellClass, columnIndex + 1, rowIndex, columnIndex); + } + + _getGroupHeaderClass(i?: any) { + const cellClass = GROUP_HEADER_CLASS; + + return this._groupedStrategy.addAdditionalGroupCellClasses(cellClass, i + 1); + } + + _initWorkSpaceUnits() { + this._$headerPanelContainer = $('
').addClass('dx-scheduler-header-panel-container'); + this._$headerTablesContainer = $('
').addClass('dx-scheduler-header-tables-container'); + this._$headerPanel = $('
'); + setHeight($td, this._getRowHeight(rowSize)); + + if (options.getStartDate) { + date = options.getStartDate && options.getStartDate(rowIndex); + cellDateNumber = dateLocalization.format(date, 'd'); + cellDayName = dateLocalization.format(date, formatWeekday); } - if(this._$noDataContainer) { - this._$noDataContainer.empty(); - this._$noDataContainer.remove(); + if (cellTemplateOpt && cellTemplateOpt.render) { + const templateOptions = this._prepareCellTemplateOptions(`${cellDateNumber} ${cellDayName}`, date, i, $td); - delete this._$noDataContainer; + cellTemplates.push(cellTemplateOpt.render.bind(cellTemplateOpt, templateOptions)); + } else if (cellDateNumber && cellDayName) { + $td.addClass(AGENDA_DATE_CLASS).text(`${cellDateNumber} ${cellDayName}`); } - } - - _createWorkSpaceElements() { - this._createWorkSpaceStaticElements(); - } - _createWorkSpaceStaticElements() { - this._$dateTableContainer.append(this._$dateTable); - this._dateTableScrollable.$content().append(this._$dateTableScrollableContent); + if (options.rowClass) { + $row.addClass(options.rowClass); + } - if(this._$groupTable) { - this._$dateTableScrollableContent.prepend(this._$groupTable); + if (options.cellClass) { + $td.addClass(options.cellClass); } - this._$dateTableScrollableContent.append(this._$timePanel, this._$dateTableContainer); - this.$element().append(this._dateTableScrollable.$element()); - } + $row.append($td); + this._$rows.push($row); + } + }.bind(this); - _renderDateTable() { - this._renderTableBody({ - container: getPublicElement(this._$dateTable), - rowClass: DATE_TABLE_ROW_CLASS, - cellClass: this._getDateTableCellClass() - }); + for (i = 0; i < this._rows.length; i++) { + each(this._rows[i], fillTableBody); + this._setLastRowClass(); } - _attachTablesEvents() { return noop(); } - _attachEvents() { return noop(); } - _cleanCellDataCache() { return noop(); } + $(options.container).append($('
'); - setHeight($td, this._getRowHeight(rowSize)); - - if(options.getStartDate) { - date = options.getStartDate && options.getStartDate(rowIndex); - cellDateNumber = dateLocalization.format(date, 'd'); - cellDayName = dateLocalization.format(date, formatWeekday); - } - - if(cellTemplateOpt && cellTemplateOpt.render) { - const templateOptions = this._prepareCellTemplateOptions(cellDateNumber + ' ' + cellDayName, date, i, $td); - - cellTemplates.push(cellTemplateOpt.render.bind(cellTemplateOpt, templateOptions)); - } else { - if(cellDateNumber && cellDayName) { - $td.addClass(AGENDA_DATE_CLASS).text(cellDateNumber + ' ' + cellDayName); - } - } - - if(options.rowClass) { - $row.addClass(options.rowClass); - } - - if(options.cellClass) { - $td.addClass(options.cellClass); - } - - $row.append($td); - this._$rows.push($row); - } - }).bind(this); - - for(i = 0; i < this._rows.length; i++) { - each(this._rows[i], fillTableBody); - this._setLastRowClass(); - } + _renderTimePanel() { + this._renderTableBody({ + container: getPublicElement(this._$timePanel), + rowCount: this._getTimePanelRowCount(), + cellCount: 1, + rowClass: TIME_PANEL_ROW_CLASS, + cellClass: TIME_PANEL_CELL_CLASS, + cellTemplate: this.option('dateCellTemplate'), + getStartDate: this._getTimePanelStartDate.bind(this), + }); + } - $(options.container).append($('
'); - const text = formatWeekdayAndDay(currentDate); - - if(cellTemplate) { - const templateOptions = { - model: { - text, - date: new Date(currentDate), - ...this._getGroupsForDateHeaderTemplate(templateIndex, colSpan), - }, - container: $th, - index: templateIndex, - }; - - cellTemplate.render(templateOptions); - } else { - $th.text(text); - } - - $th - .addClass(HEADER_PANEL_CELL_CLASS) - .addClass(HEADER_PANEL_WEEK_CELL_CLASS) - .attr('colSpan', colSpan); - - $cells.push($th); - - if((templateIndex % cellsInGroup) === (cellsInGroup - 1)) { - currentDate = new Date(firstViewDate); - } else { - this._incrementDate(currentDate); - } - } - - const $row = $('
'); + const text = formatWeekdayAndDay(currentDate); - $indicator.css('left', rtlOffset ? rtlOffset - width - offset : width + offset); - } + if (cellTemplate) { + const templateOptions = { + model: { + text, + date: new Date(currentDate), + ...this._getGroupsForDateHeaderTemplate(templateIndex, colSpan), + }, + container: $th, + index: templateIndex, + }; + + cellTemplate.render(templateOptions); + } else { + $th.text(text); } - } - _makeGroupRows(groups, groupByDate) { - const tableCreatorStrategy = this.option('groupOrientation') === 'vertical' ? tableCreator.VERTICAL : tableCreator.HORIZONTAL; + $th + .addClass(HEADER_PANEL_CELL_CLASS) + .addClass(HEADER_PANEL_WEEK_CELL_CLASS) + .attr('colSpan', colSpan); - return tableCreator.makeGroupedTable(tableCreatorStrategy, - groups, { - groupRowClass: GROUP_ROW_CLASS, - groupHeaderRowClass: GROUP_ROW_CLASS, - groupHeaderClass: this._getGroupHeaderClass.bind(this), - groupHeaderContentClass: GROUP_HEADER_CONTENT_CLASS, - }, - this._getCellCount() || 1, - this.option('resourceCellTemplate'), - this._getTotalRowCount(this._getGroupCount()), - groupByDate); - } + $cells.push($th); + + if ((templateIndex % cellsInGroup) === (cellsInGroup - 1)) { + currentDate = new Date(firstViewDate); + } else { + this._incrementDate(currentDate); + } + } + + const $row = $('
'); + this._$thead = $('').appendTo(this._$headerPanel); + this._$headerPanelEmptyCell = $('
').addClass('dx-scheduler-header-panel-empty-cell'); + this._$allDayTable = $('
'); + + this._$fixedContainer = $('
').addClass(FIXED_CONTAINER_CLASS); + this._$allDayContainer = $('
').addClass(ALL_DAY_CONTAINER_CLASS); + this._$dateTableScrollableContent = $('
').addClass('dx-scheduler-date-table-scrollable-content'); + this._$sidebarScrollableContent = $('
').addClass('dx-scheduler-side-bar-scrollable-content'); + + this._initAllDayPanelElements(); + + if (this.isRenovatedRender()) { + this.createRAllDayPanelElements(); + } else { + this._createAllDayPanelElements(); + } + + this._$timePanel = $('
').addClass(TIME_PANEL_CLASS); + this._$dateTable = $('
'); + this._$dateTableContainer = $('
').addClass('dx-scheduler-date-table-container'); + this._$groupTable = $('
').addClass(WORKSPACE_VERTICAL_GROUP_TABLE_CLASS); + } + + _initAllDayPanelElements() { + this._allDayTitles = []; + this._allDayTables = []; + this._allDayPanels = []; + } + + _initDateTableScrollable() { + const $dateTableScrollable = $('
').addClass(SCHEDULER_DATE_TABLE_SCROLLABLE_CLASS); + + // @ts-expect-error + this._dateTableScrollable = this._createComponent($dateTableScrollable, Scrollable, this._dateTableScrollableConfig()); + this._scrollSync.dateTable = getMemoizeScrollTo(() => this._dateTableScrollable); + } + + _createWorkSpaceElements() { + if (this.option('crossScrollingEnabled')) { + this._createWorkSpaceScrollableElements(); + } else { + this._createWorkSpaceStaticElements(); + } + } + + _createWorkSpaceStaticElements() { + this._$dateTableContainer.append(this._$dateTable); + + if (this._isVerticalGroupedWorkSpace()) { + this._$dateTableContainer.append(this._$allDayContainer); + this._$dateTableScrollableContent.append( + this._$groupTable, + this._$timePanel, + this._$dateTableContainer, + ); + this._dateTableScrollable.$content().append( + this._$dateTableScrollableContent, + ); + + this._$headerTablesContainer.append(this._$headerPanel); + } else { + this._$dateTableScrollableContent.append( + 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._appendHeaderPanelEmptyCellIfNecessary(); + this._$headerPanelContainer.append(this._$headerTablesContainer); + + this.$element().append( + this._$fixedContainer, + this._$headerPanelContainer, + this._dateTableScrollable.$element(), + ); + } + + _createWorkSpaceScrollableElements() { + this.$element().append(this._$fixedContainer); + + this._$flexContainer = $('
').addClass('dx-scheduler-work-space-flex-container'); - _initGroupedStrategy() { - const Strategy = this.isVerticalOrientation() - ? VerticalGroupedStrategy - : HorizontalGroupedStrategy; + this._createHeaderScrollable(); + + this._headerScrollable.$content().append(this._$headerPanel); + this._appendHeaderPanelEmptyCellIfNecessary(); + this._$headerPanelContainer.append(this._$headerTablesContainer); - this._groupedStrategy = new Strategy(this); - } + this.$element().append(this._$headerPanelContainer); + this.$element().append(this._$flexContainer); - _getDefaultGroupStrategy() { - return 'horizontal'; - } + this._createSidebarScrollable(); + this._$flexContainer.append(this._dateTableScrollable.$element()); - _toggleHorizontalScrollClass() { - this.$element().toggleClass(WORKSPACE_WITH_BOTH_SCROLLS_CLASS, this.option('crossScrollingEnabled')); - } + this._$dateTableContainer.append(this._$dateTable); + this._$dateTableScrollableContent.append(this._$dateTableContainer); - _toggleGroupByDateClass() { - this.$element().toggleClass(WORKSPACE_WITH_GROUP_BY_DATE_CLASS, this.isGroupedByDate()); - } + this._dateTableScrollable.$content().append(this._$dateTableScrollableContent); - _toggleWorkSpaceCountClass() { - this.$element().toggleClass(WORKSPACE_WITH_COUNT_CLASS, this._isWorkSpaceWithCount()); + if (this._isVerticalGroupedWorkSpace()) { + this._$dateTableContainer.append(this._$allDayContainer); + this._$sidebarScrollableContent.append(this._$groupTable, this._$timePanel); + } else { + this._headerScrollable.$content().append(this._$allDayPanel); + this._$allDayPanel?.append(this._$allDayContainer, this._$allDayTable); + this._$sidebarScrollableContent.append(this._$timePanel); } - _toggleWorkSpaceWithOddCells() { - this.$element().toggleClass(WORKSPACE_WITH_ODD_CELLS_CLASS, this._isWorkspaceWithOddCells()); - } + this._sidebarScrollable.$content().append(this._$sidebarScrollableContent); + } + + _appendHeaderPanelEmptyCellIfNecessary() { + this._isRenderHeaderPanelEmptyCell() && this._$headerPanelContainer.append(this._$headerPanelEmptyCell); + } + + _createHeaderScrollable() { + const $headerScrollable = $('
') + .addClass(SCHEDULER_HEADER_SCROLLABLE_CLASS) + .appendTo(this._$headerTablesContainer); + + // @ts-expect-error + this._headerScrollable = this._createComponent($headerScrollable, Scrollable, this._headerScrollableConfig()); + this._scrollSync.header = getMemoizeScrollTo(() => this._headerScrollable); + } - _toggleGroupingDirectionClass() { - this.$element().toggleClass(VERTICAL_GROUPED_WORKSPACE_CLASS, this._isVerticalGroupedWorkSpace()); - } + _createSidebarScrollable() { + const $timePanelScrollable = $('
') + .addClass(SCHEDULER_SIDEBAR_SCROLLABLE_CLASS) + .appendTo(this._$flexContainer); - _getDateTableCellClass(rowIndex, columnIndex) { - const cellClass = DATE_TABLE_CELL_CLASS + ' ' + HORIZONTAL_SIZES_CLASS + ' ' + VERTICAL_SIZES_CLASS; + // @ts-expect-error + this._sidebarScrollable = this._createComponent($timePanelScrollable, Scrollable, { + useKeyboard: false, + showScrollbar: 'never', + direction: 'vertical', + useNative: false, + updateManually: true, + bounceEnabled: false, + onScroll: (event) => { + this._scrollSync.dateTable({ top: event.scrollOffset.top }); + }, + }); + this._scrollSync.sidebar = getMemoizeScrollTo(() => this._sidebarScrollable); + } - return this._groupedStrategy - .addAdditionalGroupCellClasses(cellClass, columnIndex + 1, rowIndex, columnIndex); - } + _attachTableClasses() { + this._addTableClass(this._$dateTable, DATE_TABLE_CLASS); - _getGroupHeaderClass(i) { - const cellClass = GROUP_HEADER_CLASS; + if (this._isVerticalGroupedWorkSpace()) { + const groupCount = this._getGroupCount(); - return this._groupedStrategy.addAdditionalGroupCellClasses(cellClass, i + 1); + for (let i = 0; i < groupCount; i++) { + this._addTableClass(this._allDayTables[i], ALL_DAY_TABLE_CLASS); + } + } else if (!this.isRenovatedRender()) { + this._addTableClass(this._$allDayTable, ALL_DAY_TABLE_CLASS); } + } - _initWorkSpaceUnits() { - this._$headerPanelContainer = $('
').addClass('dx-scheduler-header-panel-container'); - this._$headerTablesContainer = $('
').addClass('dx-scheduler-header-tables-container'); - this._$headerPanel = $('
'); - this._$thead = $('').appendTo(this._$headerPanel); - this._$headerPanelEmptyCell = $('
').addClass('dx-scheduler-header-panel-empty-cell'); - this._$allDayTable = $('
'); + _attachHeaderTableClasses() { + this._addTableClass(this._$headerPanel, HEADER_PANEL_CLASS); + } - this._$fixedContainer = $('
').addClass(FIXED_CONTAINER_CLASS); - this._$allDayContainer = $('
').addClass(ALL_DAY_CONTAINER_CLASS); - this._$dateTableScrollableContent = $('
').addClass('dx-scheduler-date-table-scrollable-content'); - this._$sidebarScrollableContent = $('
').addClass('dx-scheduler-side-bar-scrollable-content'); + _addTableClass($el, className) { + ($el && !$el.hasClass(className)) && $el.addClass(className); + } - this._initAllDayPanelElements(); + _initMarkup() { + this.cache.clear(); - if(this.isRenovatedRender()) { - this.createRAllDayPanelElements(); - } else { - this._createAllDayPanelElements(); - } + this._initWorkSpaceUnits(); - this._$timePanel = $('
').addClass(TIME_PANEL_CLASS); - this._$dateTable = $('
'); - this._$dateTableContainer = $('
').addClass('dx-scheduler-date-table-container'); - this._$groupTable = $('
').addClass(WORKSPACE_VERTICAL_GROUP_TABLE_CLASS); - } + this._initVirtualScrolling(); - _initAllDayPanelElements() { - this._allDayTitles = []; - this._allDayTables = []; - this._allDayPanels = []; - } + this._initDateTableScrollable(); - _initDateTableScrollable() { - const $dateTableScrollable = $('
').addClass(SCHEDULER_DATE_TABLE_SCROLLABLE_CLASS); + this._createWorkSpaceElements(); - this._dateTableScrollable = this._createComponent($dateTableScrollable, Scrollable, this._dateTableScrollableConfig()); - this._scrollSync.dateTable = getMemoizeScrollTo(() => this._dateTableScrollable); - } + // @ts-expect-error + super._initMarkup(); - _createWorkSpaceElements() { - if(this.option('crossScrollingEnabled')) { - this._createWorkSpaceScrollableElements(); - } else { - this._createWorkSpaceStaticElements(); - } + if (!this.option('crossScrollingEnabled')) { + this._attachTableClasses(); + this._attachHeaderTableClasses(); } - _createWorkSpaceStaticElements() { - this._$dateTableContainer.append(this._$dateTable); + this._toggleGroupedClass(); - if(this._isVerticalGroupedWorkSpace()) { - this._$dateTableContainer.append(this._$allDayContainer); - this._$dateTableScrollableContent.append( - this._$groupTable, - this._$timePanel, - this._$dateTableContainer, - ); - this._dateTableScrollable.$content().append( - this._$dateTableScrollableContent, - ); + this._renderView(); + this._attachEvents(); + } + + _render() { + // @ts-expect-error + super._render(); + this._renderDateTimeIndication(); + this._setIndicationUpdateInterval(); + } - this._$headerTablesContainer.append(this._$headerPanel); - } else { - this._$dateTableScrollableContent.append( - this._$timePanel, - this._$dateTableContainer, - ); - this._dateTableScrollable.$content().append(this._$dateTableScrollableContent); - - this._$headerTablesContainer.append(this._$headerPanel, this._$allDayPanel); - this._$allDayPanel?.append(this._$allDayContainer, this._$allDayTable); - } + _toggleGroupedClass() { + (this.$element() as any).toggleClass(GROUPED_WORKSPACE_CLASS, this._getGroupCount() > 0); + } - this._appendHeaderPanelEmptyCellIfNecessary(); - this._$headerPanelContainer.append(this._$headerTablesContainer); - - this.$element().append( - this._$fixedContainer, - this._$headerPanelContainer, - this._dateTableScrollable.$element(), - ); + _renderView() { + if (this.isRenovatedRender()) { + if (this._isVerticalGroupedWorkSpace()) { + this.renderRGroupPanel(); + } + } else { + this._applyCellTemplates( + this._renderGroupHeader(), + ); } - _createWorkSpaceScrollableElements() { - this.$element().append(this._$fixedContainer); - - this._$flexContainer = $('
').addClass('dx-scheduler-work-space-flex-container'); - - this._createHeaderScrollable(); - - this._headerScrollable.$content().append(this._$headerPanel); - this._appendHeaderPanelEmptyCellIfNecessary(); - this._$headerPanelContainer.append(this._$headerTablesContainer); - - this.$element().append(this._$headerPanelContainer); - this.$element().append(this._$flexContainer); - - this._createSidebarScrollable(); - this._$flexContainer.append(this._dateTableScrollable.$element()); - - this._$dateTableContainer.append(this._$dateTable); - this._$dateTableScrollableContent.append(this._$dateTableContainer); - - this._dateTableScrollable.$content().append(this._$dateTableScrollableContent); - - if(this._isVerticalGroupedWorkSpace()) { - this._$dateTableContainer.append(this._$allDayContainer); - this._$sidebarScrollableContent.append(this._$groupTable, this._$timePanel); - } else { - this._headerScrollable.$content().append(this._$allDayPanel); - this._$allDayPanel?.append(this._$allDayContainer, this._$allDayTable); - this._$sidebarScrollableContent.append(this._$timePanel); - } - - this._sidebarScrollable.$content().append(this._$sidebarScrollableContent); + this.renderWorkSpace(); + if (this.isRenovatedRender()) { + this.virtualScrollingDispatcher.updateDimensions(); } - _appendHeaderPanelEmptyCellIfNecessary() { - this._isRenderHeaderPanelEmptyCell() && this._$headerPanelContainer.append(this._$headerPanelEmptyCell); - } + this._updateGroupTableHeight(); + this.updateHeaderEmptyCellWidth(); - _createHeaderScrollable() { - const $headerScrollable = $('
') - .addClass(SCHEDULER_HEADER_SCROLLABLE_CLASS) - .appendTo(this._$headerTablesContainer); + this._shader = new VerticalShader(this); + } - this._headerScrollable = this._createComponent($headerScrollable, Scrollable, this._headerScrollableConfig()); - this._scrollSync.header = getMemoizeScrollTo(() => this._headerScrollable); - } + updateCellsSelection() { + const renderOptions = this.generateRenderOptions(); + this.viewDataProvider.updateViewData(renderOptions); + this.renderRWorkSpace({ + timePanel: true, + dateTable: true, + allDayPanel: true, + }); + } - _createSidebarScrollable() { - const $timePanelScrollable = $('
') - .addClass(SCHEDULER_SIDEBAR_SCROLLABLE_CLASS) - .appendTo(this._$flexContainer); + _renderDateTimeIndication() { return noop(); } - this._sidebarScrollable = this._createComponent($timePanelScrollable, Scrollable, { - useKeyboard: false, - showScrollbar: 'never', - direction: 'vertical', - useNative: false, - updateManually: true, - bounceEnabled: false, - onScroll: (event) => { - this._scrollSync.dateTable({ top: event.scrollOffset.top }); - } - }); - this._scrollSync.sidebar = getMemoizeScrollTo(() => this._sidebarScrollable); - } + _setIndicationUpdateInterval() { return noop(); } - _attachTableClasses() { - this._addTableClass(this._$dateTable, DATE_TABLE_CLASS); + _refreshDateTimeIndication() { return noop(); } - if(this._isVerticalGroupedWorkSpace()) { - const groupCount = this._getGroupCount(); + _detachGroupCountClass() { + [ + ...VERTICAL_GROUP_COUNT_CLASSES, + ].forEach((className) => { + (this.$element() as any).removeClass(className); + }); + } - for(let i = 0; i < groupCount; i++) { - this._addTableClass(this._allDayTables[i], ALL_DAY_TABLE_CLASS); - } - } else { - if(!this.isRenovatedRender()) { - this._addTableClass(this._$allDayTable, ALL_DAY_TABLE_CLASS); - } - } - } + _attachGroupCountClass() { + const className = this._groupedStrategy.getGroupCountClass(this.option('groups')); - _attachHeaderTableClasses() { - this._addTableClass(this._$headerPanel, HEADER_PANEL_CLASS); - } + (this.$element() as any).addClass(className); + } - _addTableClass($el, className) { - ($el && !$el.hasClass(className)) && $el.addClass(className); - } + _getDateHeaderTemplate() { + return this.option('dateCellTemplate'); + } - _initMarkup() { - this.cache.clear(); + _toggleAllDayVisibility(isUpdateScrollable) { + const showAllDayPanel = this._isShowAllDayPanel(); + (this.$element() as any).toggleClass(WORKSPACE_WITH_ALL_DAY_CLASS, showAllDayPanel); - this._initWorkSpaceUnits(); + this._changeAllDayVisibility(); + isUpdateScrollable && this._updateScrollable(); + } - this._initVirtualScrolling(); + _changeAllDayVisibility() { + this.cache.clear(); + (this.$element() as any).toggleClass(WORKSPACE_WITH_COLLAPSED_ALL_DAY_CLASS, !this.option('allDayExpanded') && this._isShowAllDayPanel()); + } - this._initDateTableScrollable(); + _getDateTables() { + return this._$dateTable.add(this._$allDayTable); + } - this._createWorkSpaceElements(); + _getDateTable() { + return this._$dateTable; + } - super._initMarkup(); + _removeAllDayElements() { + this._$allDayTable && this._$allDayTable.remove(); + this._$allDayTitle && this._$allDayTitle.remove(); + } - if(!this.option('crossScrollingEnabled')) { - this._attachTableClasses(); - this._attachHeaderTableClasses(); - } + _cleanView() { + this.cache.clear(); + this._cleanTableWidths(); + this.cellsSelectionState.clearSelectedAndFocusedCells(); + if (!this.isRenovatedRender()) { + this._$thead.empty(); + this._$dateTable.empty(); + this._$timePanel.empty(); + this._$groupTable.empty(); - this._toggleGroupedClass(); - - this._renderView(); - this._attachEvents(); + this._$allDayTable?.empty(); + this._$sidebarTable?.empty(); } - _render() { - super._render(); - this._renderDateTimeIndication(); - this._setIndicationUpdateInterval(); - } + this._shader?.clean(); - _toggleGroupedClass() { - this.$element().toggleClass(GROUPED_WORKSPACE_CLASS, this._getGroupCount() > 0); - } + delete this._interval; + } - _renderView() { - if(this.isRenovatedRender()) { - if(this._isVerticalGroupedWorkSpace()) { - this.renderRGroupPanel(); - } - } else { - this._applyCellTemplates( - this._renderGroupHeader() - ); - } + _clean() { + (eventsEngine.off as any)(domAdapter.getDocument(), SCHEDULER_CELL_DXPOINTERUP_EVENT_NAME); + this._disposeRenovatedComponents(); - this.renderWorkSpace(); - if(this.isRenovatedRender()) { - this.virtualScrollingDispatcher.updateDimensions(); - } + // @ts-expect-error + super._clean(); + } - this._updateGroupTableHeight(); - this.updateHeaderEmptyCellWidth(); + _cleanTableWidths() { + this._$headerPanel.css('width', ''); + this._$dateTable.css('width', ''); + this._$allDayTable && this._$allDayTable.css('width', ''); + } - this._shader = new VerticalShader(this); - } + _disposeRenovatedComponents() { + this.renovatedAllDayPanel?.dispose(); + this.renovatedAllDayPanel = undefined; - updateCellsSelection() { - const renderOptions = this.generateRenderOptions(); - this.viewDataProvider.updateViewData(renderOptions); - this.renderRWorkSpace({ - timePanel: true, - dateTable: true, - allDayPanel: true - }); - } + this.renovatedDateTable?.dispose(); + this.renovatedDateTable = undefined; - _renderDateTimeIndication() { return noop(); } - _setIndicationUpdateInterval() { return noop(); } - _refreshDateTimeIndication() { return noop(); } + this.renovatedTimePanel?.dispose(); + this.renovatedTimePanel = undefined; - _detachGroupCountClass() { - [ - ...VERTICAL_GROUP_COUNT_CLASSES, - ].forEach((className) => { - this.$element().removeClass(className); - }); - } + this.renovatedGroupPanel?.dispose(); + this.renovatedGroupPanel = undefined; - _attachGroupCountClass() { - const className = this._groupedStrategy.getGroupCountClass(this.option('groups')); + this.renovatedHeaderPanel?.dispose(); + this.renovatedHeaderPanel = undefined; + } - this.$element().addClass(className); - } + getGroupedStrategy() { + return this._groupedStrategy; + } - _getDateHeaderTemplate() { - return this.option('dateCellTemplate'); - } + getFixedContainer() { + return this._$fixedContainer; + } - _toggleAllDayVisibility(isUpdateScrollable) { - const showAllDayPanel = this._isShowAllDayPanel(); - this.$element().toggleClass(WORKSPACE_WITH_ALL_DAY_CLASS, showAllDayPanel); + getAllDayContainer() { + return this._$allDayContainer; + } - this._changeAllDayVisibility(); - isUpdateScrollable && this._updateScrollable(); - } + updateRender() { + this.renderer.updateRender(); + } - _changeAllDayVisibility() { - this.cache.clear(); - this.$element().toggleClass(WORKSPACE_WITH_COLLAPSED_ALL_DAY_CLASS, !this.option('allDayExpanded') && this._isShowAllDayPanel()); - } + updateGrid() { + this.renderer._renderGrid(); + } - _getDateTables() { - return this._$dateTable.add(this._$allDayTable); - } + updateAppointments() { + (this.option('onRenderAppointments') as any)(); + this.dragBehavior?.updateDragSource(); + } - _getDateTable() { - return this._$dateTable; - } + // ---------------- + // These methods should be deleted when we get rid of old render + // ---------------- - _removeAllDayElements() { - this._$allDayTable && this._$allDayTable.remove(); - this._$allDayTitle && this._$allDayTitle.remove(); - } + _createAllDayPanelElements() { + const groupCount = this._getGroupCount(); - _cleanView() { - this.cache.clear(); - this._cleanTableWidths(); - this.cellsSelectionState.clearSelectedAndFocusedCells(); - if(!this.isRenovatedRender()) { - this._$thead.empty(); - this._$dateTable.empty(); - this._$timePanel.empty(); - this._$groupTable.empty(); + if (this._isVerticalGroupedWorkSpace() && groupCount !== 0) { + for (let i = 0; i < groupCount; i++) { + const $allDayTitle = $('
') + .addClass(ALL_DAY_TITLE_CLASS) + .text(messageLocalization.format('dxScheduler-allDay')); - this._$allDayTable?.empty(); - this._$sidebarTable?.empty(); - } + this._allDayTitles.push($allDayTitle); - this._shader?.clean(); + this._$allDayTable = $('
'); + this._allDayTables.push(this._$allDayTable); + + this._$allDayPanel = $('
') + .addClass(ALL_DAY_PANEL_CLASS) + .append(this._$allDayTable); + + this._allDayPanels.push(this._$allDayPanel); + } + } else { + this._$allDayTitle = $('
') + .addClass(ALL_DAY_TITLE_CLASS) + .text(messageLocalization.format('dxScheduler-allDay')) + .appendTo(this.$element()); + + this._$allDayTable = $('
'); + + this._$allDayPanel = $('
') + .addClass(ALL_DAY_PANEL_CLASS) + .append(this._$allDayTable); + } + } + + renderWorkSpace(isGenerateNewViewData = true) { + this.cache.clear(); + + this.viewDataProvider.update(this.generateRenderOptions(), isGenerateNewViewData); + + if (this.isRenovatedRender()) { + this.renderRWorkSpace(); + } else { + this._renderDateHeader(); + this._renderTimePanel(); + this._renderGroupAllDayPanel(); + this._renderDateTable(); + this._renderAllDayPanel(); + } + + this._initPositionHelper(); + } + + _renderGroupHeader() { + const $container = this._getGroupHeaderContainer(); + const groupCount = this._getGroupCount(); + let cellTemplates = []; + if (groupCount) { + const groupRows = this._makeGroupRows(this.option('groups'), this.option('groupByDate')); + this._attachGroupCountClass(); + $container.append(groupRows.elements); + cellTemplates = groupRows.cellTemplates; + } else { + this._detachGroupCountClass(); + } + + return cellTemplates; + } + + _applyCellTemplates(templates) { + templates?.forEach((template) => { + template(); + }); + } + + _makeGroupRows(groups, groupByDate): any { + const tableCreatorStrategy = this._isVerticalGroupedWorkSpace() ? tableCreator.VERTICAL : tableCreator.HORIZONTAL; + + return tableCreator.makeGroupedTable( + tableCreatorStrategy, + groups, + { + groupHeaderRowClass: GROUP_ROW_CLASS, + groupRowClass: GROUP_ROW_CLASS, + groupHeaderClass: this._getGroupHeaderClass.bind(this), + groupHeaderContentClass: GROUP_HEADER_CONTENT_CLASS, + }, + this._getCellCount() || 1, + this.option('resourceCellTemplate'), + this._getGroupCount(), + groupByDate, + ); + } + + _renderDateHeader(): any { + const container = this._getDateHeaderContainer(); + const $headerRow = $('
').addClass(HEADER_ROW_CLASS); + const count = this._getCellCount(); + const cellTemplate = this._getDateHeaderTemplate(); + const repeatCount = this._getCalculateHeaderCellRepeatCount(); + const templateCallbacks = []; + const groupByDate = this.isGroupedByDate(); + + if (!groupByDate) { + for (let rowIndex = 0; rowIndex < repeatCount; rowIndex++) { + for (let columnIndex = 0; columnIndex < count; columnIndex++) { + const templateIndex = rowIndex * count + columnIndex; + this._renderDateHeaderTemplate($headerRow, columnIndex, templateIndex, cellTemplate, templateCallbacks); + } + } + + container.append($headerRow); + } else { + const colSpan = groupByDate ? this._getGroupCount() : 1; + + for (let columnIndex = 0; columnIndex < count; columnIndex++) { + const templateIndex = columnIndex * repeatCount; + const cellElement = this._renderDateHeaderTemplate($headerRow, columnIndex, templateIndex, cellTemplate, templateCallbacks); + cellElement.attr('colSpan', colSpan); + } + + container.prepend($headerRow); + } + + this._applyCellTemplates(templateCallbacks); + + return $headerRow; + } + + _renderDateHeaderTemplate(container, panelCellIndex, templateIndex, cellTemplate, templateCallbacks) { + const validTemplateIndex = this.isGroupedByDate() + ? Math.floor(templateIndex / this._getGroupCount()) + : templateIndex; + const { completeDateHeaderMap } = this.viewDataProvider; + + const { + text, startDate: date, + } = completeDateHeaderMap[completeDateHeaderMap.length - 1][validTemplateIndex]; + const $cell = $('
') + .addClass(this._getHeaderPanelCellClass(panelCellIndex)) + .attr('title', text); + + if (cellTemplate?.render) { + templateCallbacks.push(cellTemplate.render.bind(cellTemplate, { + model: { + text, + date, + ...this._getGroupsForDateHeaderTemplate(templateIndex), + }, + index: templateIndex, + container: getPublicElement($cell), + })); + } else { + $cell.text(text); + } + + container.append($cell); + return $cell; + } + + _getGroupsForDateHeaderTemplate(templateIndex, indexMultiplier = 1) { + let groupIndex; + let groups; + + if (this._isHorizontalGroupedWorkSpace() && !this.isGroupedByDate()) { + groupIndex = this._getGroupIndex(0, templateIndex * indexMultiplier); + const groupsArray = getCellGroups( + groupIndex, + this.option('groups'), + ); + + groups = getGroupsObjectFromGroupsArray(groupsArray); + } + + return { groups, groupIndex }; + } + + _getHeaderPanelCellClass(i) { + const cellClass = `${HEADER_PANEL_CELL_CLASS} ${HORIZONTAL_SIZES_CLASS}`; + + return this._groupedStrategy.addAdditionalGroupCellClasses(cellClass, i + 1, undefined, undefined, this.isGroupedByDate()); + } + + _renderAllDayPanel(index?: any) { + let cellCount = this._getCellCount(); + + if (!this._isVerticalGroupedWorkSpace()) { + cellCount *= this._getGroupCount() || 1; + } - delete this._interval; - } + const cellTemplates = this._renderTableBody({ + container: this._allDayPanels.length ? getPublicElement(this._allDayTables[index]) : getPublicElement(this._$allDayTable), + rowCount: 1, + cellCount, + cellClass: this._getAllDayPanelCellClass.bind(this), + rowClass: ALL_DAY_TABLE_ROW_CLASS, + cellTemplate: this.option('dataCellTemplate'), + // TODO: remove along with old render + getCellData: this._oldRender_getAllDayCellData(index), + groupIndex: index, + }, true); - _clean() { - eventsEngine.off(domAdapter.getDocument(), SCHEDULER_CELL_DXPOINTERUP_EVENT_NAME); - this._disposeRenovatedComponents(); + this._toggleAllDayVisibility(true); + this._applyCellTemplates(cellTemplates); + } - super._clean(); - } + _renderGroupAllDayPanel() { + if (this._isVerticalGroupedWorkSpace()) { + const groupCount = this._getGroupCount(); - _cleanTableWidths() { - this._$headerPanel.css('width', ''); - this._$dateTable.css('width', ''); - this._$allDayTable && this._$allDayTable.css('width', ''); + for (let i = 0; i < groupCount; i++) { + this._renderAllDayPanel(i); + } } + } - _disposeRenovatedComponents() { - this.renovatedAllDayPanel?.dispose(); - this.renovatedAllDayPanel = undefined; - - this.renovatedDateTable?.dispose(); - this.renovatedDateTable = undefined; - - this.renovatedTimePanel?.dispose(); - this.renovatedTimePanel = undefined; - - this.renovatedGroupPanel?.dispose(); - this.renovatedGroupPanel = undefined; - - this.renovatedHeaderPanel?.dispose(); - this.renovatedHeaderPanel = undefined; - } + _getAllDayPanelCellClass(i, j) { + const cellClass = `${ALL_DAY_TABLE_CELL_CLASS} ${HORIZONTAL_SIZES_CLASS}`; - getGroupedStrategy() { - return this._groupedStrategy; - } + return this._groupedStrategy.addAdditionalGroupCellClasses(cellClass, j + 1); + } - getFixedContainer() { - return this._$fixedContainer; - } + _renderTimePanel() { + const repeatCount = this._groupedStrategy.calculateTimeCellRepeatCount(); - getAllDayContainer() { - return this._$allDayContainer; - } + const getTimeCellGroups = (rowIndex) => { + if (!this._isVerticalGroupedWorkSpace()) { + return {}; + } - updateRender() { - this.renderer.updateRender(); - } + const groupIndex = this._getGroupIndex(rowIndex, 0); - updateGrid() { - this.renderer._renderGrid(); - } + const groupsArray = getCellGroups( + groupIndex, + this.option('groups'), + ); - updateAppointments() { - this.option('onRenderAppointments')(); - this.dragBehavior?.updateDragSource(); - } + const groups = getGroupsObjectFromGroupsArray(groupsArray); - // ---------------- - // These methods should be deleted when we get rid of old render - // ---------------- + return { groupIndex, groups }; + }; - _createAllDayPanelElements() { - const groupCount = this._getGroupCount(); + const getData = (rowIndex, field) => { + let allDayPanelsCount = 0; + if (this.isAllDayPanelVisible) { + allDayPanelsCount = 1; + } + if (this.isGroupedAllDayPanel()) { + allDayPanelsCount = Math.ceil((rowIndex + 1) / this._getRowCount()); + } - if(this._isVerticalGroupedWorkSpace() && groupCount !== 0) { - for(let i = 0; i < groupCount; i++) { - const $allDayTitle = $('
') - .addClass(ALL_DAY_TITLE_CLASS) - .text(messageLocalization.format('dxScheduler-allDay')); + const validRowIndex = rowIndex + allDayPanelsCount; - this._allDayTitles.push($allDayTitle); + return this.viewDataProvider.completeTimePanelMap[validRowIndex][field]; + }; - this._$allDayTable = $(''); - this._allDayTables.push(this._$allDayTable); + this._renderTableBody({ + container: getPublicElement(this._$timePanel), + rowCount: this._getTimePanelRowCount() * repeatCount, + cellCount: 1, + cellClass: this._getTimeCellClass.bind(this), + rowClass: TIME_PANEL_ROW_CLASS, + cellTemplate: this.option('timeCellTemplate'), + getCellText: (rowIndex) => getData(rowIndex, 'text'), + getCellDate: (rowIndex) => getData(rowIndex, 'startDate'), + groupCount: this._getGroupCount(), + allDayElements: this._insertAllDayRowsIntoDateTable() ? this._allDayTitles : undefined, + getTemplateData: getTimeCellGroups.bind(this), + }); + } + + _getTimeCellClass(i) { + const cellClass = `${TIME_PANEL_CELL_CLASS} ${VERTICAL_SIZES_CLASS}`; + + return this._isVerticalGroupedWorkSpace() + ? this._groupedStrategy.addAdditionalGroupCellClasses(cellClass, i, i) + : cellClass; + } + + _renderDateTable() { + const groupCount = this._getGroupCount(); + this._renderTableBody({ + container: getPublicElement(this._$dateTable), + rowCount: this._getTotalRowCount(groupCount), + cellCount: this._getTotalCellCount(groupCount), + cellClass: this._getDateTableCellClass.bind(this), + rowClass: DATE_TABLE_ROW_CLASS, + cellTemplate: this.option('dataCellTemplate'), + // TODO: remove along with old render + getCellData: (_, rowIndex, columnIndex) => { + const isGroupedAllDayPanel = this.isGroupedAllDayPanel(); + let validRowIndex = rowIndex; + + if (isGroupedAllDayPanel) { + const rowCount = this._getRowCount(); + const allDayPanelsCount = Math.ceil(rowIndex / rowCount); + validRowIndex += allDayPanelsCount; + } + + const { cellData } = this.viewDataProvider.viewDataMap.dateTableMap[validRowIndex][columnIndex]; - this._$allDayPanel = $('
') - .addClass(ALL_DAY_PANEL_CLASS) - .append(this._$allDayTable); + return { + value: this._filterCellDataFields(cellData), + fullValue: cellData, + key: CELL_DATA, + }; + }, + allDayElements: this._insertAllDayRowsIntoDateTable() ? this._allDayPanels : undefined, + groupCount, + groupByDate: this.option('groupByDate'), + }); + } + + _insertAllDayRowsIntoDateTable() { + return this._groupedStrategy.insertAllDayRowsIntoDateTable(); + } + + _renderTableBody(options, delayCellTemplateRendering?: any): any { + let result: any[] = []; + if (!delayCellTemplateRendering) { + this._applyCellTemplates( + tableCreator.makeTable(options), + ); + } else { + result = tableCreator.makeTable(options); + } + + return result; + } +} - this._allDayPanels.push(this._$allDayPanel); - } - } else { - this._$allDayTitle = $('
') - .addClass(ALL_DAY_TITLE_CLASS) - .text(messageLocalization.format('dxScheduler-allDay')) - .appendTo(this.$element()); +const createDragBehaviorConfig = ( + container, + rootElement, + isDefaultDraggingMode, + dragBehavior, + enableDefaultDragging, + disableDefaultDragging, + getDroppableCell, + getDateTables, + removeDroppableCellClass, + getCellWidth, + options, +) => { + const state: any = { + dragElement: undefined, + itemData: undefined, + }; - this._$allDayTable = $('
'); + const isItemDisabled = () => { + const { itemData } = state; - this._$allDayPanel = $('
') - .addClass(ALL_DAY_PANEL_CLASS) - .append(this._$allDayTable); - } + if (itemData) { + const getter: any = compileGetter('disabled'); + return getter(itemData); } - renderWorkSpace(isGenerateNewViewData = true) { - this.cache.clear(); + return true; + }; - this.viewDataProvider.update(this.generateRenderOptions(), isGenerateNewViewData); + const createDragAppointment = (itemData, settings, appointments) => { + const appointmentIndex = appointments.option('items').length; - if(this.isRenovatedRender()) { - this.renderRWorkSpace(); - } else { - this._renderDateHeader(); - this._renderTimePanel(); - this._renderGroupAllDayPanel(); - this._renderDateTable(); - this._renderAllDayPanel(); - } - - this._initPositionHelper(); - } - - _renderGroupHeader() { - const $container = this._getGroupHeaderContainer(); - const groupCount = this._getGroupCount(); - let cellTemplates = []; - if(groupCount) { - const groupRows = this._makeGroupRows(this.option('groups'), this.option('groupByDate')); - this._attachGroupCountClass(); - $container.append(groupRows.elements); - cellTemplates = groupRows.cellTemplates; - } else { - this._detachGroupCountClass(); - } + settings.isCompact = false; + settings.virtual = false; - return cellTemplates; - } + const items = appointments._renderItem(appointmentIndex, { + itemData, + settings: [settings], + }); - _applyCellTemplates(templates) { - templates?.forEach(function(template) { - template(); - }); - } + return items[0]; + }; - _makeGroupRows(groups, groupByDate) { - const tableCreatorStrategy = this._isVerticalGroupedWorkSpace() ? tableCreator.VERTICAL : tableCreator.HORIZONTAL; - - return tableCreator.makeGroupedTable(tableCreatorStrategy, - groups, { - groupHeaderRowClass: GROUP_ROW_CLASS, - groupRowClass: GROUP_ROW_CLASS, - groupHeaderClass: this._getGroupHeaderClass.bind(this), - groupHeaderContentClass: GROUP_HEADER_CONTENT_CLASS, - }, - this._getCellCount() || 1, - this.option('resourceCellTemplate'), - this._getGroupCount(), - groupByDate - ); + const onDragStart = (e) => { + if (!isDefaultDraggingMode) { + disableDefaultDragging(); } - _renderDateHeader() { - const container = this._getDateHeaderContainer(); - const $headerRow = $('
').addClass(HEADER_ROW_CLASS); - const count = this._getCellCount(); - const cellTemplate = this._getDateHeaderTemplate(); - const repeatCount = this._getCalculateHeaderCellRepeatCount(); - const templateCallbacks = []; - const groupByDate = this.isGroupedByDate(); - - if(!groupByDate) { - for(let rowIndex = 0; rowIndex < repeatCount; rowIndex++) { - for(let columnIndex = 0; columnIndex < count; columnIndex++) { - const templateIndex = rowIndex * count + columnIndex; - this._renderDateHeaderTemplate($headerRow, columnIndex, templateIndex, cellTemplate, templateCallbacks); - } - } - - container.append($headerRow); - } else { - const colSpan = groupByDate ? this._getGroupCount() : 1; - - for(let columnIndex = 0; columnIndex < count; columnIndex++) { - const templateIndex = columnIndex * repeatCount; - const cellElement = this._renderDateHeaderTemplate($headerRow, columnIndex, templateIndex, cellTemplate, templateCallbacks); - cellElement.attr('colSpan', colSpan); - } + const canceled = e.cancel; + const { event } = e; + const $itemElement = $(e.itemElement); + const appointments = e.component._appointments; - container.prepend($headerRow); + state.itemData = options.getItemData(e.itemElement, appointments); + const settings = options.getItemSettings($itemElement, e); + const { initialPosition } = options; + if (!isItemDisabled()) { + event.data = event.data || {}; + if (!canceled) { + if (!settings.isCompact) { + dragBehavior.updateDragSource(state.itemData, settings); } - this._applyCellTemplates(templateCallbacks); - - return $headerRow; - } - - _renderDateHeaderTemplate(container, panelCellIndex, templateIndex, cellTemplate, templateCallbacks) { - const validTemplateIndex = this.isGroupedByDate() - ? Math.floor(templateIndex / this._getGroupCount()) - : templateIndex; - const completeDateHeaderMap = this.viewDataProvider.completeDateHeaderMap; - - const { - text, startDate: date, - } = completeDateHeaderMap[completeDateHeaderMap.length - 1][validTemplateIndex]; - const $cell = $('
') - .addClass(this._getHeaderPanelCellClass(panelCellIndex)) - .attr('title', text); - - if(cellTemplate?.render) { - templateCallbacks.push(cellTemplate.render.bind(cellTemplate, { - model: { - text, - date, - ...this._getGroupsForDateHeaderTemplate(templateIndex), - }, - index: templateIndex, - container: getPublicElement($cell) - })); - } else { - $cell.text(text); - } - - container.append($cell); - return $cell; - } + state.dragElement = createDragAppointment(state.itemData, settings, appointments); - _getGroupsForDateHeaderTemplate(templateIndex, indexMultiplier = 1) { - let groupIndex; - let groups; + event.data.itemElement = state.dragElement; + event.data.initialPosition = initialPosition ?? locate($(state.dragElement)); + event.data.itemData = state.itemData; + event.data.itemSettings = settings; - if(this._isHorizontalGroupedWorkSpace() && !this.isGroupedByDate()) { - groupIndex = this._getGroupIndex(0, templateIndex * indexMultiplier); - const groupsArray = getCellGroups( - groupIndex, - this.option('groups') - ); + dragBehavior.onDragStart(event.data); - groups = getGroupsObjectFromGroupsArray(groupsArray); - } - - return { groups, groupIndex }; + resetPosition($(state.dragElement)); + } } + }; - _getHeaderPanelCellClass(i) { - const cellClass = HEADER_PANEL_CELL_CLASS + ' ' + HORIZONTAL_SIZES_CLASS; + const getElementsFromPoint = () => { + const appointmentWidth = getWidth(state.dragElement); + const cellWidth = getCellWidth(); - return this._groupedStrategy.addAdditionalGroupCellClasses( - cellClass, i + 1, undefined, undefined, this.isGroupedByDate(), - ); - } + const isWideAppointment = appointmentWidth > cellWidth; + const isNarrowAppointment = appointmentWidth <= DRAGGING_MOUSE_FAULT; - _renderAllDayPanel(index) { - let cellCount = this._getCellCount(); + const dragElementContainer = $(state.dragElement).parent(); + const boundingRect = getBoundingRect(dragElementContainer.get(0)); - if(!this._isVerticalGroupedWorkSpace()) { - cellCount *= (this._getGroupCount() || 1); - } + const newX = boundingRect.left; + const newY = boundingRect.top; - const cellTemplates = this._renderTableBody({ - container: this._allDayPanels.length ? getPublicElement(this._allDayTables[index]) : getPublicElement(this._$allDayTable), - rowCount: 1, - cellCount: cellCount, - cellClass: this._getAllDayPanelCellClass.bind(this), - rowClass: ALL_DAY_TABLE_ROW_CLASS, - cellTemplate: this.option('dataCellTemplate'), - // TODO: remove along with old render - getCellData: this._oldRender_getAllDayCellData(index), - groupIndex: index - }, true); - - this._toggleAllDayVisibility(true); - this._applyCellTemplates(cellTemplates); - } - - _renderGroupAllDayPanel() { - if(this._isVerticalGroupedWorkSpace()) { - const groupCount = this._getGroupCount(); - - for(let i = 0; i < groupCount; i++) { - this._renderAllDayPanel(i); - } - } + if (isWideAppointment) { + return (domAdapter as any).elementsFromPoint(newX + DRAGGING_MOUSE_FAULT, newY + DRAGGING_MOUSE_FAULT); + } if (isNarrowAppointment) { + return (domAdapter as any).elementsFromPoint(newX, newY); } + return (domAdapter as any).elementsFromPoint(newX + appointmentWidth / 2, newY + DRAGGING_MOUSE_FAULT); + }; - _getAllDayPanelCellClass(i, j) { - const cellClass = ALL_DAY_TABLE_CELL_CLASS + ' ' + HORIZONTAL_SIZES_CLASS; - - return this._groupedStrategy.addAdditionalGroupCellClasses(cellClass, j + 1); + const onDragMove = () => { + if (isDefaultDraggingMode) { + return; } - _renderTimePanel() { - const repeatCount = this._groupedStrategy.calculateTimeCellRepeatCount(); - - const getTimeCellGroups = (rowIndex) => { - if(!this._isVerticalGroupedWorkSpace()) { - return {}; - } + const elements = getElementsFromPoint(); - const groupIndex = this._getGroupIndex(rowIndex, 0); + const isMoveUnderControl = !!elements.find((el) => el === rootElement.get(0)); + const dateTables = getDateTables(); - const groupsArray = getCellGroups( - groupIndex, - this.option('groups') - ); - - const groups = getGroupsObjectFromGroupsArray(groupsArray); - - return { groupIndex, groups }; - }; + const droppableCell = elements.find((el) => { + const { classList } = el; - const getData = (rowIndex, field) => { - let allDayPanelsCount = 0; - if(this.isAllDayPanelVisible) { - allDayPanelsCount = 1; - } - if(this.isGroupedAllDayPanel()) { - allDayPanelsCount = Math.ceil((rowIndex + 1) / this._getRowCount()); - } + const isCurrentSchedulerElement = dateTables.find(el).length === 1; - const validRowIndex = rowIndex + allDayPanelsCount; - - return this.viewDataProvider.completeTimePanelMap[validRowIndex][field]; - }; - - this._renderTableBody({ - container: getPublicElement(this._$timePanel), - rowCount: this._getTimePanelRowCount() * repeatCount, - cellCount: 1, - cellClass: this._getTimeCellClass.bind(this), - rowClass: TIME_PANEL_ROW_CLASS, - cellTemplate: this.option('timeCellTemplate'), - getCellText: (rowIndex) => getData(rowIndex, 'text'), - getCellDate: (rowIndex) => getData(rowIndex, 'startDate'), - groupCount: this._getGroupCount(), - allDayElements: this._insertAllDayRowsIntoDateTable() ? this._allDayTitles : undefined, - getTemplateData: getTimeCellGroups.bind(this), - }); - } + return isCurrentSchedulerElement + && ( + classList.contains(DATE_TABLE_CELL_CLASS) + || classList.contains(ALL_DAY_TABLE_CELL_CLASS) + ); + }); - _getTimeCellClass(i) { - const cellClass = TIME_PANEL_CELL_CLASS + ' ' + VERTICAL_SIZES_CLASS; + if (droppableCell) { + if (!getDroppableCell().is(droppableCell)) { + removeDroppableCellClass(); + } - return this._isVerticalGroupedWorkSpace() - ? this._groupedStrategy.addAdditionalGroupCellClasses(cellClass, i, i) - : cellClass; + $(droppableCell).addClass(DATE_TABLE_DROPPABLE_CELL_CLASS); + } else if (!isMoveUnderControl) { + removeDroppableCellClass(); } + }; - _renderDateTable() { - const groupCount = this._getGroupCount(); - this._renderTableBody({ - container: getPublicElement(this._$dateTable), - rowCount: this._getTotalRowCount(groupCount), - cellCount: this._getTotalCellCount(groupCount), - cellClass: this._getDateTableCellClass.bind(this), - rowClass: DATE_TABLE_ROW_CLASS, - cellTemplate: this.option('dataCellTemplate'), - // TODO: remove along with old render - getCellData: (_, rowIndex, columnIndex) => { - const isGroupedAllDayPanel = this.isGroupedAllDayPanel(); - let validRowIndex = rowIndex; - - if(isGroupedAllDayPanel) { - const rowCount = this._getRowCount(); - const allDayPanelsCount = Math.ceil(rowIndex / rowCount); - validRowIndex += allDayPanelsCount; - } - - const cellData = this.viewDataProvider.viewDataMap.dateTableMap[validRowIndex][columnIndex].cellData; - - return { - value: this._filterCellDataFields(cellData), - fullValue: cellData, - key: CELL_DATA, - }; - }, - allDayElements: this._insertAllDayRowsIntoDateTable() ? this._allDayPanels : undefined, - groupCount: groupCount, - groupByDate: this.option('groupByDate') - }); + const onDragEnd = (e) => { + if (!isDefaultDraggingMode) { + enableDefaultDragging(); } - _insertAllDayRowsIntoDateTable() { - return this._groupedStrategy.insertAllDayRowsIntoDateTable(); + if (!isItemDisabled()) { + dragBehavior.onDragEnd(e); } - _renderTableBody(options, delayCellTemplateRendering) { - let result = []; - if(!delayCellTemplateRendering) { - this._applyCellTemplates( - tableCreator.makeTable(options) - ); - } else { - result = tableCreator.makeTable(options); - } + state.dragElement?.remove(); + removeDroppableCellClass(); + }; - return result; + const cursorOffset = options.isSetCursorOffset + ? () => { + const $dragElement = $(state.dragElement); + return { + x: getWidth($dragElement) / 2, + y: getHeight($dragElement) / 2, + }; } -} + : undefined; -const createDragBehaviorConfig = ( + return { container, - rootElement, - isDefaultDraggingMode, - dragBehavior, - enableDefaultDragging, - disableDefaultDragging, - getDroppableCell, - getDateTables, - removeDroppableCellClass, - getCellWidth, - options) => { - - const state = { - dragElement: undefined, - itemData: undefined, - }; - - const isItemDisabled = () => { - const { itemData } = state; - - if(itemData) { - const getter = compileGetter('disabled'); - return getter(itemData); - } - - return true; - }; - - const createDragAppointment = (itemData, settings, appointments) => { - const appointmentIndex = appointments.option('items').length; - - settings.isCompact = false; - settings.virtual = false; - - const items = appointments._renderItem(appointmentIndex, { - itemData, - settings: [settings] - }); - - return items[0]; - }; - - const onDragStart = e => { - if(!isDefaultDraggingMode) { - disableDefaultDragging(); - } - - const canceled = e.cancel; - const event = e.event; - const $itemElement = $(e.itemElement); - const appointments = e.component._appointments; - - state.itemData = options.getItemData(e.itemElement, appointments); - const settings = options.getItemSettings($itemElement, e); - const initialPosition = options.initialPosition; - - if(!isItemDisabled()) { - event.data = event.data || {}; - if(!canceled) { - if(!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; - - dragBehavior.onDragStart(event.data); - - resetPosition($(state.dragElement)); - } - } - }; - - const getElementsFromPoint = () => { - const appointmentWidth = getWidth(state.dragElement); - const cellWidth = getCellWidth(); - - const isWideAppointment = appointmentWidth > cellWidth; - const isNarrowAppointment = appointmentWidth <= DRAGGING_MOUSE_FAULT; - - const dragElementContainer = $(state.dragElement).parent(); - const boundingRect = getBoundingRect(dragElementContainer.get(0)); - - const newX = boundingRect.left; - const newY = boundingRect.top; - - - if(isWideAppointment) { - return domAdapter.elementsFromPoint(newX + DRAGGING_MOUSE_FAULT, newY + DRAGGING_MOUSE_FAULT); - } else if(isNarrowAppointment) { - return domAdapter.elementsFromPoint(newX, newY); - } - return domAdapter.elementsFromPoint(newX + appointmentWidth / 2, newY + DRAGGING_MOUSE_FAULT); - }; - - const onDragMove = () => { - if(isDefaultDraggingMode) { - return; - } - - const elements = getElementsFromPoint(); - - const isMoveUnderControl = !!elements.find(el => el === rootElement.get(0)); - const dateTables = getDateTables(); - - const droppableCell = elements.find(el => { - const classList = el.classList; - - const isCurrentSchedulerElement = dateTables.find(el).length === 1; - - return isCurrentSchedulerElement && - ( - classList.contains(DATE_TABLE_CELL_CLASS) || - classList.contains(ALL_DAY_TABLE_CELL_CLASS) - ); - }); - - if(droppableCell) { - if(!getDroppableCell().is(droppableCell)) { - removeDroppableCellClass(); - } - - $(droppableCell).addClass(DATE_TABLE_DROPPABLE_CELL_CLASS); - } else if(!isMoveUnderControl) { - removeDroppableCellClass(); - } - }; - - const onDragEnd = e => { - if(!isDefaultDraggingMode) { - enableDefaultDragging(); - } - - if(!isItemDisabled()) { - dragBehavior.onDragEnd(e); - } - - state.dragElement?.remove(); - removeDroppableCellClass(); - }; - - const cursorOffset = options.isSetCursorOffset - ? () => { - const $dragElement = $(state.dragElement); - return { - x: getWidth($dragElement) / 2, - y: getHeight($dragElement) / 2 - }; - } - : undefined; - - return { - container, - dragTemplate: () => state.dragElement, - onDragStart, - onDragMove, - onDragEnd, - cursorOffset, - filter: options.filter - }; + dragTemplate: () => state.dragElement, + onDragStart, + onDragMove, + onDragEnd, + cursorOffset, + filter: options.filter, + }; }; export default SchedulerWorkSpace; 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 c86a8e9a97b3..3f120d16f4e2 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 @@ -1,29 +1,30 @@ -import registerComponent from '../../../core/component_registrator'; -import { VIEWS } from '../constants'; -import SchedulerWorkSpaceVertical from './ui.scheduler.work_space_vertical'; +import registerComponent from '@js/core/component_registrator'; + +import { VIEWS } from '../m_constants'; +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() { return VIEWS.DAY; } - _getElementClass() { - return DAY_CLASS; - } + _getElementClass() { + return DAY_CLASS; + } - _renderDateHeader() { - return this.option('intervalCount') === 1 ? null : super._renderDateHeader(); - } + _renderDateHeader() { + return this.option('intervalCount') === 1 ? null : super._renderDateHeader(); + } - renderRHeaderPanel() { - if(this.option('intervalCount') === 1) { - super.renderRHeaderPanel(false); - } else { - super.renderRHeaderPanel(true); - } + renderRHeaderPanel() { + if (this.option('intervalCount') === 1) { + super.renderRHeaderPanel(false); + } else { + super.renderRHeaderPanel(true); } + } } -registerComponent('dxSchedulerWorkSpaceDay', SchedulerWorkSpaceDay); +registerComponent('dxSchedulerWorkSpaceDay', SchedulerWorkSpaceDay as any); export default SchedulerWorkSpaceDay; diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_grouped_strategy_horizontal.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_grouped_strategy_horizontal.ts index 273348b28790..0751cb6497c0 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_grouped_strategy_horizontal.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_grouped_strategy_horizontal.ts @@ -1,229 +1,224 @@ +import { getBoundingRect } from '@js/core/utils/position'; -import { getBoundingRect } from '../../../core/utils/position'; -import { FIRST_GROUP_CELL_CLASS, LAST_GROUP_CELL_CLASS } from '../classes'; +import { FIRST_GROUP_CELL_CLASS, LAST_GROUP_CELL_CLASS } from '../m_classes'; class HorizontalGroupedStrategy { - constructor(workSpace) { - this._workSpace = workSpace; - } - - prepareCellIndexes(cellCoordinates, groupIndex, inAllDay) { - const groupByDay = this._workSpace.isGroupedByDate(); - - if(!groupByDay) { - return { - rowIndex: cellCoordinates.rowIndex, - columnIndex: cellCoordinates.columnIndex + groupIndex * this._workSpace._getCellCount() - }; - } else { - return { - rowIndex: cellCoordinates.rowIndex, - columnIndex: cellCoordinates.columnIndex * this._workSpace._getGroupCount() + groupIndex - }; - } - } + constructor(public _workSpace) { + } - getGroupIndex(rowIndex, columnIndex) { - const groupByDay = this._workSpace.isGroupedByDate(); - const groupCount = this._workSpace._getGroupCount(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + prepareCellIndexes(cellCoordinates, groupIndex, inAllDay?: any) { + const groupByDay = this._workSpace.isGroupedByDate(); - if(groupByDay) { - return columnIndex % groupCount; - } else { - return Math.floor(columnIndex / this._workSpace._getCellCount()); - } + if (!groupByDay) { + return { + rowIndex: cellCoordinates.rowIndex, + columnIndex: cellCoordinates.columnIndex + groupIndex * this._workSpace._getCellCount(), + }; } + return { + rowIndex: cellCoordinates.rowIndex, + columnIndex: cellCoordinates.columnIndex * this._workSpace._getGroupCount() + groupIndex, + }; + } - calculateHeaderCellRepeatCount() { - return this._workSpace._getGroupCount() || 1; - } + getGroupIndex(rowIndex, columnIndex) { + const groupByDay = this._workSpace.isGroupedByDate(); + const groupCount = this._workSpace._getGroupCount(); - insertAllDayRowsIntoDateTable() { - return false; + if (groupByDay) { + return columnIndex % groupCount; } + return Math.floor(columnIndex / this._workSpace._getCellCount()); + } - getTotalCellCount(groupCount) { - groupCount = groupCount || 1; + calculateHeaderCellRepeatCount() { + return this._workSpace._getGroupCount() || 1; + } - return this._workSpace._getCellCount() * groupCount; - } + insertAllDayRowsIntoDateTable() { + return false; + } - getTotalRowCount() { - return this._workSpace._getRowCount(); - } + getTotalCellCount(groupCount) { + groupCount = groupCount || 1; - calculateTimeCellRepeatCount() { - return 1; - } + return this._workSpace._getCellCount() * groupCount; + } - getWorkSpaceMinWidth() { - return getBoundingRect(this._workSpace.$element().get(0)).width - this._workSpace.getTimePanelWidth(); - } + getTotalRowCount() { + return this._workSpace._getRowCount(); + } - getAllDayOffset() { - return this._workSpace.getAllDayHeight(); - } + calculateTimeCellRepeatCount() { + return 1; + } - getGroupCountClass(groups) { - return undefined; - } + getWorkSpaceMinWidth() { + return getBoundingRect(this._workSpace.$element().get(0)).width - this._workSpace.getTimePanelWidth(); + } - getLeftOffset() { - return this._workSpace.getTimePanelWidth(); - } + getAllDayOffset() { + return this._workSpace.getAllDayHeight(); + } - _createGroupBoundOffset(startCell, endCell, cellWidth) { - const extraOffset = cellWidth / 2; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getGroupCountClass(groups: any) { + return undefined; + } - const startOffset = startCell ? startCell.offset().left - extraOffset : 0; - const endOffset = endCell ? endCell.offset().left + cellWidth + extraOffset : 0; + getLeftOffset() { + return this._workSpace.getTimePanelWidth(); + } - return { - left: startOffset, - right: endOffset, - top: 0, - bottom: 0 - }; - } + _createGroupBoundOffset(startCell, endCell, cellWidth) { + const extraOffset = cellWidth / 2; - _getGroupedByDateBoundOffset($cells, cellWidth) { - const firstCellIndex = 0; - const lastCellIndex = $cells.length - 1; + const startOffset = startCell ? startCell.offset().left - extraOffset : 0; + const endOffset = endCell ? endCell.offset().left + cellWidth + extraOffset : 0; - const startCell = $cells.eq(firstCellIndex); - const endCell = $cells.eq(lastCellIndex); + return { + left: startOffset, + right: endOffset, + top: 0, + bottom: 0, + }; + } - return this._createGroupBoundOffset(startCell, endCell, cellWidth); - } + _getGroupedByDateBoundOffset($cells, cellWidth) { + const firstCellIndex = 0; + const lastCellIndex = $cells.length - 1; + + const startCell = $cells.eq(firstCellIndex); + const endCell = $cells.eq(lastCellIndex); - getGroupBoundsOffset(cellCount, $cells, cellWidth, coordinates, groupedDataMap) { - if(this._workSpace.isGroupedByDate()) { - return this._getGroupedByDateBoundOffset($cells, cellWidth); - } + return this._createGroupBoundOffset(startCell, endCell, cellWidth); + } - let startCell; - let endCell; + getGroupBoundsOffset(cellCount, $cells, cellWidth, coordinates, groupedDataMap) { + if (this._workSpace.isGroupedByDate()) { + return this._getGroupedByDateBoundOffset($cells, cellWidth); + } - const cellIndex = this._workSpace.getCellIndexByCoordinates(coordinates); - const groupIndex = coordinates.groupIndex || Math.floor(cellIndex / cellCount); + let startCell; + let endCell; - const currentCellGroup = groupedDataMap.dateTableGroupedMap[groupIndex]; + const cellIndex = this._workSpace.getCellIndexByCoordinates(coordinates); + const groupIndex = coordinates.groupIndex || Math.floor(cellIndex / cellCount); - if(currentCellGroup) { - const groupRowLength = currentCellGroup[0].length; + const currentCellGroup = groupedDataMap.dateTableGroupedMap[groupIndex]; - const groupStartPosition = currentCellGroup[0][0].position; - const groupEndPosition = currentCellGroup[0][groupRowLength - 1].position; + if (currentCellGroup) { + const groupRowLength = currentCellGroup[0].length; - startCell = $cells.eq(groupStartPosition.columnIndex); - endCell = $cells.eq(groupEndPosition.columnIndex); - } + const groupStartPosition = currentCellGroup[0][0].position; + const groupEndPosition = currentCellGroup[0][groupRowLength - 1].position; - return this._createGroupBoundOffset(startCell, endCell, cellWidth); + startCell = $cells.eq(groupStartPosition.columnIndex); + endCell = $cells.eq(groupEndPosition.columnIndex); } - shiftIndicator($indicator, height, rtlOffset, groupIndex) { - const offset = this._getIndicatorOffset(groupIndex); + return this._createGroupBoundOffset(startCell, endCell, cellWidth); + } - const horizontalOffset = rtlOffset ? rtlOffset - offset : offset; + shiftIndicator($indicator, height, rtlOffset, groupIndex) { + const offset = this._getIndicatorOffset(groupIndex); - $indicator.css('left', horizontalOffset); - $indicator.css('top', height); - } + const horizontalOffset = rtlOffset ? rtlOffset - offset : offset; - _getIndicatorOffset(groupIndex) { - const groupByDay = this._workSpace.isGroupedByDate(); + $indicator.css('left', horizontalOffset); + $indicator.css('top', height); + } - return groupByDay ? this._calculateGroupByDateOffset(groupIndex) : this._calculateOffset(groupIndex); - } + _getIndicatorOffset(groupIndex) { + const groupByDay = this._workSpace.isGroupedByDate(); - _calculateOffset(groupIndex) { - const indicatorStartPosition = this._workSpace.getIndicatorOffset(groupIndex); - const offset = this._workSpace._getCellCount() * this._workSpace.getRoundedCellWidth(groupIndex - 1, 0) * groupIndex; + return groupByDay ? this._calculateGroupByDateOffset(groupIndex) : this._calculateOffset(groupIndex); + } - return indicatorStartPosition + offset; - } + _calculateOffset(groupIndex) { + const indicatorStartPosition = this._workSpace.getIndicatorOffset(groupIndex); + const offset = this._workSpace._getCellCount() * this._workSpace.getRoundedCellWidth(groupIndex - 1, 0) * groupIndex; - _calculateGroupByDateOffset(groupIndex) { - return this._workSpace.getIndicatorOffset(0) * this._workSpace._getGroupCount() + this._workSpace.getRoundedCellWidth(groupIndex - 1, 0) * groupIndex; - } + return indicatorStartPosition + offset; + } - getShaderOffset(i, width) { - const offset = this._workSpace._getCellCount() * this._workSpace.getRoundedCellWidth(i - 1) * i; - return this._workSpace.option('rtlEnabled') ? getBoundingRect(this._workSpace._dateTableScrollable.$content().get(0)).width - offset - this._workSpace.getTimePanelWidth() - width : offset; - } + _calculateGroupByDateOffset(groupIndex) { + return this._workSpace.getIndicatorOffset(0) * this._workSpace._getGroupCount() + this._workSpace.getRoundedCellWidth(groupIndex - 1, 0) * groupIndex; + } - getShaderTopOffset(i) { - return -this.getShaderMaxHeight() * (i > 0 ? 1 : 0); - } + getShaderOffset(i, width) { + const offset = this._workSpace._getCellCount() * this._workSpace.getRoundedCellWidth(i - 1) * i; + return this._workSpace.option('rtlEnabled') ? getBoundingRect(this._workSpace._dateTableScrollable.$content().get(0)).width - offset - this._workSpace.getTimePanelWidth() - width : offset; + } - getShaderHeight() { - const height = this._workSpace.getIndicationHeight(); + getShaderTopOffset(i) { + return -this.getShaderMaxHeight() * (i > 0 ? 1 : 0); + } - return height; - } + getShaderHeight() { + const height = this._workSpace.getIndicationHeight(); - getShaderMaxHeight() { - return getBoundingRect(this._workSpace._dateTableScrollable.$content().get(0)).height; - } + return height; + } - getShaderWidth(i) { - return this._workSpace.getIndicationWidth(i); - } + getShaderMaxHeight() { + return getBoundingRect(this._workSpace._dateTableScrollable.$content().get(0)).height; + } - getScrollableScrollTop(allDay) { - return !allDay ? this._workSpace.getScrollable().scrollTop() : 0; - } + getShaderWidth(i) { + return this._workSpace.getIndicationWidth(i); + } - // --------------- - // We do not need these nethods in renovation - // --------------- + getScrollableScrollTop(allDay) { + return !allDay ? this._workSpace.getScrollable().scrollTop() : 0; + } - addAdditionalGroupCellClasses(cellClass, index, i, j, applyUnconditionally = false) { - cellClass = this._addLastGroupCellClass(cellClass, index, applyUnconditionally); + // --------------- + // We do not need these nethods in renovation + // --------------- - return this._addFirstGroupCellClass(cellClass, index, applyUnconditionally); - } + addAdditionalGroupCellClasses(cellClass, index, i, j, applyUnconditionally = false) { + cellClass = this._addLastGroupCellClass(cellClass, index, applyUnconditionally); - _addLastGroupCellClass(cellClass, index, applyUnconditionally) { - if(applyUnconditionally) { - return `${cellClass} ${LAST_GROUP_CELL_CLASS}`; - } + return this._addFirstGroupCellClass(cellClass, index, applyUnconditionally); + } - const groupByDate = this._workSpace.isGroupedByDate(); + _addLastGroupCellClass(cellClass, index, applyUnconditionally) { + if (applyUnconditionally) { + return `${cellClass} ${LAST_GROUP_CELL_CLASS}`; + } - if(groupByDate) { - if(index % this._workSpace._getGroupCount() === 0) { - return `${cellClass} ${LAST_GROUP_CELL_CLASS}`; - } - } else { - if(index % this._workSpace._getCellCount() === 0) { - return `${cellClass} ${LAST_GROUP_CELL_CLASS}`; - } - } + const groupByDate = this._workSpace.isGroupedByDate(); - return cellClass; + if (groupByDate) { + if (index % this._workSpace._getGroupCount() === 0) { + return `${cellClass} ${LAST_GROUP_CELL_CLASS}`; + } + } else if (index % this._workSpace._getCellCount() === 0) { + return `${cellClass} ${LAST_GROUP_CELL_CLASS}`; } - _addFirstGroupCellClass(cellClass, index, applyUnconditionally) { - if(applyUnconditionally) { - return `${cellClass} ${FIRST_GROUP_CELL_CLASS}`; - } + return cellClass; + } - const groupByDate = this._workSpace.isGroupedByDate(); + _addFirstGroupCellClass(cellClass, index, applyUnconditionally) { + if (applyUnconditionally) { + return `${cellClass} ${FIRST_GROUP_CELL_CLASS}`; + } - if(groupByDate) { - if((index - 1) % this._workSpace._getGroupCount() === 0) { - return `${cellClass} ${FIRST_GROUP_CELL_CLASS}`; - } - } else { - if((index - 1) % this._workSpace._getCellCount() === 0) { - return `${cellClass} ${FIRST_GROUP_CELL_CLASS}`; - } - } + const groupByDate = this._workSpace.isGroupedByDate(); - return cellClass; + if (groupByDate) { + if ((index - 1) % this._workSpace._getGroupCount() === 0) { + return `${cellClass} ${FIRST_GROUP_CELL_CLASS}`; + } + } else if ((index - 1) % this._workSpace._getCellCount() === 0) { + return `${cellClass} ${FIRST_GROUP_CELL_CLASS}`; } + + return cellClass; + } } export default HorizontalGroupedStrategy; diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_grouped_strategy_vertical.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_grouped_strategy_vertical.ts index 6351f044d770..81700d5b1e47 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_grouped_strategy_vertical.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_grouped_strategy_vertical.ts @@ -1,185 +1,194 @@ -import { getBoundingRect } from '../../../core/utils/position'; -import { Cache } from './cache'; -import { FIRST_GROUP_CELL_CLASS, LAST_GROUP_CELL_CLASS } from '../classes'; -import { calculateDayDuration, getVerticalGroupCountClass } from '../../../renovation/ui/scheduler/view_model/to_test/views/utils/base'; +import { getBoundingRect } from '@js/core/utils/position'; +import { calculateDayDuration, getVerticalGroupCountClass } from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/base'; + +import { FIRST_GROUP_CELL_CLASS, LAST_GROUP_CELL_CLASS } from '../m_classes'; +import { Cache } from './m_cache'; const WORK_SPACE_BORDER = 1; class VerticalGroupedStrategy { - constructor(workSpace) { - this._workSpace = workSpace; - this.cache = new Cache(); - } + cache = new Cache(); - prepareCellIndexes(cellCoordinates, groupIndex, inAllDayRow) { - let rowIndex = cellCoordinates.rowIndex + groupIndex * this._workSpace._getRowCount(); + _groupBoundsOffset: any; - if(this._workSpace.supportAllDayRow() && this._workSpace.option('showAllDayPanel')) { - rowIndex += groupIndex; + _$container: any; - if(!inAllDayRow) { - rowIndex += 1; - } - } + constructor(public _workSpace) { + } - return { - rowIndex: rowIndex, - columnIndex: cellCoordinates.columnIndex - }; - } + prepareCellIndexes(cellCoordinates, groupIndex, inAllDayRow) { + let rowIndex = cellCoordinates.rowIndex + groupIndex * this._workSpace._getRowCount(); - getGroupIndex(rowIndex) { - return Math.floor(rowIndex / this._workSpace._getRowCount()); - } + if (this._workSpace.supportAllDayRow() && this._workSpace.option('showAllDayPanel')) { + rowIndex += groupIndex; - calculateHeaderCellRepeatCount() { - return 1; + if (!inAllDayRow) { + rowIndex += 1; + } } - insertAllDayRowsIntoDateTable() { - return this._workSpace.option('showAllDayPanel'); - } + return { + rowIndex, + columnIndex: cellCoordinates.columnIndex, + }; + } - getTotalCellCount() { - return this._workSpace._getCellCount(); - } + getGroupIndex(rowIndex) { + return Math.floor(rowIndex / this._workSpace._getRowCount()); + } - getTotalRowCount() { - return this._workSpace._getRowCount() * this._workSpace._getGroupCount(); - } + calculateHeaderCellRepeatCount() { + return 1; + } - calculateTimeCellRepeatCount() { - return this._workSpace._getGroupCount() || 1; - } + insertAllDayRowsIntoDateTable() { + return this._workSpace.option('showAllDayPanel'); + } - getWorkSpaceMinWidth() { - let minWidth = this._workSpace._getWorkSpaceWidth(); - const workspaceContainerWidth = getBoundingRect(this._workSpace.$element().get(0)).width - this._workSpace.getTimePanelWidth() - this._workSpace.getGroupTableWidth() - 2 * WORK_SPACE_BORDER; + getTotalCellCount() { + return this._workSpace._getCellCount(); + } - if(minWidth < workspaceContainerWidth) { - minWidth = workspaceContainerWidth; - } + getTotalRowCount() { + return this._workSpace._getRowCount() * this._workSpace._getGroupCount(); + } - return minWidth; - } + calculateTimeCellRepeatCount() { + return this._workSpace._getGroupCount() || 1; + } - getAllDayOffset() { - return 0; - } + getWorkSpaceMinWidth() { + let minWidth = this._workSpace._getWorkSpaceWidth(); + const workspaceContainerWidth = getBoundingRect(this._workSpace.$element().get(0)).width - this._workSpace.getTimePanelWidth() - this._workSpace.getGroupTableWidth() - 2 * WORK_SPACE_BORDER; - getGroupCountClass(groups) { - return getVerticalGroupCountClass(groups); + if (minWidth < workspaceContainerWidth) { + minWidth = workspaceContainerWidth; } - getLeftOffset() { - return this._workSpace.getTimePanelWidth() + this._workSpace.getGroupTableWidth(); - } + return minWidth; + } - getGroupBoundsOffset(groupIndex, [$firstCell, $lastCell]) { - return this.cache.get(`groupBoundsOffset${groupIndex}`, () => { - const startDayHour = this._workSpace.option('startDayHour'); - const endDayHour = this._workSpace.option('endDayHour'); - const hoursInterval = this._workSpace.option('hoursInterval'); - - const dayHeight = (calculateDayDuration(startDayHour, endDayHour) / hoursInterval) * this._workSpace.getCellHeight(); - const scrollTop = this.getScrollableScrollTop(); - const headerRowHeight = getBoundingRect(this._workSpace._$headerPanelContainer.get(0)).height; - - let topOffset = groupIndex * dayHeight + headerRowHeight + this._workSpace.option('getHeaderHeight')() - scrollTop; - - if(this._workSpace.option('showAllDayPanel') && this._workSpace.supportAllDayRow()) { - topOffset += this._workSpace.getCellHeight() * (groupIndex + 1); - } - - const bottomOffset = topOffset + dayHeight; - - const { left } = $firstCell.getBoundingClientRect(); - const { right } = $lastCell.getBoundingClientRect(); - return this._groupBoundsOffset = { - left, - right, - top: topOffset, - bottom: bottomOffset - }; - }); - } + getAllDayOffset() { + return 0; + } - shiftIndicator($indicator, height, rtlOffset, i) { - const offset = this._workSpace.getIndicatorOffset(0); - const tableOffset = this._workSpace.option('crossScrollingEnabled') ? 0 : this._workSpace.getGroupTableWidth(); - const horizontalOffset = rtlOffset ? rtlOffset - offset : offset; - let verticalOffset = this._workSpace._getRowCount() * this._workSpace.getCellHeight() * i; + getGroupCountClass(groups) { + return getVerticalGroupCountClass(groups); + } - if(this._workSpace.supportAllDayRow() && this._workSpace.option('showAllDayPanel')) { - verticalOffset += this._workSpace.getAllDayHeight() * (i + 1); - } + getLeftOffset() { + return this._workSpace.getTimePanelWidth() + this._workSpace.getGroupTableWidth(); + } - $indicator.css('left', horizontalOffset + tableOffset); - $indicator.css('top', height + verticalOffset); - } + getGroupBoundsOffset(groupIndex, [$firstCell, $lastCell]) { + return this.cache.get(`groupBoundsOffset${groupIndex}`, () => { + const startDayHour = this._workSpace.option('startDayHour'); + const endDayHour = this._workSpace.option('endDayHour'); + const hoursInterval = this._workSpace.option('hoursInterval'); - getShaderOffset(i, width) { - const offset = this._workSpace.option('crossScrollingEnabled') ? 0 : this._workSpace.getGroupTableWidth(); - return this._workSpace.option('rtlEnabled') ? getBoundingRect(this._$container.get(0)).width - offset - this._workSpace.getWorkSpaceLeftOffset() - width : offset; - } + const dayHeight = (calculateDayDuration(startDayHour, endDayHour) / hoursInterval) * this._workSpace.getCellHeight(); + const scrollTop = this.getScrollableScrollTop(); + const headerRowHeight = getBoundingRect(this._workSpace._$headerPanelContainer.get(0)).height; - getShaderTopOffset(i) { - return 0; - } + let topOffset = groupIndex * dayHeight + headerRowHeight + this._workSpace.option('getHeaderHeight')() - scrollTop; - getShaderHeight() { - let height = this._workSpace.getIndicationHeight(); + if (this._workSpace.option('showAllDayPanel') && this._workSpace.supportAllDayRow()) { + topOffset += this._workSpace.getCellHeight() * (groupIndex + 1); + } - if(this._workSpace.supportAllDayRow() && this._workSpace.option('showAllDayPanel')) { - height += this._workSpace.getCellHeight(); - } + const bottomOffset = topOffset + dayHeight; - return height; - } + const { left } = $firstCell.getBoundingClientRect(); + const { right } = $lastCell.getBoundingClientRect(); + this._groupBoundsOffset = { + left, + right, + top: topOffset, + bottom: bottomOffset, + }; - getShaderMaxHeight() { - let height = this._workSpace._getRowCount() * this._workSpace.getCellHeight(); + return this._groupBoundsOffset; + }); + } - if(this._workSpace.supportAllDayRow() && this._workSpace.option('showAllDayPanel')) { - height += this._workSpace.getCellHeight(); - } + shiftIndicator($indicator, height, rtlOffset, i) { + const offset = this._workSpace.getIndicatorOffset(0); + const tableOffset = this._workSpace.option('crossScrollingEnabled') ? 0 : this._workSpace.getGroupTableWidth(); + const horizontalOffset = rtlOffset ? rtlOffset - offset : offset; + let verticalOffset = this._workSpace._getRowCount() * this._workSpace.getCellHeight() * i; - return height; + if (this._workSpace.supportAllDayRow() && this._workSpace.option('showAllDayPanel')) { + verticalOffset += this._workSpace.getAllDayHeight() * (i + 1); } - getShaderWidth() { - return this._workSpace.getIndicationWidth(0); - } + $indicator.css('left', horizontalOffset + tableOffset); + $indicator.css('top', height + verticalOffset); + } + + getShaderOffset(i, width) { + const offset = this._workSpace.option('crossScrollingEnabled') ? 0 : this._workSpace.getGroupTableWidth(); + return this._workSpace.option('rtlEnabled') ? getBoundingRect(this._$container.get(0)).width - offset - this._workSpace.getWorkSpaceLeftOffset() - width : offset; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getShaderTopOffset(i) { + return 0; + } + + getShaderHeight() { + let height = this._workSpace.getIndicationHeight(); - getScrollableScrollTop() { - return this._workSpace.getScrollable().scrollTop(); + if (this._workSpace.supportAllDayRow() && this._workSpace.option('showAllDayPanel')) { + height += this._workSpace.getCellHeight(); } - // ------------ - // We do not need these methods in renovation - // ------------ + return height; + } - addAdditionalGroupCellClasses(cellClass, index, i, j) { - cellClass = this._addLastGroupCellClass(cellClass, i + 1); + getShaderMaxHeight() { + let height = this._workSpace._getRowCount() * this._workSpace.getCellHeight(); - return this._addFirstGroupCellClass(cellClass, i + 1); + if (this._workSpace.supportAllDayRow() && this._workSpace.option('showAllDayPanel')) { + height += this._workSpace.getCellHeight(); } - _addLastGroupCellClass(cellClass, index) { - if(index % this._workSpace._getRowCount() === 0) { - return `${cellClass} ${LAST_GROUP_CELL_CLASS}`; - } + return height; + } - return cellClass; + getShaderWidth() { + return this._workSpace.getIndicationWidth(0); + } + + getScrollableScrollTop() { + return this._workSpace.getScrollable().scrollTop(); + } + + // ------------ + // We do not need these methods in renovation + // ------------ + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + addAdditionalGroupCellClasses(cellClass, index, i, j) { + cellClass = this._addLastGroupCellClass(cellClass, i + 1); + + return this._addFirstGroupCellClass(cellClass, i + 1); + } + + _addLastGroupCellClass(cellClass, index) { + if (index % this._workSpace._getRowCount() === 0) { + return `${cellClass} ${LAST_GROUP_CELL_CLASS}`; } - _addFirstGroupCellClass(cellClass, index) { - if((index - 1) % this._workSpace._getRowCount() === 0) { - return `${cellClass} ${FIRST_GROUP_CELL_CLASS}`; - } + return cellClass; + } - return cellClass; + _addFirstGroupCellClass(cellClass, index) { + if ((index - 1) % this._workSpace._getRowCount() === 0) { + return `${cellClass} ${FIRST_GROUP_CELL_CLASS}`; } + + return cellClass; + } } export default VerticalGroupedStrategy; 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 9e076e859d45..a5455d9ef481 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,14 +1,15 @@ -import { setWidth } from '../../../core/utils/size'; -import $ from '../../../core/renderer'; -import SchedulerWorkSpace from './ui.scheduler.work_space'; -import registerComponent from '../../../core/component_registrator'; -import dateUtils from '../../../core/utils/date'; -import { extend } from '../../../core/utils/extend'; -import { getBoundingRect } from '../../../core/utils/position'; -import { hasWindow } from '../../../core/utils/window'; -import { HEADER_CURRENT_TIME_CELL_CLASS } from '../classes'; -import { getToday } from '../../../renovation/ui/scheduler/view_model/to_test/views/utils/base'; -import timezoneUtils from '../utils.timeZone'; +import registerComponent from '@js/core/component_registrator'; +import $ 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 { getToday } from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/base'; + +import { HEADER_CURRENT_TIME_CELL_CLASS } from '../m_classes'; +import timezoneUtils from '../m_utils_time_zone'; +import SchedulerWorkSpace from './m_work_space'; const toMs = dateUtils.dateToMilliseconds; @@ -16,297 +17,297 @@ const SCHEDULER_DATE_TIME_INDICATOR_CLASS = 'dx-scheduler-date-time-indicator'; const TIME_PANEL_CURRENT_TIME_CELL_CLASS = 'dx-scheduler-time-panel-current-time-cell'; class SchedulerWorkSpaceIndicator extends SchedulerWorkSpace { - _getToday() { - return getToday(this.option('indicatorTime'), this.timeZoneCalculator); - } - - isIndicationOnView() { - if(this.option('showCurrentTimeIndicator')) { - const today = this._getToday(); - const endViewDate = dateUtils.trimTime(this.getEndViewDate()); + _indicatorInterval: any; - return dateUtils.dateInRange(today, this.getStartViewDate(), new Date(endViewDate.getTime() + toMs('day'))); - } - return false; - } + // @ts-expect-error + _getToday() { + return getToday(this.option('indicatorTime') as any, this.timeZoneCalculator); + } - isIndicationAvailable() { - if(!hasWindow()) { - return false; - } + isIndicationOnView() { + if (this.option('showCurrentTimeIndicator')) { + const today = this._getToday(); + const endViewDate = dateUtils.trimTime(this.getEndViewDate()); - const today = this._getToday(); + return dateUtils.dateInRange(today, this.getStartViewDate(), new Date(endViewDate.getTime() + toMs('day'))); + } + return false; + } - return today >= dateUtils.trimTime(new Date(this.getStartViewDate())); + isIndicationAvailable() { + if (!hasWindow()) { + return false; } - isIndicatorVisible() { - const today = this._getToday(); + const today = this._getToday(); - // Subtracts 1 ms from the real endViewDate instead of 1 minute - const endViewDate = new Date(this.getEndViewDate().getTime() + toMs('minute') - 1); - const firstViewDate = new Date(this.getStartViewDate()); - firstViewDate.setFullYear(today.getFullYear(), today.getMonth(), today.getDate()); - endViewDate.setFullYear(today.getFullYear(), today.getMonth(), today.getDate()); + return today >= dateUtils.trimTime(new Date(this.getStartViewDate())); + } - return dateUtils.dateInRange(today, firstViewDate, endViewDate); - } + isIndicatorVisible() { + const today = this._getToday(); - _renderDateTimeIndication() { - if(this.isIndicationAvailable()) { - if(this.option('shadeUntilCurrentTime')) { - this._shader.render(); - } - - if(this.isIndicationOnView() && this.isIndicatorVisible()) { - const groupCount = this._getGroupCount() || 1; - const $container = this._dateTableScrollable.$content(); - const height = this.getIndicationHeight(); - const rtlOffset = this._getRtlOffset(this.getCellWidth()); - - this._renderIndicator(height, rtlOffset, $container, groupCount); - this._setCurrentTimeCells(); - } - } - } + // Subtracts 1 ms from the real endViewDate instead of 1 minute + const endViewDate = new Date(this.getEndViewDate().getTime() + toMs('minute') - 1); + const firstViewDate = new Date(this.getStartViewDate()); + firstViewDate.setFullYear(today.getFullYear(), today.getMonth(), today.getDate()); + endViewDate.setFullYear(today.getFullYear(), today.getMonth(), today.getDate()); - _renderIndicator(height, rtlOffset, $container, groupCount) { - const groupedByDate = this.isGroupedByDate(); - const repeatCount = groupedByDate ? 1 : groupCount; - for(let i = 0; i < repeatCount; i++) { - const $indicator = this._createIndicator($container); - - setWidth( - $indicator, - groupedByDate ? this.getCellWidth() * groupCount : this.getCellWidth() - ); - this._groupedStrategy.shiftIndicator($indicator, height, rtlOffset, i); - } - } + return dateUtils.dateInRange(today, firstViewDate, endViewDate); + } - _createIndicator($container) { - const $indicator = $('
').addClass(SCHEDULER_DATE_TIME_INDICATOR_CLASS); - $container.append($indicator); + _renderDateTimeIndication() { + if (this.isIndicationAvailable()) { + if (this.option('shadeUntilCurrentTime')) { + this._shader.render(); + } - return $indicator; - } + if (this.isIndicationOnView() && this.isIndicatorVisible()) { + const groupCount = this._getGroupCount() || 1; + const $container = this._dateTableScrollable.$content(); + const height = this.getIndicationHeight(); + const rtlOffset = this._getRtlOffset(this.getCellWidth()); - _getRtlOffset(width) { - return this.option('rtlEnabled') ? getBoundingRect(this._dateTableScrollable.$content().get(0)).width - this.getTimePanelWidth() - width : 0; + this._renderIndicator(height, rtlOffset, $container, groupCount); + this._setCurrentTimeCells(); + } } + } + + _renderIndicator(height, rtlOffset, $container, groupCount) { + const groupedByDate = this.isGroupedByDate(); + const repeatCount = groupedByDate ? 1 : groupCount; + for (let i = 0; i < repeatCount; i++) { + const $indicator = this._createIndicator($container); + + setWidth( + $indicator, + groupedByDate ? this.getCellWidth() * groupCount : this.getCellWidth(), + ); + this._groupedStrategy.shiftIndicator($indicator, height, rtlOffset, i); + } + } - _setIndicationUpdateInterval() { - if(!this.option('showCurrentTimeIndicator') || this.option('indicatorUpdateInterval') === 0) { - return; - } + _createIndicator($container) { + const $indicator = $('
').addClass(SCHEDULER_DATE_TIME_INDICATOR_CLASS); + $container.append($indicator); - this._clearIndicatorUpdateInterval(); + return $indicator; + } - this._indicatorInterval = setInterval(function() { - this._refreshDateTimeIndication(); - }.bind(this), this.option('indicatorUpdateInterval')); - } + _getRtlOffset(width) { + return this.option('rtlEnabled') ? getBoundingRect(this._dateTableScrollable.$content().get(0)).width - this.getTimePanelWidth() - width : 0; + } - _clearIndicatorUpdateInterval() { - if(this._indicatorInterval) { - clearInterval(this._indicatorInterval); - delete this._indicatorInterval; - } + _setIndicationUpdateInterval() { + if (!this.option('showCurrentTimeIndicator') || this.option('indicatorUpdateInterval') === 0) { + return; } - _isVerticalShader() { - return true; + this._clearIndicatorUpdateInterval(); + + this._indicatorInterval = setInterval(() => { + this._refreshDateTimeIndication(); + }, this.option('indicatorUpdateInterval')); + } + + _clearIndicatorUpdateInterval() { + if (this._indicatorInterval) { + clearInterval(this._indicatorInterval); + delete this._indicatorInterval; } + } - getIndicationWidth(groupIndex) { - const maxWidth = this.getCellWidth() * this._getCellCount(); + _isVerticalShader() { + return true; + } - let difference = this._getIndicatorDuration(); - if(difference > this._getCellCount()) { - difference = this._getCellCount(); - } - const width = difference * this.getRoundedCellWidth(groupIndex, groupIndex * this._getCellCount(), difference); + getIndicationWidth(groupIndex) { + const maxWidth = this.getCellWidth() * this._getCellCount(); - return maxWidth < width ? maxWidth : width; + let difference = this._getIndicatorDuration(); + if (difference > this._getCellCount()) { + difference = this._getCellCount(); } + const width = difference * this.getRoundedCellWidth(groupIndex, groupIndex * this._getCellCount(), difference); - getIndicatorOffset(groupIndex) { - const difference = this._getIndicatorDuration() - 1; - const offset = difference * this.getRoundedCellWidth(groupIndex, groupIndex * this._getCellCount(), difference); + return maxWidth < width ? maxWidth : width; + } - return offset; - } + getIndicatorOffset(groupIndex) { + const difference = this._getIndicatorDuration() - 1; + const offset = difference * this.getRoundedCellWidth(groupIndex, groupIndex * this._getCellCount(), difference); - _getIndicatorDuration() { - const today = this._getToday(); - const firstViewDate = new Date(this.getStartViewDate()); - let timeDiff = today.getTime() - firstViewDate.getTime(); - if(this.option('type') === 'workWeek') { - timeDiff = timeDiff - (this._getWeekendsCount(Math.round(timeDiff / toMs('day'))) * toMs('day')); - } + return offset; + } - return Math.ceil((timeDiff + 1) / toMs('day')); + _getIndicatorDuration() { + const today = this._getToday(); + const firstViewDate = new Date(this.getStartViewDate()); + let timeDiff = today.getTime() - firstViewDate.getTime(); + if (this.option('type') === 'workWeek') { + timeDiff -= this._getWeekendsCount(Math.round(timeDiff / toMs('day'))) * toMs('day'); } - getIndicationHeight() { - const today = timezoneUtils.getDateWithoutTimezoneChange(this._getToday()); - const cellHeight = this.getCellHeight(); - const date = new Date(this.getStartViewDate()); - - if(this.isIndicationOnView()) { - date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate()); - } + return Math.ceil((timeDiff + 1) / toMs('day')); + } - const duration = today.getTime() - date.getTime(); - const cellCount = duration / this.getCellDuration(); + getIndicationHeight() { + const today = timezoneUtils.getDateWithoutTimezoneChange(this._getToday()); + const cellHeight = this.getCellHeight(); + const date = new Date(this.getStartViewDate()); - return cellCount * cellHeight; + if (this.isIndicationOnView()) { + date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate()); } - _dispose() { - this._clearIndicatorUpdateInterval(); - super._dispose.apply(this, arguments); - } + const duration = today.getTime() - date.getTime(); + const cellCount = duration / this.getCellDuration(); - _refreshDateTimeIndication() { - this._cleanDateTimeIndicator(); - this._cleanCurrentTimeCells(); + return cellCount * cellHeight; + } - this._shader?.clean(); + _dispose() { + this._clearIndicatorUpdateInterval(); + super._dispose.apply(this, arguments as any); + } - this._renderDateTimeIndication(); - } + _refreshDateTimeIndication() { + this._cleanDateTimeIndicator(); + this._cleanCurrentTimeCells(); - _setCurrentTimeCells() { - const timePanelCells = this._getTimePanelCells(); - const currentTimeCellIndices = this._getCurrentTimePanelCellIndices(); - currentTimeCellIndices.forEach((timePanelCellIndex) => { - timePanelCells.eq(timePanelCellIndex) - .addClass(TIME_PANEL_CURRENT_TIME_CELL_CLASS); - }); - } + this._shader?.clean(); + + this._renderDateTimeIndication(); + } - _isCurrentTimeHeaderCell(headerIndex) { - if(this.isIndicationOnView()) { - const completeDateHeaderMap = this.viewDataProvider.completeDateHeaderMap; - const date = completeDateHeaderMap[completeDateHeaderMap.length - 1][headerIndex].startDate; + _setCurrentTimeCells() { + const timePanelCells = this._getTimePanelCells(); + const currentTimeCellIndices = this._getCurrentTimePanelCellIndices(); + currentTimeCellIndices.forEach((timePanelCellIndex) => { + timePanelCells.eq(timePanelCellIndex) + .addClass(TIME_PANEL_CURRENT_TIME_CELL_CLASS); + }); + } - return dateUtils.sameDate(date, this._getToday()); - } + _isCurrentTimeHeaderCell(headerIndex) { + if (this.isIndicationOnView()) { + const { completeDateHeaderMap } = this.viewDataProvider; + const date = completeDateHeaderMap[completeDateHeaderMap.length - 1][headerIndex].startDate; - return false; + return dateUtils.sameDate(date, this._getToday()); } - _getHeaderPanelCellClass(i) { - const cellClass = super._getHeaderPanelCellClass(i); + return false; + } - if(this._isCurrentTimeHeaderCell(i)) { - return cellClass + ' ' + HEADER_CURRENT_TIME_CELL_CLASS; - } + _getHeaderPanelCellClass(i) { + const cellClass = super._getHeaderPanelCellClass(i); - return cellClass; + if (this._isCurrentTimeHeaderCell(i)) { + return `${cellClass} ${HEADER_CURRENT_TIME_CELL_CLASS}`; } - _cleanView() { - super._cleanView(); + return cellClass; + } - this._cleanDateTimeIndicator(); - } + _cleanView() { + super._cleanView(); - _dimensionChanged() { - super._dimensionChanged(); + this._cleanDateTimeIndicator(); + } - this._refreshDateTimeIndication(); - } + _dimensionChanged() { + super._dimensionChanged(); - _cleanDateTimeIndicator() { - this.$element().find('.' + SCHEDULER_DATE_TIME_INDICATOR_CLASS).remove(); - } + this._refreshDateTimeIndication(); + } - _cleanCurrentTimeCells() { - this.$element() - .find(`.${TIME_PANEL_CURRENT_TIME_CELL_CLASS}`) - .removeClass(TIME_PANEL_CURRENT_TIME_CELL_CLASS); - } + _cleanDateTimeIndicator() { + (this.$element() as any).find(`.${SCHEDULER_DATE_TIME_INDICATOR_CLASS}`).remove(); + } + + _cleanCurrentTimeCells() { + (this.$element() as any) + .find(`.${TIME_PANEL_CURRENT_TIME_CELL_CLASS}`) + .removeClass(TIME_PANEL_CURRENT_TIME_CELL_CLASS); + } - _cleanWorkSpace() { - super._cleanWorkSpace(); + _cleanWorkSpace() { + super._cleanWorkSpace(); - this._renderDateTimeIndication(); + this._renderDateTimeIndication(); + this._setIndicationUpdateInterval(); + } + + _optionChanged(args) { + switch (args.name) { + case 'showCurrentTimeIndicator': + case 'indicatorTime': + this._cleanWorkSpace(); + break; + case 'indicatorUpdateInterval': this._setIndicationUpdateInterval(); + break; + case 'showAllDayPanel': + super._optionChanged(args); + this._refreshDateTimeIndication(); + break; + case 'allDayExpanded': + super._optionChanged(args); + this._refreshDateTimeIndication(); + break; + case 'crossScrollingEnabled': + super._optionChanged(args); + this._refreshDateTimeIndication(); + break; + case 'shadeUntilCurrentTime': + this._refreshDateTimeIndication(); + break; + default: + super._optionChanged(args); } - - _optionChanged(args) { - - switch(args.name) { - case 'showCurrentTimeIndicator': - case 'indicatorTime': - this._cleanWorkSpace(); - break; - case 'indicatorUpdateInterval': - this._setIndicationUpdateInterval(); - break; - case 'showAllDayPanel': - super._optionChanged(args); - this._refreshDateTimeIndication(); - break; - case 'allDayExpanded': - super._optionChanged(args); - this._refreshDateTimeIndication(); - break; - case 'crossScrollingEnabled': - super._optionChanged(args); - this._refreshDateTimeIndication(); - break; - case 'shadeUntilCurrentTime': - this._refreshDateTimeIndication(); - break; - default: - super._optionChanged(args); - } + } + + _getDefaultOptions() { + return extend(super._getDefaultOptions(), { + showCurrentTimeIndicator: true, + indicatorTime: new Date(), + indicatorUpdateInterval: 5 * toMs('minute'), + shadeUntilCurrentTime: true, + }); + } + + _getCurrentTimePanelCellIndices() { + const rowCountPerGroup = this._getTimePanelRowCount(); + const today = this._getToday(); + const index = this.getCellIndexByDate(today); + const { rowIndex: currentTimeRowIndex } = this._getCellCoordinatesByIndex(index); + + if (currentTimeRowIndex === undefined) { + return []; } - _getDefaultOptions() { - return extend(super._getDefaultOptions(), { - showCurrentTimeIndicator: true, - indicatorTime: new Date(), - indicatorUpdateInterval: 5 * toMs('minute'), - shadeUntilCurrentTime: true - }); + let cellIndices; + if (currentTimeRowIndex === 0) { + cellIndices = [currentTimeRowIndex]; + } else { + cellIndices = currentTimeRowIndex % 2 === 0 + ? [currentTimeRowIndex - 1, currentTimeRowIndex] + : [currentTimeRowIndex, currentTimeRowIndex + 1]; } - _getCurrentTimePanelCellIndices() { - const rowCountPerGroup = this._getTimePanelRowCount(); - const today = this._getToday(); - const index = this.getCellIndexByDate(today); - const { rowIndex: currentTimeRowIndex } = this._getCellCoordinatesByIndex(index); - - if(currentTimeRowIndex === undefined) { - return []; - } - - let cellIndices; - if(currentTimeRowIndex === 0) { - cellIndices = [currentTimeRowIndex]; - } else { - cellIndices = currentTimeRowIndex % 2 === 0 - ? [currentTimeRowIndex - 1, currentTimeRowIndex] - : [currentTimeRowIndex, currentTimeRowIndex + 1]; - } - - const verticalGroupCount = this._isVerticalGroupedWorkSpace() - ? this._getGroupCount() - : 1; - - return [...(new Array(verticalGroupCount))] - .reduce((currentIndices, _, groupIndex) => { - return [ - ...currentIndices, - ...cellIndices.map(cellIndex => rowCountPerGroup * groupIndex + cellIndex), - ]; - }, []); - } + const verticalGroupCount = this._isVerticalGroupedWorkSpace() + ? this._getGroupCount() + : 1; + + return [...new Array(verticalGroupCount)] + .reduce((currentIndices, _, groupIndex) => [ + ...currentIndices, + ...cellIndices.map((cellIndex) => rowCountPerGroup * groupIndex + cellIndex), + ], []); + } } -registerComponent('dxSchedulerWorkSpace', SchedulerWorkSpaceIndicator); +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 a229d9d190aa..2871a83eecf3 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 @@ -1,17 +1,20 @@ -import { noop } from '../../../core/utils/common'; -import registerComponent from '../../../core/component_registrator'; -import SchedulerWorkSpace from './ui.scheduler.work_space.indicator'; -import dateUtils from '../../../core/utils/date'; -import { getBoundingRect } from '../../../core/utils/position'; -import { utils } from '../utils'; -import { hasWindow } from '../../../core/utils/window'; -import dxrMonthDateTableLayout from '../../../renovation/ui/scheduler/workspaces/month/date_table/layout.j'; +import registerComponent from '@js/core/component_registrator'; +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'; +import { formatWeekday } from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/base'; import { - getViewStartByOptions, - getCellText, -} from '../../../renovation/ui/scheduler/view_model/to_test/views/utils/month'; -import { formatWeekday } from '../../../renovation/ui/scheduler/view_model/to_test/views/utils/base'; -import { VIEWS } from '../constants'; + getCellText, + getViewStartByOptions, +} from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/month'; + +// NOTE: Renovation component import. +// @ts-expect-error +import dxrMonthDateTableLayout from '../../../renovation/ui/scheduler/workspaces/month/date_table/layout.j'; +import { VIEWS } from '../m_constants'; +import { utils } from '../m_utils'; +import SchedulerWorkSpace from './m_work_space_indicator'; const MONTH_CLASS = 'dx-scheduler-work-space-month'; @@ -23,168 +26,172 @@ const DATE_TABLE_OTHER_MONTH_DATE_CLASS = 'dx-scheduler-date-table-other-month'; const toMs = dateUtils.dateToMilliseconds; class SchedulerWorkSpaceMonth extends SchedulerWorkSpace { - get type() { return VIEWS.MONTH; } - - _getElementClass() { - return MONTH_CLASS; + get type() { return VIEWS.MONTH; } + + _getElementClass() { + return MONTH_CLASS; + } + + _getFormat() { + return formatWeekday; + } + + _getIntervalBetween(currentDate) { + const firstViewDate = this.getStartViewDate(); + const timeZoneOffset = dateUtils.getTimezonesDifference(firstViewDate, currentDate); + + return currentDate.getTime() - (firstViewDate.getTime() - (this.option('startDayHour') as any) * 3600000) - timeZoneOffset; + } + + _getDateGenerationOptions() { + return { + ...super._getDateGenerationOptions(), + cellCountInDay: 1, + }; + } + + // TODO: temporary fix, in the future, if we replace table layout on div layout, getCellWidth method need remove. Details in T712431 + // TODO: there is a test for this bug, when changing the layout, the test will also be useless + getCellWidth() { + return this.cache.get('cellWidth', () => { + const DAYS_IN_WEEK = 7; + + let averageWidth = 0; + const cells = this._getCells().slice(0, DAYS_IN_WEEK); + cells.each((index, element) => { + averageWidth += hasWindow() ? getBoundingRect(element).width : 0; + }); + + return cells.length === 0 ? undefined : averageWidth / DAYS_IN_WEEK; + }); + } + + _insertAllDayRowsIntoDateTable() { + return false; + } + + _getCellCoordinatesByIndex(index) { + const rowIndex = Math.floor(index / this._getCellCount()); + const columnIndex = index - this._getCellCount() * rowIndex; + + return { + rowIndex, + columnIndex, + }; + } + + _needCreateCrossScrolling() { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + return this.option('crossScrollingEnabled') || this._isVerticalGroupedWorkSpace(); + } + + _getViewStartByOptions() { + return getViewStartByOptions( + this.option('startDate') as any, + this.option('currentDate') as any, + this.option('intervalCount') as any, + dateUtils.getFirstMonthDate(this.option('startDate')) as any, + ); + } + + _updateIndex(index) { + return index; + } + + isIndicationAvailable() { + return false; + } + + getIntervalDuration() { + return toMs('day'); + } + + getTimePanelWidth() { + return 0; + } + + supportAllDayRow() { + return false; + } + + keepOriginalHours() { + return true; + } + + getWorkSpaceLeftOffset() { + return 0; + } + + needApplyCollectorOffset() { + return true; + } + + _getHeaderDate() { + return this._getViewStartByOptions(); + } + + scrollToTime() { return noop(); } + + renderRAllDayPanel() {} + + renderRTimeTable() {} + + renderRDateTable() { + utils.renovation.renderComponent( + this, + this._$dateTable, + dxrMonthDateTableLayout, + 'renovatedDateTable', + this._getRDateTableProps(), + ); + } + + // ------------- + // We need these methods for now but they are useless for renovation + // ------------- + + _createWorkSpaceElements() { + if (this._isVerticalGroupedWorkSpace()) { + this._createWorkSpaceScrollableElements(); + } else { + super._createWorkSpaceElements(); } + } + + _toggleAllDayVisibility() { return noop(); } - _getFormat() { - return formatWeekday; - } + _changeAllDayVisibility() { return noop(); } - _getIntervalBetween(currentDate) { - const firstViewDate = this.getStartViewDate(); - const timeZoneOffset = dateUtils.getTimezonesDifference(firstViewDate, currentDate); + // -------------- + // These methods should be deleted when we get rid of old render + // -------------- - return currentDate.getTime() - (firstViewDate.getTime() - this.option('startDayHour') * 3600000) - timeZoneOffset; - } + _renderTimePanel() { return noop(); } - _getDateGenerationOptions() { - return { - ...super._getDateGenerationOptions(), - cellCountInDay: 1, - }; - } + _renderAllDayPanel() { return noop(); } - // TODO: temporary fix, in the future, if we replace table layout on div layout, getCellWidth method need remove. Details in T712431 - // TODO: there is a test for this bug, when changing the layout, the test will also be useless - getCellWidth() { - return this.cache.get('cellWidth', () => { - const DAYS_IN_WEEK = 7; + _setMonthClassesToCell($cell, data) { + $cell + .toggleClass(DATE_TABLE_CURRENT_DATE_CLASS, data.isCurrentDate) + .toggleClass(DATE_TABLE_FIRST_OF_MONTH_CLASS, data.firstDayOfMonth) + .toggleClass(DATE_TABLE_OTHER_MONTH_DATE_CLASS, data.otherMonth); + } - let averageWidth = 0; - const cells = this._getCells().slice(0, DAYS_IN_WEEK); - cells.each((index, element) => { - averageWidth += hasWindow() ? getBoundingRect(element).width : 0; - }); + _createAllDayPanelElements() {} - return cells.length === 0 ? undefined : averageWidth / DAYS_IN_WEEK; - }); - } - - _insertAllDayRowsIntoDateTable() { - return false; - } - _getCellCoordinatesByIndex(index) { - const rowIndex = Math.floor(index / this._getCellCount()); - const columnIndex = index - this._getCellCount() * rowIndex; - - return { - rowIndex: rowIndex, - columnIndex, - }; - } + _renderTableBody(options) { + options.getCellText = (rowIndex, columnIndex) => { + const date = this.viewDataProvider.completeViewDataMap[rowIndex][columnIndex].startDate; - _needCreateCrossScrolling() { - return this.option('crossScrollingEnabled') || this._isVerticalGroupedWorkSpace(); - } + return getCellText(date, this.option('intervalCount') as any); + }; + options.getCellTextClass = DATE_TABLE_CELL_TEXT_CLASS; + options.setAdditionalClasses = this._setMonthClassesToCell.bind(this); - _getViewStartByOptions() { - return getViewStartByOptions( - this.option('startDate'), - this.option('currentDate'), - this.option('intervalCount'), - dateUtils.getFirstMonthDate(this.option('startDate')), - ); - } - - _updateIndex(index) { - return index; - } - - isIndicationAvailable() { - return false; - } - - getIntervalDuration() { - return toMs('day'); - } - - getTimePanelWidth() { - return 0; - } - - supportAllDayRow() { - return false; - } - - keepOriginalHours() { - return true; - } - - getWorkSpaceLeftOffset() { - return 0; - } - - needApplyCollectorOffset() { - return true; - } - - _getHeaderDate() { - return this._getViewStartByOptions(); - } - - scrollToTime() { return noop(); } - - renderRAllDayPanel() {} - - renderRTimeTable() {} - - renderRDateTable() { - utils.renovation.renderComponent( - this, - this._$dateTable, - dxrMonthDateTableLayout, - 'renovatedDateTable', - this._getRDateTableProps(), - ); - } - - // ------------- - // We need these methods for now but they are useless for renovation - // ------------- - - _createWorkSpaceElements() { - if(this._isVerticalGroupedWorkSpace()) { - this._createWorkSpaceScrollableElements(); - } else { - super._createWorkSpaceElements(); - } - } - - _toggleAllDayVisibility() { return noop(); } - _changeAllDayVisibility() { return noop(); } - - // -------------- - // These methods should be deleted when we get rid of old render - // -------------- - - _renderTimePanel() { return noop(); } - _renderAllDayPanel() { return noop(); } - - _setMonthClassesToCell($cell, data) { - $cell - .toggleClass(DATE_TABLE_CURRENT_DATE_CLASS, data.isCurrentDate) - .toggleClass(DATE_TABLE_FIRST_OF_MONTH_CLASS, data.firstDayOfMonth) - .toggleClass(DATE_TABLE_OTHER_MONTH_DATE_CLASS, data.otherMonth); - } - - _createAllDayPanelElements() {} - - _renderTableBody(options) { - options.getCellText = (rowIndex, columnIndex) => { - const date = this.viewDataProvider.completeViewDataMap[rowIndex][columnIndex].startDate; - - return getCellText(date, this.option('intervalCount')); - }; - options.getCellTextClass = DATE_TABLE_CELL_TEXT_CLASS; - options.setAdditionalClasses = this._setMonthClassesToCell.bind(this), - - super._renderTableBody(options); - } + super._renderTableBody(options); + } } -registerComponent('dxSchedulerWorkSpaceMonth', SchedulerWorkSpaceMonth); +registerComponent('dxSchedulerWorkSpaceMonth', SchedulerWorkSpaceMonth as any); export default SchedulerWorkSpaceMonth; diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_vertical.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_vertical.ts index e348e8685437..0258c9fbcce5 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_vertical.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_vertical.ts @@ -1,24 +1,24 @@ -import SchedulerWorkSpaceIndicator from './ui.scheduler.work_space.indicator'; -import { formatWeekdayAndDay } from '../../../renovation/ui/scheduler/view_model/to_test/views/utils/base'; +import { formatWeekdayAndDay } from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/base'; -class SchedulerWorkspaceVertical extends SchedulerWorkSpaceIndicator { - _getFormat() { - return formatWeekdayAndDay; - } +import SchedulerWorkSpaceIndicator from './m_work_space_indicator'; - generateRenderOptions() { - const options = super.generateRenderOptions(); +class SchedulerWorkspaceVertical extends SchedulerWorkSpaceIndicator { + _getFormat() { + return formatWeekdayAndDay; + } - return { - ...options, - isGenerateTimePanelData: true, - }; - } + generateRenderOptions() { + const options = super.generateRenderOptions(); - _isRenderHeaderPanelEmptyCell() { - return true; - } + return { + ...options, + isGenerateTimePanelData: true, + }; + } + _isRenderHeaderPanelEmptyCell() { + return true; + } } export default SchedulerWorkspaceVertical; 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 45d991229111..f565cb79e508 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 @@ -1,21 +1,22 @@ -import registerComponent from '../../../core/component_registrator'; -import { VIEWS } from '../constants'; -import SchedulerWorkSpaceVertical from './ui.scheduler.work_space_vertical'; -import { calculateViewStartDate } from '../../../renovation/ui/scheduler/view_model/to_test/views/utils/week'; +import registerComponent from '@js/core/component_registrator'; +import { calculateViewStartDate } from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/week'; + +import { VIEWS } from '../m_constants'; +import SchedulerWorkSpaceVertical from './m_work_space_vertical'; const WEEK_CLASS = 'dx-scheduler-work-space-week'; class SchedulerWorkSpaceWeek extends SchedulerWorkSpaceVertical { - get type() { return VIEWS.WEEK; } + get type() { return VIEWS.WEEK; } - _getElementClass() { - return WEEK_CLASS; - } + _getElementClass() { + return WEEK_CLASS; + } - _calculateViewStartDate() { - return calculateViewStartDate(this.option('startDate'), this._firstDayOfWeek()); - } + _calculateViewStartDate() { + return calculateViewStartDate(this.option('startDate') as any, this._firstDayOfWeek()); + } } -registerComponent('dxSchedulerWorkSpaceWeek', SchedulerWorkSpaceWeek); +registerComponent('dxSchedulerWorkSpaceWeek', SchedulerWorkSpaceWeek as any); export default SchedulerWorkSpaceWeek; diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_work_week.ts b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_work_week.ts index bba90803e2d9..75d32325b680 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_work_week.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/m_work_space_work_week.ts @@ -1,25 +1,27 @@ -import registerComponent from '../../../core/component_registrator'; +import registerComponent from '@js/core/component_registrator'; import { - getWeekendsCount, -} from '../../../renovation/ui/scheduler/view_model/to_test/views/utils/work_week'; -import SchedulerWorkSpaceWeek from './ui.scheduler.work_space_week'; -import { VIEWS } from '../constants'; + getWeekendsCount, +} from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/work_week'; + +import { VIEWS } from '../m_constants'; +import SchedulerWorkSpaceWeek from './m_work_space_week'; const WORK_WEEK_CLASS = 'dx-scheduler-work-space-work-week'; class SchedulerWorkSpaceWorkWeek extends SchedulerWorkSpaceWeek { - get type() { return VIEWS.WORK_WEEK; } + get type() { return VIEWS.WORK_WEEK; } - constructor(...args) { - super(...args); + constructor(...args: any[]) { + // @ts-expect-error + super(...args); - this._getWeekendsCount = getWeekendsCount; - } + this._getWeekendsCount = getWeekendsCount; + } - _getElementClass() { - return WORK_WEEK_CLASS; - } + _getElementClass() { + return WORK_WEEK_CLASS; + } } -registerComponent('dxSchedulerWorkSpaceWorkWeek', SchedulerWorkSpaceWorkWeek); +registerComponent('dxSchedulerWorkSpaceWorkWeek', SchedulerWorkSpaceWorkWeek as any); export default SchedulerWorkSpaceWorkWeek; diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/__tests__/utils.test.ts b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/__tests__/utils.test.ts index 002fccff1237..4b5a0fbc6b3b 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/__tests__/utils.test.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/__tests__/utils.test.ts @@ -1,7 +1,13 @@ -/* eslint-disable no-undef */ -import { alignToFirstDayOfWeek, alignToLastDayOfWeek, calculateDaysBetweenDates, calculateAlignedWeeksBetweenDates } from '../utils'; +/* eslint-disable no-undef, forbidden-imports/no-restricted-imports */ import each from 'jest-each'; +import { + alignToFirstDayOfWeek, + alignToLastDayOfWeek, + calculateAlignedWeeksBetweenDates, + calculateDaysBetweenDates, +} from '../m_utils'; + /* Calendar for 2023-06 for easier test cases understanding: @@ -16,19 +22,19 @@ mon tue wed thu fri sat sun */ describe('alignToFirstDayOfWeek', () => { - each` + each` date | firstDayOfWeek ${new Date('2023-06-26')} | ${1} ${new Date('2023-06-25')} | ${0} `.it('should return the same value if date is first day of week', ({ - date, firstDayOfWeek - }) => { - const resultDate = alignToFirstDayOfWeek(date, firstDayOfWeek); - expect(resultDate).toEqual(date); - }); - - each` - date | rightDate | firstDayOfWeek + date, firstDayOfWeek, + }) => { + const resultDate = alignToFirstDayOfWeek(date, firstDayOfWeek); + expect(resultDate).toEqual(date); + }); + + each` + date | rightDate | firstDayOfWeek ${new Date('2023-06-29')} | ${new Date('2023-06-26')} | ${1} ${new Date('2023-06-27')} | ${new Date('2023-06-26')} | ${1} ${new Date('2023-06-30')} | ${new Date('2023-06-26')} | ${1} @@ -37,82 +43,82 @@ describe('alignToFirstDayOfWeek', () => { ${new Date('2023-06-26')} | ${new Date('2023-06-25')} | ${0} `.it('should return first day of current week', ({ - date, rightDate, firstDayOfWeek - }) => { - const resultDate = alignToFirstDayOfWeek(date, firstDayOfWeek); - expect(resultDate).toEqual(rightDate); - }); + date, rightDate, firstDayOfWeek, + }) => { + const resultDate = alignToFirstDayOfWeek(date, firstDayOfWeek); + expect(resultDate).toEqual(rightDate); + }); }); describe('alignToLastDayOfWeek', () => { - each` + each` date | firstDayOfWeek ${new Date('2023-06-25')} | ${1} ${new Date('2023-06-24')} | ${0} `.it('should return the same value if date is last day of week', ({ - date, firstDayOfWeek - }) => { - const resultDate = alignToLastDayOfWeek(date, firstDayOfWeek); - expect(resultDate).toEqual(date); - }); - - each` - date | rightDate | firstDayOfWeek + date, firstDayOfWeek, + }) => { + const resultDate = alignToLastDayOfWeek(date, firstDayOfWeek); + expect(resultDate).toEqual(date); + }); + + each` + date | rightDate | firstDayOfWeek ${new Date('2023-06-12')} | ${new Date('2023-06-18')} | ${1} ${new Date('2023-06-14')} | ${new Date('2023-06-18')} | ${1} ${new Date('2023-06-16')} | ${new Date('2023-06-18')} | ${1} ${new Date('2023-06-21')} | ${new Date('2023-06-25')} | ${1} `.it('should return last day of current week', ({ - date, rightDate, firstDayOfWeek - }) => { - const resultDate = alignToLastDayOfWeek(date, firstDayOfWeek); - expect(resultDate).toEqual(rightDate); - }); + date, rightDate, firstDayOfWeek, + }) => { + const resultDate = alignToLastDayOfWeek(date, firstDayOfWeek); + expect(resultDate).toEqual(rightDate); + }); }); describe('calculateDaysBetweenDates', () => { - each` + each` fromDate | toDate | res ${new Date('2023-06-28')} | ${new Date('2023-06-28')} | ${1} ${new Date('2023-06-28')} | ${new Date('2023-06-29')} | ${2} ${new Date('2023-06-28')} | ${new Date('2023-07-04')} | ${7} `.it('should return right count of days between dates', ({ - fromDate, toDate, res - }) => { - expect(calculateDaysBetweenDates(fromDate, toDate)).toBe(res); - }); + fromDate, toDate, res, + }) => { + expect(calculateDaysBetweenDates(fromDate, toDate)).toBe(res); + }); - each` + each` fromDate | toDate | res ${new Date('2023-06-28T23:59:00')} | ${new Date('2023-06-29T00:01:00')} | ${2} ${new Date('2023-06-28T00:01:00')} | ${new Date('2023-06-28T23:59:00')} | ${1} `.it('should return right count of days between dates when they have non-zero time', ({ - fromDate, toDate, res - }) => { - expect(calculateDaysBetweenDates(fromDate, toDate)).toBe(res); - }); + fromDate, toDate, res, + }) => { + expect(calculateDaysBetweenDates(fromDate, toDate)).toBe(res); + }); }); describe('calculateAlignedWeeksBetweenDates', () => { - each` + each` fromDate | toDate | res ${new Date('2023-10-01')} | ${new Date('2023-10-31')} | ${6} ${new Date('2023-06-01')} | ${new Date('2023-07-31')} | ${10} `.it('should return right count of days between dates', ({ - fromDate, toDate, res - }) => { - expect(calculateAlignedWeeksBetweenDates(fromDate, toDate, 1)).toBe(res); - }); + fromDate, toDate, res, + }) => { + expect(calculateAlignedWeeksBetweenDates(fromDate, toDate, 1)).toBe(res); + }); - each` + each` fromDate | toDate | res ${new Date('2023-06-04')} | ${new Date('2023-06-12')} | ${6} ${new Date('2023-06-05')} | ${new Date('2023-06-12')} | ${6} ${new Date('2023-06-05')} | ${new Date('2023-06-11')} | ${6} `.it('should return at least 6 weeks in order to not make breaking change', ({ - fromDate, toDate, res - }) => { - expect(calculateAlignedWeeksBetweenDates(fromDate, toDate, 1)).toBe(res); - }); + fromDate, toDate, res, + }) => { + expect(calculateAlignedWeeksBetweenDates(fromDate, toDate, 1)).toBe(res); + }); }); diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_date_header_data_generator.ts b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_date_header_data_generator.ts index ce608573896e..95f8ec5b2a70 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_date_header_data_generator.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_date_header_data_generator.ts @@ -1,237 +1,237 @@ -import dateUtils from '../../../../core/utils/date'; -import { getGroupCount } from '../../../../__internal/scheduler/resources/m_utils'; +import dateUtils from '@js/core/utils/date'; import { - getHeaderCellText, - formatWeekdayAndDay, - getHorizontalGroupCount, - getTotalCellCountByCompleteData, - getDisplayedCellCount, -} from '../../../../renovation/ui/scheduler/view_model/to_test/views/utils/base'; + formatWeekdayAndDay, + getDisplayedCellCount, + getHeaderCellText, + getHorizontalGroupCount, + getTotalCellCountByCompleteData, +} from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/base'; -export class DateHeaderDataGenerator { - constructor(viewDataGenerator) { - this._viewDataGenerator = viewDataGenerator; - } - - getCompleteDateHeaderMap(options, completeViewDataMap) { - const { - isGenerateWeekDaysHeaderData, - } = options; - - const result = []; - - if(isGenerateWeekDaysHeaderData) { - const weekDaysRow = this._generateWeekDaysHeaderRowMap(options, completeViewDataMap); - result.push(weekDaysRow); - } - - const dateRow = this._generateHeaderDateRow(options, completeViewDataMap); +import { getGroupCount } from '../../resources/m_utils'; - result.push(dateRow); +export class DateHeaderDataGenerator { + constructor(public _viewDataGenerator) { + } - return result; - } + getCompleteDateHeaderMap(options, completeViewDataMap) { + const { + isGenerateWeekDaysHeaderData, + } = options; - _generateWeekDaysHeaderRowMap(options, completeViewDataMap) { - const { - isGroupedByDate, - groups, - groupOrientation, - startDayHour, - endDayHour, - hoursInterval, - isHorizontalGrouping, - intervalCount, - } = options; - - const cellCountInDay = this._viewDataGenerator.getCellCountInDay(startDayHour, endDayHour, hoursInterval); - const horizontalGroupCount = getHorizontalGroupCount(groups, groupOrientation); - const index = completeViewDataMap[0][0].allDay ? 1 : 0; - const colSpan = isGroupedByDate ? horizontalGroupCount * cellCountInDay : cellCountInDay; - - const groupCount = getGroupCount(groups); - const datesRepeatCount = isHorizontalGrouping && !isGroupedByDate - ? groupCount - : 1; - - const daysInGroup = this._viewDataGenerator.daysInInterval * intervalCount; - const daysInView = daysInGroup * datesRepeatCount; - - const weekDaysRow = []; - - for(let dayIndex = 0; dayIndex < daysInView; dayIndex += 1) { - const cell = completeViewDataMap[index][dayIndex * colSpan]; - - weekDaysRow.push({ - ...cell, - colSpan, - text: formatWeekdayAndDay(cell.startDate), - isFirstGroupCell: false, - isLastGroupCell: false, - }); - } - - return weekDaysRow; - } + const result: any[] = []; - _generateHeaderDateRow(options, completeViewDataMap) { - const { - today, - isGroupedByDate, - groupOrientation, - groups, - headerCellTextFormat, - getDateForHeaderText, - interval, - startViewDate, - startDayHour, - endDayHour, - hoursInterval, - intervalCount, - currentDate, - viewType, - } = options; - - const horizontalGroupCount = getHorizontalGroupCount(groups, groupOrientation); - const index = completeViewDataMap[0][0].allDay ? 1 : 0; - const colSpan = isGroupedByDate ? horizontalGroupCount : 1; - const isVerticalGrouping = groupOrientation === 'vertical'; - const cellCountInGroupRow = this._viewDataGenerator.getCellCount({ - intervalCount, currentDate, viewType, - hoursInterval, startDayHour, endDayHour, - }); - const cellCountInDay = this._viewDataGenerator.getCellCountInDay( - startDayHour, endDayHour, hoursInterval, - ); - - const slicedByColumnsData = isGroupedByDate - ? completeViewDataMap[index].filter((_, columnIndex) => columnIndex % horizontalGroupCount === 0) - : completeViewDataMap[index]; - - return slicedByColumnsData.map(({ - startDate, - endDate, - isFirstGroupCell, - isLastGroupCell, - ...restProps - }, index) => { - const text = getHeaderCellText( - index % cellCountInGroupRow, - startDate, - headerCellTextFormat, - getDateForHeaderText, - { - interval, - startViewDate, - startDayHour, - cellCountInDay, - }, - ); - - return ({ - ...restProps, - startDate, - text, - today: dateUtils.sameDate(startDate, today), - colSpan, - isFirstGroupCell: isGroupedByDate || (isFirstGroupCell && !isVerticalGrouping), - isLastGroupCell: isGroupedByDate || (isLastGroupCell && !isVerticalGrouping), - }); - }); + if (isGenerateWeekDaysHeaderData) { + const weekDaysRow = this._generateWeekDaysHeaderRowMap(options, completeViewDataMap); + result.push(weekDaysRow); } - generateDateHeaderData(completeDateHeaderMap, completeViewDataMap, options) { - const { - isGenerateWeekDaysHeaderData, - cellWidth, - isProvideVirtualCellsWidth, - startDayHour, - endDayHour, - hoursInterval, - isMonthDateHeader, - } = options; - - const dataMap = []; - let weekDayRowConfig = {}; - const validCellWidth = cellWidth || 0; - - if(isGenerateWeekDaysHeaderData) { - weekDayRowConfig = this._generateDateHeaderDataRow( - options, - completeDateHeaderMap, - completeViewDataMap, - this._viewDataGenerator.getCellCountInDay( - startDayHour, endDayHour, hoursInterval, - ), - 0, - validCellWidth, - ); - - dataMap.push(weekDayRowConfig.dateRow); - } - - const datesRowConfig = this._generateDateHeaderDataRow( - options, - completeDateHeaderMap, - completeViewDataMap, - 1, - isGenerateWeekDaysHeaderData ? 1 : 0, - validCellWidth, - ); - - dataMap.push(datesRowConfig.dateRow); - - return { - dataMap, - leftVirtualCellWidth: isProvideVirtualCellsWidth ? datesRowConfig.leftVirtualCellWidth : undefined, - rightVirtualCellWidth: isProvideVirtualCellsWidth ? datesRowConfig.rightVirtualCellWidth : undefined, - leftVirtualCellCount: datesRowConfig.leftVirtualCellCount, - rightVirtualCellCount: datesRowConfig.rightVirtualCellCount, - weekDayLeftVirtualCellWidth: weekDayRowConfig.leftVirtualCellWidth, - weekDayRightVirtualCellWidth: weekDayRowConfig.rightVirtualCellWidth, - weekDayLeftVirtualCellCount: weekDayRowConfig.leftVirtualCellCount, - weekDayRightVirtualCellCount: weekDayRowConfig.rightVirtualCellCount, - isMonthDateHeader, - }; + const dateRow = this._generateHeaderDateRow(options, completeViewDataMap); + + result.push(dateRow); + + return result; + } + + _generateWeekDaysHeaderRowMap(options, completeViewDataMap) { + const { + isGroupedByDate, + groups, + groupOrientation, + startDayHour, + endDayHour, + hoursInterval, + isHorizontalGrouping, + intervalCount, + } = options; + + const cellCountInDay = this._viewDataGenerator.getCellCountInDay(startDayHour, endDayHour, hoursInterval); + const horizontalGroupCount = getHorizontalGroupCount(groups, groupOrientation); + const index = completeViewDataMap[0][0].allDay ? 1 : 0; + const colSpan = isGroupedByDate ? horizontalGroupCount * cellCountInDay : cellCountInDay; + + const groupCount = getGroupCount(groups); + const datesRepeatCount = isHorizontalGrouping && !isGroupedByDate + ? groupCount + : 1; + + const daysInGroup = this._viewDataGenerator.daysInInterval * intervalCount; + const daysInView = daysInGroup * datesRepeatCount; + + const weekDaysRow: any[] = []; + + for (let dayIndex = 0; dayIndex < daysInView; dayIndex += 1) { + const cell = completeViewDataMap[index][dayIndex * colSpan]; + + weekDaysRow.push({ + ...cell, + colSpan, + text: formatWeekdayAndDay(cell.startDate), + isFirstGroupCell: false, + isLastGroupCell: false, + }); } - _generateDateHeaderDataRow( + return weekDaysRow; + } + + _generateHeaderDateRow(options, completeViewDataMap) { + const { + today, + isGroupedByDate, + groupOrientation, + groups, + headerCellTextFormat, + getDateForHeaderText, + interval, + startViewDate, + startDayHour, + endDayHour, + hoursInterval, + intervalCount, + currentDate, + viewType, + } = options; + + const horizontalGroupCount = getHorizontalGroupCount(groups, groupOrientation); + const index = completeViewDataMap[0][0].allDay ? 1 : 0; + const colSpan = isGroupedByDate ? horizontalGroupCount : 1; + const isVerticalGrouping = groupOrientation === 'vertical'; + const cellCountInGroupRow = this._viewDataGenerator.getCellCount({ + intervalCount, + currentDate, + viewType, + hoursInterval, + startDayHour, + endDayHour, + }); + const cellCountInDay = this._viewDataGenerator.getCellCountInDay(startDayHour, endDayHour, hoursInterval); + + const slicedByColumnsData = isGroupedByDate + ? completeViewDataMap[index].filter((_, columnIndex) => columnIndex % horizontalGroupCount === 0) + : completeViewDataMap[index]; + + return slicedByColumnsData.map(({ + startDate, + endDate, + isFirstGroupCell, + isLastGroupCell, + ...restProps + }, index) => { + const text = getHeaderCellText( + index % cellCountInGroupRow, + startDate, + headerCellTextFormat, + getDateForHeaderText, + { + interval, + startViewDate, + startDayHour, + cellCountInDay, + }, + ); + + return { + ...restProps, + startDate, + text, + today: dateUtils.sameDate(startDate, today), + colSpan, + isFirstGroupCell: isGroupedByDate || (isFirstGroupCell && !isVerticalGrouping), + isLastGroupCell: isGroupedByDate || (isLastGroupCell && !isVerticalGrouping), + }; + }); + } + + generateDateHeaderData(completeDateHeaderMap, completeViewDataMap, options) { + const { + isGenerateWeekDaysHeaderData, + cellWidth, + isProvideVirtualCellsWidth, + startDayHour, + endDayHour, + hoursInterval, + isMonthDateHeader, + } = options; + + const dataMap: any[] = []; + let weekDayRowConfig: any = {}; + const validCellWidth = cellWidth || 0; + + if (isGenerateWeekDaysHeaderData) { + weekDayRowConfig = this._generateDateHeaderDataRow( options, completeDateHeaderMap, completeViewDataMap, - baseColSpan, - rowIndex, - cellWidth, - ) { - const { - startCellIndex, - cellCount, - isProvideVirtualCellsWidth, - groups, - groupOrientation, - isGroupedByDate, - } = options; - - const horizontalGroupCount = getHorizontalGroupCount(groups, groupOrientation); - const colSpan = isGroupedByDate ? horizontalGroupCount * baseColSpan : baseColSpan; - const leftVirtualCellCount = Math.floor(startCellIndex / colSpan); - const displayedCellCount = getDisplayedCellCount(cellCount, completeViewDataMap); - const actualCellCount = Math.ceil((startCellIndex + displayedCellCount) / colSpan); - const totalCellCount = getTotalCellCountByCompleteData(completeViewDataMap); - - const dateRow = completeDateHeaderMap[rowIndex].slice(leftVirtualCellCount, actualCellCount); - - const finalLeftVirtualCellCount = leftVirtualCellCount * colSpan; - const finalLeftVirtualCellWidth = finalLeftVirtualCellCount * cellWidth; - const finalRightVirtualCellCount = totalCellCount - actualCellCount * colSpan; - const finalRightVirtualCellWidth = finalRightVirtualCellCount * cellWidth; - - return { - dateRow, - leftVirtualCellCount: finalLeftVirtualCellCount, - leftVirtualCellWidth: isProvideVirtualCellsWidth ? finalLeftVirtualCellWidth : undefined, - rightVirtualCellCount: finalRightVirtualCellCount, - rightVirtualCellWidth: isProvideVirtualCellsWidth ? finalRightVirtualCellWidth : undefined, - }; + this._viewDataGenerator.getCellCountInDay(startDayHour, endDayHour, hoursInterval), + 0, + validCellWidth, + ); + + dataMap.push(weekDayRowConfig.dateRow); } + + const datesRowConfig = this._generateDateHeaderDataRow( + options, + completeDateHeaderMap, + completeViewDataMap, + 1, + isGenerateWeekDaysHeaderData ? 1 : 0, + validCellWidth, + ); + + dataMap.push(datesRowConfig.dateRow); + + return { + dataMap, + leftVirtualCellWidth: isProvideVirtualCellsWidth ? datesRowConfig.leftVirtualCellWidth : undefined, + rightVirtualCellWidth: isProvideVirtualCellsWidth ? datesRowConfig.rightVirtualCellWidth : undefined, + leftVirtualCellCount: datesRowConfig.leftVirtualCellCount, + rightVirtualCellCount: datesRowConfig.rightVirtualCellCount, + weekDayLeftVirtualCellWidth: weekDayRowConfig.leftVirtualCellWidth, + weekDayRightVirtualCellWidth: weekDayRowConfig.rightVirtualCellWidth, + weekDayLeftVirtualCellCount: weekDayRowConfig.leftVirtualCellCount, + weekDayRightVirtualCellCount: weekDayRowConfig.rightVirtualCellCount, + isMonthDateHeader, + }; + } + + _generateDateHeaderDataRow( + options, + completeDateHeaderMap, + completeViewDataMap, + baseColSpan, + rowIndex, + cellWidth, + ) { + const { + startCellIndex, + cellCount, + isProvideVirtualCellsWidth, + groups, + groupOrientation, + isGroupedByDate, + } = options; + + const horizontalGroupCount = getHorizontalGroupCount(groups, groupOrientation); + const colSpan = isGroupedByDate ? horizontalGroupCount * baseColSpan : baseColSpan; + const leftVirtualCellCount = Math.floor(startCellIndex / colSpan); + const displayedCellCount = getDisplayedCellCount(cellCount, completeViewDataMap); + const actualCellCount = Math.ceil((startCellIndex + displayedCellCount) / colSpan); + const totalCellCount = getTotalCellCountByCompleteData(completeViewDataMap); + + const dateRow = completeDateHeaderMap[rowIndex].slice(leftVirtualCellCount, actualCellCount); + + const finalLeftVirtualCellCount = leftVirtualCellCount * colSpan; + const finalLeftVirtualCellWidth = finalLeftVirtualCellCount * cellWidth; + const finalRightVirtualCellCount = totalCellCount - actualCellCount * colSpan; + const finalRightVirtualCellWidth = finalRightVirtualCellCount * cellWidth; + + return { + dateRow, + leftVirtualCellCount: finalLeftVirtualCellCount, + leftVirtualCellWidth: isProvideVirtualCellsWidth ? finalLeftVirtualCellWidth : undefined, + rightVirtualCellCount: finalRightVirtualCellCount, + rightVirtualCellWidth: isProvideVirtualCellsWidth ? finalRightVirtualCellWidth : undefined, + }; + } } diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_grouped_data_map_provider.ts b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_grouped_data_map_provider.ts index bb6a59165541..e88959aee42f 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_grouped_data_map_provider.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_grouped_data_map_provider.ts @@ -1,220 +1,229 @@ -import dateUtils from '../../../../core/utils/date'; -import { isDateAndTimeView } from '../../../../renovation/ui/scheduler/view_model/to_test/views/utils/base'; +import dateUtils from '@js/core/utils/date'; +import { isDateAndTimeView } from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/base'; export class GroupedDataMapProvider { - constructor(viewDataGenerator, viewDataMap, completeViewDataMap, viewOptions) { - this.groupedDataMap = viewDataGenerator.generateGroupedDataMap(viewDataMap); - this.completeViewDataMap = completeViewDataMap; - this._viewOptions = viewOptions; - } + groupedDataMap: any; - getGroupStartDate(groupIndex) { - const firstRow = this.getFirstGroupRow(groupIndex); + completeViewDataMap: any; - if(firstRow) { - const { startDate } = firstRow[0].cellData; + _viewOptions: any; - return startDate; - } - } + constructor(viewDataGenerator, viewDataMap, completeViewDataMap, viewOptions) { + this.groupedDataMap = viewDataGenerator.generateGroupedDataMap(viewDataMap); + this.completeViewDataMap = completeViewDataMap; + this._viewOptions = viewOptions; + } - getGroupEndDate(groupIndex) { - const lastRow = this.getLastGroupRow(groupIndex); + getGroupStartDate(groupIndex) { + const firstRow = this.getFirstGroupRow(groupIndex); - if(lastRow) { - const lastColumnIndex = lastRow.length - 1; - const { cellData } = lastRow[lastColumnIndex]; - const { endDate } = cellData; + if (firstRow) { + const { startDate } = firstRow[0].cellData; - return endDate; - } + return startDate; } + } - findGroupCellStartDate(groupIndex, startDate, endDate, isFindByDate) { - const groupData = this.getGroupFromDateTableGroupMap(groupIndex); - const checkCellStartDate = (rowIndex, columnIndex) => { - const { cellData } = groupData[rowIndex][columnIndex]; - let { - startDate: secondMin, - endDate: secondMax - } = cellData; - - if(isFindByDate) { - secondMin = dateUtils.trimTime(secondMin); - secondMax = dateUtils.setToDayEnd(secondMin); - } - - if(dateUtils.intervalsOverlap({ - firstMin: startDate, - firstMax: endDate, - secondMin, - secondMax - })) { - return secondMin; - } - }; - const searchVertical = () => { - const cellCount = groupData[0].length; - for(let columnIndex = 0; columnIndex < cellCount; ++columnIndex) { - for(let rowIndex = 0; rowIndex < groupData.length; ++rowIndex) { - const result = checkCellStartDate(rowIndex, columnIndex); - if(result) return result; - } - } - }; - const searchHorizontal = () => { - for(let rowIndex = 0; rowIndex < groupData.length; ++rowIndex) { - const row = groupData[rowIndex]; - for(let columnIndex = 0; columnIndex < row.length; ++columnIndex) { - const result = checkCellStartDate(rowIndex, columnIndex); - if(result) return result; - } - } - }; - - const startDateVerticalSearch = searchVertical(); - const startDateHorizontalSearch = searchHorizontal(); - - return startDateVerticalSearch > startDateHorizontalSearch - ? startDateHorizontalSearch - : startDateVerticalSearch; - } + getGroupEndDate(groupIndex) { + const lastRow = this.getLastGroupRow(groupIndex); - findAllDayGroupCellStartDate(groupIndex, startDate) { - const groupStartDate = this.getGroupStartDate(groupIndex); + if (lastRow) { + const lastColumnIndex = lastRow.length - 1; + const { cellData } = lastRow[lastColumnIndex]; + const { endDate } = cellData; - return groupStartDate > startDate - ? groupStartDate - : startDate; + return endDate; } - - findCellPositionInMap(cellInfo) { - const { groupIndex, startDate, isAllDay, index } = cellInfo; - - const startTime = isAllDay - ? dateUtils.trimTime(startDate).getTime() - : startDate.getTime(); - - const isStartDateInCell = cellData => { - if(!isDateAndTimeView(this._viewOptions.viewType)) { - return dateUtils.sameDate(startDate, cellData.startDate); - } - - const cellStartTime = cellData.startDate.getTime(); - const cellEndTime = cellData.endDate.getTime(); - - return isAllDay - ? cellData.allDay && startTime >= cellStartTime && startTime <= cellEndTime - : startTime >= cellStartTime && startTime < cellEndTime; - }; - - const { - allDayPanelGroupedMap, - dateTableGroupedMap - } = this.groupedDataMap; - - const rows = isAllDay && !this._viewOptions.isVerticalGrouping - ? (allDayPanelGroupedMap[groupIndex] ? [allDayPanelGroupedMap[groupIndex]] : []) - : dateTableGroupedMap[groupIndex] || []; - - for(let rowIndex = 0; rowIndex < rows.length; ++rowIndex) { - const row = rows[rowIndex]; - - for(let columnIndex = 0; columnIndex < row.length; ++columnIndex) { - const cell = row[columnIndex]; - const { cellData } = cell; - - if(this._isSameGroupIndexAndIndex(cellData, groupIndex, index)) { - if(isStartDateInCell(cellData)) { - return cell.position; - } - } - } + } + + findGroupCellStartDate(groupIndex, startDate, endDate, isFindByDate) { + const groupData = this.getGroupFromDateTableGroupMap(groupIndex); + const checkCellStartDate = (rowIndex, columnIndex) => { + const { cellData } = groupData[rowIndex][columnIndex]; + let { + startDate: secondMin, + endDate: secondMax, + } = cellData; + + if (isFindByDate) { + secondMin = dateUtils.trimTime(secondMin); + secondMax = dateUtils.setToDayEnd(secondMin); + } + + if (dateUtils.intervalsOverlap({ + firstMin: startDate, + firstMax: endDate, + secondMin, + secondMax, + })) { + return secondMin; + } + }; + const searchVertical = () => { + const cellCount = groupData[0].length; + for (let columnIndex = 0; columnIndex < cellCount; ++columnIndex) { + for (let rowIndex = 0; rowIndex < groupData.length; ++rowIndex) { + const result = checkCellStartDate(rowIndex, columnIndex); + if (result) return result; } - - return undefined; - } - - _isSameGroupIndexAndIndex(cellData, groupIndex, index) { - return cellData.groupIndex === groupIndex - && (index === undefined || cellData.index === index); - } - - getCellsGroup(groupIndex) { - const { dateTableGroupedMap } = this.groupedDataMap; - const groupData = dateTableGroupedMap[groupIndex]; - - if(groupData) { - const { cellData } = groupData[0][0]; - - return cellData.groups; + } + }; + const searchHorizontal = () => { + for (let rowIndex = 0; rowIndex < groupData.length; ++rowIndex) { + const row = groupData[rowIndex]; + for (let columnIndex = 0; columnIndex < row.length; ++columnIndex) { + const result = checkCellStartDate(rowIndex, columnIndex); + if (result) return result; } + } + }; + + const startDateVerticalSearch = searchVertical(); + const startDateHorizontalSearch = searchHorizontal(); + + return startDateVerticalSearch > startDateHorizontalSearch + ? startDateHorizontalSearch + : startDateVerticalSearch; + } + + findAllDayGroupCellStartDate(groupIndex, startDate) { + const groupStartDate = this.getGroupStartDate(groupIndex); + + return groupStartDate > startDate + ? groupStartDate + : startDate; + } + + findCellPositionInMap(cellInfo) { + const { + groupIndex, startDate, isAllDay, index, + } = cellInfo; + + const startTime = isAllDay + ? dateUtils.trimTime(startDate).getTime() + : startDate.getTime(); + + const isStartDateInCell = (cellData) => { + if (!isDateAndTimeView(this._viewOptions.viewType)) { + return dateUtils.sameDate(startDate, cellData.startDate); + } + + const cellStartTime = cellData.startDate.getTime(); + const cellEndTime = cellData.endDate.getTime(); + + return isAllDay + ? cellData.allDay && startTime >= cellStartTime && startTime <= cellEndTime + : startTime >= cellStartTime && startTime < cellEndTime; + }; + + const { + allDayPanelGroupedMap, + dateTableGroupedMap, + } = this.groupedDataMap; + + const rows = isAllDay && !this._viewOptions.isVerticalGrouping + ? allDayPanelGroupedMap[groupIndex] ? [allDayPanelGroupedMap[groupIndex]] : [] + : dateTableGroupedMap[groupIndex] || []; + + for (let rowIndex = 0; rowIndex < rows.length; ++rowIndex) { + const row = rows[rowIndex]; + + for (let columnIndex = 0; columnIndex < row.length; ++columnIndex) { + const cell = row[columnIndex]; + const { cellData } = cell; + + if (this._isSameGroupIndexAndIndex(cellData, groupIndex, index)) { + if (isStartDateInCell(cellData)) { + return cell.position; + } + } + } } - getCompletedGroupsInfo() { - const { dateTableGroupedMap } = this.groupedDataMap; - return dateTableGroupedMap.map(groupData => { - const firstCell = groupData[0][0]; - const { - allDay, - groupIndex - } = firstCell.cellData; - - return { - allDay, - groupIndex, - startDate: this.getGroupStartDate(groupIndex), - endDate: this.getGroupEndDate(groupIndex) - }; - }).filter(({ startDate }) => !!startDate); - } - - getGroupIndices() { - return this.getCompletedGroupsInfo() - .map(({ groupIndex }) => groupIndex); - } - - getGroupFromDateTableGroupMap(groupIndex) { - const { dateTableGroupedMap } = this.groupedDataMap; + return undefined; + } - return dateTableGroupedMap[groupIndex]; - } + _isSameGroupIndexAndIndex(cellData, groupIndex, index) { + return cellData.groupIndex === groupIndex + && (index === undefined || cellData.index === index); + } - getFirstGroupRow(groupIndex) { - const groupedData = this.getGroupFromDateTableGroupMap(groupIndex); + getCellsGroup(groupIndex) { + const { dateTableGroupedMap } = this.groupedDataMap; + const groupData = dateTableGroupedMap[groupIndex]; - if(groupedData) { - const { cellData } = groupedData[0][0]; + if (groupData) { + const { cellData } = groupData[0][0]; - return !cellData.allDay - ? groupedData[0] - : groupedData[1]; - } + return cellData.groups; } + } + + getCompletedGroupsInfo() { + const { dateTableGroupedMap } = this.groupedDataMap; + return dateTableGroupedMap.map((groupData) => { + const firstCell = groupData[0][0]; + const { + allDay, + groupIndex, + } = firstCell.cellData; + + return { + allDay, + groupIndex, + startDate: this.getGroupStartDate(groupIndex), + endDate: this.getGroupEndDate(groupIndex), + }; + }).filter(({ startDate }) => !!startDate); + } + + getGroupIndices() { + return this.getCompletedGroupsInfo() + .map(({ groupIndex }) => groupIndex); + } + + getGroupFromDateTableGroupMap(groupIndex) { + const { dateTableGroupedMap } = this.groupedDataMap; + + return dateTableGroupedMap[groupIndex]; + } + + getFirstGroupRow(groupIndex) { + const groupedData = this.getGroupFromDateTableGroupMap(groupIndex); + + if (groupedData) { + const { cellData } = groupedData[0][0]; + + return !cellData.allDay + ? groupedData[0] + : groupedData[1]; + } + } - getLastGroupRow(groupIndex) { - const { dateTableGroupedMap } = this.groupedDataMap; - const groupedData = dateTableGroupedMap[groupIndex]; + getLastGroupRow(groupIndex) { + const { dateTableGroupedMap } = this.groupedDataMap; + const groupedData = dateTableGroupedMap[groupIndex]; - if(groupedData) { - const lastRowIndex = groupedData.length - 1; + if (groupedData) { + const lastRowIndex = groupedData.length - 1; - return groupedData[lastRowIndex]; - } + return groupedData[lastRowIndex]; } + } - getLastGroupCellPosition(groupIndex) { - const groupRow = this.getLastGroupRow(groupIndex); + getLastGroupCellPosition(groupIndex) { + const groupRow = this.getLastGroupRow(groupIndex); - return groupRow?.[groupRow?.length - 1].position; - } + // eslint-disable-next-line no-unsafe-optional-chaining + return groupRow?.[groupRow?.length - 1].position; + } - getRowCountInGroup(groupIndex) { - const groupRow = this.getLastGroupRow(groupIndex); - const cellAmount = groupRow.length; - const lastCellData = groupRow[cellAmount - 1].cellData; - const lastCellIndex = lastCellData.index; + getRowCountInGroup(groupIndex) { + const groupRow = this.getLastGroupRow(groupIndex); + const cellAmount = groupRow.length; + const lastCellData = groupRow[cellAmount - 1].cellData; + const lastCellIndex = lastCellData.index; - return (lastCellIndex + 1) / groupRow.length; - } + return (lastCellIndex + 1) / groupRow.length; + } } diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_time_panel_data_generator.ts b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_time_panel_data_generator.ts index 01d983767eff..9bc0e51e3cdf 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_time_panel_data_generator.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_time_panel_data_generator.ts @@ -1,122 +1,129 @@ -import { getIsGroupedAllDayPanel, getKeyByGroup } from '../../../../renovation/ui/scheduler/workspaces/utils'; -import { getDisplayedRowCount } from '../../../../renovation/ui/scheduler/view_model/to_test/views/utils/base'; -import { getTimePanelCellText } from '../../../../renovation/ui/scheduler/view_model/to_test/views/utils/week'; +import { getDisplayedRowCount } from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/base'; +import { getTimePanelCellText } from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/week'; +import { getIsGroupedAllDayPanel, getKeyByGroup } from '@js/renovation/ui/scheduler/workspaces/utils'; export class TimePanelDataGenerator { - constructor(viewDataGenerator) { - this._viewDataGenerator = viewDataGenerator; - } - - getCompleteTimePanelMap(options, completeViewDataMap) { - const { - startViewDate, - cellDuration, - startDayHour, - isVerticalGrouping, - intervalCount, - currentDate, - viewType, - hoursInterval, - endDayHour, - } = options; - - const rowCountInGroup = this._viewDataGenerator.getRowCount({ - intervalCount, currentDate, viewType, - hoursInterval, startDayHour, endDayHour, + constructor(public _viewDataGenerator) { + } + + getCompleteTimePanelMap(options, completeViewDataMap) { + const { + startViewDate, + cellDuration, + startDayHour, + isVerticalGrouping, + intervalCount, + currentDate, + viewType, + hoursInterval, + endDayHour, + } = options; + + const rowCountInGroup = this._viewDataGenerator.getRowCount({ + intervalCount, + currentDate, + viewType, + hoursInterval, + startDayHour, + endDayHour, + }); + const cellCountInGroupRow = this._viewDataGenerator.getCellCount({ + intervalCount, + currentDate, + viewType, + hoursInterval, + startDayHour, + endDayHour, + }); + let allDayRowsCount = 0; + + return completeViewDataMap.map((row, index) => { + const { + allDay, + startDate, + endDate, + groups, + groupIndex, + isFirstGroupCell, + isLastGroupCell, + index: cellIndex, + ...restCellProps + } = row[0]; + + if (allDay) { + allDayRowsCount += 1; + } + + const timeIndex = (index - allDayRowsCount) % rowCountInGroup; + + return { + ...restCellProps, + startDate, + allDay, + text: getTimePanelCellText(timeIndex, startDate, startViewDate, cellDuration, startDayHour), + groups: isVerticalGrouping ? groups : undefined, + groupIndex: isVerticalGrouping ? groupIndex : undefined, + isFirstGroupCell: isVerticalGrouping && isFirstGroupCell, + isLastGroupCell: isVerticalGrouping && isLastGroupCell, + index: Math.floor(cellIndex / cellCountInGroupRow), + }; + }); + } + + generateTimePanelData(completeTimePanelMap, options) { + const { + startRowIndex, + rowCount, + topVirtualRowHeight, + bottomVirtualRowHeight, + isGroupedAllDayPanel, + isVerticalGrouping, + isAllDayPanelVisible, + } = options; + + const indexDifference = isVerticalGrouping || !isAllDayPanelVisible ? 0 : 1; + const correctedStartRowIndex = startRowIndex + indexDifference; + + const displayedRowCount = getDisplayedRowCount(rowCount, completeTimePanelMap); + const timePanelMap = completeTimePanelMap + .slice(correctedStartRowIndex, correctedStartRowIndex + displayedRowCount); + + const timePanelData: any = { + topVirtualRowHeight, + bottomVirtualRowHeight, + isGroupedAllDayPanel, + }; + + const { + previousGroupedData: groupedData, + } = this._generateTimePanelDataFromMap(timePanelMap, isVerticalGrouping); + + timePanelData.groupedData = groupedData; + + return timePanelData; + } + + _generateTimePanelDataFromMap(timePanelMap, isVerticalGrouping) { + return timePanelMap.reduce(({ previousGroupIndex, previousGroupedData }, cellData) => { + const currentGroupIndex = cellData.groupIndex; + if (currentGroupIndex !== previousGroupIndex) { + previousGroupedData.push({ + dateTable: [], + isGroupedAllDayPanel: getIsGroupedAllDayPanel(!!cellData.allDay, isVerticalGrouping), + groupIndex: currentGroupIndex, + key: getKeyByGroup(currentGroupIndex, isVerticalGrouping), }); - const cellCountInGroupRow = this._viewDataGenerator.getCellCount({ - intervalCount, currentDate, viewType, - hoursInterval, startDayHour, endDayHour, - }); - let allDayRowsCount = 0; - - return completeViewDataMap.map((row, index) => { - const { - allDay, - startDate, - endDate, - groups, - groupIndex, - isFirstGroupCell, - isLastGroupCell, - index: cellIndex, - ...restCellProps - } = row[0]; - - if(allDay) { - allDayRowsCount += 1; - } - - const timeIndex = (index - allDayRowsCount) % rowCountInGroup; - - return { - ...restCellProps, - startDate, - allDay, - text: getTimePanelCellText(timeIndex, startDate, startViewDate, cellDuration, startDayHour), - groups: isVerticalGrouping ? groups : undefined, - groupIndex: isVerticalGrouping ? groupIndex : undefined, - isFirstGroupCell: isVerticalGrouping && isFirstGroupCell, - isLastGroupCell: isVerticalGrouping && isLastGroupCell, - index: Math.floor(cellIndex / cellCountInGroupRow), - }; - }); - } - - generateTimePanelData(completeTimePanelMap, options) { - const { - startRowIndex, - rowCount, - topVirtualRowHeight, - bottomVirtualRowHeight, - isGroupedAllDayPanel, - isVerticalGrouping, - isAllDayPanelVisible, - } = options; - - const indexDifference = isVerticalGrouping || !isAllDayPanelVisible ? 0 : 1; - const correctedStartRowIndex = startRowIndex + indexDifference; - - const displayedRowCount = getDisplayedRowCount(rowCount, completeTimePanelMap); - const timePanelMap = completeTimePanelMap - .slice(correctedStartRowIndex, correctedStartRowIndex + displayedRowCount); - - const timePanelData = { - topVirtualRowHeight, - bottomVirtualRowHeight, - isGroupedAllDayPanel, - }; - - const { - previousGroupedData: groupedData, - } = this._generateTimePanelDataFromMap(timePanelMap, isVerticalGrouping); - - timePanelData.groupedData = groupedData; - - return timePanelData; - } - - _generateTimePanelDataFromMap(timePanelMap, isVerticalGrouping) { - return timePanelMap.reduce(({ previousGroupIndex, previousGroupedData }, cellData) => { - const currentGroupIndex = cellData.groupIndex; - if(currentGroupIndex !== previousGroupIndex) { - previousGroupedData.push({ - dateTable: [], - isGroupedAllDayPanel: getIsGroupedAllDayPanel(!!cellData.allDay, isVerticalGrouping), - groupIndex: currentGroupIndex, - key: getKeyByGroup(currentGroupIndex, isVerticalGrouping), - }); - } - if(cellData.allDay) { - previousGroupedData[previousGroupedData.length - 1].allDayPanel = cellData; - } else { - previousGroupedData[previousGroupedData.length - 1].dateTable.push(cellData); - } - - return { - previousGroupIndex: currentGroupIndex, - previousGroupedData, - }; - }, { previousGroupIndex: -1, previousGroupedData: [] }); - } + } + if (cellData.allDay) { + previousGroupedData[previousGroupedData.length - 1].allDayPanel = cellData; + } else { + previousGroupedData[previousGroupedData.length - 1].dateTable.push(cellData); + } + + return { + previousGroupIndex: currentGroupIndex, + previousGroupedData, + }; + }, { previousGroupIndex: -1, previousGroupedData: [] }); + } } diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_utils.ts b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_utils.ts index 2ca0fb20110b..136f07e79769 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_utils.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_utils.ts @@ -1,63 +1,65 @@ -import dateUtils from '../../../../core/utils/date'; -import { VIEWS } from '../../constants'; -import { ViewDataGenerator } from './view_data_generator'; -import { ViewDataGeneratorDay } from './view_data_generator_day'; -import { ViewDataGeneratorMonth } from './view_data_generator_month'; -import { ViewDataGeneratorTimelineMonth } from './view_data_generator_timeline_month'; -import { ViewDataGeneratorWeek } from './view_data_generator_week'; -import { ViewDataGeneratorWorkWeek } from './view_data_generator_work_week'; +import dateUtils from '@js/core/utils/date'; + +import { VIEWS } from '../../m_constants'; +import { ViewDataGenerator } from './m_view_data_generator'; +import { ViewDataGeneratorDay } from './m_view_data_generator_day'; +// eslint-disable-next-line import/no-cycle +import { ViewDataGeneratorMonth } from './m_view_data_generator_month'; +import { ViewDataGeneratorTimelineMonth } from './m_view_data_generator_timeline_month'; +import { ViewDataGeneratorWeek } from './m_view_data_generator_week'; +import { ViewDataGeneratorWorkWeek } from './m_view_data_generator_work_week'; const DAYS_IN_WEEK = 7; const MS_IN_DAY = 24 * 60 * 60 * 1000; export const getViewDataGeneratorByViewType = (viewType) => { - switch(viewType) { - case VIEWS.MONTH: - return new ViewDataGeneratorMonth(); - case VIEWS.TIMELINE_MONTH: - return new ViewDataGeneratorTimelineMonth(); - case VIEWS.DAY: - case VIEWS.TIMELINE_DAY: - return new ViewDataGeneratorDay(); - case VIEWS.WEEK: - case VIEWS.TIMELINE_WEEK: - return new ViewDataGeneratorWeek(); - case VIEWS.WORK_WEEK: - case VIEWS.TIMELINE_WORK_WEEK: - return new ViewDataGeneratorWorkWeek(); - default: - return new ViewDataGenerator(); - } + switch (viewType) { + case VIEWS.MONTH: + return new ViewDataGeneratorMonth(); + case VIEWS.TIMELINE_MONTH: + return new ViewDataGeneratorTimelineMonth(); + case VIEWS.DAY: + case VIEWS.TIMELINE_DAY: + return new ViewDataGeneratorDay(); + case VIEWS.WEEK: + case VIEWS.TIMELINE_WEEK: + return new ViewDataGeneratorWeek(); + case VIEWS.WORK_WEEK: + case VIEWS.TIMELINE_WORK_WEEK: + return new ViewDataGeneratorWorkWeek(); + default: + return new ViewDataGenerator(); + } }; export function alignToFirstDayOfWeek(date, firstDayOfWeek) { - const newDate = new Date(date); - let dayDiff = newDate.getDay() - firstDayOfWeek; + const newDate = new Date(date); + let dayDiff = newDate.getDay() - firstDayOfWeek; - if(dayDiff < 0) { - dayDiff += DAYS_IN_WEEK; - } + if (dayDiff < 0) { + dayDiff += DAYS_IN_WEEK; + } - newDate.setDate(newDate.getDate() - dayDiff); + newDate.setDate(newDate.getDate() - dayDiff); - return newDate; + return newDate; } export function alignToLastDayOfWeek(date, firstDayOfWeek) { - const newDate = alignToFirstDayOfWeek(date, firstDayOfWeek); - newDate.setDate(newDate.getDate() + DAYS_IN_WEEK - 1); - return newDate; + const newDate = alignToFirstDayOfWeek(date, firstDayOfWeek); + newDate.setDate(newDate.getDate() + DAYS_IN_WEEK - 1); + return newDate; } export function calculateDaysBetweenDates(fromDate, toDate) { - const msDiff = dateUtils.trimTime(toDate).getTime() - dateUtils.trimTime(fromDate).getTime(); - return Math.round(msDiff / MS_IN_DAY) + 1; + const msDiff = dateUtils.trimTime(toDate).getTime() - dateUtils.trimTime(fromDate).getTime(); + return Math.round(msDiff / MS_IN_DAY) + 1; } export function calculateAlignedWeeksBetweenDates(fromDate, toDate, firstDayOfWeek) { - const alignedFromDate = alignToFirstDayOfWeek(fromDate, firstDayOfWeek); - const alignedToDate = alignToLastDayOfWeek(toDate, firstDayOfWeek); + const alignedFromDate = alignToFirstDayOfWeek(fromDate, firstDayOfWeek); + const alignedToDate = alignToLastDayOfWeek(toDate, firstDayOfWeek); - const weekCount = calculateDaysBetweenDates(alignedFromDate, alignedToDate) / DAYS_IN_WEEK; - return Math.max(weekCount, 6); + const weekCount = calculateDaysBetweenDates(alignedFromDate, alignedToDate) / DAYS_IN_WEEK; + return Math.max(weekCount, 6); } diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator.ts b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator.ts index ba3184b58e9e..568e85bbf721 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator.ts @@ -1,711 +1,706 @@ -import dateUtils from '../../../../core/utils/date'; -import { HORIZONTAL_GROUP_ORIENTATION } from '../../constants'; -import { getAllGroups, getGroupCount } from '../../../../__internal/scheduler/resources/m_utils'; +import dateUtils from '@js/core/utils/date'; import { - calculateCellIndex, - calculateDayDuration, - isHorizontalView, - getStartViewDateWithoutDST, - getDisplayedRowCount, - getTotalCellCountByCompleteData, - getTotalRowCountByCompleteData, - getDisplayedCellCount, -} from '../../../../renovation/ui/scheduler/view_model/to_test/views/utils/base'; -import { getIsGroupedAllDayPanel, getKeyByGroup } from '../../../../renovation/ui/scheduler/workspaces/utils'; + calculateCellIndex, + calculateDayDuration, + getDisplayedCellCount, + getDisplayedRowCount, + getStartViewDateWithoutDST, + getTotalCellCountByCompleteData, + getTotalRowCountByCompleteData, + isHorizontalView, +} from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/base'; +import { getIsGroupedAllDayPanel, getKeyByGroup } from '@js/renovation/ui/scheduler/workspaces/utils'; + +import { HORIZONTAL_GROUP_ORIENTATION } from '../../m_constants'; +import { getAllGroups, getGroupCount } from '../../resources/m_utils'; const HOUR_MS = dateUtils.dateToMilliseconds('hour'); const DAY_MS = dateUtils.dateToMilliseconds('day'); export class ViewDataGenerator { - get daysInInterval() { return 1; } + readonly daysInInterval: number = 1; - get isWorkView() { return false; } + readonly isWorkView: boolean = false; - get tableAllDay() { return false; } + tableAllDay = false; - isSkippedDate() { - return false; - } - - getStartViewDate(options) { - return this._calculateStartViewDate(options); - } - - getCompleteViewDataMap(options) { - const { - groups, - isGroupedByDate, - isHorizontalGrouping, - isVerticalGrouping, - intervalCount, - currentDate, - viewType, - startDayHour, - endDayHour, - hoursInterval, - } = options; - - this._setVisibilityDates(options); - this.setHiddenInterval(startDayHour, endDayHour, hoursInterval); - - const groupsList = getAllGroups(groups); - const cellCountInGroupRow = this.getCellCount({ - intervalCount, - currentDate, - viewType, - startDayHour, - endDayHour, - hoursInterval, - }); - const rowCountInGroup = this.getRowCount({ - intervalCount, - currentDate, - viewType, - hoursInterval, - startDayHour, - endDayHour, - }); - - let viewDataMap = []; - const allDayPanelData = this._generateAllDayPanelData(options, rowCountInGroup, cellCountInGroupRow); - const viewCellsData = this._generateViewCellsData(options, rowCountInGroup, cellCountInGroupRow); - - allDayPanelData && viewDataMap.push(allDayPanelData); - viewDataMap.push(...viewCellsData); - - if(isHorizontalGrouping && !isGroupedByDate) { - viewDataMap = this._transformViewDataMapForHorizontalGrouping(viewDataMap, groupsList); - } - - if(isVerticalGrouping) { - viewDataMap = this._transformViewDataMapForVerticalGrouping(viewDataMap, groupsList); - } + hiddenInterval: any; - if(isGroupedByDate) { - viewDataMap = this._transformViewDataMapForGroupingByDate(viewDataMap, groupsList); - } - - const completeViewDataMap = this._addKeysToCells(viewDataMap); - - return completeViewDataMap; - } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + isSkippedDate(date: any) { + return false; + } - _transformViewDataMapForHorizontalGrouping(viewDataMap, groupsList) { - const result = viewDataMap.map(row => row.slice()); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _calculateStartViewDate(options: any) {} - groupsList.slice(1).forEach((groups, index) => { - const groupIndex = index + 1; + getStartViewDate(options): any { + return this._calculateStartViewDate(options); + } - viewDataMap.forEach((row, rowIndex) => { - const nextGroupRow = row.map((cellData) => { - return ({ - ...cellData, - groups, - groupIndex, - }); - }); - - result[rowIndex].push(...nextGroupRow); - }); + getCompleteViewDataMap(options) { + const { + groups, + isGroupedByDate, + isHorizontalGrouping, + isVerticalGrouping, + intervalCount, + currentDate, + viewType, + startDayHour, + endDayHour, + hoursInterval, + } = options; + + this._setVisibilityDates(options); + this.setHiddenInterval(startDayHour, endDayHour, hoursInterval); + + const groupsList = getAllGroups(groups); + const cellCountInGroupRow = this.getCellCount({ + intervalCount, + currentDate, + viewType, + startDayHour, + endDayHour, + hoursInterval, + }); + const rowCountInGroup = this.getRowCount({ + intervalCount, + currentDate, + viewType, + hoursInterval, + startDayHour, + endDayHour, + }); + + let viewDataMap: any[] = []; + const allDayPanelData = this._generateAllDayPanelData(options, rowCountInGroup, cellCountInGroupRow); + const viewCellsData = this._generateViewCellsData(options, rowCountInGroup, cellCountInGroupRow); + + allDayPanelData && viewDataMap.push(allDayPanelData); + viewDataMap.push(...viewCellsData); + + if (isHorizontalGrouping && !isGroupedByDate) { + viewDataMap = this._transformViewDataMapForHorizontalGrouping(viewDataMap, groupsList); + } + + if (isVerticalGrouping) { + viewDataMap = this._transformViewDataMapForVerticalGrouping(viewDataMap, groupsList); + } + + if (isGroupedByDate) { + viewDataMap = this._transformViewDataMapForGroupingByDate(viewDataMap, groupsList); + } + + const completeViewDataMap = this._addKeysToCells(viewDataMap); + + return completeViewDataMap; + } + + _transformViewDataMapForHorizontalGrouping(viewDataMap, groupsList) { + const result = viewDataMap.map((row) => row.slice()); + + groupsList.slice(1).forEach((groups, index) => { + const groupIndex = index + 1; + + viewDataMap.forEach((row, rowIndex) => { + const nextGroupRow = row.map((cellData) => ({ + ...cellData, + groups, + groupIndex, + })); + + result[rowIndex].push(...nextGroupRow); + }); + }); + + return result; + } + + _transformViewDataMapForVerticalGrouping(viewDataMap, groupsList) { + const result = viewDataMap.map((row) => row.slice()); + + groupsList.slice(1).forEach((groups, index) => { + const groupIndex = index + 1; + + const nextGroupMap = viewDataMap.map((cellsRow) => { + const nextRow = cellsRow.map((cellData) => ({ + ...cellData, + groupIndex, + groups, + })); + + return nextRow; + }); + + result.push(...nextGroupMap); + }); + + return result; + } + + _transformViewDataMapForGroupingByDate(viewDataMap, groupsList) { + const correctedGroupList = groupsList.slice(1); + const correctedGroupCount = correctedGroupList.length; + + const result = viewDataMap.map((cellsRow) => { + const groupedByDateCellsRow = cellsRow.reduce((currentRow, cell) => { + const rowWithCurrentCell = [ + ...currentRow, + { + ...cell, + isFirstGroupCell: true, + isLastGroupCell: correctedGroupCount === 0, + }, + ...correctedGroupList.map((groups, index) => ({ + ...cell, + groups, + groupIndex: index + 1, + isFirstGroupCell: false, + isLastGroupCell: index === correctedGroupCount - 1, + })), + ]; + + return rowWithCurrentCell; + }, []); + + return groupedByDateCellsRow; + }); + + return result; + } + + _addKeysToCells(viewDataMap) { + const totalColumnCount = viewDataMap[0].length; + const { + currentViewDataMap: result, + } = viewDataMap.reduce(({ allDayPanelsCount, currentViewDataMap }, row, rowIndex) => { + const isAllDay = row[0].allDay; + + const keyBase = (rowIndex - allDayPanelsCount) * totalColumnCount; + + const currentAllDayPanelsCount = isAllDay + ? allDayPanelsCount + 1 + : allDayPanelsCount; + + currentViewDataMap[rowIndex].forEach((cell, columnIndex) => { + cell.key = keyBase + columnIndex; + }); + + return { allDayPanelsCount: currentAllDayPanelsCount, currentViewDataMap }; + }, { + allDayPanelsCount: 0, + currentViewDataMap: viewDataMap, + }); + + return result; + } + + generateViewDataMap(completeViewDataMap, options) { + const { + rowCount, + startCellIndex, + startRowIndex, + cellCount, + isVerticalGrouping, + isAllDayPanelVisible, + } = options; + + const sliceCells = (row, rowIndex, startIndex, count) => { + const sliceToIndex = count !== undefined + ? startIndex + count + : undefined; + + return row + .slice(startIndex, sliceToIndex) + .map((cellData, columnIndex) => ( + { + cellData, + position: { + rowIndex, + columnIndex, + }, + })); + }; + + let correctedStartRowIndex = startRowIndex; + let allDayPanelMap = []; + if (this._isStandaloneAllDayPanel(isVerticalGrouping, isAllDayPanelVisible)) { + correctedStartRowIndex++; + allDayPanelMap = sliceCells(completeViewDataMap[0], 0, startCellIndex, cellCount); + } + + const displayedRowCount = getDisplayedRowCount(rowCount, completeViewDataMap); + + const dateTableMap = completeViewDataMap + .slice(correctedStartRowIndex, correctedStartRowIndex + displayedRowCount) + .map((row, rowIndex) => sliceCells(row, rowIndex, startCellIndex, cellCount)); + + return { + allDayPanelMap, + dateTableMap, + }; + } + + _isStandaloneAllDayPanel(isVerticalGrouping, isAllDayPanelVisible) { + return !isVerticalGrouping && isAllDayPanelVisible; + } + + getViewDataFromMap(completeViewDataMap, viewDataMap, options) { + const { + topVirtualRowHeight, + bottomVirtualRowHeight, + leftVirtualCellWidth, + rightVirtualCellWidth, + cellCount, + rowCount, + startRowIndex, + startCellIndex, + isProvideVirtualCellsWidth, + isGroupedAllDayPanel, + isVerticalGrouping, + isAllDayPanelVisible, + } = options; + const { + allDayPanelMap, + dateTableMap, + } = viewDataMap; + + const { + groupedData, + } = dateTableMap.reduce(({ previousGroupIndex, groupedData }, cellsRow) => { + const cellDataRow = cellsRow.map(({ cellData }) => cellData); + + const firstCell = cellDataRow[0]; + const isAllDayRow = firstCell.allDay; + const currentGroupIndex = firstCell.groupIndex; + + if (currentGroupIndex !== previousGroupIndex) { + groupedData.push({ + dateTable: [], + isGroupedAllDayPanel: getIsGroupedAllDayPanel(!!isAllDayRow, isVerticalGrouping), + groupIndex: currentGroupIndex, + key: getKeyByGroup(currentGroupIndex, isVerticalGrouping), }); - - return result; - } - - _transformViewDataMapForVerticalGrouping(viewDataMap, groupsList) { - const result = viewDataMap.map(row => row.slice()); - - groupsList.slice(1).forEach((groups, index) => { - const groupIndex = index + 1; - - const nextGroupMap = viewDataMap.map((cellsRow) => { - const nextRow = cellsRow.map((cellData) => { - return ({ - ...cellData, - groupIndex, - groups, - }); - }); - - return nextRow; - }); - - result.push(...nextGroupMap); + } + + if (isAllDayRow) { + groupedData[groupedData.length - 1].allDayPanel = cellDataRow; + } else { + groupedData[groupedData.length - 1].dateTable.push({ + cells: cellDataRow, + key: cellDataRow[0].key - startCellIndex, }); + } - return result; - } - - _transformViewDataMapForGroupingByDate(viewDataMap, groupsList) { - const correctedGroupList = groupsList.slice(1); - const correctedGroupCount = correctedGroupList.length; - - const result = viewDataMap.map((cellsRow) => { - const groupedByDateCellsRow = cellsRow.reduce((currentRow, cell) => { - const rowWithCurrentCell = [ - ...currentRow, - { - ...cell, - isFirstGroupCell: true, - isLastGroupCell: correctedGroupCount === 0, - }, - ...correctedGroupList.map((groups, index) => ({ - ...cell, - groups, - groupIndex: index + 1, - isFirstGroupCell: false, - isLastGroupCell: index === correctedGroupCount - 1, - })), - ]; - - return rowWithCurrentCell; - }, []); - - return groupedByDateCellsRow; - }); + return { + groupedData, + previousGroupIndex: currentGroupIndex, + }; + }, { previousGroupIndex: -1, groupedData: [] }); - return result; + if (this._isStandaloneAllDayPanel(isVerticalGrouping, isAllDayPanelVisible)) { + groupedData[0].allDayPanel = allDayPanelMap.map(({ cellData }) => cellData); } - _addKeysToCells(viewDataMap) { - const totalColumnCount = viewDataMap[0].length; - const { - currentViewDataMap: result, - } = viewDataMap.reduce(({ allDayPanelsCount, currentViewDataMap }, row, rowIndex) => { - const isAllDay = row[0].allDay; - - const keyBase = (rowIndex - allDayPanelsCount) * totalColumnCount; + const totalCellCount = getTotalCellCountByCompleteData(completeViewDataMap); + const totalRowCount = getTotalRowCountByCompleteData(completeViewDataMap); + const displayedCellCount = getDisplayedCellCount(cellCount, completeViewDataMap); + const displayedRowCount = getDisplayedRowCount(rowCount, completeViewDataMap); - const currentAllDayPanelsCount = isAllDay - ? allDayPanelsCount + 1 - : allDayPanelsCount; + return { + groupedData, + topVirtualRowHeight, + bottomVirtualRowHeight, + leftVirtualCellWidth: isProvideVirtualCellsWidth ? leftVirtualCellWidth : undefined, + rightVirtualCellWidth: isProvideVirtualCellsWidth ? rightVirtualCellWidth : undefined, + isGroupedAllDayPanel, + leftVirtualCellCount: startCellIndex, + rightVirtualCellCount: cellCount === undefined ? 0 : totalCellCount - startCellIndex - displayedCellCount, + topVirtualRowCount: startRowIndex, + bottomVirtualRowCount: totalRowCount - startRowIndex - displayedRowCount, + }; + } - currentViewDataMap[rowIndex].forEach((cell, columnIndex) => { - cell.key = keyBase + columnIndex; - }); + _generateViewCellsData(options, rowCount, cellCountInGroupRow) { + const viewCellsData: any[] = []; - return { allDayPanelsCount: currentAllDayPanelsCount, currentViewDataMap }; - }, { - allDayPanelsCount: 0, - currentViewDataMap: viewDataMap, - }); - - return result; + for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) { + viewCellsData.push(this._generateCellsRow(options, false, rowIndex, rowCount, cellCountInGroupRow)); } - generateViewDataMap(completeViewDataMap, options) { - const { - rowCount, - startCellIndex, - startRowIndex, - cellCount, - isVerticalGrouping, - isAllDayPanelVisible, - } = options; - - const sliceCells = (row, rowIndex, startIndex, count) => { - const sliceToIndex = count !== undefined - ? startIndex + count - : undefined; - - return row - .slice(startIndex, sliceToIndex) - .map((cellData, columnIndex) => ( - { - cellData, - position: { - rowIndex, - columnIndex - } - }) - ); - - }; - - let correctedStartRowIndex = startRowIndex; - let allDayPanelMap = []; - if(this._isStandaloneAllDayPanel(isVerticalGrouping, isAllDayPanelVisible)) { - correctedStartRowIndex++; - allDayPanelMap = sliceCells(completeViewDataMap[0], 0, startCellIndex, cellCount); - } + return viewCellsData; + } - const displayedRowCount = getDisplayedRowCount(rowCount, completeViewDataMap); - - const dateTableMap = completeViewDataMap - .slice(correctedStartRowIndex, correctedStartRowIndex + displayedRowCount) - .map((row, rowIndex) => sliceCells(row, rowIndex, startCellIndex, cellCount)); - - return { - allDayPanelMap, - dateTableMap - }; + _generateAllDayPanelData(options, rowCount, columnCount) { + if (!options.isAllDayPanelVisible) { + return null; } - _isStandaloneAllDayPanel(isVerticalGrouping, isAllDayPanelVisible) { - return !isVerticalGrouping && isAllDayPanelVisible; - } + return this._generateCellsRow(options, true, 0, rowCount, columnCount); + } - getViewDataFromMap(completeViewDataMap, viewDataMap, options) { - const { - topVirtualRowHeight, - bottomVirtualRowHeight, - leftVirtualCellWidth, - rightVirtualCellWidth, - cellCount, - rowCount, - startRowIndex, - startCellIndex, - isProvideVirtualCellsWidth, - isGroupedAllDayPanel, - isVerticalGrouping, - isAllDayPanelVisible, - } = options; - const { - allDayPanelMap, - dateTableMap - } = viewDataMap; - - const { - groupedData, - } = dateTableMap.reduce(({ previousGroupIndex, groupedData }, cellsRow) => { - const cellDataRow = cellsRow.map(({ cellData }) => cellData); - - const firstCell = cellDataRow[0]; - const isAllDayRow = firstCell.allDay; - const currentGroupIndex = firstCell.groupIndex; - - if(currentGroupIndex !== previousGroupIndex) { - groupedData.push({ - dateTable: [], - isGroupedAllDayPanel: getIsGroupedAllDayPanel(!!isAllDayRow, isVerticalGrouping), - groupIndex: currentGroupIndex, - key: getKeyByGroup(currentGroupIndex, isVerticalGrouping), - }); - } - - if(isAllDayRow) { - groupedData[groupedData.length - 1].allDayPanel = cellDataRow; - } else { - groupedData[groupedData.length - 1].dateTable.push({ - cells: cellDataRow, - key: cellDataRow[0].key - startCellIndex, - }); - } - - return { - groupedData, - previousGroupIndex: currentGroupIndex, - }; - }, { previousGroupIndex: -1, groupedData: [] }); - - if(this._isStandaloneAllDayPanel(isVerticalGrouping, isAllDayPanelVisible)) { - groupedData[0].allDayPanel = allDayPanelMap.map(({ cellData }) => cellData); - } + _generateCellsRow(options, allDay, rowIndex, rowCount, columnCount) { + const cellsRow: any[] = []; - const totalCellCount = getTotalCellCountByCompleteData(completeViewDataMap); - const totalRowCount = getTotalRowCountByCompleteData(completeViewDataMap); - const displayedCellCount = getDisplayedCellCount(cellCount, completeViewDataMap); - const displayedRowCount = getDisplayedRowCount(rowCount, completeViewDataMap); - - return { - groupedData, - topVirtualRowHeight, - bottomVirtualRowHeight, - leftVirtualCellWidth: isProvideVirtualCellsWidth ? leftVirtualCellWidth : undefined, - rightVirtualCellWidth: isProvideVirtualCellsWidth ? rightVirtualCellWidth : undefined, - isGroupedAllDayPanel, - leftVirtualCellCount: startCellIndex, - rightVirtualCellCount: cellCount === undefined ? 0 : totalCellCount - startCellIndex - displayedCellCount, - topVirtualRowCount: startRowIndex, - bottomVirtualRowCount: totalRowCount - startRowIndex - displayedRowCount, - }; - } + for (let columnIndex = 0; columnIndex < columnCount; ++columnIndex) { + const cellDataValue: any = this.getCellData(rowIndex, columnIndex, options, allDay); - _generateViewCellsData(options, rowCount, cellCountInGroupRow) { - const viewCellsData = []; + cellDataValue.index = rowIndex * columnCount + columnIndex; - for(let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) { - viewCellsData.push(this._generateCellsRow( - options, false, rowIndex, rowCount, cellCountInGroupRow, - )); - } + cellDataValue.isFirstGroupCell = this._isFirstGroupCell(rowIndex, columnIndex, options, rowCount, columnCount); + cellDataValue.isLastGroupCell = this._isLastGroupCell(rowIndex, columnIndex, options, rowCount, columnCount); - return viewCellsData; + cellsRow.push(cellDataValue); } - _generateAllDayPanelData(options, rowCount, columnCount) { - if(!options.isAllDayPanelVisible) { - return null; - } - - return this._generateCellsRow(options, true, 0, rowCount, columnCount); - } + return cellsRow; + } - _generateCellsRow(options, allDay, rowIndex, rowCount, columnCount) { - const cellsRow = []; + getCellData(rowIndex, columnIndex, options, allDay) { + return allDay + ? this.prepareAllDayCellData(options, rowIndex, columnIndex) + : this.prepareCellData(options, rowIndex, columnIndex); + } - for(let columnIndex = 0; columnIndex < columnCount; ++columnIndex) { - const cellDataValue = this.getCellData(rowIndex, columnIndex, options, allDay); + prepareCellData(options, rowIndex, columnIndex) { + const { + groups, + startDayHour, + endDayHour, + interval, + hoursInterval, + } = options; - cellDataValue.index = rowIndex * columnCount + columnIndex; - - cellDataValue.isFirstGroupCell = this._isFirstGroupCell( - rowIndex, columnIndex, options, rowCount, columnCount, - ); - cellDataValue.isLastGroupCell = this._isLastGroupCell( - rowIndex, columnIndex, options, rowCount, columnCount, - ); - - cellsRow.push(cellDataValue); - } + const groupsList = getAllGroups(groups); - return cellsRow; - } + const startDate = this.getDateByCellIndices( + options, + rowIndex, + columnIndex, + this.getCellCountInDay(startDayHour, endDayHour, hoursInterval), + ); + const endDate = this.calculateEndDate(startDate, interval, endDayHour); - getCellData(rowIndex, columnIndex, options, allDay) { - return allDay - ? this.prepareAllDayCellData(options, rowIndex, columnIndex) - : this.prepareCellData(options, rowIndex, columnIndex); - } + const data: any = { + startDate, + endDate, + allDay: this.tableAllDay, + groupIndex: 0, + }; - prepareCellData(options, rowIndex, columnIndex) { - const { - groups, - startDayHour, - endDayHour, - interval, - hoursInterval, - } = options; - - const groupsList = getAllGroups(groups); - - const startDate = this.getDateByCellIndices( - options, - rowIndex, - columnIndex, - this.getCellCountInDay(startDayHour, endDayHour, hoursInterval), - ); - const endDate = this.calculateEndDate(startDate, interval, endDayHour); - - const data = { - startDate: startDate, - endDate: endDate, - allDay: this.tableAllDay, - groupIndex: 0, - }; - - if(groupsList.length > 0) { - data.groups = groupsList[0]; - } + if (groupsList.length > 0) { + // eslint-disable-next-line prefer-destructuring + data.groups = groupsList[0]; + } + + return data; + } + + prepareAllDayCellData(options, rowIndex, columnIndex) { + const data = this.prepareCellData(options, rowIndex, columnIndex); + const startDate = dateUtils.trimTime(data.startDate); + + return { + ...data, + startDate, + endDate: startDate, + allDay: true, + }; + } + + getDateByCellIndices(options, rowIndex, columnIndex, cellCountInDay) { + let { startViewDate } = options; + const { + startDayHour, + interval, + firstDayOfWeek, + intervalCount, + } = options; - return data; - } + const isStartViewDateDuringDST = startViewDate.getHours() !== Math.floor(startDayHour); - prepareAllDayCellData(options, rowIndex, columnIndex) { - const data = this.prepareCellData(options, rowIndex, columnIndex); - const startDate = dateUtils.trimTime(data.startDate); + if (isStartViewDateDuringDST) { + const dateWithCorrectHours = getStartViewDateWithoutDST(startViewDate, startDayHour); - return { - ...data, - startDate, - endDate: startDate, - allDay: true, - }; + // @ts-expect-error + startViewDate = new Date(dateWithCorrectHours - dateUtils.dateToMilliseconds('day')); } - getDateByCellIndices(options, rowIndex, columnIndex, cellCountInDay) { - let startViewDate = options.startViewDate; - const { - startDayHour, - interval, - firstDayOfWeek, - intervalCount, - } = options; - - const isStartViewDateDuringDST = startViewDate.getHours() !== Math.floor(startDayHour); + const columnCountBase = this.getCellCount(options); + const rowCountBase = this.getRowCount(options); + const cellIndex = this._calculateCellIndex(rowIndex, columnIndex, rowCountBase, columnCountBase); + const millisecondsOffset = this.getMillisecondsOffset(cellIndex, interval, cellCountInDay); - if(isStartViewDateDuringDST) { - const dateWithCorrectHours = getStartViewDateWithoutDST(startViewDate, startDayHour); - - startViewDate = new Date(dateWithCorrectHours - dateUtils.dateToMilliseconds('day')); + const offsetByCount = this.isWorkView + ? this.getTimeOffsetByColumnIndex( + columnIndex, + this.getFirstDayOfWeek(firstDayOfWeek), + columnCountBase, + intervalCount, + ) : 0; + + const startViewDateTime = startViewDate.getTime(); + const currentDate = new Date(startViewDateTime + millisecondsOffset + offsetByCount); + + const timeZoneDifference = isStartViewDateDuringDST + ? 0 + : dateUtils.getTimezonesDifference(startViewDate, currentDate); + + currentDate.setTime(currentDate.getTime() + timeZoneDifference); + + return currentDate; + } + + getMillisecondsOffset(cellIndex, interval, cellCountInDay) { + const dayIndex = Math.floor(cellIndex / cellCountInDay); + const realHiddenInterval = dayIndex * this.hiddenInterval; + + return interval * cellIndex + realHiddenInterval; + } + + getTimeOffsetByColumnIndex(columnIndex, firstDayOfWeek, columnCount, intervalCount) { + const firstDayOfWeekDiff = Math.max(0, firstDayOfWeek - 1); + const columnsInWeek = columnCount / intervalCount; + const weekendCount = Math.floor((columnIndex + firstDayOfWeekDiff) / columnsInWeek); + + return DAY_MS * weekendCount * 2; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + calculateEndDate(startDate, interval, endDayHour?: any) { + const result = new Date(startDate); + result.setMilliseconds(result.getMilliseconds() + Math.round(interval)); + + return result; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _calculateCellIndex(rowIndex, columnIndex, rowCount, columnCountBase) { + return (calculateCellIndex as any)(rowIndex, columnIndex, rowCount); + } + + generateGroupedDataMap(viewDataMap) { + const { + allDayPanelMap, + dateTableMap, + } = viewDataMap; + + const { previousGroupedDataMap: dateTableGroupedMap } = dateTableMap.reduce((previousOptions, cellsRow) => { + const { + previousGroupedDataMap, previousRowIndex, previousGroupIndex, + } = previousOptions; + const { groupIndex: currentGroupIndex } = cellsRow[0].cellData; + const currentRowIndex = currentGroupIndex === previousGroupIndex + ? previousRowIndex + 1 + : 0; + + cellsRow.forEach((cell) => { + const { groupIndex } = cell.cellData; + + if (!previousGroupedDataMap[groupIndex]) { + previousGroupedDataMap[groupIndex] = []; + } + if (!previousGroupedDataMap[groupIndex][currentRowIndex]) { + previousGroupedDataMap[groupIndex][currentRowIndex] = []; } - const columnCountBase = this.getCellCount(options); - const rowCountBase = this.getRowCount(options); - const cellIndex = this._calculateCellIndex(rowIndex, columnIndex, rowCountBase, columnCountBase); - const millisecondsOffset = this.getMillisecondsOffset(cellIndex, interval, cellCountInDay); + previousGroupedDataMap[groupIndex][currentRowIndex].push(cell); + }); - const offsetByCount = this.isWorkView - ? this.getTimeOffsetByColumnIndex( - columnIndex, - this.getFirstDayOfWeek(firstDayOfWeek), - columnCountBase, - intervalCount, - ) : 0; + return { + previousGroupedDataMap, + previousRowIndex: currentRowIndex, + previousGroupIndex: currentGroupIndex, + }; + }, { + previousGroupedDataMap: [], + previousRowIndex: -1, + previousGroupIndex: -1, + }); - const startViewDateTime = startViewDate.getTime(); - const currentDate = new Date(startViewDateTime + millisecondsOffset + offsetByCount); + const allDayPanelGroupedMap: any = []; + allDayPanelMap?.forEach((cell) => { + const { groupIndex } = cell.cellData; - const timeZoneDifference = isStartViewDateDuringDST - ? 0 - : dateUtils.getTimezonesDifference(startViewDate, currentDate); + if (!allDayPanelGroupedMap[groupIndex]) { + allDayPanelGroupedMap[groupIndex] = []; + } - currentDate.setTime(currentDate.getTime() + timeZoneDifference); + allDayPanelGroupedMap[groupIndex].push(cell); + }); - return currentDate; - } + return { + allDayPanelGroupedMap, + dateTableGroupedMap, + }; + } - getMillisecondsOffset(cellIndex, interval, cellCountInDay) { - const dayIndex = Math.floor(cellIndex / cellCountInDay); - const realHiddenInterval = dayIndex * this.hiddenInterval; + _isFirstGroupCell(rowIndex, columnIndex, options, rowCount, columnCount) { + const { + groupOrientation, + groups, + isGroupedByDate, + } = options; - return interval * cellIndex + realHiddenInterval; - } + const groupCount = getGroupCount(groups); - getTimeOffsetByColumnIndex(columnIndex, firstDayOfWeek, columnCount, intervalCount) { - const firstDayOfWeekDiff = Math.max(0, firstDayOfWeek - 1); - const columnsInWeek = columnCount / intervalCount; - const weekendCount = Math.floor((columnIndex + firstDayOfWeekDiff) / columnsInWeek); - - return DAY_MS * weekendCount * 2; + if (isGroupedByDate) { + return columnIndex % groupCount === 0; } - calculateEndDate(startDate, interval, endDayHour) { - const result = new Date(startDate); - result.setMilliseconds(result.getMilliseconds() + Math.round(interval)); - - return result; + if (groupOrientation === HORIZONTAL_GROUP_ORIENTATION) { + return columnIndex % columnCount === 0; } - _calculateCellIndex(rowIndex, columnIndex, rowCount, columnCount) { - return calculateCellIndex(rowIndex, columnIndex, rowCount); - } + return rowIndex % rowCount === 0; + } - generateGroupedDataMap(viewDataMap) { - const { - allDayPanelMap, - dateTableMap - } = viewDataMap; - - const { previousGroupedDataMap: dateTableGroupedMap } = dateTableMap.reduce((previousOptions, cellsRow) => { - const { - previousGroupedDataMap, previousRowIndex, previousGroupIndex, - } = previousOptions; - const { groupIndex: currentGroupIndex } = cellsRow[0].cellData; - const currentRowIndex = currentGroupIndex === previousGroupIndex - ? previousRowIndex + 1 - : 0; - - cellsRow.forEach((cell) => { - const { groupIndex } = cell.cellData; - - if(!previousGroupedDataMap[groupIndex]) { - previousGroupedDataMap[groupIndex] = []; - } - if(!previousGroupedDataMap[groupIndex][currentRowIndex]) { - previousGroupedDataMap[groupIndex][currentRowIndex] = []; - } - - previousGroupedDataMap[groupIndex][currentRowIndex].push(cell); - }); - - return { - previousGroupedDataMap, - previousRowIndex: currentRowIndex, - previousGroupIndex: currentGroupIndex, - }; - }, { - previousGroupedDataMap: [], - previousRowIndex: -1, - previousGroupIndex: -1, - }); - - const allDayPanelGroupedMap = []; - allDayPanelMap?.forEach((cell) => { - - const { groupIndex } = cell.cellData; + _isLastGroupCell(rowIndex, columnIndex, options, rowCount, columnCount) { + const { + groupOrientation, + groups, + isGroupedByDate, + } = options; - if(!allDayPanelGroupedMap[groupIndex]) { - allDayPanelGroupedMap[groupIndex] = []; - } + const groupCount = getGroupCount(groups); - allDayPanelGroupedMap[groupIndex].push(cell); - }); - - return { - allDayPanelGroupedMap, - dateTableGroupedMap - }; + if (isGroupedByDate) { + return (columnIndex + 1) % groupCount === 0; } - _isFirstGroupCell(rowIndex, columnIndex, options, rowCount, columnCount) { - const { - groupOrientation, - groups, - isGroupedByDate, - } = options; - - const groupCount = getGroupCount(groups); - - if(isGroupedByDate) { - return columnIndex % groupCount === 0; - } - - if(groupOrientation === HORIZONTAL_GROUP_ORIENTATION) { - return columnIndex % columnCount === 0; - } - - return rowIndex % rowCount === 0; + if (groupOrientation === HORIZONTAL_GROUP_ORIENTATION) { + return (columnIndex + 1) % columnCount === 0; } - _isLastGroupCell(rowIndex, columnIndex, options, rowCount, columnCount) { - const { - groupOrientation, - groups, - isGroupedByDate, - } = options; + return (rowIndex + 1) % rowCount === 0; + } - const groupCount = getGroupCount(groups); + markSelectedAndFocusedCells(viewDataMap, renderOptions) { + const { + selectedCells, + focusedCell, + } = renderOptions; - if(isGroupedByDate) { - return (columnIndex + 1) % groupCount === 0; - } - - if(groupOrientation === HORIZONTAL_GROUP_ORIENTATION) { - return (columnIndex + 1) % columnCount === 0; - } - - return (rowIndex + 1) % rowCount === 0; + if (!selectedCells && !focusedCell) { + return viewDataMap; } - markSelectedAndFocusedCells(viewDataMap, renderOptions) { - const { - selectedCells, - focusedCell, - } = renderOptions; + const { + allDayPanelMap, + dateTableMap, + } = viewDataMap; - if(!selectedCells && !focusedCell) { - return viewDataMap; - } + const nextDateTableMap = dateTableMap.map((row) => this._markSelectedAndFocusedCellsInRow(row, selectedCells, focusedCell)); + const nextAllDayMap = this._markSelectedAndFocusedCellsInRow(allDayPanelMap, selectedCells, focusedCell); - const { - allDayPanelMap, - dateTableMap - } = viewDataMap; + return { + allDayPanelMap: nextAllDayMap, + dateTableMap: nextDateTableMap, + }; + } - const nextDateTableMap = dateTableMap.map((row) => { - return this._markSelectedAndFocusedCellsInRow(row, selectedCells, focusedCell); - }); - const nextAllDayMap = this._markSelectedAndFocusedCellsInRow(allDayPanelMap, selectedCells, focusedCell); + _markSelectedAndFocusedCellsInRow(dataRow, selectedCells, focusedCell) { + return dataRow.map((cell) => { + const { + index, + groupIndex, + allDay, + startDate, + } = cell.cellData; - return { - allDayPanelMap: nextAllDayMap, - dateTableMap: nextDateTableMap, - }; - } - - _markSelectedAndFocusedCellsInRow(dataRow, selectedCells, focusedCell) { - return dataRow.map((cell) => { - const { - index, - groupIndex, - allDay, - startDate, - } = cell.cellData; - - const indexInSelectedCells = selectedCells.findIndex(({ - index: selectedCellIndex, - groupIndex: selectedCellGroupIndex, - allDay: selectedCellAllDay, - startDate: selectedCellStartDate, - }) => ( - groupIndex === selectedCellGroupIndex + const indexInSelectedCells = selectedCells.findIndex(({ + index: selectedCellIndex, + groupIndex: selectedCellGroupIndex, + allDay: selectedCellAllDay, + startDate: selectedCellStartDate, + }) => groupIndex === selectedCellGroupIndex && (index === selectedCellIndex || (selectedCellIndex === undefined && startDate.getTime() === selectedCellStartDate.getTime())) - && !!allDay === !!selectedCellAllDay - )); + && !!allDay === !!selectedCellAllDay); - const isFocused = !!focusedCell + const isFocused = !!focusedCell && index === focusedCell.cellData.index && groupIndex === focusedCell.cellData.groupIndex && allDay === focusedCell.cellData.allDay; - if(!isFocused && indexInSelectedCells === -1) { - return cell; - } - - return { - ...cell, - cellData: { - ...cell.cellData, - isSelected: indexInSelectedCells > -1, - isFocused, - }, - }; - }); - } - - getInterval(hoursInterval) { - return hoursInterval * HOUR_MS; - } - - _getIntervalDuration(intervalCount) { - return dateUtils.dateToMilliseconds('day') * intervalCount; - } - - _setVisibilityDates() {} - - getCellCountInDay(startDayHour, endDayHour, hoursInterval) { - const result = calculateDayDuration(startDayHour, endDayHour) / hoursInterval; - - return Math.ceil(result); - } - - getCellCount(options) { - const { - intervalCount, - viewType, - startDayHour, - endDayHour, - hoursInterval, - } = options; - - const cellCountInDay = this.getCellCountInDay(startDayHour, endDayHour, hoursInterval); - const columnCountInDay = isHorizontalView(viewType) - ? cellCountInDay - : 1; - - return this.daysInInterval * intervalCount * columnCountInDay; - } - - getRowCount(options) { - const { - viewType, - startDayHour, - endDayHour, - hoursInterval, - } = options; - - const cellCountInDay = this.getCellCountInDay(startDayHour, endDayHour, hoursInterval); - const rowCountInDay = !isHorizontalView(viewType) - ? cellCountInDay - : 1; - - return rowCountInDay; - } - - setHiddenInterval(startDayHour, endDayHour, hoursInterval) { - this.hiddenInterval = DAY_MS - this.getVisibleDayDuration(startDayHour, endDayHour, hoursInterval); - } - - getVisibleDayDuration(startDayHour, endDayHour, hoursInterval) { - const cellCountInDay = this.getCellCountInDay(startDayHour, endDayHour, hoursInterval); - - return hoursInterval * cellCountInDay * HOUR_MS; - } - - getFirstDayOfWeek(firstDayOfWeekOption) { - return firstDayOfWeekOption; - } + if (!isFocused && indexInSelectedCells === -1) { + return cell; + } + + return { + ...cell, + cellData: { + ...cell.cellData, + isSelected: indexInSelectedCells > -1, + isFocused, + }, + }; + }); + } + + getInterval(hoursInterval) { + return hoursInterval * HOUR_MS; + } + + _getIntervalDuration(intervalCount) { + return dateUtils.dateToMilliseconds('day') * intervalCount; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _setVisibilityDates(options: any) {} + + getCellCountInDay(startDayHour, endDayHour, hoursInterval) { + const result = calculateDayDuration(startDayHour, endDayHour) / hoursInterval; + + return Math.ceil(result); + } + + getCellCount(options) { + const { + intervalCount, + viewType, + startDayHour, + endDayHour, + hoursInterval, + } = options; + + const cellCountInDay = this.getCellCountInDay(startDayHour, endDayHour, hoursInterval); + const columnCountInDay = isHorizontalView(viewType) + ? cellCountInDay + : 1; + + return this.daysInInterval * intervalCount * columnCountInDay; + } + + getRowCount(options) { + const { + viewType, + startDayHour, + endDayHour, + hoursInterval, + } = options; + + const cellCountInDay = this.getCellCountInDay(startDayHour, endDayHour, hoursInterval); + const rowCountInDay = !isHorizontalView(viewType) + ? cellCountInDay + : 1; + + return rowCountInDay; + } + + setHiddenInterval(startDayHour, endDayHour, hoursInterval) { + this.hiddenInterval = DAY_MS - this.getVisibleDayDuration(startDayHour, endDayHour, hoursInterval); + } + + getVisibleDayDuration(startDayHour, endDayHour, hoursInterval) { + const cellCountInDay = this.getCellCountInDay(startDayHour, endDayHour, hoursInterval); + + return hoursInterval * cellCountInDay * HOUR_MS; + } + + getFirstDayOfWeek(firstDayOfWeekOption) { + return firstDayOfWeekOption; + } } diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator_day.ts b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator_day.ts index df12ebfd7eb7..5b0632bbb076 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator_day.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator_day.ts @@ -1,13 +1,14 @@ -import { calculateStartViewDate } from '../../../../renovation/ui/scheduler/view_model/to_test/views/utils/day'; -import { ViewDataGenerator } from './view_data_generator'; +import { calculateStartViewDate } from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/day'; + +import { ViewDataGenerator } from './m_view_data_generator'; export class ViewDataGeneratorDay extends ViewDataGenerator { - _calculateStartViewDate(options) { - return calculateStartViewDate( - options.currentDate, - options.startDayHour, - options.startDate, - this._getIntervalDuration(options.intervalCount), - ); - } + _calculateStartViewDate(options) { + return calculateStartViewDate( + options.currentDate, + options.startDayHour, + options.startDate, + this._getIntervalDuration(options.intervalCount), + ); + } } diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator_month.ts b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator_month.ts index 1614c430526d..dd4c385c0f3d 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator_month.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator_month.ts @@ -1,109 +1,116 @@ -import { getToday, setOptionHour } from '../../../../renovation/ui/scheduler/view_model/to_test/views/utils/base'; -import { ViewDataGenerator } from './view_data_generator'; -import dateUtils from '../../../../core/utils/date'; +import dateUtils from '@js/core/utils/date'; +import dateLocalization from '@js/localization/date'; +import { getToday, setOptionHour } from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/base'; import { - calculateCellIndex, - calculateStartViewDate, - getCellText, - isFirstCellInMonthWithIntervalCount, - getViewStartByOptions, -} from '../../../../renovation/ui/scheduler/view_model/to_test/views/utils/month'; -import { calculateAlignedWeeksBetweenDates } from './utils'; -import dateLocalization from '../../../../localization/date'; + calculateCellIndex, + calculateStartViewDate, + getCellText, + getViewStartByOptions, + isFirstCellInMonthWithIntervalCount, +} from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/month'; + +// eslint-disable-next-line import/no-cycle +import { calculateAlignedWeeksBetweenDates } from './m_utils'; +import { ViewDataGenerator } from './m_view_data_generator'; const DAY_IN_MILLISECONDS = dateUtils.dateToMilliseconds('day'); const DAYS_IN_WEEK = 7; export class ViewDataGeneratorMonth extends ViewDataGenerator { - get tableAllDay() { return undefined; } - - getCellData(rowIndex, columnIndex, options, allDay) { - const data = super.getCellData(rowIndex, columnIndex, options, false); - - const startDate = data.startDate; - const { - indicatorTime, - timeZoneCalculator, - intervalCount, - } = options; - - data.today = this.isCurrentDate(startDate, indicatorTime, timeZoneCalculator); - data.otherMonth = this.isOtherMonth(startDate, this._minVisibleDate, this._maxVisibleDate); - data.firstDayOfMonth = isFirstCellInMonthWithIntervalCount(startDate, intervalCount); - data.text = getCellText(startDate, intervalCount); - - return data; - } - - isCurrentDate(date, indicatorTime, timeZoneCalculator) { - return dateUtils.sameDate(date, getToday(indicatorTime, timeZoneCalculator)); - } - - isOtherMonth(cellDate, minDate, maxDate) { - return !dateUtils.dateInRange(cellDate, minDate, maxDate, 'date'); - } - - _calculateCellIndex(rowIndex, columnIndex, rowCount, columnCount) { - return calculateCellIndex(rowIndex, columnIndex, rowCount, columnCount); - } - - calculateEndDate(startDate, interval, endDayHour) { - return setOptionHour(startDate, endDayHour); - } - - getInterval() { - return DAY_IN_MILLISECONDS; - } - - _calculateStartViewDate(options) { - return calculateStartViewDate( - options.currentDate, - options.startDayHour, - options.startDate, - options.intervalCount, - this.getFirstDayOfWeek(options.firstDayOfWeek), - ); - } - - _setVisibilityDates(options) { - const { - intervalCount, - startDate, - currentDate, - } = options; - - const firstMonthDate = dateUtils.getFirstMonthDate(startDate); - const viewStart = getViewStartByOptions(startDate, currentDate, intervalCount, firstMonthDate); - - this._minVisibleDate = new Date(viewStart.setDate(1)); - - const nextMonthDate = new Date(viewStart.setMonth(viewStart.getMonth() + intervalCount)); - this._maxVisibleDate = new Date(nextMonthDate.setDate(0)); - } - - getCellCount() { - return DAYS_IN_WEEK; - } - - getRowCount(options) { - const startDate = new Date(options.currentDate); - startDate.setDate(1); - - const endDate = new Date(startDate); - endDate.setMonth(endDate.getMonth() + options.intervalCount); - endDate.setDate(0); - - return calculateAlignedWeeksBetweenDates( - startDate, - endDate, - options.firstDayOfWeek ?? dateLocalization.firstDayOfWeekIndex(), - ); - } - - getCellCountInDay() { - return 1; - } - - setHiddenInterval() { - this.hiddenInterval = 0; - } + _minVisibleDate: any; + + _maxVisibleDate: any; + + tableAllDay: any = undefined; + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getCellData(rowIndex, columnIndex, options, allDay) { + const data = super.getCellData(rowIndex, columnIndex, options, false); + + const { startDate } = data; + const { + indicatorTime, + timeZoneCalculator, + intervalCount, + } = options; + + data.today = this.isCurrentDate(startDate, indicatorTime, timeZoneCalculator); + data.otherMonth = this.isOtherMonth(startDate, this._minVisibleDate, this._maxVisibleDate); + data.firstDayOfMonth = isFirstCellInMonthWithIntervalCount(startDate, intervalCount); + data.text = getCellText(startDate, intervalCount); + + return data; + } + + isCurrentDate(date, indicatorTime, timeZoneCalculator) { + return dateUtils.sameDate(date, getToday(indicatorTime, timeZoneCalculator)); + } + + isOtherMonth(cellDate, minDate, maxDate) { + return !dateUtils.dateInRange(cellDate, minDate, maxDate, 'date'); + } + + _calculateCellIndex(rowIndex, columnIndex, rowCount, columnCount) { + return calculateCellIndex(rowIndex, columnIndex, rowCount, columnCount); + } + + calculateEndDate(startDate, interval, endDayHour) { + return setOptionHour(startDate, endDayHour); + } + + getInterval() { + return DAY_IN_MILLISECONDS; + } + + _calculateStartViewDate(options) { + return calculateStartViewDate( + options.currentDate, + options.startDayHour, + options.startDate, + options.intervalCount, + this.getFirstDayOfWeek(options.firstDayOfWeek), + ); + } + + _setVisibilityDates(options) { + const { + intervalCount, + startDate, + currentDate, + } = options; + + const firstMonthDate: any = dateUtils.getFirstMonthDate(startDate); + const viewStart = getViewStartByOptions(startDate, currentDate, intervalCount, firstMonthDate); + + this._minVisibleDate = new Date(viewStart.setDate(1)); + + const nextMonthDate = new Date(viewStart.setMonth(viewStart.getMonth() + intervalCount)); + this._maxVisibleDate = new Date(nextMonthDate.setDate(0)); + } + + getCellCount() { + return DAYS_IN_WEEK; + } + + getRowCount(options) { + const startDate = new Date(options.currentDate); + startDate.setDate(1); + + const endDate = new Date(startDate); + endDate.setMonth(endDate.getMonth() + options.intervalCount); + endDate.setDate(0); + + return calculateAlignedWeeksBetweenDates( + startDate, + endDate, + options.firstDayOfWeek ?? dateLocalization.firstDayOfWeekIndex(), + ); + } + + getCellCountInDay() { + return 1; + } + + setHiddenInterval() { + this.hiddenInterval = 0; + } } diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator_timeline_month.ts b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator_timeline_month.ts index f7ab0c5dd0d4..da802bc17d0f 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator_timeline_month.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator_timeline_month.ts @@ -1,44 +1,45 @@ -import { ViewDataGenerator } from './view_data_generator'; -import { calculateCellIndex } from '../../../../renovation/ui/scheduler/view_model/to_test/views/utils/month'; -import { calculateStartViewDate } from '../../../../renovation/ui/scheduler/view_model/to_test/views/utils/timeline_month'; -import { setOptionHour } from '../../../../renovation/ui/scheduler/view_model/to_test/views/utils/base'; -import dateUtils from '../../../../core/utils/date'; +import dateUtils from '@js/core/utils/date'; +import { setOptionHour } from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/base'; +import { calculateCellIndex } from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/month'; +import { calculateStartViewDate } from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/timeline_month'; + +import { ViewDataGenerator } from './m_view_data_generator'; const DAY_IN_MILLISECONDS = dateUtils.dateToMilliseconds('day'); export class ViewDataGeneratorTimelineMonth extends ViewDataGenerator { - _calculateCellIndex(rowIndex, columnIndex, rowCount, columnCount) { - return calculateCellIndex(rowIndex, columnIndex, rowCount, columnCount); - } - - calculateEndDate(startDate, interval, endDayHour) { - return setOptionHour(startDate, endDayHour); - } - - getInterval() { - return DAY_IN_MILLISECONDS; + _calculateCellIndex(rowIndex, columnIndex, rowCount, columnCount) { + return calculateCellIndex(rowIndex, columnIndex, rowCount, columnCount); + } + + calculateEndDate(startDate, interval, endDayHour) { + return setOptionHour(startDate, endDayHour); + } + + getInterval() { + return DAY_IN_MILLISECONDS; + } + + _calculateStartViewDate(options: any) { + return calculateStartViewDate( + options.currentDate, + options.startDayHour, + options.startDate, + options.intervalCount, + ); + } + + getCellCount(options) { + const { intervalCount, currentDate } = options; + let cellCount = 0; + for (let i = 1; i <= intervalCount; i++) { + cellCount += new Date(currentDate.getFullYear(), currentDate.getMonth() + i, 0).getDate(); } - _calculateStartViewDate(options) { - return calculateStartViewDate( - options.currentDate, - options.startDayHour, - options.startDate, - options.intervalCount, - ); - } - - getCellCount(options) { - const { intervalCount, currentDate } = options; - let cellCount = 0; - for(let i = 1; i <= intervalCount; i++) { - cellCount += new Date(currentDate.getFullYear(), currentDate.getMonth() + i, 0).getDate(); - } + return cellCount; + } - return cellCount; - } - - setHiddenInterval() { - this.hiddenInterval = 0; - } + setHiddenInterval() { + this.hiddenInterval = 0; + } } diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator_week.ts b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator_week.ts index b48af01a6044..977a25387f44 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator_week.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator_week.ts @@ -1,20 +1,21 @@ -import { calculateStartViewDate, getIntervalDuration } from '../../../../renovation/ui/scheduler/view_model/to_test/views/utils/week'; -import { ViewDataGenerator } from './view_data_generator'; +import { calculateStartViewDate, getIntervalDuration } from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/week'; + +import { ViewDataGenerator } from './m_view_data_generator'; export class ViewDataGeneratorWeek extends ViewDataGenerator { - get daysInInterval() { return 7; } + readonly daysInInterval: number = 7; - _getIntervalDuration(intervalCount) { - return getIntervalDuration(intervalCount); - } + _getIntervalDuration(intervalCount) { + return getIntervalDuration(intervalCount); + } - _calculateStartViewDate(options) { - return calculateStartViewDate( - options.currentDate, - options.startDayHour, - options.startDate, - this._getIntervalDuration(options.intervalCount), - this.getFirstDayOfWeek(options.firstDayOfWeek), - ); - } + _calculateStartViewDate(options) { + return calculateStartViewDate( + options.currentDate, + options.startDayHour, + options.startDate, + this._getIntervalDuration(options.intervalCount), + this.getFirstDayOfWeek(options.firstDayOfWeek), + ); + } } diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator_work_week.ts b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator_work_week.ts index ef31f3575502..c18b5ebf06eb 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator_work_week.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator_work_week.ts @@ -1,29 +1,30 @@ import { - calculateStartViewDate, - isDataOnWeekend, -} from '../../../../renovation/ui/scheduler/view_model/to_test/views/utils/work_week'; -import { ViewDataGeneratorWeek } from './view_data_generator_week'; + calculateStartViewDate, + isDataOnWeekend, +} from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/work_week'; + +import { ViewDataGeneratorWeek } from './m_view_data_generator_week'; export class ViewDataGeneratorWorkWeek extends ViewDataGeneratorWeek { - get daysInInterval() { return 5; } + readonly daysInInterval = 5; - get isWorkView() { return true; } + readonly isWorkView: boolean = true; - isSkippedDate(date) { - return isDataOnWeekend(date); - } + isSkippedDate(date) { + return isDataOnWeekend(date); + } - _calculateStartViewDate(options) { - return calculateStartViewDate( - options.currentDate, - options.startDayHour, - options.startDate, - this._getIntervalDuration(options.intervalCount), - this.getFirstDayOfWeek(options.firstDayOfWeek), - ); - } + _calculateStartViewDate(options) { + return calculateStartViewDate( + options.currentDate, + options.startDayHour, + options.startDate, + this._getIntervalDuration(options.intervalCount), + this.getFirstDayOfWeek(options.firstDayOfWeek), + ); + } - getFirstDayOfWeek(firstDayOfWeekOption) { - return firstDayOfWeekOption || 0; - } + getFirstDayOfWeek(firstDayOfWeekOption) { + return firstDayOfWeekOption || 0; + } } diff --git a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_provider.ts b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_provider.ts index 8996237c2450..c035935673b9 100644 --- a/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_provider.ts +++ b/packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_provider.ts @@ -1,467 +1,489 @@ -import dateUtils from '../../../../core/utils/date'; -import { getGroupPanelData } from '../../../../renovation/ui/scheduler/view_model/group_panel/utils'; -import { isGroupingByDate, isHorizontalGroupingApplied, isVerticalGroupingApplied } from '../../../../renovation/ui/scheduler/workspaces/utils'; -import { calculateIsGroupedAllDayPanel } from '../../../../renovation/ui/scheduler/view_model/to_test/views/utils/base'; -import { DateHeaderDataGenerator } from './date_header_data_generator'; -import { GroupedDataMapProvider } from './grouped_data_map_provider'; -import { TimePanelDataGenerator } from './time_panel_data_generator'; -import { getViewDataGeneratorByViewType } from './utils'; -import timeZoneUtils from '../../utils.timeZone'; +import dateUtils from '@js/core/utils/date'; +import { getGroupPanelData } from '@js/renovation/ui/scheduler/view_model/group_panel/utils'; +import { calculateIsGroupedAllDayPanel } from '@js/renovation/ui/scheduler/view_model/to_test/views/utils/base'; +import { isGroupingByDate, isHorizontalGroupingApplied, isVerticalGroupingApplied } from '@js/renovation/ui/scheduler/workspaces/utils'; -export default class ViewDataProvider { - constructor(viewType) { - this.viewDataGenerator = getViewDataGeneratorByViewType(viewType); - this.viewData = {}; - this.completeViewDataMap = []; - this.completeDateHeaderMap = []; - this.viewDataMap = {}; - this._groupedDataMapProvider = null; - } - - get groupedDataMap() { return this._groupedDataMapProvider.groupedDataMap; } - - get hiddenInterval() { return this.viewDataGenerator.hiddenInterval; } - - isSkippedDate(date) { return this.viewDataGenerator.isSkippedDate(date); } - - update(options, isGenerateNewViewData) { - this.viewDataGenerator = getViewDataGeneratorByViewType(options.viewType); - - const viewDataGenerator = this.viewDataGenerator; - const dateHeaderDataGenerator = new DateHeaderDataGenerator(viewDataGenerator); - const timePanelDataGenerator = new TimePanelDataGenerator(viewDataGenerator); - - const renderOptions = this._transformRenderOptions(options); - - renderOptions.interval = this.viewDataGenerator.getInterval(renderOptions.hoursInterval); - this._options = renderOptions; - - if(isGenerateNewViewData) { - this.completeViewDataMap = viewDataGenerator.getCompleteViewDataMap(renderOptions); - this.completeDateHeaderMap = dateHeaderDataGenerator - .getCompleteDateHeaderMap(renderOptions, this.completeViewDataMap); - if(renderOptions.isGenerateTimePanelData) { - this.completeTimePanelMap = timePanelDataGenerator - .getCompleteTimePanelMap(renderOptions, this.completeViewDataMap); - } - } - - this.viewDataMap = viewDataGenerator.generateViewDataMap(this.completeViewDataMap, renderOptions); - this.updateViewData(renderOptions); - - - this._groupedDataMapProvider = new GroupedDataMapProvider( - this.viewDataGenerator, - this.viewDataMap, - this.completeViewDataMap, - { - isVerticalGrouping: renderOptions.isVerticalGrouping, - viewType: renderOptions.viewType, - }, - ); - - this.dateHeaderData = dateHeaderDataGenerator - .generateDateHeaderData(this.completeDateHeaderMap, this.completeViewDataMap, renderOptions); - - if(renderOptions.isGenerateTimePanelData) { - this.timePanelData = timePanelDataGenerator.generateTimePanelData( - this.completeTimePanelMap, - renderOptions, - ); - } - } - - createGroupedDataMapProvider() { - this._groupedDataMapProvider = new GroupedDataMapProvider( - this.viewDataGenerator, - this.viewDataMap, - this.completeViewDataMap, - { - isVerticalGrouping: this._options.isVerticalGrouping, - viewType: this._options.viewType, - }, - ); - } - - updateViewData(options) { - const renderOptions = this._transformRenderOptions(options); - this.viewDataMapWithSelection = this.viewDataGenerator - .markSelectedAndFocusedCells(this.viewDataMap, renderOptions); - this.viewData = this.viewDataGenerator - .getViewDataFromMap( - this.completeViewDataMap, - this.viewDataMapWithSelection, - renderOptions, - ); - } +import timeZoneUtils from '../../m_utils_time_zone'; +import { DateHeaderDataGenerator } from './m_date_header_data_generator'; +import { GroupedDataMapProvider } from './m_grouped_data_map_provider'; +import { TimePanelDataGenerator } from './m_time_panel_data_generator'; +import { getViewDataGeneratorByViewType } from './m_utils'; - _transformRenderOptions(renderOptions) { - const { - groups, - groupOrientation, - groupByDate, - isAllDayPanelVisible, - ...restOptions - } = renderOptions; - - return { - ...restOptions, - startViewDate: this.viewDataGenerator._calculateStartViewDate(renderOptions), - isVerticalGrouping: isVerticalGroupingApplied(groups, groupOrientation), - isHorizontalGrouping: isHorizontalGroupingApplied(groups, groupOrientation), - isGroupedByDate: isGroupingByDate(groups, groupOrientation, groupByDate), - isGroupedAllDayPanel: calculateIsGroupedAllDayPanel( - groups, groupOrientation, isAllDayPanelVisible, - ), - groups, - groupOrientation, - isAllDayPanelVisible, - }; - } +export default class ViewDataProvider { + viewDataGenerator: any; - getGroupPanelData(options) { - const renderOptions = this._transformRenderOptions(options); - if(renderOptions.groups.length > 0) { - const cellCount = this.getCellCount(renderOptions); - return getGroupPanelData( - renderOptions.groups, - cellCount, - renderOptions.isGroupedByDate, - renderOptions.isGroupedByDate ? 1 : cellCount, - ); - } + viewData: any; - return undefined; - } + completeViewDataMap: any[]; - getGroupStartDate(groupIndex) { - return this._groupedDataMapProvider.getGroupStartDate(groupIndex); - } + completeDateHeaderMap: any[]; - getGroupEndDate(groupIndex) { - return this._groupedDataMapProvider.getGroupEndDate(groupIndex); - } + viewDataMap: any; - findGroupCellStartDate(groupIndex, startDate, endDate, isFindByDate = false) { - return this._groupedDataMapProvider.findGroupCellStartDate(groupIndex, startDate, endDate, isFindByDate); - } + _groupedDataMapProvider: any; - findAllDayGroupCellStartDate(groupIndex, startDate) { - return this._groupedDataMapProvider.findAllDayGroupCellStartDate(groupIndex, startDate); - } + _options: any; - findCellPositionInMap(cellInfo) { - return this._groupedDataMapProvider.findCellPositionInMap(cellInfo); - } + completeTimePanelMap: any; - hasAllDayPanel() { - const { viewData } = this.viewDataMap; - const { allDayPanel } = viewData.groupedData[0]; + dateHeaderData: any; - return !viewData.isGroupedAllDayPanel && allDayPanel?.length > 0; - } + timePanelData: any; + + viewDataMapWithSelection: any; + + constructor(viewType) { + this.viewDataGenerator = getViewDataGeneratorByViewType(viewType); + this.viewData = {}; + this.completeViewDataMap = []; + this.completeDateHeaderMap = []; + this.viewDataMap = {}; + this._groupedDataMapProvider = null; + } + + get groupedDataMap() { return this._groupedDataMapProvider.groupedDataMap; } + + get hiddenInterval() { return this.viewDataGenerator.hiddenInterval; } + + isSkippedDate(date) { return this.viewDataGenerator.isSkippedDate(date); } + + update(options, isGenerateNewViewData) { + this.viewDataGenerator = getViewDataGeneratorByViewType(options.viewType); + + const { viewDataGenerator } = this; + const dateHeaderDataGenerator = new DateHeaderDataGenerator(viewDataGenerator); + const timePanelDataGenerator = new TimePanelDataGenerator(viewDataGenerator); + + const renderOptions = this._transformRenderOptions(options); + + renderOptions.interval = this.viewDataGenerator.getInterval(renderOptions.hoursInterval); + this._options = renderOptions; + + if (isGenerateNewViewData) { + this.completeViewDataMap = viewDataGenerator.getCompleteViewDataMap(renderOptions); + this.completeDateHeaderMap = dateHeaderDataGenerator + .getCompleteDateHeaderMap(renderOptions, this.completeViewDataMap); + if (renderOptions.isGenerateTimePanelData) { + this.completeTimePanelMap = timePanelDataGenerator + .getCompleteTimePanelMap(renderOptions, this.completeViewDataMap); + } + } + + this.viewDataMap = viewDataGenerator.generateViewDataMap(this.completeViewDataMap, renderOptions); + this.updateViewData(renderOptions); + + this._groupedDataMapProvider = new GroupedDataMapProvider( + this.viewDataGenerator, + this.viewDataMap, + this.completeViewDataMap, + { + isVerticalGrouping: renderOptions.isVerticalGrouping, + viewType: renderOptions.viewType, + }, + ); + + this.dateHeaderData = dateHeaderDataGenerator + .generateDateHeaderData(this.completeDateHeaderMap, this.completeViewDataMap, renderOptions); + + if (renderOptions.isGenerateTimePanelData) { + this.timePanelData = timePanelDataGenerator.generateTimePanelData( + this.completeTimePanelMap, + renderOptions, + ); + } + } + + createGroupedDataMapProvider() { + this._groupedDataMapProvider = new GroupedDataMapProvider( + this.viewDataGenerator, + this.viewDataMap, + this.completeViewDataMap, + { + isVerticalGrouping: this._options.isVerticalGrouping, + viewType: this._options.viewType, + }, + ); + } + + updateViewData(options) { + const renderOptions = this._transformRenderOptions(options); + this.viewDataMapWithSelection = this.viewDataGenerator + .markSelectedAndFocusedCells(this.viewDataMap, renderOptions); + this.viewData = this.viewDataGenerator + .getViewDataFromMap( + this.completeViewDataMap, + this.viewDataMapWithSelection, + renderOptions, + ); + } + + _transformRenderOptions(renderOptions) { + const { + groups, + groupOrientation, + groupByDate, + isAllDayPanelVisible, + ...restOptions + } = renderOptions; + + return { + ...restOptions, + startViewDate: this.viewDataGenerator._calculateStartViewDate(renderOptions), + isVerticalGrouping: isVerticalGroupingApplied(groups, groupOrientation), + isHorizontalGrouping: isHorizontalGroupingApplied(groups, groupOrientation), + isGroupedByDate: isGroupingByDate(groups, groupOrientation, groupByDate), + isGroupedAllDayPanel: calculateIsGroupedAllDayPanel(groups, groupOrientation, isAllDayPanelVisible), + groups, + groupOrientation, + isAllDayPanelVisible, + }; + } + + getGroupPanelData(options) { + const renderOptions = this._transformRenderOptions(options); + if (renderOptions.groups.length > 0) { + const cellCount = this.getCellCount(renderOptions); + return getGroupPanelData( + renderOptions.groups, + cellCount, + renderOptions.isGroupedByDate, + renderOptions.isGroupedByDate ? 1 : cellCount, + ); + } + + return undefined; + } + + getGroupStartDate(groupIndex) { + return this._groupedDataMapProvider.getGroupStartDate(groupIndex); + } + + getGroupEndDate(groupIndex) { + return this._groupedDataMapProvider.getGroupEndDate(groupIndex); + } + + findGroupCellStartDate(groupIndex, startDate, endDate, isFindByDate = false) { + return this._groupedDataMapProvider.findGroupCellStartDate(groupIndex, startDate, endDate, isFindByDate); + } + + findAllDayGroupCellStartDate(groupIndex, startDate) { + return this._groupedDataMapProvider.findAllDayGroupCellStartDate(groupIndex, startDate); + } + + findCellPositionInMap(cellInfo) { + return this._groupedDataMapProvider.findCellPositionInMap(cellInfo); + } + + hasAllDayPanel() { + const { viewData } = this.viewDataMap; + const { allDayPanel } = viewData.groupedData[0]; + + return !viewData.isGroupedAllDayPanel && allDayPanel?.length > 0; + } + + getCellsGroup(groupIndex) { + return this._groupedDataMapProvider.getCellsGroup(groupIndex); + } + + getCompletedGroupsInfo() { + return this._groupedDataMapProvider.getCompletedGroupsInfo(); + } + + getGroupIndices() { + return this._groupedDataMapProvider.getGroupIndices(); + } + + getLastGroupCellPosition(groupIndex) { + return this._groupedDataMapProvider.getLastGroupCellPosition(groupIndex); + } + + getRowCountInGroup(groupIndex) { + return this._groupedDataMapProvider.getRowCountInGroup(groupIndex); + } + + getCellData(rowIndex, columnIndex, isAllDay, rtlEnabled) { + const row = isAllDay && !this._options.isVerticalGrouping + ? this.viewDataMap.allDayPanelMap + : this.viewDataMap.dateTableMap[rowIndex]; + + const actualColumnIndex = !rtlEnabled + ? columnIndex + : row.length - 1 - columnIndex; + + const { cellData } = row[actualColumnIndex]; + + return cellData; + } + + getCellsByGroupIndexAndAllDay(groupIndex, allDay) { + const rowsPerGroup = this._getRowCountWithAllDayRows(); + const isShowAllDayPanel = this._options.isAllDayPanelVisible; + + const firstRowInGroup = this._options.isVerticalGrouping + ? groupIndex * rowsPerGroup + : 0; + const lastRowInGroup = this._options.isVerticalGrouping + ? (groupIndex + 1) * rowsPerGroup - 1 + : rowsPerGroup; + const correctedFirstRow = isShowAllDayPanel && !allDay + ? firstRowInGroup + 1 + : firstRowInGroup; + const correctedLastRow = allDay ? correctedFirstRow : lastRowInGroup; + + return this.completeViewDataMap + .slice(correctedFirstRow, correctedLastRow + 1) + .map((row) => row.filter(({ groupIndex: currentGroupIndex }) => groupIndex === currentGroupIndex)); + } + + getCellCountWithGroup(groupIndex, rowIndex = 0) { + const { dateTableGroupedMap } = this.groupedDataMap; - getCellsGroup(groupIndex) { - return this._groupedDataMapProvider.getCellsGroup(groupIndex); - } + return dateTableGroupedMap + .filter((_, index) => index <= groupIndex) + .reduce( + (previous, row) => previous + row[rowIndex].length, + 0, + ); + } - getCompletedGroupsInfo() { - return this._groupedDataMapProvider.getCompletedGroupsInfo(); + hasGroupAllDayPanel(groupIndex) { + if (this._options.isVerticalGrouping) { + return !!this.groupedDataMap.dateTableGroupedMap[groupIndex]?.[0][0].cellData.allDay; } - getGroupIndices() { - return this._groupedDataMapProvider.getGroupIndices(); - } - - getLastGroupCellPosition(groupIndex) { - return this._groupedDataMapProvider.getLastGroupCellPosition(groupIndex); - } + return this.groupedDataMap.allDayPanelGroupedMap[groupIndex]?.length > 0; + } - getRowCountInGroup(groupIndex) { - return this._groupedDataMapProvider.getRowCountInGroup(groupIndex); - } + isGroupIntersectDateInterval(groupIndex, startDate, endDate) { + const groupStartDate = this.getGroupStartDate(groupIndex); + const groupEndDate = this.getGroupEndDate(groupIndex); - getCellData(rowIndex, columnIndex, isAllDay, rtlEnabled) { - const row = isAllDay && !this._options.isVerticalGrouping - ? this.viewDataMap.allDayPanelMap - : this.viewDataMap.dateTableMap[rowIndex]; + return startDate < groupEndDate && endDate > groupStartDate; + } - const actualColumnIndex = !rtlEnabled - ? columnIndex - : (row.length - 1 - columnIndex); + findGlobalCellPosition(date, groupIndex = 0, allDay = false) { + const { completeViewDataMap } = this; - const { cellData } = row[actualColumnIndex]; + const showAllDayPanel = this._options.isAllDayPanelVisible; - return cellData; - } + for (let rowIndex = 0; rowIndex < completeViewDataMap.length; rowIndex += 1) { + const currentRow = completeViewDataMap[rowIndex]; - getCellsByGroupIndexAndAllDay(groupIndex, allDay) { - const rowsPerGroup = this._getRowCountWithAllDayRows(); - const isShowAllDayPanel = this._options.isAllDayPanelVisible; - - const firstRowInGroup = this._options.isVerticalGrouping - ? groupIndex * rowsPerGroup - : 0; - const lastRowInGroup = this._options.isVerticalGrouping - ? (groupIndex + 1) * rowsPerGroup - 1 - : rowsPerGroup; - const correctedFirstRow = isShowAllDayPanel && !allDay - ? firstRowInGroup + 1 - : firstRowInGroup; - const correctedLastRow = allDay ? correctedFirstRow : lastRowInGroup; - - return this.completeViewDataMap - .slice(correctedFirstRow, correctedLastRow + 1) - .map(row => row.filter(({ groupIndex: currentGroupIndex }) => groupIndex === currentGroupIndex)); - } - - getCellCountWithGroup(groupIndex, rowIndex = 0) { - const { dateTableGroupedMap } = this.groupedDataMap; - - return dateTableGroupedMap - .filter((_, index) => index <= groupIndex) - .reduce( - (previous, row) => previous + row[rowIndex].length, - 0 - ); - } - - hasGroupAllDayPanel(groupIndex) { - if(this._options.isVerticalGrouping) { - return !!this.groupedDataMap.dateTableGroupedMap[groupIndex]?.[0][0].cellData.allDay; - } - - return this.groupedDataMap.allDayPanelGroupedMap[groupIndex]?.length > 0; - } - - isGroupIntersectDateInterval(groupIndex, startDate, endDate) { - const groupStartDate = this.getGroupStartDate(groupIndex); - const groupEndDate = this.getGroupEndDate(groupIndex); - - return startDate < groupEndDate && endDate > groupStartDate; - } - - findGlobalCellPosition(date, groupIndex = 0, allDay = false) { - const { completeViewDataMap } = this; - - const showAllDayPanel = this._options.isAllDayPanelVisible; - - for(let rowIndex = 0; rowIndex < completeViewDataMap.length; rowIndex += 1) { - const currentRow = completeViewDataMap[rowIndex]; - - for(let columnIndex = 0; columnIndex < currentRow.length; columnIndex += 1) { - const cellData = currentRow[columnIndex]; - const { - startDate: currentStartDate, - endDate: currentEndDate, - groupIndex: currentGroupIndex, - allDay: currentAllDay, - } = cellData; + for (let columnIndex = 0; columnIndex < currentRow.length; columnIndex += 1) { + const cellData = currentRow[columnIndex]; + const { + startDate: currentStartDate, + endDate: currentEndDate, + groupIndex: currentGroupIndex, + allDay: currentAllDay, + } = cellData; - if(groupIndex === currentGroupIndex + if (groupIndex === currentGroupIndex && allDay === !!currentAllDay && this._compareDatesAndAllDay(date, currentStartDate, currentEndDate, allDay)) { - return { - position: { - columnIndex, - rowIndex: showAllDayPanel && !this._options.isVerticalGrouping - ? rowIndex - 1 - : rowIndex, - }, - cellData, - }; - } - } + return { + position: { + columnIndex, + rowIndex: showAllDayPanel && !this._options.isVerticalGrouping + ? rowIndex - 1 + : rowIndex, + }, + cellData, + }; } + } } - _compareDatesAndAllDay(date, cellStartDate, cellEndDate, allDay) { - const time = date.getTime(); - const trimmedTime = dateUtils.trimTime(date).getTime(); - const cellStartTime = cellStartDate.getTime(); - const cellEndTime = cellEndDate.getTime(); + return undefined; + } + + _compareDatesAndAllDay(date, cellStartDate, cellEndDate, allDay) { + const time = date.getTime(); + const trimmedTime = dateUtils.trimTime(date).getTime(); + const cellStartTime = cellStartDate.getTime(); + const cellEndTime = cellEndDate.getTime(); - return (!allDay + return (!allDay && time >= cellStartTime && time < cellEndTime) || (allDay && trimmedTime === cellStartTime); - } + } - getSkippedDaysCount(groupIndex, startDate, endDate, daysCount) { - const { dateTableGroupedMap } = this._groupedDataMapProvider.groupedDataMap; - const groupedData = dateTableGroupedMap[groupIndex]; - let includedDays = 0; + getSkippedDaysCount(groupIndex, startDate, endDate, daysCount) { + const { dateTableGroupedMap } = this._groupedDataMapProvider.groupedDataMap; + const groupedData = dateTableGroupedMap[groupIndex]; + let includedDays = 0; - for(let rowIndex = 0; rowIndex < groupedData.length; rowIndex += 1) { - for(let columnIndex = 0; columnIndex < groupedData[rowIndex].length; columnIndex += 1) { - const cell = groupedData[rowIndex][columnIndex].cellData; - if(startDate.getTime() < cell.endDate.getTime() + for (let rowIndex = 0; rowIndex < groupedData.length; rowIndex += 1) { + for (let columnIndex = 0; columnIndex < groupedData[rowIndex].length; columnIndex += 1) { + const cell = groupedData[rowIndex][columnIndex].cellData; + if (startDate.getTime() < cell.endDate.getTime() && endDate.getTime() > cell.startDate.getTime()) { - includedDays += 1; - } - } - } - - const lastCell = groupedData[groupedData.length - 1][groupedData[0].length - 1].cellData; - const lastCellStart = dateUtils.trimTime(lastCell.startDate); - const daysAfterView = Math.floor((endDate.getTime() - lastCellStart.getTime()) / dateUtils.dateToMilliseconds('day')); - - const deltaDays = daysAfterView > 0 ? daysAfterView : 0; - - return daysCount - includedDays - deltaDays; - } - - getColumnsCount() { - const { dateTableMap } = this.viewDataMap; - return dateTableMap - ? dateTableMap[0].length - : 0; - } - - getViewEdgeIndices(isAllDayPanel) { - if(isAllDayPanel) { - return { - firstColumnIndex: 0, - lastColumnIndex: this.viewDataMap.allDayPanelMap.length - 1, - firstRowIndex: 0, - lastRowIndex: 0, - }; - } - - return { - firstColumnIndex: 0, - lastColumnIndex: this.viewDataMap.dateTableMap[0].length - 1, - firstRowIndex: 0, - lastRowIndex: this.viewDataMap.dateTableMap.length - 1, - }; - } - - getGroupEdgeIndices(groupIndex, isAllDay) { - const groupedDataMap = this.groupedDataMap.dateTableGroupedMap[groupIndex]; - const cellsCount = groupedDataMap[0].length; - const rowsCount = groupedDataMap.length; - - const firstColumnIndex = groupedDataMap[0][0].position.columnIndex; - const lastColumnIndex = groupedDataMap[0][cellsCount - 1].position.columnIndex; - - if(isAllDay) { - return { - firstColumnIndex, - lastColumnIndex, - firstRowIndex: 0, - lastRowIndex: 0, - }; + includedDays += 1; } - - return { - firstColumnIndex, - lastColumnIndex, - firstRowIndex: groupedDataMap[0][0].position.rowIndex, - lastRowIndex: groupedDataMap[rowsCount - 1][0].position.rowIndex, - }; - } - - isSameCell(firstCellData, secondCellData) { - const { - startDate: firstStartDate, - groupIndex: firstGroupIndex, - allDay: firstAllDay, - index: firstIndex, - } = firstCellData; - const { - startDate: secondStartDate, - groupIndex: secondGroupIndex, - allDay: secondAllDay, - index: secondIndex, - } = secondCellData; - - return ( - firstStartDate.getTime() === secondStartDate.getTime() + } + } + + const lastCell = groupedData[groupedData.length - 1][groupedData[0].length - 1].cellData; + const lastCellStart = dateUtils.trimTime(lastCell.startDate); + const daysAfterView = Math.floor((endDate.getTime() - lastCellStart.getTime()) / dateUtils.dateToMilliseconds('day')); + + const deltaDays = daysAfterView > 0 ? daysAfterView : 0; + + return daysCount - includedDays - deltaDays; + } + + getColumnsCount() { + const { dateTableMap } = this.viewDataMap; + return dateTableMap + ? dateTableMap[0].length + : 0; + } + + getViewEdgeIndices(isAllDayPanel) { + if (isAllDayPanel) { + return { + firstColumnIndex: 0, + lastColumnIndex: this.viewDataMap.allDayPanelMap.length - 1, + firstRowIndex: 0, + lastRowIndex: 0, + }; + } + + return { + firstColumnIndex: 0, + lastColumnIndex: this.viewDataMap.dateTableMap[0].length - 1, + firstRowIndex: 0, + lastRowIndex: this.viewDataMap.dateTableMap.length - 1, + }; + } + + getGroupEdgeIndices(groupIndex, isAllDay) { + const groupedDataMap = this.groupedDataMap.dateTableGroupedMap[groupIndex]; + const cellsCount = groupedDataMap[0].length; + const rowsCount = groupedDataMap.length; + + const firstColumnIndex = groupedDataMap[0][0].position.columnIndex; + const lastColumnIndex = groupedDataMap[0][cellsCount - 1].position.columnIndex; + + if (isAllDay) { + return { + firstColumnIndex, + lastColumnIndex, + firstRowIndex: 0, + lastRowIndex: 0, + }; + } + + return { + firstColumnIndex, + lastColumnIndex, + firstRowIndex: groupedDataMap[0][0].position.rowIndex, + lastRowIndex: groupedDataMap[rowsCount - 1][0].position.rowIndex, + }; + } + + isSameCell(firstCellData, secondCellData) { + const { + startDate: firstStartDate, + groupIndex: firstGroupIndex, + allDay: firstAllDay, + index: firstIndex, + } = firstCellData; + const { + startDate: secondStartDate, + groupIndex: secondGroupIndex, + allDay: secondAllDay, + index: secondIndex, + } = secondCellData; + + return ( + firstStartDate.getTime() === secondStartDate.getTime() && firstGroupIndex === secondGroupIndex && firstAllDay === secondAllDay && firstIndex === secondIndex - ); - } + ); + } - getLastViewDate() { - const completeViewDataMap = this.completeViewDataMap; - const rowsCount = completeViewDataMap.length - 1; + getLastViewDate() { + const { completeViewDataMap } = this; + const rowsCount = completeViewDataMap.length - 1; - return completeViewDataMap[rowsCount][completeViewDataMap[rowsCount].length - 1].endDate; - } + return completeViewDataMap[rowsCount][completeViewDataMap[rowsCount].length - 1].endDate; + } - getStartViewDate() { - return this._options.startViewDate; - } + getStartViewDate() { + return this._options.startViewDate; + } - getIntervalDuration(intervalCount) { - return this.viewDataGenerator._getIntervalDuration(intervalCount); - } + getIntervalDuration(intervalCount) { + return this.viewDataGenerator._getIntervalDuration(intervalCount); + } - getLastCellEndDate() { - return new Date( - this.getLastViewDate().getTime() - dateUtils.dateToMilliseconds('minute') - ); - } + getLastCellEndDate() { + return new Date( + this.getLastViewDate().getTime() - dateUtils.dateToMilliseconds('minute'), + ); + } - getLastViewDateByEndDayHour(endDayHour) { - const lastCellEndDate = this.getLastCellEndDate(); - const endTime = dateUtils.dateTimeFromDecimal(endDayHour); + getLastViewDateByEndDayHour(endDayHour) { + const lastCellEndDate = this.getLastCellEndDate(); + const endTime = dateUtils.dateTimeFromDecimal(endDayHour); - const endDateOfLastViewCell = new Date( - lastCellEndDate.setHours( - endTime.hours, - endTime.minutes - ) - ); + const endDateOfLastViewCell = new Date( + lastCellEndDate.setHours( + endTime.hours, + endTime.minutes, + ), + ); - return this._adjustEndDateByDaylightDiff(lastCellEndDate, endDateOfLastViewCell); - } + return this._adjustEndDateByDaylightDiff(lastCellEndDate, endDateOfLastViewCell); + } - _adjustEndDateByDaylightDiff(startDate, endDate) { - const daylightDiff = timeZoneUtils.getDaylightOffsetInMs(startDate, endDate); + _adjustEndDateByDaylightDiff(startDate, endDate) { + const daylightDiff = timeZoneUtils.getDaylightOffsetInMs(startDate, endDate); - const endDateOfLastViewCell = new Date(endDate.getTime() - daylightDiff); + const endDateOfLastViewCell = new Date(endDate.getTime() - daylightDiff); - return new Date(endDateOfLastViewCell.getTime() - dateUtils.dateToMilliseconds('minute')); - } + return new Date(endDateOfLastViewCell.getTime() - dateUtils.dateToMilliseconds('minute')); + } - getCellCountInDay(startDayHour, endDayHour, hoursInterval) { - return this.viewDataGenerator.getCellCountInDay(startDayHour, endDayHour, hoursInterval); - } + getCellCountInDay(startDayHour, endDayHour, hoursInterval) { + return this.viewDataGenerator.getCellCountInDay(startDayHour, endDayHour, hoursInterval); + } - getCellCount(options) { - return this.viewDataGenerator.getCellCount(options); - } + getCellCount(options) { + return this.viewDataGenerator.getCellCount(options); + } - getRowCount(options) { - return this.viewDataGenerator.getRowCount(options); - } + getRowCount(options) { + return this.viewDataGenerator.getRowCount(options); + } - getVisibleDayDuration(startDayHour, endDayHour, hoursInterval) { - return this.viewDataGenerator.getVisibleDayDuration(startDayHour, endDayHour, hoursInterval); - } + getVisibleDayDuration(startDayHour, endDayHour, hoursInterval) { + return this.viewDataGenerator.getVisibleDayDuration(startDayHour, endDayHour, hoursInterval); + } - _getRowCountWithAllDayRows() { - const allDayRowCount = this._options.isAllDayPanelVisible ? 1 : 0; + _getRowCountWithAllDayRows() { + const allDayRowCount = this._options.isAllDayPanelVisible ? 1 : 0; - return this.getRowCount(this._options) + allDayRowCount; - } + return this.getRowCount(this._options) + allDayRowCount; + } - getFirstDayOfWeek(firstDayOfWeekOption) { - return this.viewDataGenerator.getFirstDayOfWeek(firstDayOfWeekOption); - } + getFirstDayOfWeek(firstDayOfWeekOption) { + return this.viewDataGenerator.getFirstDayOfWeek(firstDayOfWeekOption); + } - setViewOptions(options) { - this._options = this._transformRenderOptions(options); - } + setViewOptions(options) { + this._options = this._transformRenderOptions(options); + } - getViewOptions() { - return this._options; - } + getViewOptions() { + return this._options; + } - getViewPortGroupCount() { - const { dateTableGroupedMap } = this.groupedDataMap; - return dateTableGroupedMap?.length || 0; - } + getViewPortGroupCount() { + const { dateTableGroupedMap } = this.groupedDataMap; + return dateTableGroupedMap?.length || 0; + } } diff --git a/packages/devextreme/js/ui/recurrence_editor.js b/packages/devextreme/js/ui/recurrence_editor.js index 04d7276649de..d2d799d96e25 100644 --- a/packages/devextreme/js/ui/recurrence_editor.js +++ b/packages/devextreme/js/ui/recurrence_editor.js @@ -1,2 +1,19 @@ -import RecurrenceEditor from './scheduler/recurrence_editor'; +import RecurrenceEditor from '../__internal/scheduler/m_recurrence_editor'; export default RecurrenceEditor; + +/** + * @name dxRecurrenceEditorOptions.startDate + * @type Date + * @default new Date() + * @hidden + */ + + +/** + * @name dxRecurrenceEditorOptions.firstDayOfWeek + * @type Enums.FirstDayOfWeek + * @default undefined + * @hidden + */ + + diff --git a/packages/devextreme/js/ui/scheduler.js b/packages/devextreme/js/ui/scheduler.js index cfcb5ea0f514..c4c714ea3824 100644 --- a/packages/devextreme/js/ui/scheduler.js +++ b/packages/devextreme/js/ui/scheduler.js @@ -1,3 +1,38 @@ import Scheduler from './scheduler/ui.scheduler'; +// STYLE scheduler + export default Scheduler; + +/** + * @name dxSchedulerOptions.activeStateEnabled + * @hidden + */ + +/** + * @name dxSchedulerOptions.hoverStateEnabled + * @hidden + */ + +/** + * @name dxScheduler.registerKeyHandler + * @publicName registerKeyHandler(key, handler) + * @hidden + */ + +/** + * @hidden + * @name dxSchedulerOptions.indicatorTime + * @type Date + * @default undefined + */ + +/** + * @hidden + * @name dxSchedulerOptions.appointmentPopupTemplate + * @type template|function + * @default "appointmentPopup" + * @type_function_param1 appointmentData:object + * @type_function_param2 contentElement:DxElement + * @type_function_return string|Element|jQuery + */ diff --git a/packages/devextreme/js/ui/scheduler/ui.scheduler.js b/packages/devextreme/js/ui/scheduler/ui.scheduler.js new file mode 100644 index 000000000000..e384aa341abc --- /dev/null +++ b/packages/devextreme/js/ui/scheduler/ui.scheduler.js @@ -0,0 +1,3 @@ +import Scheduler from '../../__internal/scheduler/m_scheduler'; + +export default Scheduler; diff --git a/packages/devextreme/js/ui/scheduler/utils.timeZone.js b/packages/devextreme/js/ui/scheduler/utils.timeZone.js new file mode 100644 index 000000000000..5f6d89248586 --- /dev/null +++ b/packages/devextreme/js/ui/scheduler/utils.timeZone.js @@ -0,0 +1,3 @@ +import timezoneUtils from '../../__internal/scheduler/m_utils_time_zone'; + +export default timezoneUtils;