Skip to content

Commit 2aaef69

Browse files
authored
refactor appointment date provider and filter strategies (DevExpress#29495) (DevExpress#29535)
Co-authored-by: Vladimir Bushmanov <[email protected]>
1 parent 61557dd commit 2aaef69

File tree

13 files changed

+228
-240
lines changed

13 files changed

+228
-240
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 & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
1-
/* eslint-disable max-classes-per-file */
21
import query from '@js/common/data/query';
32
import { wrapToArray } from '@js/core/utils/array';
43
import dateUtils from '@js/core/utils/date';
5-
import { each, map } from '@js/core/utils/iterator';
4+
import { map } from '@js/core/utils/iterator';
65
import { isDefined, isFunction } from '@js/core/utils/type';
76
import { dateUtilsTs } from '@ts/core/utils/date';
7+
import type { AppointmentDataItem } from '@ts/scheduler/r1/types';
88
import {
9-
getAppointmentTakesAllDay, getDatesWithoutTime, hasResourceValue, isDateAndTimeView,
10-
isTimelineView,
9+
getDatesWithoutTime, hasResourceValue, isAppointmentTakesAllDay, isTimelineView,
1110
} from '@ts/scheduler/r1/utils/index';
1211
import type { AppointmentDataAccessor } from '@ts/scheduler/utils';
1312

1413
import { createAppointmentAdapter } from '../../m_appointment_adapter';
1514
import { getRecurrenceProcessor } from '../../m_recurrence';
16-
import {
17-
getResourcesDataByGroups,
18-
} from '../../resources/m_utils';
1915
import {
2016
_appointmentPartInInterval,
2117
compareDateWithEndDayHour,
@@ -28,25 +24,18 @@ import {
2824

2925
const toMs = dateUtils.dateToMilliseconds;
3026

31-
const FilterStrategies = {
32-
virtual: 'virtual',
33-
standard: 'standard',
34-
};
35-
3627
export class AppointmentFilterBaseStrategy {
28+
public static readonly strategyName: string = 'standard';
29+
3730
options: any;
3831

3932
dataAccessors: AppointmentDataAccessor;
4033

4134
constructor(options) {
4235
this.options = options;
4336
this.dataAccessors = this.options.dataAccessors;
44-
45-
this._init();
4637
}
4738

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

5241
get viewStartDayHour() { return this.options.startDayHour; }
@@ -82,11 +71,7 @@ export class AppointmentFilterBaseStrategy {
8271
: result;
8372
}
8473

85-
_init() {
86-
this.setDataAccessors(this.dataAccessors);
87-
}
88-
89-
filter(preparedItems) {
74+
filter(preparedItems: AppointmentDataItem[]) {
9075
const [min, max] = this.dateRange;
9176
const { viewOffset } = this.options;
9277
const allDay = !this.showAllDayPanel && this.supportAllDayRow
@@ -109,36 +94,19 @@ export class AppointmentFilterBaseStrategy {
10994
}
11095

11196
// 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(
97+
hasAllDayAppointments(filteredItems, preparedItems: AppointmentDataItem[]): boolean {
98+
return filteredItems
99+
.map((item) => createAppointmentAdapter(
124100
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;
101+
this.dataAccessors,
102+
this.timeZoneCalculator,
103+
))
104+
.some((item) => isAppointmentTakesAllDay(item, this.allDayPanelMode));
137105
}
138106

139107
private _createAllDayAppointmentFilter() {
140108
return [[
141-
(appointment) => getAppointmentTakesAllDay(
109+
(appointment) => isAppointmentTakesAllDay(
142110
appointment,
143111
this.allDayPanelMode,
144112
),
@@ -178,7 +146,7 @@ export class AppointmentFilterBaseStrategy {
178146
const startDate = dateUtilsTs.addOffsets(appointment.startDate, [-viewOffset]);
179147
const endDate = dateUtilsTs.addOffsets(appointment.endDate, [-viewOffset]);
180148

181-
const appointmentTakesAllDay = getAppointmentTakesAllDay(
149+
const appointmentTakesAllDay = isAppointmentTakesAllDay(
182150
appointment,
183151
this.allDayPanelMode,
184152
);
@@ -336,12 +304,12 @@ export class AppointmentFilterBaseStrategy {
336304
return result;
337305
}
338306

339-
filterLoadedAppointments(filterOptions, preparedItems) {
307+
filterLoadedAppointments(filterOptions, preparedItems: AppointmentDataItem[]) {
340308
const filteredItems = this.filterPreparedItems(filterOptions, preparedItems);
341309
return filteredItems.map(({ rawAppointment }) => rawAppointment);
342310
}
343311

344-
filterPreparedItems(filterOptions, preparedItems) {
312+
filterPreparedItems(filterOptions, preparedItems: AppointmentDataItem[]) {
345313
const combinedFilter = this._createCombinedFilter(filterOptions);
346314

347315
// @ts-expect-error
@@ -351,7 +319,7 @@ export class AppointmentFilterBaseStrategy {
351319
.toArray();
352320
}
353321

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

0 commit comments

Comments
 (0)