diff --git a/packages/devextreme/eslint.config.mjs b/packages/devextreme/eslint.config.mjs
index 03ba17a3e76b..b971ed40dfaa 100644
--- a/packages/devextreme/eslint.config.mjs
+++ b/packages/devextreme/eslint.config.mjs
@@ -579,6 +579,25 @@ export default [
'devextreme-custom/no-deferred': 'off',
},
},
+ // Strict TypeScript rules for scheduler/header
+ {
+ files: ['js/__internal/scheduler/header/**/*.ts?(x)'],
+ languageOptions: {
+ parser: tsParser,
+ ecmaVersion: 5,
+ sourceType: 'script',
+ parserOptions: {
+ project: './tsconfig.json',
+ tsconfigRootDir: `${__dirname}/js/__internal`,
+ },
+ },
+ rules: {
+ '@typescript-eslint/no-explicit-any': 'error',
+ '@typescript-eslint/explicit-function-return-type': 'error',
+ '@typescript-eslint/no-unsafe-return': 'error',
+ '@typescript-eslint/explicit-module-boundary-types': 'error',
+ },
+ },
// Rules for grid controls
{
files: [
diff --git a/packages/devextreme/js/__internal/scheduler/__tests__/__mock__/model/scheduler.ts b/packages/devextreme/js/__internal/scheduler/__tests__/__mock__/model/scheduler.ts
index 7b04f562f7d6..0953be819e1b 100644
--- a/packages/devextreme/js/__internal/scheduler/__tests__/__mock__/model/scheduler.ts
+++ b/packages/devextreme/js/__internal/scheduler/__tests__/__mock__/model/scheduler.ts
@@ -1,6 +1,6 @@
import { ToolbarModel } from '@ts/scheduler/__tests__/__mock__/model/toolbar';
-import { APPOINTMENT_POPUP_CLASS } from '../../../appointment_popup/m_popup';
+import { APPOINTMENT_POPUP_CLASS } from '../../../appointment_popup/components/m_popup';
import { POPUP_DIALOG_CLASS } from '../../../m_scheduler';
import type { AppointmentModel } from './appointment';
import { createAppointmentModel } from './appointment';
diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/components/form/m_form.ts
similarity index 90%
rename from packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts
rename to packages/devextreme/js/__internal/scheduler/appointment_popup/components/form/m_form.ts
index 816df5c92e08..e43d2fdbbb95 100644
--- a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts
+++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/components/form/m_form.ts
@@ -19,17 +19,17 @@ import { current, isFluent } from '@js/ui/themes';
import { dateSerialization } from '@ts/core/utils/m_date_serialization';
import type Popup from '@ts/ui/popup/m_popup';
-import timeZoneUtils from '../m_utils_time_zone';
-import type { SafeAppointment } from '../types';
-import type { ResourceLoader } from '../utils/loader/resource_loader';
-import { DEFAULT_ICONS_SHOW_MODE } from '../utils/options/constants';
-import { getAppointmentGroupIndex, getRawAppointmentGroupValues, getSafeGroupValues } from '../utils/resource_manager/appointment_groups_utils';
-import type { ResourceManager } from '../utils/resource_manager/resource_manager';
-import { customizeFormItems } from './m_customize_form_items';
-import { RecurrenceForm } from './m_recurrence_form';
-import { createFormIconTemplate, getStartDateCommonConfig, RecurrenceRule } from './utils';
-
-const CLASSES = {
+import timeZoneUtils from '../../../m_utils_time_zone';
+import type { SafeAppointment } from '../../../types';
+import type { ResourceLoader } from '../../../utils/loader/resource_loader';
+import { DEFAULT_ICONS_SHOW_MODE } from '../../../utils/options/constants';
+import { getAppointmentGroupIndex, getRawAppointmentGroupValues, getSafeGroupValues } from '../../../utils/resource_manager/appointment_groups_utils';
+import type { ResourceManager } from '../../../utils/resource_manager/resource_manager';
+import { customizeFormItems } from '../../factories/customize_form_items/m_customize_form_items';
+import { createFormIconTemplate, getStartDateCommonConfig, RecurrenceRule } from '../../utils';
+import { RecurrenceForm } from '../recurrence_form/m_recurrence_form';
+
+const FORM_CLASSES = {
form: 'dx-scheduler-form',
icon: 'dx-icon',
hidden: 'dx-hidden',
@@ -260,7 +260,7 @@ export class AppointmentForm {
xs: 1,
},
elementAttr: {
- class: CLASSES.form,
+ class: FORM_CLASSES.form,
},
onFieldDataChanged: (e) => {
const {
@@ -304,8 +304,8 @@ export class AppointmentForm {
},
onContentReady: (e): void => {
const $formElement = e.component.$element();
- this._$mainGroup = $formElement.find(`.${CLASSES.mainGroup}`);
- this._$recurrenceGroup = $formElement.find(`.${CLASSES.recurrenceGroup}`);
+ this._$mainGroup = $formElement.find(`.${FORM_CLASSES.mainGroup}`);
+ this._$recurrenceGroup = $formElement.find(`.${FORM_CLASSES.recurrenceGroup}`);
onContentReady?.call(this, e);
},
@@ -320,7 +320,7 @@ export class AppointmentForm {
name: MAIN_GROUP_NAME,
itemType: 'group',
colSpan: 1,
- cssClass: CLASSES.mainGroup,
+ cssClass: FORM_CLASSES.mainGroup,
items: [
this.createSubjectGroup(),
this.createDateRangeGroup(),
@@ -337,7 +337,7 @@ export class AppointmentForm {
return {
name: SUBJECT_GROUP_NAME,
itemType: 'group',
- cssClass: `${CLASSES.subjectGroup} ${CLASSES.groupWithIcon}`,
+ cssClass: `${FORM_CLASSES.subjectGroup} ${FORM_CLASSES.groupWithIcon}`,
colCount: 2,
colCountByScreen: {
xs: 2,
@@ -346,14 +346,14 @@ export class AppointmentForm {
{
name: SUBJECT_ICON_NAME,
colSpan: 1,
- cssClass: CLASSES.formIcon,
+ cssClass: FORM_CLASSES.formIcon,
template: createFormIconTemplate('isnotblank'),
},
{
name: SUBJECT_EDITOR_NAME,
colSpan: 1,
itemType: 'simple',
- cssClass: CLASSES.textEditor,
+ cssClass: FORM_CLASSES.textEditor,
dataField: textExpr,
label: {
text: messageLocalization.format('dxScheduler-editorLabelTitle'),
@@ -368,7 +368,7 @@ export class AppointmentForm {
return {
name: DATE_GROUP_NAME,
itemType: 'group',
- cssClass: `${CLASSES.dateRangeGroup} ${CLASSES.groupWithIcon}`,
+ cssClass: `${FORM_CLASSES.dateRangeGroup} ${FORM_CLASSES.groupWithIcon}`,
colCount: 2,
colCountByScreen: {
xs: 2,
@@ -377,7 +377,7 @@ export class AppointmentForm {
{
name: DATE_ICON_NAME,
colSpan: 1,
- cssClass: CLASSES.formIcon,
+ cssClass: FORM_CLASSES.formIcon,
template: createFormIconTemplate('clock'),
},
{
@@ -401,7 +401,7 @@ export class AppointmentForm {
name: ALL_DAY_EDITOR_NAME,
itemType: 'simple',
dataField: allDayExpr,
- cssClass: CLASSES.allDaySwitch,
+ cssClass: FORM_CLASSES.allDaySwitch,
label: {
text: messageLocalization.format('dxScheduler-allDay'),
location: 'left',
@@ -445,18 +445,18 @@ export class AppointmentForm {
startDateExpr,
{
name: START_DATE_GROUP_NAME,
- cssClass: CLASSES.startDateGroup,
+ cssClass: FORM_CLASSES.startDateGroup,
},
{
name: START_DATE_EDITOR_NAME,
label: {
text: messageLocalization.format('dxScheduler-editorLabelStartDate'),
},
- cssClass: CLASSES.startDateEditor,
+ cssClass: FORM_CLASSES.startDateEditor,
},
{
name: START_TIME_EDITOR_NAME,
- cssClass: CLASSES.startTimeEditor,
+ cssClass: FORM_CLASSES.startTimeEditor,
editorOptions: {
inputAttr: {
'aria-label': messageLocalization.format('dxScheduler-editorAriaLabelStartTime'),
@@ -466,7 +466,7 @@ export class AppointmentForm {
{
name: START_DATE_TIMEZONE_EDITOR_NAME,
dataField: startDateTimeZoneExpr,
- cssClass: CLASSES.startDateTimeZoneEditor,
+ cssClass: FORM_CLASSES.startDateTimeZoneEditor,
editorOptions: {
onValueChanged: (e) => {
const endDateTimeZoneEditor = this.dxForm.getEditor(endDateTimeZoneExpr);
@@ -485,18 +485,18 @@ export class AppointmentForm {
endDateExpr,
{
name: END_DATE_GROUP_NAME,
- cssClass: CLASSES.endDateGroup,
+ cssClass: FORM_CLASSES.endDateGroup,
},
{
name: END_DATE_EDITOR_NAME,
label: {
text: messageLocalization.format('dxScheduler-editorLabelEndDate'),
},
- cssClass: CLASSES.endDateEditor,
+ cssClass: FORM_CLASSES.endDateEditor,
},
{
name: END_TIME_EDITOR_NAME,
- cssClass: CLASSES.endTimeEditor,
+ cssClass: FORM_CLASSES.endTimeEditor,
editorOptions: {
inputAttr: {
'aria-label': messageLocalization.format('dxScheduler-editorAriaLabelEndTime'),
@@ -506,7 +506,7 @@ export class AppointmentForm {
{
name: END_DATE_TIMEZONE_EDITOR_NAME,
dataField: endDateTimeZoneExpr,
- cssClass: CLASSES.endDateTimeZoneEditor,
+ cssClass: FORM_CLASSES.endDateTimeZoneEditor,
},
);
}
@@ -654,19 +654,19 @@ export class AppointmentForm {
colCountByScreen: {
xs: 2,
},
- cssClass: `${CLASSES.repeatGroup} ${CLASSES.groupWithIcon}`,
+ cssClass: `${FORM_CLASSES.repeatGroup} ${FORM_CLASSES.groupWithIcon}`,
items: [
{
name: REPEAT_ICON_NAME,
colSpan: 1,
- cssClass: CLASSES.formIcon,
+ cssClass: FORM_CLASSES.formIcon,
template: createFormIconTemplate('repeat'),
},
{
name: REPEAT_EDITOR_NAME,
colSpan: 1,
itemType: 'simple',
- cssClass: CLASSES.repeatEditor,
+ cssClass: FORM_CLASSES.repeatEditor,
label: {
text: messageLocalization.format('dxScheduler-editorLabelRecurrence'),
},
@@ -710,12 +710,12 @@ export class AppointmentForm {
colCountByScreen: {
xs: 2,
},
- cssClass: `${CLASSES.descriptionGroup} ${CLASSES.groupWithIcon}`,
+ cssClass: `${FORM_CLASSES.descriptionGroup} ${FORM_CLASSES.groupWithIcon}`,
items: [
{
name: DESCRIPTION_ICON_NAME,
colSpan: 1,
- cssClass: CLASSES.formIcon,
+ cssClass: FORM_CLASSES.formIcon,
template: createFormIconTemplate('description'),
},
{
@@ -723,7 +723,7 @@ export class AppointmentForm {
dataField: descriptionExpr,
colSpan: 1,
itemType: 'simple',
- cssClass: CLASSES.descriptionEditor,
+ cssClass: FORM_CLASSES.descriptionEditor,
label: {
text: messageLocalization.format('dxScheduler-editorLabelDescription'),
},
@@ -771,12 +771,12 @@ export class AppointmentForm {
colCountByScreen: {
xs: 2,
},
- cssClass: `${CLASSES.resourcesGroup} ${CLASSES.groupWithIcon}`,
+ cssClass: `${FORM_CLASSES.resourcesGroup} ${FORM_CLASSES.groupWithIcon}`,
items: [
{
name: `${RESOURCES_GROUP_NAME}Icon`,
colSpan: 1,
- cssClass: `${CLASSES.formIcon} ${CLASSES.defaultResourceIcon}`,
+ cssClass: `${FORM_CLASSES.formIcon} ${FORM_CLASSES.defaultResourceIcon}`,
template: createFormIconTemplate('addcircleoutline'),
},
{
@@ -800,12 +800,12 @@ export class AppointmentForm {
colCountByScreen: {
xs: 2,
},
- cssClass: CLASSES.groupWithIcon,
+ cssClass: FORM_CLASSES.groupWithIcon,
items: [
{
colSpan: 1,
name: `${dataField}Icon`,
- cssClass: CLASSES.formIcon,
+ cssClass: FORM_CLASSES.formIcon,
template: createFormIconTemplate(icon),
},
item,
@@ -820,20 +820,20 @@ export class AppointmentForm {
colCountByScreen: {
xs: 1,
},
- cssClass: CLASSES.resourcesGroup,
+ cssClass: FORM_CLASSES.resourcesGroup,
items: resourcesItems,
} as GroupItem;
}
private setStylingModeToEditors(item: FormItem, showIcon: boolean): void {
const itemClasses = (item.cssClass ?? '').split(' ');
- const isIconItem = itemClasses.includes(CLASSES.formIcon);
+ const isIconItem = itemClasses.includes(FORM_CLASSES.formIcon);
if (isIconItem) {
- const isHidden = itemClasses.includes(CLASSES.hidden);
+ const isHidden = itemClasses.includes(FORM_CLASSES.hidden);
if (!showIcon && !isHidden) {
- item.cssClass += ` ${CLASSES.hidden}`;
+ item.cssClass += ` ${FORM_CLASSES.hidden}`;
}
return;
@@ -870,14 +870,14 @@ export class AppointmentForm {
}
if (this._$mainGroup) {
- this._$mainGroup.removeClass(CLASSES.mainHidden);
+ this._$mainGroup.removeClass(FORM_CLASSES.mainHidden);
this._$mainGroup.removeAttr('inert');
this.focusFirstFocusableInGroup(this._$mainGroup);
}
if (this._$recurrenceGroup) {
- this._$recurrenceGroup.addClass(CLASSES.recurrenceHidden);
+ this._$recurrenceGroup.addClass(FORM_CLASSES.recurrenceHidden);
this._$recurrenceGroup.attr('inert', true);
}
}
@@ -893,12 +893,12 @@ export class AppointmentForm {
}
if (this._$mainGroup) {
- this._$mainGroup.addClass(CLASSES.mainHidden);
+ this._$mainGroup.addClass(FORM_CLASSES.mainHidden);
this._$mainGroup.attr('inert', true);
}
if (this._$recurrenceGroup) {
- this._$recurrenceGroup.removeClass(CLASSES.recurrenceHidden);
+ this._$recurrenceGroup.removeClass(FORM_CLASSES.recurrenceHidden);
this._$recurrenceGroup.removeAttr('inert');
this.focusFirstFocusableInGroup(this._$recurrenceGroup);
@@ -935,7 +935,7 @@ export class AppointmentForm {
groupIndex,
});
- const $icon = this.dxForm.$element().find(`.${CLASSES.subjectGroup} .${CLASSES.formIcon} .${CLASSES.icon}`);
+ const $icon = this.dxForm.$element().find(`.${FORM_CLASSES.subjectGroup} .${FORM_CLASSES.formIcon} .${FORM_CLASSES.icon}`);
$icon.css('color', color ?? '');
}
@@ -988,7 +988,7 @@ export class AppointmentForm {
this.showRecurrenceGroup();
},
elementAttr: {
- class: `${CLASSES.recurrenceSettingsButton} dx-shape-standard`,
+ class: `${FORM_CLASSES.recurrenceSettingsButton} dx-shape-standard`,
},
},
});
@@ -1023,7 +1023,7 @@ export class AppointmentForm {
}
private focusFirstFocusableInGroup($group: dxElementWrapper): void {
- const focusTarget = $group.find(`.${CLASSES.fieldItemContent} [tabindex]`).first().get(0) as HTMLElement;
+ const focusTarget = $group.find(`.${FORM_CLASSES.fieldItemContent} [tabindex]`).first().get(0) as HTMLElement;
focusTarget?.focus({ preventScroll: true });
}
}
diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/components/m_popup.test.ts
similarity index 99%
rename from packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts
rename to packages/devextreme/js/__internal/scheduler/appointment_popup/components/m_popup.test.ts
index 4399ec0365d1..12e9903b60bd 100644
--- a/packages/devextreme/js/__internal/scheduler/appointment_popup/appointment_popup.test.ts
+++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/components/m_popup.test.ts
@@ -7,10 +7,10 @@ import type { GroupItem, Item as FormItem } from '@js/ui/form';
import type { ToolbarItem } from '@js/ui/popup';
import { toMilliseconds } from '@ts/utils/toMilliseconds';
-import fx from '../../../common/core/animation/fx';
-import { createScheduler } from '../__tests__/__mock__/create_scheduler';
-import { setupSchedulerTestEnvironment } from '../__tests__/__mock__/m_mock_scheduler';
-import { DEFAULT_SCHEDULER_OPTIONS } from '../utils/options/constants';
+import fx from '../../../../common/core/animation/fx';
+import { createScheduler } from '../../__tests__/__mock__/create_scheduler';
+import { setupSchedulerTestEnvironment } from '../../__tests__/__mock__/m_mock_scheduler';
+import { DEFAULT_SCHEDULER_OPTIONS } from '../../utils/options/constants';
const CLASSES = {
icon: 'dx-scheduler-form-icon',
diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/components/m_popup.ts
similarity index 97%
rename from packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts
rename to packages/devextreme/js/__internal/scheduler/appointment_popup/components/m_popup.ts
index 4bc0628fe93c..31109e9d83c7 100644
--- a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_popup.ts
+++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/components/m_popup.ts
@@ -12,11 +12,11 @@ import type dxPopup from '@js/ui/popup';
import Popup from '@js/ui/popup/ui.popup';
import { current, isFluent } from '@js/ui/themes';
-import { hide as hideLoading, show as showLoading } from '../m_loading';
-import type { SafeAppointment } from '../types';
-import { AppointmentAdapter } from '../utils/appointment_adapter/appointment_adapter';
-import { getAppointmentGroupValues, getRawAppointmentGroupValues } from '../utils/resource_manager/appointment_groups_utils';
-import type { AppointmentForm } from './m_form';
+import { hide as hideLoading, show as showLoading } from '../../m_loading';
+import type { SafeAppointment } from '../../types';
+import { AppointmentAdapter } from '../../utils/appointment_adapter/appointment_adapter';
+import { getAppointmentGroupValues, getRawAppointmentGroupValues } from '../../utils/resource_manager/appointment_groups_utils';
+import type { AppointmentForm } from './form/m_form';
export const APPOINTMENT_POPUP_CLASS = 'dx-scheduler-appointment-popup';
diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_recurrence_form.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/components/recurrence_form/m_recurrence_form.ts
similarity index 91%
rename from packages/devextreme/js/__internal/scheduler/appointment_popup/m_recurrence_form.ts
rename to packages/devextreme/js/__internal/scheduler/appointment_popup/components/recurrence_form/m_recurrence_form.ts
index 8429a6552c4f..af2a2e972383 100644
--- a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_recurrence_form.ts
+++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/components/recurrence_form/m_recurrence_form.ts
@@ -11,30 +11,10 @@ import type { Properties as NumberBoxProperties } from '@js/ui/number_box';
import type { Properties as RadioGroupProperties } from '@js/ui/radio_group';
import type { Properties as SelectBoxProperties } from '@js/ui/select_box';
import { capitalize } from '@ts/core/utils/capitalize';
+import { RECURRENCE_FORM_CLASSES } from '@ts/scheduler/appointment_popup/core/constants';
-import type Scheduler from '../m_scheduler';
-import { createFormIconTemplate, getStartDateCommonConfig, RecurrenceRule } from './utils';
-
-const CLASSES = {
- groupWithIcon: 'dx-scheduler-form-group-with-icon',
- formIcon: 'dx-scheduler-form-icon',
-
- recurrenceGroup: 'dx-scheduler-form-recurrence-group',
- recurrenceHidden: 'dx-scheduler-form-recurrence-group-hidden',
-
- recurrenceStartDateEditor: 'dx-scheduler-form-recurrence-start-date-editor',
- frequencyEditor: 'dx-scheduler-form-recurrence-frequency-editor',
- byMonthEditor: 'dx-scheduler-form-recurrence-by-month-editor',
- dayOfMonthEditor: 'dx-scheduler-form-day-of-month-editor',
- countEditor: 'dx-scheduler-form-recurrence-count-editor',
- daysOfWeekButtons: 'dx-scheduler-days-of-week-buttons',
- dayOfMonthGroup: 'dx-scheduler-form-day-of-month-group',
- dayOfYearGroup: 'dx-scheduler-form-day-of-year-group',
-
- recurrenceEndGroup: 'dx-scheduler-form-recurrence-end-group',
- recurrenceEndEditors: 'dx-scheduler-form-recurrence-end-editors',
- recurrenceSettingsGroup: 'dx-scheduler-form-recurrence-settings-group',
-};
+import type Scheduler from '../../../m_scheduler';
+import { createFormIconTemplate, getStartDateCommonConfig, RecurrenceRule } from '../../utils';
const frequenciesValues = [
{
@@ -150,7 +130,7 @@ export class RecurrenceForm {
name,
colSpan: 1,
editorType: 'dxNumberBox',
- cssClass: CLASSES.dayOfMonthEditor,
+ cssClass: RECURRENCE_FORM_CLASSES.dayOfMonthEditor,
label: labelVisible
? { text: messageLocalization.format('dxScheduler-recurrenceRepeatOn') }
: { visible: false },
@@ -186,7 +166,7 @@ export class RecurrenceForm {
return {
name: RECURRENCE_GROUP_NAME,
itemType: 'group',
- cssClass: `${CLASSES.recurrenceGroup} ${CLASSES.recurrenceHidden}`,
+ cssClass: `${RECURRENCE_FORM_CLASSES.recurrenceGroup} ${RECURRENCE_FORM_CLASSES.recurrenceHidden}`,
colSpan: 1,
items: [
this.createRecurrenceStartDateGroup(),
@@ -204,12 +184,12 @@ export class RecurrenceForm {
colCountByScreen: {
xs: 2,
},
- cssClass: CLASSES.groupWithIcon,
+ cssClass: RECURRENCE_FORM_CLASSES.groupWithIcon,
items: [
{
name: ICON_NAMES.recurrenceStartDateIcon,
colSpan: 1,
- cssClass: CLASSES.formIcon,
+ cssClass: RECURRENCE_FORM_CLASSES.formIcon,
template: createFormIconTemplate('clock'),
},
extend(
@@ -217,7 +197,7 @@ export class RecurrenceForm {
getStartDateCommonConfig(this.scheduler.getFirstDayOfWeek()),
{
name: EDITOR_NAMES.recurrenceStartDateEditor,
- cssClass: CLASSES.recurrenceStartDateEditor,
+ cssClass: RECURRENCE_FORM_CLASSES.recurrenceStartDateEditor,
label: {
text: messageLocalization.format('dxScheduler-editorLabelStartDate'),
},
@@ -239,7 +219,7 @@ export class RecurrenceForm {
return {
itemType: 'group',
name: GROUP_NAMES.recurrenceRuleGroup,
- cssClass: `${CLASSES.recurrenceSettingsGroup} ${CLASSES.groupWithIcon}`,
+ cssClass: `${RECURRENCE_FORM_CLASSES.recurrenceSettingsGroup} ${RECURRENCE_FORM_CLASSES.groupWithIcon}`,
colCount: 2,
colCountByScreen: {
xs: 2,
@@ -248,7 +228,7 @@ export class RecurrenceForm {
{
name: ICON_NAMES.recurrenceRuleIcon,
colSpan: 1,
- cssClass: CLASSES.formIcon,
+ cssClass: RECURRENCE_FORM_CLASSES.formIcon,
template: createFormIconTemplate('repeat'),
},
{
@@ -308,7 +288,7 @@ export class RecurrenceForm {
{
itemType: 'simple',
name: EDITOR_NAMES.recurrencePeriodEditor,
- cssClass: CLASSES.frequencyEditor,
+ cssClass: RECURRENCE_FORM_CLASSES.frequencyEditor,
colSpan: 1,
editorType: 'dxSelectBox',
label: {
@@ -357,7 +337,7 @@ export class RecurrenceForm {
visible: false,
},
template: (): dxElementWrapper => {
- const $container = $('
').addClass(CLASSES.daysOfWeekButtons);
+ const $container = $('
').addClass(RECURRENCE_FORM_CLASSES.daysOfWeekButtons);
this.weekDayItems.forEach((item) => {
const buttonContainer = $('
').appendTo($container);
@@ -400,7 +380,7 @@ export class RecurrenceForm {
private createDayOfMonthGroup(): SimpleItem {
return {
...this.createByMonthDayNumberBoxItem(EDITOR_NAMES.recurrenceDayOfMonthEditor, true),
- cssClass: `${CLASSES.dayOfMonthEditor} ${CLASSES.dayOfMonthGroup}`,
+ cssClass: `${RECURRENCE_FORM_CLASSES.dayOfMonthEditor} ${RECURRENCE_FORM_CLASSES.dayOfMonthGroup}`,
};
}
@@ -408,7 +388,7 @@ export class RecurrenceForm {
return {
itemType: 'group',
name: GROUP_NAMES.recurrenceDayOfYearGroup,
- cssClass: CLASSES.dayOfYearGroup,
+ cssClass: RECURRENCE_FORM_CLASSES.dayOfYearGroup,
colCount: 2,
colCountByScreen: {
xs: 2,
@@ -418,7 +398,7 @@ export class RecurrenceForm {
itemType: 'simple',
name: EDITOR_NAMES.recurrenceDayOfYearMonthEditor,
colSpan: 1,
- cssClass: CLASSES.byMonthEditor,
+ cssClass: RECURRENCE_FORM_CLASSES.byMonthEditor,
editorType: 'dxSelectBox',
label: {
text: messageLocalization.format('dxScheduler-recurrenceRepeatEvery'),
@@ -448,12 +428,12 @@ export class RecurrenceForm {
colCountByScreen: {
xs: 2,
},
- cssClass: `${CLASSES.groupWithIcon} ${CLASSES.recurrenceEndGroup}`,
+ cssClass: `${RECURRENCE_FORM_CLASSES.groupWithIcon} ${RECURRENCE_FORM_CLASSES.recurrenceEndGroup}`,
items: [
{
name: ICON_NAMES.recurrenceEndIcon,
colSpan: 1,
- cssClass: CLASSES.formIcon,
+ cssClass: RECURRENCE_FORM_CLASSES.formIcon,
template: createFormIconTemplate('description'),
},
{
@@ -506,7 +486,7 @@ export class RecurrenceForm {
return {
itemType: 'group',
name: GROUP_NAMES.recurrenceEndEditorsGroup,
- cssClass: CLASSES.recurrenceEndEditors,
+ cssClass: RECURRENCE_FORM_CLASSES.recurrenceEndEditors,
colSpan: 1,
items: [
{
@@ -542,7 +522,7 @@ export class RecurrenceForm {
{
itemType: 'simple',
name: EDITOR_NAMES.recurrenceEndCountEditor,
- cssClass: CLASSES.countEditor,
+ cssClass: RECURRENCE_FORM_CLASSES.countEditor,
label: {
visible: false,
},
diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/core/constants.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/core/constants.ts
new file mode 100644
index 000000000000..d772105f4e86
--- /dev/null
+++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/core/constants.ts
@@ -0,0 +1,20 @@
+export const RECURRENCE_FORM_CLASSES = {
+ groupWithIcon: 'dx-scheduler-form-group-with-icon',
+ formIcon: 'dx-scheduler-form-icon',
+
+ recurrenceGroup: 'dx-scheduler-form-recurrence-group',
+ recurrenceHidden: 'dx-scheduler-form-recurrence-group-hidden',
+
+ recurrenceStartDateEditor: 'dx-scheduler-form-recurrence-start-date-editor',
+ frequencyEditor: 'dx-scheduler-form-recurrence-frequency-editor',
+ byMonthEditor: 'dx-scheduler-form-recurrence-by-month-editor',
+ dayOfMonthEditor: 'dx-scheduler-form-day-of-month-editor',
+ countEditor: 'dx-scheduler-form-recurrence-count-editor',
+ daysOfWeekButtons: 'dx-scheduler-days-of-week-buttons',
+ dayOfMonthGroup: 'dx-scheduler-form-day-of-month-group',
+ dayOfYearGroup: 'dx-scheduler-form-day-of-year-group',
+
+ recurrenceEndGroup: 'dx-scheduler-form-recurrence-end-group',
+ recurrenceEndEditors: 'dx-scheduler-form-recurrence-end-editors',
+ recurrenceSettingsGroup: 'dx-scheduler-form-recurrence-settings-group',
+};
diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/customize_form_items.test.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/factories/customize_form_items/customize_form_items.test.ts
similarity index 100%
rename from packages/devextreme/js/__internal/scheduler/appointment_popup/customize_form_items.test.ts
rename to packages/devextreme/js/__internal/scheduler/appointment_popup/factories/customize_form_items/customize_form_items.test.ts
diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_customize_form_items.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/factories/customize_form_items/m_customize_form_items.ts
similarity index 100%
rename from packages/devextreme/js/__internal/scheduler/appointment_popup/m_customize_form_items.ts
rename to packages/devextreme/js/__internal/scheduler/appointment_popup/factories/customize_form_items/m_customize_form_items.ts
diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_legacy_form.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/legacy/m_legacy_form.ts
similarity index 99%
rename from packages/devextreme/js/__internal/scheduler/appointment_popup/m_legacy_form.ts
rename to packages/devextreme/js/__internal/scheduler/appointment_popup/legacy/m_legacy_form.ts
index ab4d026edc26..358772b11e3a 100644
--- a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_legacy_form.ts
+++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/legacy/m_legacy_form.ts
@@ -1,4 +1,4 @@
-import '../m_recurrence_editor';
+import '../../m_recurrence_editor';
import '@js/ui/text_area';
import '@js/ui/tag_box';
import '@js/ui/switch';
@@ -14,7 +14,7 @@ import { extend } from '@js/core/utils/extend';
import Form from '@js/ui/form';
import { current, isFluent } from '@js/ui/themes';
-import timeZoneUtils from '../m_utils_time_zone';
+import timeZoneUtils from '../../m_utils_time_zone';
const SCREEN_SIZE_OF_SINGLE_COLUMN = 600;
diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_legacy_popup.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/legacy/m_legacy_popup.ts
similarity index 97%
rename from packages/devextreme/js/__internal/scheduler/appointment_popup/m_legacy_popup.ts
rename to packages/devextreme/js/__internal/scheduler/appointment_popup/legacy/m_legacy_popup.ts
index 60ead1a03da7..ea8b31d52389 100644
--- a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_legacy_popup.ts
+++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/legacy/m_legacy_popup.ts
@@ -9,11 +9,11 @@ import {
getMaxWidth,
getPopupToolbarItems,
isPopupFullScreenNeeded,
-} from '@ts/scheduler/r1/appointment_popup/index';
+} from '@ts/scheduler/r1/appointment_popup';
-import { hide as hideLoading, show as showLoading } from '../m_loading';
-import { AppointmentAdapter } from '../utils/appointment_adapter/appointment_adapter';
-import { getAppointmentGroupValues, getRawAppointmentGroupValues } from '../utils/resource_manager/appointment_groups_utils';
+import { hide as hideLoading, show as showLoading } from '../../m_loading';
+import { AppointmentAdapter } from '../../utils/appointment_adapter/appointment_adapter';
+import { getAppointmentGroupValues, getRawAppointmentGroupValues } from '../../utils/resource_manager/appointment_groups_utils';
const toMs = dateUtils.dateToMilliseconds;
diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/utils.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/utils/index.ts
similarity index 93%
rename from packages/devextreme/js/__internal/scheduler/appointment_popup/utils.ts
rename to packages/devextreme/js/__internal/scheduler/appointment_popup/utils/index.ts
index 1fc0c27db421..a1bb5798dfee 100644
--- a/packages/devextreme/js/__internal/scheduler/appointment_popup/utils.ts
+++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/utils/index.ts
@@ -6,9 +6,9 @@ import { isDefined } from '@js/core/utils/type';
import type { Properties as DateBoxProperties } from '@js/ui/date_box';
import type { SimpleItem } from '@js/ui/form';
-import { getRecurrenceString, parseRecurrenceRule } from '../recurrence/base';
-import { daysFromByDayRule } from '../recurrence/days_from_by_day_rule';
-import type { Rule } from '../recurrence/types';
+import { getRecurrenceString, parseRecurrenceRule } from '../../recurrence/base';
+import { daysFromByDayRule } from '../../recurrence/days_from_by_day_rule';
+import type { Rule } from '../../recurrence/types';
export const createFormIconTemplate = (iconName: string): () => dxElementWrapper => {
if (iconName.length === 0) {
diff --git a/packages/devextreme/js/__internal/scheduler/header/components/calendar/m_calendar.ts b/packages/devextreme/js/__internal/scheduler/header/components/calendar/m_calendar.ts
new file mode 100644
index 000000000000..749f01cb95c7
--- /dev/null
+++ b/packages/devextreme/js/__internal/scheduler/header/components/calendar/m_calendar.ts
@@ -0,0 +1,139 @@
+import registerComponent from '@js/core/component_registrator';
+import devices from '@js/core/devices';
+import type { dxElementWrapper } from '@js/core/renderer';
+import $ from '@js/core/renderer';
+import type { DateLike } from '@js/ui/calendar';
+import Popover from '@js/ui/popover/ui.popover';
+import Popup from '@js/ui/popup/ui.popup';
+import Widget from '@ts/core/widget/widget';
+import type { KeyboardKeyDownEvent } from '@ts/events/core/m_keyboard_processor';
+import type { CalendarProperties } from '@ts/ui/calendar/calendar';
+import Calendar from '@ts/ui/calendar/calendar';
+import Scrollable from '@ts/ui/scroll_view/scrollable';
+
+import type { HeaderCalendarOptions } from '../../core/types';
+
+const CALENDAR_CLASS = 'dx-scheduler-navigator-calendar';
+const CALENDAR_POPOVER_CLASS = 'dx-scheduler-navigator-calendar-popover';
+
+export default class SchedulerCalendar extends Widget
{
+ _overlay?: Popup | Popover;
+
+ _calendar?: Calendar;
+
+ async show(target: HTMLElement): Promise {
+ if (!SchedulerCalendar._isMobileLayout()) {
+ this._overlay?.option('target', target);
+ }
+
+ await this._overlay?.show();
+ }
+
+ async hide(): Promise {
+ await this._overlay?.hide();
+ }
+
+ _keyboardHandler(opts: KeyboardKeyDownEvent): boolean {
+ this._calendar?._keyboardHandler(opts);
+ return true;
+ }
+
+ _init(): void {
+ super._init();
+ this.$element();
+ }
+
+ _render(): void {
+ super._render();
+ this._renderOverlay();
+ }
+
+ _renderOverlay(): void {
+ this.$element().addClass(CALENDAR_POPOVER_CLASS);
+
+ const isMobileLayout = SchedulerCalendar._isMobileLayout();
+
+ const overlayConfig = {
+ contentTemplate: (): dxElementWrapper => this._createOverlayContent(),
+ onShown: (): void => {
+ this._calendar?.focus();
+ },
+ defaultOptionsRules: [
+ {
+ device: (): boolean => isMobileLayout,
+ options: {
+ fullScreen: true,
+ showCloseButton: false,
+ toolbarItems: [{ shortcut: 'cancel' }],
+ _ignorePreventScrollEventsDeprecation: true,
+ preventScrollEvents: false,
+ enableBodyScroll: false,
+ },
+ },
+ ],
+ };
+
+ if (isMobileLayout) {
+ this._overlay = this._createComponent(this.$element(), Popup, overlayConfig);
+ } else {
+ this._overlay = this._createComponent(this.$element(), Popover, overlayConfig);
+ }
+ }
+
+ _createOverlayContent(): dxElementWrapper {
+ const result = $('').addClass(CALENDAR_CLASS);
+ this._calendar = this._createComponent(result, Calendar, this._getCalendarOptions());
+
+ if (SchedulerCalendar._isMobileLayout()) {
+ const scrollable = this._createScrollable(result);
+ return scrollable.$element();
+ }
+
+ return result;
+ }
+
+ _createScrollable(content: dxElementWrapper): Scrollable {
+ const result = this._createComponent('
', Scrollable, {
+ height: 'auto',
+ direction: 'both',
+ });
+ result.$content().append(content);
+
+ return result;
+ }
+
+ _optionChanged(
+ { name, value } : { name: string; value: DateLike | DateLike[] },
+ ): void {
+ switch (name) {
+ case 'value':
+ this._calendar?.option('value', value);
+ break;
+ default:
+ break;
+ }
+ }
+
+ _getCalendarOptions(): CalendarProperties {
+ const {
+ value, min, max, firstDayOfWeek, focusStateEnabled, tabIndex, onValueChanged,
+ } = this.option();
+ return {
+ value,
+ min,
+ max,
+ firstDayOfWeek,
+ focusStateEnabled,
+ tabIndex,
+ onValueChanged,
+ // @ts-expect-error skipFocusCheck is an internal Calendar property
+ skipFocusCheck: true,
+ };
+ }
+
+ static _isMobileLayout(): boolean {
+ return !devices.current().generic;
+ }
+}
+
+registerComponent('dxSchedulerCalendarPopup', SchedulerCalendar);
diff --git a/packages/devextreme/js/__internal/scheduler/header/m_header.ts b/packages/devextreme/js/__internal/scheduler/header/components/m_header.ts
similarity index 52%
rename from packages/devextreme/js/__internal/scheduler/header/m_header.ts
rename to packages/devextreme/js/__internal/scheduler/header/components/m_header.ts
index 049eb0e857f2..6d7a5d017a43 100644
--- a/packages/devextreme/js/__internal/scheduler/header/m_header.ts
+++ b/packages/devextreme/js/__internal/scheduler/header/components/m_header.ts
@@ -1,4 +1,3 @@
-import '@js/ui/button_group';
import '@js/ui/drop_down_button';
import registerComponent from '@js/core/component_registrator';
@@ -8,27 +7,33 @@ import $ from '@js/core/renderer';
import { getPathParts } from '@js/core/utils/data';
import dateUtils from '@js/core/utils/date';
import { extend } from '@js/core/utils/extend';
+import type { ItemClickEvent } from '@js/ui/button_group';
+import type { DateNavigatorTextInfo, ToolbarItem } from '@js/ui/scheduler';
import Toolbar from '@js/ui/toolbar';
-import Widget from '@js/ui/widget/ui.widget';
-import type { NormalizedView } from '@ts/scheduler/utils/options/types';
-
-import type { Direction } from './constants';
-import SchedulerCalendar from './m_calendar';
+import type { OptionChanged } from '@ts/core/widget/types';
+import Widget from '@ts/core/widget/widget';
+import type { NormalizedView, SafeSchedulerOptions } from '@ts/scheduler/utils/options/types';
+
+import type { Direction } from '../core/constants';
+import type {
+ EventMapHandler,
+ HeaderOptions, IntervalOptions,
+} from '../core/types';
import {
getDateNavigator,
-} from './m_date_navigator';
+ getTodayButtonOptions,
+} from '../factories/date_navigator/m_date_navigator';
+import {
+ getDropDownViewSwitcher,
+ getTabViewSwitcher,
+} from '../factories/view_switcher/m_view_switcher';
import {
getCaption,
getNextIntervalDate,
getStep,
nextWeek,
-} from './m_utils';
-import {
- getDropDownViewSwitcher,
- getTabViewSwitcher,
-} from './m_view_switcher';
-import { getTodayButtonOptions } from './today';
-import type { HeaderOptions, IntervalOptions } from './types';
+} from '../utils';
+import SchedulerCalendar from './calendar/m_calendar';
const CLASSES = {
component: 'dx-scheduler-header',
@@ -41,20 +46,19 @@ const ITEM_NAMES = {
};
export class SchedulerHeader extends Widget
{
- eventMap: any;
+ eventMap!: Map;
- _toolbar!: Toolbar;
+ _toolbar?: Toolbar;
- _calendar: any;
+ _calendar?: SchedulerCalendar;
- get captionText() {
+ get captionText(): string {
return this._getCaption().text;
}
getIntervalOptions(date: Date): IntervalOptions {
- const currentView = this.option('currentView');
+ const { currentView, firstDayOfWeek } = this.option();
const step = getStep(currentView.type);
- const firstDayOfWeek = this.option('firstDayOfWeek');
return {
date,
@@ -65,44 +69,43 @@ export class SchedulerHeader extends Widget {
};
}
- _getDefaultOptions() {
- // @ts-expect-error
+ _getDefaultOptions(): HeaderOptions & { _useShortDateFormat: boolean } {
return extend(super._getDefaultOptions(), {
_useShortDateFormat: !devices.real().generic || devices.isSimulator(),
- });
+ }) as HeaderOptions & { _useShortDateFormat: boolean };
}
- _createEventMap() {
- this.eventMap = new Map(
- [
- ['currentView', []],
- ['views', []],
- ['currentDate', [this._getCalendarOptionUpdater('value')]],
- ['min', [this._getCalendarOptionUpdater('min')]],
- ['max', [this._getCalendarOptionUpdater('max')]],
- ['tabIndex', [this.repaint.bind(this)]],
- ['focusStateEnabled', [this.repaint.bind(this)]],
- ['useDropDownViewSwitcher', [this.repaint.bind(this)]],
- ['indicatorTime', []],
- ],
- );
+ _createEventMap(): void {
+ this.eventMap = new Map([
+ ['currentView', []],
+ ['views', []],
+ ['currentDate', [this._getCalendarOptionUpdater('value')]],
+ ['min', [this._getCalendarOptionUpdater('min')]],
+ ['max', [this._getCalendarOptionUpdater('max')]],
+ ['tabIndex', [this.repaint.bind(this)]],
+ ['focusStateEnabled', [this.repaint.bind(this)]],
+ ['useDropDownViewSwitcher', [this.repaint.bind(this)]],
+ ['indicatorTime', []],
+ ] as [string, EventMapHandler[]][]);
}
- _addEvent(name, event) {
+ _addEvent(name: string, event: EventMapHandler): void {
if (!this.eventMap.has(name)) {
this.eventMap.set(name, []);
}
const events = this.eventMap.get(name);
- this.eventMap.set(name, [...events, event]);
+ if (events) {
+ this.eventMap.set(name, [...events, event]);
+ }
}
- _optionChanged(args) {
+ _optionChanged(args: OptionChanged): void {
const { name, value } = args;
if (this.eventMap.has(name)) {
const events = this.eventMap.get(name);
- events.forEach((event) => {
+ events?.forEach((event) => {
event(value);
});
}
@@ -119,32 +122,27 @@ export class SchedulerHeader extends Widget {
this.repaint();
break;
case fullName === 'toolbar.items':
- this._toolbar.option(
+ this._toolbar?.option(
'items',
- (value as []).map((item) => this._parseItem(item)),
+ (value as []).map((item: ToolbarItem) => this._parseItem(item)),
);
break;
case parts[1] === 'items' && parts.length === 3:
- // `toolbar.items[i]` case
- this._toolbar.option(optionName, this._parseItem(value));
+ this._toolbar?.option(optionName, this._parseItem(value as ToolbarItem));
break;
default:
- // `toolbar.prop` case
- // `toolbar.items[i].prop` case
- this._toolbar.option(optionName, value);
+ this._toolbar?.option(optionName, value);
}
}
- _init() {
- // @ts-expect-error
+ _init(): void {
super._init();
this._createEventMap();
this.$element().addClass(CLASSES.component);
}
- _render() {
- // @ts-expect-error
+ _render(): void {
super._render();
this._createEventMap();
@@ -152,20 +150,19 @@ export class SchedulerHeader extends Widget {
this._toggleVisibility();
}
- _renderToolbar() {
+ _renderToolbar(): void {
const config = this._createToolbarConfig();
const toolbarElement = $('');
toolbarElement.appendTo(this.$element());
- // @ts-expect-error
this._toolbar = this._createComponent(toolbarElement, Toolbar, config);
}
_toggleVisibility(): void {
- const toolbarOptions = this.option('toolbar') as any;
- const isHeaderShown = toolbarOptions.visible
- || (toolbarOptions.visible === undefined && toolbarOptions.items.length);
+ const { toolbar } = this.option();
+ const isHeaderShown = toolbar.visible
+ ?? (toolbar.visible === undefined && toolbar.items.length);
if (isHeaderShown) {
this.$element().removeClass(CLASSES.invisible);
@@ -174,17 +171,17 @@ export class SchedulerHeader extends Widget
{
}
}
- _createToolbarConfig() {
- const options = this.option('toolbar');
- const parsedItems = options.items.map((element) => this._parseItem(element));
+ _createToolbarConfig(): SafeSchedulerOptions['toolbar'] {
+ const { toolbar } = this.option();
+ const parsedItems = toolbar.items.map((element) => this._parseItem(element));
return {
- ...options,
+ ...toolbar,
items: parsedItems,
};
}
- _parseItem(item) {
+ _parseItem(item: ToolbarItem | string): ToolbarItem {
const itemName = typeof item === 'string' ? item : item.name;
const itemOptions = typeof item === 'string' ? {} : item;
@@ -205,74 +202,82 @@ export class SchedulerHeader extends Widget {
}
}
- return extend(true, {}, item);
+ return extend(true, {}, item) as ToolbarItem;
}
- _callEvent(event, arg) {
+ _callEvent(event: string, arg: unknown): void {
if (this.eventMap.has(event)) {
const events = this.eventMap.get(event);
- events.forEach((event) => event(arg));
+ if (events) {
+ events.forEach((eventMapHandler) => eventMapHandler(arg));
+ }
}
}
- _updateCurrentView(view: Required) {
- this.option('onCurrentViewChange')(view.name);
+ _updateCurrentView(view: Required): void {
+ const { onCurrentViewChange } = this.option();
+ onCurrentViewChange(view.name);
}
- _updateCalendarValueAndCurrentDate(date) {
+ _updateCalendarValueAndCurrentDate(date: Date): void {
this._updateCurrentDate(date);
- this._calendar.option('value', date);
+ this._calendar?.option('value', date);
}
- _updateCurrentDate(date) {
- this.option('onCurrentDateChange')(date);
+ _updateCurrentDate(date: Date): void {
+ const { onCurrentDateChange } = this.option();
+ onCurrentDateChange(date);
this._callEvent('currentDate', date);
}
- _renderCalendar() {
- // @ts-expect-error
+ _renderCalendar(): void {
+ const {
+ currentDate, min, max, firstDayOfWeek, focusStateEnabled, tabIndex,
+ } = this.option();
this._calendar = this._createComponent('', SchedulerCalendar, {
- value: this.option('currentDate'),
- min: this.option('min'),
- max: this.option('max'),
- firstDayOfWeek: this.option('firstDayOfWeek'),
- focusStateEnabled: this.option('focusStateEnabled'),
- tabIndex: this.option('tabIndex'),
- onValueChanged: (e) => {
+ value: currentDate,
+ min,
+ max,
+ firstDayOfWeek,
+ focusStateEnabled,
+ tabIndex,
+ onValueChanged: async (e) => {
this._updateCurrentDate(e.value);
- this._calendar.hide();
+ await this._calendar?.hide();
},
});
this._calendar.$element().appendTo(this.$element());
}
- _getCalendarOptionUpdater(name) {
- return (value) => {
+ _getCalendarOptionUpdater(name: string) {
+ return (value: unknown): void => {
if (this._calendar) {
this._calendar.option(name, value);
}
};
}
- _getNextDate(direction: Direction, initialDate?: Date) {
- const date = initialDate ?? this.option('currentDate');
+ _getNextDate(direction: Direction, initialDate?: Date): Date {
+ const { currentDate } = this.option();
+ const date = initialDate ?? currentDate;
const options = this.getIntervalOptions(date);
return getNextIntervalDate(options, direction);
}
- _getDisplayedDate() {
- const startViewDate = new Date(this.option('startViewDate'));
- const isMonth = this.option('currentView')?.type === 'month';
+ _getDisplayedDate(): Date {
+ const { startViewDate, currentView } = this.option();
+ const isMonth = currentView.type === 'month';
return isMonth ? nextWeek(startViewDate) : startViewDate;
}
- _getCaptionOptions() {
- let date = this.option('currentDate');
+ _getCaptionOptions(): IntervalOptions {
+ const { currentDate, startViewDate } = this.option();
+ let date = currentDate;
- if (this.option('startViewDate')) {
+ if (startViewDate) {
date = this._getDisplayedDate();
}
@@ -281,28 +286,27 @@ export class SchedulerHeader extends Widget
{
return this.getIntervalOptions(date);
}
- _getCaption() {
+ _getCaption(): DateNavigatorTextInfo {
+ const { customizeDateNavigatorText } = this.option();
const options = this._getCaptionOptions();
- const customizationFunction = this.option('customizeDateNavigatorText');
const useShortDateFormat = this.option('_useShortDateFormat');
- return getCaption(options, Boolean(useShortDateFormat), customizationFunction);
+ return getCaption(options, Boolean(useShortDateFormat), customizeDateNavigatorText);
}
- _updateDateByDirection(direction: Direction) {
+ _updateDateByDirection(direction: Direction): void {
const date = this._getNextDate(direction);
this._updateCalendarValueAndCurrentDate(date);
}
- _showCalendar(e) {
- this._calendar.show(e.element);
+ async _showCalendar(e: ItemClickEvent): Promise {
+ await this._calendar?.show(e.element);
}
- _hideCalendar() {
- this._calendar.hide();
+ async _hideCalendar(): Promise {
+ await this._calendar?.hide();
}
}
-// @ts-expect-error
registerComponent('dxSchedulerHeader', SchedulerHeader);
diff --git a/packages/devextreme/js/__internal/scheduler/header/constants.ts b/packages/devextreme/js/__internal/scheduler/header/core/constants.ts
similarity index 100%
rename from packages/devextreme/js/__internal/scheduler/header/constants.ts
rename to packages/devextreme/js/__internal/scheduler/header/core/constants.ts
diff --git a/packages/devextreme/js/__internal/scheduler/header/types.ts b/packages/devextreme/js/__internal/scheduler/header/core/types.ts
similarity index 58%
rename from packages/devextreme/js/__internal/scheduler/header/types.ts
rename to packages/devextreme/js/__internal/scheduler/header/core/types.ts
index 98c2ca07eab7..a478fd11e277 100644
--- a/packages/devextreme/js/__internal/scheduler/header/types.ts
+++ b/packages/devextreme/js/__internal/scheduler/header/core/types.ts
@@ -1,4 +1,7 @@
-import type { NormalizedView, SafeSchedulerOptions } from '../utils/options/types';
+import type { FirstDayOfWeek } from '@js/common';
+import type { ValueChangedEvent } from '@js/ui/calendar';
+
+import type { NormalizedView, SafeSchedulerOptions } from '../../utils/options/types';
export interface HeaderOptions {
currentView: NormalizedView;
@@ -11,7 +14,7 @@ export interface HeaderOptions {
tabIndex?: number;
focusStateEnabled?: boolean;
useDropDownViewSwitcher: boolean;
- firstDayOfWeek?: number;
+ firstDayOfWeek?: FirstDayOfWeek;
toolbar: SafeSchedulerOptions['toolbar'];
onCurrentViewChange: (name: string) => void;
onCurrentDateChange: (date: Date) => void;
@@ -27,3 +30,15 @@ export interface IntervalOptions {
intervalCount: number;
agendaDuration?: number;
}
+
+export interface HeaderCalendarOptions {
+ value: Date;
+ min?: Date;
+ max?: Date;
+ firstDayOfWeek?: FirstDayOfWeek;
+ focusStateEnabled?: boolean;
+ tabIndex?: number;
+ onValueChanged?: (e: ValueChangedEvent) => void;
+}
+
+export type EventMapHandler = (value: unknown) => void;
diff --git a/packages/devextreme/js/__internal/scheduler/header/m_date_navigator.test.ts b/packages/devextreme/js/__internal/scheduler/header/factories/date_navigator/m_date_navigator.test.ts
similarity index 88%
rename from packages/devextreme/js/__internal/scheduler/header/m_date_navigator.test.ts
rename to packages/devextreme/js/__internal/scheduler/header/factories/date_navigator/m_date_navigator.test.ts
index 12748972021a..6ce13ab04888 100644
--- a/packages/devextreme/js/__internal/scheduler/header/m_date_navigator.test.ts
+++ b/packages/devextreme/js/__internal/scheduler/header/factories/date_navigator/m_date_navigator.test.ts
@@ -1,14 +1,16 @@
import {
describe, expect, it, jest,
} from '@jest/globals';
+import type { ToolbarItem } from '@js/ui/scheduler';
+import type { SchedulerHeader } from '../../components/m_header';
import {
CLASS, DEFAULT_ITEMS, getDateNavigator, ITEMS_NAME,
} from './m_date_navigator';
describe('getDateNavigator', () => {
it('should return default options in case of item is empty', () => {
- expect(getDateNavigator({} as any, {})).toEqual({
+ expect(getDateNavigator({} as SchedulerHeader, {})).toEqual({
location: 'before',
name: 'dateNavigator',
widget: 'dxButtonGroup',
@@ -26,13 +28,13 @@ describe('getDateNavigator', () => {
});
});
it('should return replace items in correct order with custom options', () => {
- expect(getDateNavigator({} as any, {
+ expect(getDateNavigator({} as SchedulerHeader, {
customField: 'customField',
options: {
customOption: 'customOption',
items: ['dateInterval', 'next', { key: 'customButton' }],
},
- } as any)).toEqual({
+ } as ToolbarItem)).toEqual({
location: 'before',
name: 'dateNavigator',
widget: 'dxButtonGroup',
@@ -54,7 +56,7 @@ describe('getDateNavigator', () => {
it('should handle default and custom click callback', () => {
const customClick = jest.fn();
const event = { itemData: { clickHandler: jest.fn() } };
- const config = getDateNavigator({} as any, {
+ const config = getDateNavigator({} as SchedulerHeader, {
options: { onItemClick: customClick },
});
diff --git a/packages/devextreme/js/__internal/scheduler/header/m_date_navigator.ts b/packages/devextreme/js/__internal/scheduler/header/factories/date_navigator/m_date_navigator.ts
similarity index 84%
rename from packages/devextreme/js/__internal/scheduler/header/m_date_navigator.ts
rename to packages/devextreme/js/__internal/scheduler/header/factories/date_navigator/m_date_navigator.ts
index 7e9405f55a1f..f6e0979f4e8d 100644
--- a/packages/devextreme/js/__internal/scheduler/header/m_date_navigator.ts
+++ b/packages/devextreme/js/__internal/scheduler/header/factories/date_navigator/m_date_navigator.ts
@@ -2,13 +2,13 @@ import messageLocalization from '@js/common/core/localization/message';
import dateUtils from '@js/core/utils/date';
import type { ContentReadyEvent } from '@js/ui/button';
import type { Item as ButtonGroupItem, ItemClickEvent, Properties as ButtonGroupOptions } from '@js/ui/button_group';
-import { isMaterialBased } from '@js/ui/themes';
+import { current, isMaterialBased } from '@js/ui/themes';
import type { Item as ToolbarItem } from '@js/ui/toolbar';
import { dateUtilsTs } from '@ts/core/utils/date';
import { extend } from '@ts/core/utils/m_extend';
-import type { SchedulerHeader } from '@ts/scheduler/header/m_header';
+import type { SchedulerHeader } from '@ts/scheduler/header/components/m_header';
-import { Direction } from './constants';
+import { Direction } from '../../core/constants';
export const CLASS = {
container: 'dx-scheduler-navigator',
@@ -31,7 +31,7 @@ const { trimTime } = dateUtils;
interface DateNavigatorItem extends ButtonGroupItem {
key: string;
- clickHandler: (event: ItemClickEvent) => void;
+ clickHandler: (event: ItemClickEvent) => Promise | void;
onContentReady: (event: ContentReadyEvent) => void;
}
@@ -154,9 +154,28 @@ const getNextButtonOptions = (header: SchedulerHeader): DateNavigatorItem => {
};
};
+export const getTodayButtonOptions = (
+ header: SchedulerHeader,
+ item: ToolbarItem,
+): ToolbarItem => extend(true, {}, {
+ location: 'before',
+ locateInMenu: 'auto',
+ widget: 'dxButton',
+ cssClass: 'dx-scheduler-today',
+ options: {
+ text: messageLocalization.format('dxScheduler-navigationToday'),
+ icon: 'today',
+ stylingMode: 'outlined',
+ type: 'normal',
+ onClick() {
+ const { indicatorTime } = header.option();
+ header._updateCurrentDate(indicatorTime ?? new Date());
+ },
+ },
+}, item) as ToolbarItem;
+
export const getDateNavigator = (header: SchedulerHeader, item: ToolbarItem): ToolbarItem => {
- // @ts-expect-error current theme used
- const stylingMode = isMaterialBased() ? 'text' : 'contained';
+ const stylingMode = isMaterialBased(current()) ? 'text' : 'contained';
const config: ToolbarItem = extend(true, {}, {
location: 'before',
name: 'dateNavigator',
@@ -170,7 +189,8 @@ export const getDateNavigator = (header: SchedulerHeader, item: ToolbarItem): To
const options = config.options as ButtonGroupOptions;
const { onItemClick } = options;
- options.items = (options.items ?? DEFAULT_ITEMS).map((groupItem) => {
+ const items = (options.items ?? DEFAULT_ITEMS);
+ options.items = items.map((groupItem: ButtonGroupItem | string) => {
switch (groupItem) {
case ITEMS_NAME.previousButton:
return getPreviousButtonOptions(header);
@@ -179,7 +199,7 @@ export const getDateNavigator = (header: SchedulerHeader, item: ToolbarItem): To
case ITEMS_NAME.calendarButton:
return getCalendarButtonOptions(header);
default:
- return groupItem;
+ return groupItem as ButtonGroupItem;
}
});
options.onItemClick = (event): void => {
diff --git a/packages/devextreme/js/__internal/scheduler/header/m_view_switcher.integration.test.ts b/packages/devextreme/js/__internal/scheduler/header/factories/view_switcher/m_view_switcher.integration.test.ts
similarity index 98%
rename from packages/devextreme/js/__internal/scheduler/header/m_view_switcher.integration.test.ts
rename to packages/devextreme/js/__internal/scheduler/header/factories/view_switcher/m_view_switcher.integration.test.ts
index 91309438ba91..e9f3fdb4d68b 100644
--- a/packages/devextreme/js/__internal/scheduler/header/m_view_switcher.integration.test.ts
+++ b/packages/devextreme/js/__internal/scheduler/header/factories/view_switcher/m_view_switcher.integration.test.ts
@@ -7,8 +7,8 @@ import type { dxElementWrapper } from '@js/core/renderer';
import $ from '@js/core/renderer';
import { loadMessages, locale } from '@js/localization';
-import type { Properties as SchedulerProperties } from '../../../ui/scheduler';
-import Scheduler from '../../../ui/scheduler';
+import type { Properties as SchedulerProperties } from '../../../../../ui/scheduler';
+import Scheduler from '../../../../../ui/scheduler';
const SCHEDULER_CONTAINER_ID = 'schedulerContainer';
diff --git a/packages/devextreme/js/__internal/scheduler/header/m_view_switcher.ts b/packages/devextreme/js/__internal/scheduler/header/factories/view_switcher/m_view_switcher.ts
similarity index 69%
rename from packages/devextreme/js/__internal/scheduler/header/m_view_switcher.ts
rename to packages/devextreme/js/__internal/scheduler/header/factories/view_switcher/m_view_switcher.ts
index 822499f11681..c9956ab243e7 100644
--- a/packages/devextreme/js/__internal/scheduler/header/m_view_switcher.ts
+++ b/packages/devextreme/js/__internal/scheduler/header/factories/view_switcher/m_view_switcher.ts
@@ -1,12 +1,12 @@
-import { isFluent } from '@js/ui/themes';
+import { current, isFluent } from '@js/ui/themes';
import type { Item as ToolbarItem } from '@js/ui/toolbar';
-import type { NormalizedView } from '../utils/options/types';
-import type { SchedulerHeader } from './m_header';
+import type { NormalizedView } from '../../../utils/options/types';
+import type { SchedulerHeader } from '../../components/m_header';
import {
formatViews,
getViewName,
-} from './m_utils';
+} from '../../utils';
const ClASS = {
container: 'dx-scheduler-view-switcher',
@@ -14,25 +14,32 @@ const ClASS = {
dropDownButtonContent: 'dx-scheduler-view-switcher-dropdown-button-content',
};
-const getViewsAndSelectedView = (header: SchedulerHeader) => {
- const views = formatViews(header.option('views'));
- const selectedView = getViewName(header.option('currentView'));
- const isSelectedViewInViews = views.some((view) => view.name === selectedView);
+const getViewsAndSelectedView = (header: SchedulerHeader):
+{
+ selectedView: string | undefined;
+ views: NormalizedView[];
+} => {
+ const { views, currentView } = header.option();
+ const formattedViews = formatViews(views);
+ const selectedView = getViewName(currentView);
+ const isSelectedViewInViews = formattedViews.some((view) => view.name === selectedView);
return {
selectedView: isSelectedViewInViews ? selectedView : undefined,
- views,
+ views: formattedViews,
};
};
const isViewSwitcherVisible = (views: NormalizedView[]): boolean => views.length > 1;
-export const getTabViewSwitcher = (header: SchedulerHeader, item): ToolbarItem => {
+export const getTabViewSwitcher = (
+ header: SchedulerHeader,
+ item: ToolbarItem,
+): ToolbarItem => {
const { selectedView, views } = getViewsAndSelectedView(header);
const isVisible = isViewSwitcherVisible(views);
- // @ts-expect-error
- const stylingMode = isFluent() ? 'outlined' : 'contained';
+ const stylingMode = isFluent(current()) ? 'outlined' : 'contained';
return {
widget: 'dxButtonGroup',
@@ -53,7 +60,7 @@ export const getTabViewSwitcher = (header: SchedulerHeader, item): ToolbarItem =
const viewSwitcher = e.component;
header._addEvent('currentView', (view) => {
- viewSwitcher.option('selectedItemKeys', [getViewName(view)]);
+ viewSwitcher.option('selectedItemKeys', [getViewName(view as NormalizedView)]);
});
},
},
@@ -61,7 +68,10 @@ export const getTabViewSwitcher = (header: SchedulerHeader, item): ToolbarItem =
} as ToolbarItem;
};
-export const getDropDownViewSwitcher = (header: SchedulerHeader, item): ToolbarItem => {
+export const getDropDownViewSwitcher = (
+ header: SchedulerHeader,
+ item: ToolbarItem,
+): ToolbarItem => {
const { selectedView, views } = getViewsAndSelectedView(header);
const isVisible = isViewSwitcherVisible(views);
@@ -88,8 +98,8 @@ export const getDropDownViewSwitcher = (header: SchedulerHeader, item): ToolbarI
onContentReady: (e) => {
const viewSwitcher = e.component;
- header._addEvent('currentView', (view: NormalizedView) => {
- viewSwitcher.option('selectedItemKey', getViewName(view));
+ header._addEvent('currentView', (view) => {
+ viewSwitcher.option('selectedItemKey', getViewName(view as NormalizedView));
});
},
dropDownOptions: {
diff --git a/packages/devextreme/js/__internal/scheduler/header/m_calendar.ts b/packages/devextreme/js/__internal/scheduler/header/m_calendar.ts
deleted file mode 100644
index eaf76198bbe8..000000000000
--- a/packages/devextreme/js/__internal/scheduler/header/m_calendar.ts
+++ /dev/null
@@ -1,126 +0,0 @@
-import registerComponent from '@js/core/component_registrator';
-import devices from '@js/core/devices';
-import $ from '@js/core/renderer';
-import Calendar from '@js/ui/calendar';
-import Popover from '@js/ui/popover/ui.popover';
-import Popup from '@js/ui/popup/ui.popup';
-import type { dxSchedulerOptions } from '@js/ui/scheduler';
-import Scrollable from '@js/ui/scroll_view/ui.scrollable';
-import Widget from '@js/ui/widget/ui.widget';
-
-const CALENDAR_CLASS = 'dx-scheduler-navigator-calendar';
-const CALENDAR_POPOVER_CLASS = 'dx-scheduler-navigator-calendar-popover';
-
-export default class SchedulerCalendar extends Widget {
- _overlay: any;
-
- _calendar: any;
-
- show(target) {
- if (!this._isMobileLayout()) {
- this._overlay.option('target', target);
- }
- this._overlay.show();
- }
-
- hide() {
- this._overlay.hide();
- }
-
- _keyboardHandler(opts): void {
- this._calendar?._keyboardHandler(opts);
- }
-
- _init(): void {
- // @ts-expect-error
- super._init();
- this.$element();
- }
-
- _render(): void {
- // @ts-expect-error
- super._render();
- this._renderOverlay();
- }
-
- _renderOverlay(): void {
- this.$element().addClass(CALENDAR_POPOVER_CLASS);
-
- const isMobileLayout = this._isMobileLayout();
-
- const overlayType = isMobileLayout ? Popup : Popover;
-
- // @ts-expect-error
- this._overlay = this._createComponent(this.$element(), overlayType, {
- contentTemplate: () => this._createOverlayContent(),
- onShown: () => this._calendar.focus(),
- defaultOptionsRules: [
- {
- device: () => isMobileLayout,
- options: {
- fullScreen: true,
- showCloseButton: false,
- toolbarItems: [{ shortcut: 'cancel' }],
- _ignorePreventScrollEventsDeprecation: true,
- preventScrollEvents: false,
- enableBodyScroll: false,
- },
- },
- ],
- });
- }
-
- _createOverlayContent() {
- const result = $('').addClass(CALENDAR_CLASS);
- // @ts-expect-error
- this._calendar = this._createComponent(result, Calendar, this._getCalendarOptions());
-
- if (this._isMobileLayout()) {
- const scrollable = this._createScrollable(result);
- return scrollable.$element();
- }
-
- return result;
- }
-
- _createScrollable(content) {
- // @ts-expect-error
- const result = this._createComponent('
', Scrollable, {
- height: 'auto',
- direction: 'both',
- });
- result.$content().append(content);
-
- return result;
- }
-
- _optionChanged({ name, value }) {
- switch (name) {
- case 'value':
- this._calendar?.option('value', value);
- break;
- default:
- break;
- }
- }
-
- _getCalendarOptions() {
- return {
- value: this.option('value'),
- min: this.option('min'),
- max: this.option('max'),
- firstDayOfWeek: this.option('firstDayOfWeek'),
- focusStateEnabled: this.option('focusStateEnabled'),
- onValueChanged: this.option('onValueChanged'),
- skipFocusCheck: true,
- tabIndex: this.option('tabIndex'),
- };
- }
-
- _isMobileLayout() {
- return !devices.current().generic;
- }
-}
-
-// @ts-expect-error
-registerComponent('dxSchedulerCalendarPopup', SchedulerCalendar);
diff --git a/packages/devextreme/js/__internal/scheduler/header/today.ts b/packages/devextreme/js/__internal/scheduler/header/today.ts
deleted file mode 100644
index b4b94339aa75..000000000000
--- a/packages/devextreme/js/__internal/scheduler/header/today.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import messageLocalization from '@js/common/core/localization/message';
-import type { Item as ToolbarItem } from '@js/ui/toolbar';
-import { extend } from '@ts/core/utils/m_extend';
-
-import type { SchedulerHeader } from './m_header';
-
-export const getTodayButtonOptions = (
- header: SchedulerHeader,
- item: ToolbarItem,
-): ToolbarItem => extend(true, {}, {
- location: 'before',
- locateInMenu: 'auto',
- widget: 'dxButton',
- cssClass: 'dx-scheduler-today',
- options: {
- text: messageLocalization.format('dxScheduler-navigationToday'),
- icon: 'today',
- stylingMode: 'outlined',
- type: 'normal',
- onClick() {
- header._updateCurrentDate(header.option('indicatorTime') ?? new Date());
- },
- },
-}, item) as ToolbarItem;
diff --git a/packages/devextreme/js/__internal/scheduler/header/m_utils.ts b/packages/devextreme/js/__internal/scheduler/header/utils/index.ts
similarity index 89%
rename from packages/devextreme/js/__internal/scheduler/header/m_utils.ts
rename to packages/devextreme/js/__internal/scheduler/header/utils/index.ts
index 7c8770d56997..8fcb6bc7778e 100644
--- a/packages/devextreme/js/__internal/scheduler/header/m_utils.ts
+++ b/packages/devextreme/js/__internal/scheduler/header/utils/index.ts
@@ -3,11 +3,12 @@ import dateUtils from '@js/core/utils/date';
import { isFunction, isObject } from '@js/core/utils/type';
import messageLocalization from '@js/localization/message';
import type { DateNavigatorTextInfo, Properties } from '@js/ui/scheduler';
+import type { BaseFormat } from '@ts/core/localization/date';
import { camelize } from '@ts/core/utils/m_inflector';
-import type { IntervalOptions, Step } from '@ts/scheduler/header/types';
+import type { IntervalOptions, Step } from '@ts/scheduler/header/core/types';
import type { NormalizedView, RawViewType, ViewType } from '@ts/scheduler/utils/options/types';
-import type { Direction } from './constants';
+import type { Direction } from '../core/constants';
const DAY_FORMAT = 'd';
@@ -62,7 +63,7 @@ const getDateAfterWorkWeek = (workWeekStart: Date): Date => {
let workDaysCount = 0;
while (workDaysCount < DAYS_IN_WORK_WEEK) {
if (!isWeekend(date)) {
- workDaysCount++;
+ workDaysCount += 1;
}
date = nextDay(date);
@@ -71,56 +72,34 @@ const getDateAfterWorkWeek = (workWeekStart: Date): Date => {
return date;
};
-const nextAgendaStart = (date: Date, agendaDuration: number): Date => addDateInterval(date, { days: agendaDuration }, 1);
+const nextAgendaStart = (
+ date: Date,
+ agendaDuration: number,
+): Date => addDateInterval(date, { days: agendaDuration }, 1);
-const getIntervalStartDate = (options: IntervalOptions) => {
+const getIntervalStartDate = (options: IntervalOptions): Date => {
const { date, step, firstDayOfWeek } = options;
- // eslint-disable-next-line default-case
switch (step) {
case 'day':
case 'week':
case 'month':
- return getPeriodStart(date, step, false, firstDayOfWeek);
+ return getPeriodStart(date, step, false, firstDayOfWeek) as Date;
case 'workWeek':
- // eslint-disable-next-line no-case-declarations
- const firstWeekDay = getWeekStart(date, firstDayOfWeek);
- return getWorkWeekStart(firstWeekDay);
+ return getWorkWeekStart(getWeekStart(date, firstDayOfWeek));
case 'agenda':
return new Date(date);
+ default:
+ return new Date(date);
}
};
-const getIntervalEndDate = (startDate: Date, options: IntervalOptions) => {
- const { intervalCount, step, agendaDuration } = options;
-
- let periodStartDate;
- let periodEndDate;
- let nextPeriodStartDate = new Date(startDate);
-
- for (let i = 0; i < intervalCount; i++) {
- periodStartDate = nextPeriodStartDate;
-
- periodEndDate = getPeriodEndDate(periodStartDate, step, agendaDuration!);
-
- nextPeriodStartDate = getNextPeriodStartDate(periodEndDate, step);
- }
-
- return periodEndDate;
-};
-
-export const getCaptionInterval = (options: IntervalOptions): {
- startDate: Date;
- endDate: Date;
-} => {
- const startDate = getIntervalStartDate(options);
- const endDate = getIntervalEndDate(startDate, options);
-
- return { startDate, endDate };
-};
-
-const getPeriodEndDate = (currentPeriodStartDate: Date, step: Step, agendaDuration: number): Date => {
- let date;
+const getPeriodEndDate = (
+ currentPeriodStartDate: Date,
+ step: Step,
+ agendaDuration: number,
+): Date => {
+ let date: Date = new Date();
// eslint-disable-next-line default-case
switch (step) {
@@ -156,29 +135,22 @@ const getNextPeriodStartDate = (currentPeriodEndDate: Date, step): Date => {
return date;
};
-export const getNextIntervalDate = (options, direction: Direction): Date => {
- const {
- date, step, intervalCount, agendaDuration,
- } = options;
+const getIntervalEndDate = (startDate: Date, options: IntervalOptions): Date => {
+ const { intervalCount, step, agendaDuration } = options;
- let dayDuration;
- // eslint-disable-next-line default-case
- switch (step) {
- case 'day':
- dayDuration = Number(intervalCount);
- break;
- case 'week':
- case 'workWeek':
- dayDuration = 7 * intervalCount;
- break;
- case 'agenda':
- dayDuration = agendaDuration;
- break;
- case 'month':
- return getNextMonthDate(date, intervalCount, direction);
+ let periodStartDate = new Date(startDate);
+ let periodEndDate = new Date(startDate);
+ let nextPeriodStartDate = new Date(startDate);
+
+ for (let i = 0; i < intervalCount; i += 1) {
+ periodStartDate = nextPeriodStartDate;
+
+ periodEndDate = getPeriodEndDate(periodStartDate, step, agendaDuration ?? 0);
+
+ nextPeriodStartDate = getNextPeriodStartDate(periodEndDate, step);
}
- return addDateInterval(date, { days: dayDuration }, direction);
+ return periodEndDate;
};
const getNextMonthDate = (date: Date, intervalCount: number, direction: Direction): Date => {
@@ -202,9 +174,34 @@ const getNextMonthDate = (date: Date, intervalCount: number, direction: Directio
return thatMonthMinDate;
};
+export const getNextIntervalDate = (options: IntervalOptions, direction: Direction): Date => {
+ const {
+ date, step, intervalCount, agendaDuration,
+ } = options;
+
+ let dayDuration = 0;
+ // eslint-disable-next-line default-case
+ switch (step) {
+ case 'day':
+ dayDuration = Number(intervalCount);
+ break;
+ case 'week':
+ case 'workWeek':
+ dayDuration = 7 * intervalCount;
+ break;
+ case 'agenda':
+ dayDuration = agendaDuration ?? 0;
+ break;
+ case 'month':
+ return getNextMonthDate(date, intervalCount, direction);
+ }
+
+ return addDateInterval(date, { days: dayDuration }, direction);
+};
+
const getDateMonthFormatter = (isShort: boolean) => {
const monthType = isShort ? 'abbreviated' : 'wide';
- const months = dateLocalization.getMonthNames(monthType as any);
+ const months = dateLocalization.getMonthNames(monthType as BaseFormat);
return (date: Date): string => {
const day = formatDate(date, 'day');
@@ -240,6 +237,16 @@ const getDifferentYearCaption = (startDate: Date, endDate: Date): string => {
return `${firstDateText}-${lastDateDateText}`;
};
+export const getCaptionInterval = (options: IntervalOptions): {
+ startDate: Date;
+ endDate: Date;
+} => {
+ const startDate = getIntervalStartDate(options);
+ const endDate = getIntervalEndDate(startDate, options);
+
+ return { startDate, endDate };
+};
+
const getSameYearCaption = (startDate: Date, endDate: Date, isShort: boolean): string => {
const isDifferentMonthDates = startDate.getMonth() !== endDate.getMonth();
const useShortFormat = isDifferentMonthDates || isShort;
diff --git a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts
index d4bba916e5ae..827a67108350 100644
--- a/packages/devextreme/js/__internal/scheduler/m_scheduler.ts
+++ b/packages/devextreme/js/__internal/scheduler/m_scheduler.ts
@@ -28,20 +28,20 @@ import {
import { hasWindow } from '@js/core/utils/window';
import DataHelperMixin from '@js/data_helper';
import { custom as customDialog } from '@js/ui/dialog';
-import type { Appointment, AppointmentTooltipShowingEvent } from '@js/ui/scheduler';
+import type { Appointment, AppointmentTooltipShowingEvent, FirstDayOfWeek } from '@js/ui/scheduler';
import errors from '@js/ui/widget/ui.errors';
import { dateUtilsTs } from '@ts/core/utils/date';
import { createA11yStatusContainer } from './a11y_status/a11y_status_render';
import { getA11yStatusText } from './a11y_status/a11y_status_text';
-import { AppointmentForm } from './appointment_popup/m_form';
-import { AppointmentForm as AppointmentLegacyForm } from './appointment_popup/m_legacy_form';
-import { ACTION_TO_APPOINTMENT, AppointmentPopup as AppointmentLegacyPopup } from './appointment_popup/m_legacy_popup';
-import { AppointmentPopup } from './appointment_popup/m_popup';
+import { AppointmentForm } from './appointment_popup/components/form/m_form';
+import { AppointmentPopup } from './appointment_popup/components/m_popup';
+import { AppointmentForm as AppointmentLegacyForm } from './appointment_popup/legacy/m_legacy_form';
+import { ACTION_TO_APPOINTMENT, AppointmentPopup as AppointmentLegacyPopup } from './appointment_popup/legacy/m_legacy_popup';
import AppointmentCollection from './appointments/m_appointment_collection';
import NotifyScheduler from './base/m_widget_notify_scheduler';
-import { SchedulerHeader } from './header/m_header';
-import type { HeaderOptions } from './header/types';
+import { SchedulerHeader } from './header/components/m_header';
+import type { HeaderOptions } from './header/core/types';
import { CompactAppointmentsHelper } from './m_compact_appointments_helper';
import { hide as hideLoading, show as showLoading } from './m_loading';
import type { SubscribeKey, SubscribeMethods } from './m_subscribes';
@@ -2134,10 +2134,10 @@ class Scheduler extends SchedulerOptionsBaseWidget {
}
}
- getFirstDayOfWeek() {
+ getFirstDayOfWeek(): FirstDayOfWeek {
return isDefined(this.getViewOption('firstDayOfWeek'))
- ? this.getViewOption('firstDayOfWeek')
- : dateLocalization.firstDayOfWeekIndex();
+ ? this.getViewOption('firstDayOfWeek') as FirstDayOfWeek
+ : dateLocalization.firstDayOfWeekIndex() as FirstDayOfWeek;
}
_validateKeyFieldIfAgendaExist() {
diff --git a/packages/devextreme/playground/jquery.html b/packages/devextreme/playground/jquery.html
index abaed02d03f2..64c2ac4e265a 100644
--- a/packages/devextreme/playground/jquery.html
+++ b/packages/devextreme/playground/jquery.html
@@ -1,5 +1,6 @@
+
DevExtreme jQuery Example
@@ -42,6 +43,7 @@
+
Test header
@@ -49,15 +51,20 @@
Te
-
+
-
+
+