Skip to content

Commit 17fa613

Browse files
authored
refactor appointment date provider and filter strategies (DevExpress#29536)
Co-authored-by: Vladimir Bushmanov <[email protected]>
1 parent 095d511 commit 17fa613

File tree

13 files changed

+223
-234
lines changed

13 files changed

+223
-234
lines changed

packages/devextreme/js/__internal/scheduler/appointments/data_provider/m_appointment_data_provider.ts

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,18 @@ import { combineRemoteFilter } from '@ts/scheduler/r1/filterting/index';
33
import type { AppointmentDataAccessor } from '@ts/scheduler/utils';
44

55
import { AppointmentDataSource } from './m_appointment_data_source';
6-
import { AppointmentFilterBaseStrategy, AppointmentFilterVirtualStrategy } from './m_appointment_filter';
6+
import { AppointmentFilterBaseStrategy } from './m_appointment_filter';
7+
import { AppointmentFilterVirtualStrategy } from './m_appointment_filter_virtual';
78

8-
const FilterStrategies = {
9-
virtual: 'virtual',
10-
standard: 'standard',
9+
type FilterStrategy = (AppointmentFilterBaseStrategy | AppointmentFilterVirtualStrategy) & {
10+
constructor: (AppointmentFilterBaseStrategy | AppointmentFilterVirtualStrategy)['constructor'] & {
11+
strategyName: string;
12+
};
13+
};
14+
15+
const FilterStrategyMap = {
16+
[AppointmentFilterVirtualStrategy.strategyName]: AppointmentFilterVirtualStrategy,
17+
[AppointmentFilterBaseStrategy.strategyName]: AppointmentFilterBaseStrategy,
1118
};
1219

1320
export class AppointmentDataProvider {
@@ -21,7 +28,7 @@ export class AppointmentDataProvider {
2128

2229
appointmentDataSource: AppointmentDataSource;
2330

24-
filterStrategy: any;
31+
filterStrategy!: FilterStrategy;
2532

2633
constructor(options) {
2734
this.options = options;
@@ -30,7 +37,6 @@ export class AppointmentDataProvider {
3037
this.timeZoneCalculator = this.options.timeZoneCalculator;
3138

3239
this.appointmentDataSource = new AppointmentDataSource(this.dataSource);
33-
3440
this.initFilterStrategy();
3541
}
3642

@@ -42,21 +48,24 @@ export class AppointmentDataProvider {
4248
return !!this.dataSource;
4349
}
4450

45-
get filterStrategyName() {
51+
get filterStrategyName(): string {
4652
return this.options.getIsVirtualScrolling()
47-
? FilterStrategies.virtual
48-
: FilterStrategies.standard;
53+
? AppointmentFilterVirtualStrategy.strategyName
54+
: AppointmentFilterBaseStrategy.strategyName;
4955
}
5056

51-
getFilterStrategy() {
52-
if (!this.filterStrategy || this.filterStrategy.strategyName !== this.filterStrategyName) {
57+
getFilterStrategy(): AppointmentFilterBaseStrategy | AppointmentFilterVirtualStrategy {
58+
if (
59+
!this.filterStrategy
60+
|| this.filterStrategy.constructor.strategyName !== this.filterStrategyName
61+
) {
5362
this.initFilterStrategy();
5463
}
5564

5665
return this.filterStrategy;
5766
}
5867

59-
initFilterStrategy() {
68+
initFilterStrategy(): void {
6069
const filterOptions = {
6170
resources: this.options.resources,
6271
dataAccessors: this.dataAccessors,
@@ -75,19 +84,18 @@ export class AppointmentDataProvider {
7584
viewDataProvider: this.options.getViewDataProvider,
7685
allDayPanelMode: this.options.allDayPanelMode,
7786
};
87+
const strategy = new FilterStrategyMap[this.filterStrategyName](filterOptions);
7888

79-
this.filterStrategy = this.filterStrategyName === FilterStrategies.virtual
80-
? new AppointmentFilterVirtualStrategy(filterOptions)
81-
: new AppointmentFilterBaseStrategy(filterOptions);
89+
this.filterStrategy = strategy as FilterStrategy;
8290
}
8391

84-
setDataSource(dataSource) {
92+
setDataSource(dataSource): void {
8593
this.dataSource = dataSource;
8694
this.initFilterStrategy();
8795
this.appointmentDataSource.setDataSource(this.dataSource);
8896
}
8997

90-
updateDataAccessors(dataAccessors) {
98+
updateDataAccessors(dataAccessors): void {
9199
this.dataAccessors = dataAccessors;
92100
this.initFilterStrategy();
93101
}
@@ -124,10 +132,6 @@ export class AppointmentDataProvider {
124132
return this.getFilterStrategy().filterLoadedAppointments(filterOption, preparedItems);
125133
}
126134

127-
calculateAppointmentEndDate(isAllDay, startDate) {
128-
return this.getFilterStrategy().calculateAppointmentEndDate(isAllDay, startDate);
129-
}
130-
131135
// Appointment data source mappings
132136
cleanState() { this.appointmentDataSource.cleanState(); }
133137

packages/devextreme/js/__internal/scheduler/appointments/data_provider/m_appointment_filter.ts

Lines changed: 18 additions & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,18 @@
11
/* eslint-disable max-classes-per-file */
22
import { wrapToArray } from '@js/core/utils/array';
33
import dateUtils from '@js/core/utils/date';
4-
import { each, map } from '@js/core/utils/iterator';
4+
import { map } from '@js/core/utils/iterator';
55
import { isDefined, isFunction } from '@js/core/utils/type';
66
import query from '@js/data/query';
77
import { dateUtilsTs } from '@ts/core/utils/date';
8+
import type { AppointmentDataItem } from '@ts/scheduler/r1/types';
89
import {
9-
getAppointmentTakesAllDay, getDatesWithoutTime, hasResourceValue, isDateAndTimeView,
10-
isTimelineView,
10+
getDatesWithoutTime, hasResourceValue, isAppointmentTakesAllDay, isTimelineView,
1111
} from '@ts/scheduler/r1/utils/index';
1212
import type { AppointmentDataAccessor } from '@ts/scheduler/utils';
1313

1414
import { createAppointmentAdapter } from '../../m_appointment_adapter';
1515
import { getRecurrenceProcessor } from '../../m_recurrence';
16-
import {
17-
getResourcesDataByGroups,
18-
} from '../../resources/m_utils';
1916
import {
2017
_appointmentPartInInterval,
2118
compareDateWithEndDayHour,
@@ -28,25 +25,18 @@ import {
2825

2926
const toMs = dateUtils.dateToMilliseconds;
3027

31-
const FilterStrategies = {
32-
virtual: 'virtual',
33-
standard: 'standard',
34-
};
35-
3628
export class AppointmentFilterBaseStrategy {
29+
public static readonly strategyName: string = 'standard';
30+
3731
options: any;
3832

3933
dataAccessors: AppointmentDataAccessor;
4034

4135
constructor(options) {
4236
this.options = options;
4337
this.dataAccessors = this.options.dataAccessors;
44-
45-
this._init();
4638
}
4739

48-
get strategyName() { return FilterStrategies.standard; }
49-
5040
get timeZoneCalculator() { return this.options.timeZoneCalculator; }
5141

5242
get viewStartDayHour() { return this.options.startDayHour; }
@@ -82,11 +72,7 @@ export class AppointmentFilterBaseStrategy {
8272
: result;
8373
}
8474

85-
_init() {
86-
this.setDataAccessors(this.dataAccessors);
87-
}
88-
89-
filter(preparedItems) {
75+
filter(preparedItems: AppointmentDataItem[]) {
9076
const [min, max] = this.dateRange;
9177
const { viewOffset } = this.options;
9278
const allDay = !this.showAllDayPanel && this.supportAllDayRow
@@ -109,36 +95,19 @@ export class AppointmentFilterBaseStrategy {
10995
}
11096

11197
// eslint-disable-next-line @typescript-eslint/no-unused-vars
112-
hasAllDayAppointments(filteredItems, preparedItems) {
113-
const adapters = filteredItems.map((item) => createAppointmentAdapter(
114-
item,
115-
this.dataAccessors,
116-
this.timeZoneCalculator,
117-
));
118-
119-
let result = false;
120-
121-
// @ts-expect-error
122-
each(adapters, (_, item) => {
123-
if (getAppointmentTakesAllDay(
98+
hasAllDayAppointments(filteredItems, preparedItems: AppointmentDataItem[]): boolean {
99+
return filteredItems
100+
.map((item) => createAppointmentAdapter(
124101
item,
125-
this.allDayPanelMode,
126-
)) {
127-
result = true;
128-
return false;
129-
}
130-
});
131-
132-
return result;
133-
}
134-
135-
setDataAccessors(dataAccessors) {
136-
this.dataAccessors = dataAccessors;
102+
this.dataAccessors,
103+
this.timeZoneCalculator,
104+
))
105+
.some((item) => isAppointmentTakesAllDay(item, this.allDayPanelMode));
137106
}
138107

139108
private _createAllDayAppointmentFilter() {
140109
return [[
141-
(appointment) => getAppointmentTakesAllDay(
110+
(appointment) => isAppointmentTakesAllDay(
142111
appointment,
143112
this.allDayPanelMode,
144113
),
@@ -178,7 +147,7 @@ export class AppointmentFilterBaseStrategy {
178147
const startDate = dateUtilsTs.addOffsets(appointment.startDate, [-viewOffset]);
179148
const endDate = dateUtilsTs.addOffsets(appointment.endDate, [-viewOffset]);
180149

181-
const appointmentTakesAllDay = getAppointmentTakesAllDay(
150+
const appointmentTakesAllDay = isAppointmentTakesAllDay(
182151
appointment,
183152
this.allDayPanelMode,
184153
);
@@ -336,134 +305,24 @@ export class AppointmentFilterBaseStrategy {
336305
return result;
337306
}
338307

339-
filterLoadedAppointments(filterOptions, preparedItems) {
308+
filterLoadedAppointments(filterOptions, preparedItems: AppointmentDataItem[]) {
340309
const filteredItems = this.filterPreparedItems(filterOptions, preparedItems);
341310
return filteredItems.map(({ rawAppointment }) => rawAppointment);
342311
}
343312

344-
filterPreparedItems(filterOptions, preparedItems) {
313+
filterPreparedItems(filterOptions, preparedItems: AppointmentDataItem[]) {
345314
const combinedFilter = this._createCombinedFilter(filterOptions);
346315

347316
return query(preparedItems)
348317
.filter(combinedFilter)
349318
.toArray();
350319
}
351320

352-
filterAllDayAppointments(preparedItems) {
321+
filterAllDayAppointments(preparedItems: AppointmentDataItem[]) {
353322
const combinedFilter = this._createAllDayAppointmentFilter();
354323
return query(preparedItems)
355324
.filter(combinedFilter)
356325
.toArray()
357326
.map(({ rawAppointment }) => rawAppointment);
358327
}
359328
}
360-
361-
export class AppointmentFilterVirtualStrategy extends AppointmentFilterBaseStrategy {
362-
get strategyName() { return FilterStrategies.virtual; }
363-
364-
get resources() { return this.options.resources; }
365-
366-
filter(preparedItems) {
367-
const { viewOffset } = this.options;
368-
const hourMs = toMs('hour');
369-
const isCalculateStartAndEndDayHour = isDateAndTimeView(this.viewType);
370-
const checkIntersectViewport = isCalculateStartAndEndDayHour && this.viewDirection === 'horizontal';
371-
372-
const isAllDayWorkspace = !this.supportAllDayRow;
373-
const showAllDayAppointments = this.showAllDayPanel || isAllDayWorkspace;
374-
375-
const endViewDate = this.viewDataProvider.getLastViewDateByEndDayHour(this.viewEndDayHour);
376-
const shiftedEndViewDate = dateUtilsTs.addOffsets(endViewDate, [viewOffset]);
377-
const filterOptions: any = [];
378-
379-
const groupsInfo = this.viewDataProvider.getCompletedGroupsInfo();
380-
groupsInfo.forEach((item) => {
381-
const { groupIndex } = item;
382-
const groupStartDate: Date = item.startDate;
383-
const groupEndDate: Date = new Date(
384-
Math.min(
385-
item.endDate.getTime(),
386-
shiftedEndViewDate.getTime(),
387-
),
388-
);
389-
390-
const startDayHour = isCalculateStartAndEndDayHour
391-
? groupStartDate.getHours()
392-
: this.viewStartDayHour;
393-
const endDayHour = isCalculateStartAndEndDayHour
394-
? startDayHour + groupStartDate.getMinutes() / 60 + (groupEndDate.getTime() - groupStartDate.getTime()) / hourMs
395-
: this.viewEndDayHour;
396-
397-
const resources = this._getPrerenderFilterResources(groupIndex);
398-
399-
const hasAllDayPanel = this.viewDataProvider.hasGroupAllDayPanel(groupIndex);
400-
401-
const supportAllDayAppointment = isAllDayWorkspace || (!!showAllDayAppointments && hasAllDayPanel);
402-
403-
filterOptions.push({
404-
isVirtualScrolling: true,
405-
startDayHour,
406-
endDayHour,
407-
viewOffset,
408-
viewStartDayHour: this.viewStartDayHour,
409-
viewEndDayHour: this.viewEndDayHour,
410-
min: dateUtilsTs.addOffsets(groupStartDate, [-viewOffset]),
411-
max: dateUtilsTs.addOffsets(groupEndDate, [-viewOffset]),
412-
supportMultiDayAppointments: isTimelineView(this.viewType),
413-
allDay: supportAllDayAppointment,
414-
resources,
415-
firstDayOfWeek: this.firstDayOfWeek,
416-
checkIntersectViewport,
417-
});
418-
});
419-
420-
return this.filterLoadedAppointments({
421-
filterOptions,
422-
groupCount: this.groupCount,
423-
}, preparedItems);
424-
}
425-
426-
filterPreparedItems({ filterOptions, groupCount }, preparedItems) {
427-
const combinedFilters: any = [];
428-
429-
let itemsToFilter = preparedItems;
430-
const needPreFilter = groupCount > 0;
431-
if (needPreFilter) {
432-
// @ts-expect-error
433-
itemsToFilter = itemsToFilter.filter(({ rawAppointment }) => {
434-
for (let i = 0; i < filterOptions.length; ++i) {
435-
const { resources } = filterOptions[i];
436-
if (this._filterAppointmentByResources(rawAppointment, resources)) {
437-
return true;
438-
}
439-
}
440-
});
441-
}
442-
443-
filterOptions.forEach((option) => {
444-
combinedFilters.length && combinedFilters.push('or');
445-
446-
const filter = this._createCombinedFilter(option);
447-
448-
combinedFilters.push(filter);
449-
});
450-
451-
return query(itemsToFilter)
452-
.filter(combinedFilters)
453-
.toArray();
454-
}
455-
456-
hasAllDayAppointments(filteredItems, preparedItems) {
457-
return this.filterAllDayAppointments(preparedItems).length > 0;
458-
}
459-
460-
_getPrerenderFilterResources(groupIndex) {
461-
const cellGroup = this.viewDataProvider.getCellsGroup(groupIndex);
462-
463-
return getResourcesDataByGroups(
464-
this.loadedResources,
465-
this.resources,
466-
[cellGroup],
467-
);
468-
}
469-
}

0 commit comments

Comments
 (0)