devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
558 lines (557 loc) • 23.5 kB
JavaScript
/**
* DevExtreme (esm/ui/scheduler/appointments/dataProvider/appointmentFilter.js)
* Version: 21.2.4
* Build date: Mon Dec 06 2021
*
* Copyright (c) 2012 - 2021 Developer Express Inc. ALL RIGHTS RESERVED
* Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
*/
import config from "../../../../core/config";
import dateUtils from "../../../../core/utils/date";
import {
equalByValue
} from "../../../../core/utils/common";
import dateSerialization from "../../../../core/utils/date_serialization";
import {
getRecurrenceProcessor
} from "../../recurrence";
import {
inArray,
wrapToArray
} from "../../../../core/utils/array";
import {
extend
} from "../../../../core/utils/extend";
import {
map,
each
} from "../../../../core/utils/iterator";
import {
isFunction,
isDefined,
isString
} from "../../../../core/utils/type";
import query from "../../../../data/query";
import {
isDateAndTimeView as calculateIsDateAndTimeView,
isSupportMultiDayAppointments
} from "../../../../renovation/ui/scheduler/view_model/to_test/views/utils/base";
import {
getResourcesDataByGroups
} from "../../resources/utils";
import {
compareDateWithStartDayHour,
compareDateWithEndDayHour,
getTrimDates,
getAppointmentTakesSeveralDays,
_appointmentPartInInterval,
getRecurrenceException,
getAppointmentTakesAllDay
} from "./utils";
import {
getPreparedDataItems,
resolveDataItems
} from "../../../../renovation/ui/scheduler/utils/data";
var toMs = dateUtils.dateToMilliseconds;
var DATE_FILTER_POSITION = 0;
var USER_FILTER_POSITION = 1;
var FilterStrategies = {
virtual: "virtual",
standard: "standard"
};
class FilterMaker {
constructor(dataAccessors) {
this._filterRegistry = null;
this.dataAccessors = dataAccessors
}
isRegistered() {
return !!this._filterRegistry
}
clearRegistry() {
delete this._filterRegistry
}
make(type, args) {
if (!this._filterRegistry) {
this._filterRegistry = {}
}
this._make(type).apply(this, args)
}
_make(type) {
switch (type) {
case "date":
return (min, max, useAccessors) => {
var startDate = useAccessors ? this.dataAccessors.getter.startDate : this.dataAccessors.expr.startDateExpr;
var endDate = useAccessors ? this.dataAccessors.getter.endDate : this.dataAccessors.expr.endDateExpr;
var recurrenceRule = this.dataAccessors.expr.recurrenceRuleExpr;
this._filterRegistry.date = [
[
[endDate, ">=", min],
[startDate, "<", max]
], "or", [recurrenceRule, "startswith", "freq"], "or", [
[endDate, min],
[startDate, min]
]
];
if (!recurrenceRule) {
this._filterRegistry.date.splice(1, 2)
}
};
case "user":
return userFilter => {
this._filterRegistry.user = userFilter
}
}
}
combine() {
var filter = [];
this._filterRegistry.date && filter.push(this._filterRegistry.date);
this._filterRegistry.user && filter.push(this._filterRegistry.user);
return filter
}
dateFilter() {
var _this$_filterRegistry;
return null === (_this$_filterRegistry = this._filterRegistry) || void 0 === _this$_filterRegistry ? void 0 : _this$_filterRegistry.date
}
}
export class AppointmentFilterBaseStrategy {
constructor(options) {
this.options = options;
this.dataSource = this.options.dataSource;
this.dataAccessors = this.options.dataAccessors;
this.preparedItems = [];
this._init()
}
get strategyName() {
return FilterStrategies.standard
}
get timeZoneCalculator() {
return this.options.timeZoneCalculator
}
get viewStartDayHour() {
return this.options.startDayHour
}
get viewEndDayHour() {
return this.options.endDayHour
}
get appointmentDuration() {
return this.options.appointmentDuration
}
get timezone() {
return this.options.timezone
}
get firstDayOfWeek() {
return this.options.firstDayOfWeek
}
get showAllDayPanel() {
return this.options.showAllDayPanel
}
get loadedResources() {
return this._resolveOption("loadedResources")
}
get supportAllDayRow() {
return this._resolveOption("supportAllDayRow")
}
get viewType() {
return this._resolveOption("viewType")
}
get viewDirection() {
return this._resolveOption("viewDirection")
}
get dateRange() {
return this._resolveOption("dateRange")
}
get groupCount() {
return this._resolveOption("groupCount")
}
get viewDataProvider() {
return this._resolveOption("viewDataProvider")
}
_resolveOption(name) {
var result = this.options[name];
return "function" === typeof result ? result() : result
}
_init() {
this.setDataAccessors(this.dataAccessors);
this.setDataSource(this.dataSource)
}
filter(preparedItems) {
var dateRange = this.dateRange;
var allDay;
if (!this.showAllDayPanel && this.supportAllDayRow) {
allDay = false
}
return this.filterLoadedAppointments({
startDayHour: this.viewStartDayHour,
endDayHour: this.viewEndDayHour,
viewStartDayHour: this.viewStartDayHour,
viewEndDayHour: this.viewEndDayHour,
min: dateRange[0],
max: dateRange[1],
resources: this.loadedResources,
allDay: allDay,
supportMultiDayAppointments: isSupportMultiDayAppointments(this.viewType),
firstDayOfWeek: this.firstDayOfWeek
}, preparedItems)
}
filterByDate(min, max, remoteFiltering, dateSerializationFormat) {
if (!this.dataSource) {
return
}
var [trimMin, trimMax] = getTrimDates(min, max);
if (!this.filterMaker.isRegistered()) {
this._createFilter(trimMin, trimMax, remoteFiltering, dateSerializationFormat)
} else {
var _this$dataSource$filt;
if ((null === (_this$dataSource$filt = this.dataSource.filter()) || void 0 === _this$dataSource$filt ? void 0 : _this$dataSource$filt.length) > 1) {
var userFilter = this._serializeRemoteFilter([this.dataSource.filter()[1]], dateSerializationFormat);
this.filterMaker.make("user", userFilter)
}
if (remoteFiltering) {
this.filterMaker.make("date", [trimMin, trimMax]);
this.dataSource.filter(this._combineRemoteFilter(dateSerializationFormat))
}
}
}
hasAllDayAppointments(appointments) {
var result = false;
if (appointments) {
each(appointments, (_, item) => {
if (getAppointmentTakesAllDay(item, this.viewStartDayHour, this.viewEndDayHour)) {
result = true;
return false
}
})
}
return result
}
setDataAccessors(dataAccessors) {
this.dataAccessors = dataAccessors;
this.filterMaker = new FilterMaker(this.dataAccessors)
}
setDataSource(dataSource) {
var _this$filterMaker;
this.dataSource = dataSource;
this._updatePreparedDataItems();
null === (_this$filterMaker = this.filterMaker) || void 0 === _this$filterMaker ? void 0 : _this$filterMaker.clearRegistry()
}
_updatePreparedDataItems() {
var updateItems = items => this.preparedItems = getPreparedDataItems(items, this.dataAccessors, this.appointmentDuration, this.timeZoneCalculator);
if (this.dataSource) {
var store = this.dataSource.store();
store.on("loaded", options => {
updateItems(resolveDataItems(options))
});
if (this.dataSource.isLoaded()) {
updateItems(this.dataSource.items())
}
}
}
_createAllDayAppointmentFilter(filterOptions) {
var {
viewStartDayHour: viewStartDayHour,
viewEndDayHour: viewEndDayHour
} = filterOptions;
return [
[appointment => getAppointmentTakesAllDay(appointment, viewStartDayHour, viewEndDayHour)]
]
}
_createCombinedFilter(filterOptions) {
var min = new Date(filterOptions.min);
var max = new Date(filterOptions.max);
var {
startDayHour: startDayHour,
endDayHour: endDayHour,
viewStartDayHour: viewStartDayHour,
viewEndDayHour: viewEndDayHour,
resources: resources,
firstDayOfWeek: firstDayOfWeek,
checkIntersectViewport: checkIntersectViewport,
supportMultiDayAppointments: supportMultiDayAppointments
} = filterOptions;
var [trimMin, trimMax] = getTrimDates(min, max);
var useRecurrence = isDefined(this.dataAccessors.getter.recurrenceRule);
return [
[appointment => {
var _appointment$visible;
var appointmentVisible = null !== (_appointment$visible = appointment.visible) && void 0 !== _appointment$visible ? _appointment$visible : true;
if (!appointmentVisible) {
return false
}
var {
startDate: startDate,
endDate: endDate,
hasRecurrenceRule: hasRecurrenceRule
} = appointment;
if (!hasRecurrenceRule) {
if (!(endDate >= trimMin && startDate < trimMax || dateUtils.sameDate(endDate, trimMin) && dateUtils.sameDate(startDate, trimMin))) {
return false
}
}
var recurrenceRule;
if (useRecurrence) {
recurrenceRule = appointment.recurrenceRule
}
var appointmentTakesAllDay = getAppointmentTakesAllDay(appointment, viewStartDayHour, viewEndDayHour);
var appointmentTakesSeveralDays = getAppointmentTakesSeveralDays(appointment);
var isAllDay = appointment.allDay;
var isLongAppointment = appointmentTakesSeveralDays || appointmentTakesAllDay;
if (null !== resources && void 0 !== resources && resources.length && !this._filterAppointmentByResources(appointment.rawAppointment, resources)) {
return false
}
if (appointmentTakesAllDay && false === filterOptions.allDay) {
return false
}
if (hasRecurrenceRule) {
var recurrenceException = getRecurrenceException(appointment, this.timeZoneCalculator, this.timezone);
if (!this._filterAppointmentByRRule({
startDate: startDate,
endDate: endDate,
recurrenceRule: recurrenceRule,
recurrenceException: recurrenceException,
allDay: appointmentTakesAllDay
}, min, max, startDayHour, endDayHour, firstDayOfWeek)) {
return false
}
}
if (!isAllDay && supportMultiDayAppointments && isLongAppointment) {
if (endDate < min && (!useRecurrence || useRecurrence && !hasRecurrenceRule)) {
return false
}
}
if (isDefined(startDayHour) && (!useRecurrence || !filterOptions.isVirtualScrolling)) {
if (!compareDateWithStartDayHour(startDate, endDate, startDayHour, appointmentTakesAllDay, appointmentTakesSeveralDays)) {
return false
}
}
if (isDefined(endDayHour)) {
if (!compareDateWithEndDayHour({
startDate: startDate,
endDate: endDate,
startDayHour: startDayHour,
endDayHour: endDayHour,
viewStartDayHour: viewStartDayHour,
viewEndDayHour: viewEndDayHour,
allDay: appointmentTakesAllDay,
severalDays: appointmentTakesSeveralDays,
min: min,
max: max,
checkIntersectViewport: checkIntersectViewport
})) {
return false
}
}
if (!isAllDay && (!isLongAppointment || supportMultiDayAppointments)) {
if (endDate < min && useRecurrence && !hasRecurrenceRule) {
return false
}
}
return true
}]
]
}
_createAppointmentFilter(filterOptions) {
if (this.filterMaker.isRegistered()) {
this.filterMaker.make("user", void 0)
}
return this._createCombinedFilter(filterOptions)
}
_excessFiltering() {
var dateFilter = this.filterMaker.dateFilter();
var dataSourceFilter = this.dataSource.filter();
return dateFilter && dataSourceFilter && (equalByValue(dataSourceFilter, dateFilter) || dataSourceFilter.length && equalByValue(dataSourceFilter[DATE_FILTER_POSITION], dateFilter))
}
_combineRemoteFilter(dateSerializationFormat) {
var combinedFilter = this.filterMaker.combine();
return this._serializeRemoteFilter(combinedFilter, dateSerializationFormat)
}
_serializeRemoteFilter(filter, dateSerializationFormat) {
if (!Array.isArray(filter)) {
return filter
}
filter = extend([], filter);
var startDate = this.dataAccessors.expr.startDateExpr;
var endDate = this.dataAccessors.expr.endDateExpr;
if (isString(filter[0])) {
if (config().forceIsoDateParsing && filter.length > 1) {
if (filter[0] === startDate || filter[0] === endDate) {
filter[filter.length - 1] = dateSerialization.serializeDate(new Date(filter[filter.length - 1]), dateSerializationFormat)
}
}
}
for (var i = 0; i < filter.length; i++) {
filter[i] = this._serializeRemoteFilter(filter[i], dateSerializationFormat)
}
return filter
}
_createFilter(min, max, remoteFiltering, dateSerializationFormat) {
if (remoteFiltering) {
this.filterMaker.make("date", [min, max]);
var userFilterPosition = this._excessFiltering() ? this.dataSource.filter()[USER_FILTER_POSITION] : this.dataSource.filter();
this.filterMaker.make("user", [userFilterPosition]);
this.dataSource.filter(this._combineRemoteFilter(dateSerializationFormat))
}
}
_filterAppointmentByResources(appointment, resources) {
var checkAppointmentResourceValues = (resourceName, resourceIndex) => {
var resourceGetter = this.dataAccessors.resources.getter[resourceName];
var resource;
if (isFunction(resourceGetter)) {
resource = resourceGetter(appointment)
}
var appointmentResourceValues = wrapToArray(resource);
var resourceData = map(resources[resourceIndex].items, item => item.id);
for (var j = 0; j < appointmentResourceValues.length; j++) {
if (inArray(appointmentResourceValues[j], resourceData) > -1) {
return true
}
}
return false
};
var result = false;
for (var i = 0; i < resources.length; i++) {
var resourceName = resources[i].name;
result = checkAppointmentResourceValues(resourceName, i);
if (!result) {
return false
}
}
return result
}
_filterAppointmentByRRule(appointment, min, max, startDayHour, endDayHour, firstDayOfWeek) {
var recurrenceRule = appointment.recurrenceRule;
var recurrenceException = appointment.recurrenceException;
var allDay = appointment.allDay;
var result = true;
var appointmentStartDate = appointment.startDate;
var appointmentEndDate = appointment.endDate;
var recurrenceProcessor = getRecurrenceProcessor();
if (allDay || _appointmentPartInInterval(appointmentStartDate, appointmentEndDate, startDayHour, endDayHour)) {
var [trimMin, trimMax] = getTrimDates(min, max);
min = trimMin;
max = new Date(trimMax.getTime() - toMs("minute"))
}
if (recurrenceRule && !recurrenceProcessor.isValidRecurrenceRule(recurrenceRule)) {
result = appointmentEndDate > min && appointmentStartDate <= max
}
if (result && recurrenceProcessor.isValidRecurrenceRule(recurrenceRule)) {
result = recurrenceProcessor.hasRecurrence({
rule: recurrenceRule,
exception: recurrenceException,
start: appointmentStartDate,
end: appointmentEndDate,
min: min,
max: max,
firstDayOfWeek: firstDayOfWeek
})
}
return result
}
filterLoadedAppointments(filterOptions, preparedItems) {
var filteredItems = this.filterPreparedItems(filterOptions, preparedItems);
return filteredItems.map(_ref => {
var {
rawAppointment: rawAppointment
} = _ref;
return rawAppointment
})
}
filterPreparedItems(filterOptions, preparedItems) {
var combinedFilter = this._createAppointmentFilter(filterOptions);
return query(preparedItems || this.preparedItems).filter(combinedFilter).toArray()
}
filterAllDayAppointments(filterOptions) {
var combinedFilter = this._createAllDayAppointmentFilter(filterOptions);
return query(this.preparedItems).filter(combinedFilter).toArray().map(_ref2 => {
var {
rawAppointment: rawAppointment
} = _ref2;
return rawAppointment
})
}
}
export class AppointmentFilterVirtualStrategy extends AppointmentFilterBaseStrategy {
get strategyName() {
return FilterStrategies.virtual
}
get resources() {
return this.options.resources
}
filter(preparedItems) {
var hourMs = toMs("hour");
var isCalculateStartAndEndDayHour = calculateIsDateAndTimeView(this.viewType);
var checkIntersectViewport = isCalculateStartAndEndDayHour && "horizontal" === this.viewDirection;
var isAllDayWorkspace = !this.supportAllDayRow;
var showAllDayAppointments = this.showAllDayPanel || isAllDayWorkspace;
var endViewDate = this.viewDataProvider.getLastViewDateByEndDayHour(this.viewEndDayHour);
var filterOptions = [];
var groupsInfo = this.viewDataProvider.getCompletedGroupsInfo();
groupsInfo.forEach(item => {
var groupIndex = item.groupIndex;
var groupStartDate = item.startDate;
var groupEndDate = new Date(Math.min(item.endDate, endViewDate));
var startDayHour = isCalculateStartAndEndDayHour ? groupStartDate.getHours() : this.viewStartDayHour;
var endDayHour = isCalculateStartAndEndDayHour ? startDayHour + groupStartDate.getMinutes() / 60 + (groupEndDate - groupStartDate) / hourMs : this.viewEndDayHour;
var resources = this._getPrerenderFilterResources(groupIndex);
var allDayPanel = this.viewDataProvider.getAllDayPanel(groupIndex);
var supportAllDayAppointment = isAllDayWorkspace || !!showAllDayAppointments && (null === allDayPanel || void 0 === allDayPanel ? void 0 : allDayPanel.length) > 0;
filterOptions.push({
isVirtualScrolling: true,
startDayHour: startDayHour,
endDayHour: endDayHour,
viewStartDayHour: this.viewStartDayHour,
viewEndDayHour: this.viewEndDayHour,
min: groupStartDate,
max: groupEndDate,
supportMultiDayAppointments: isSupportMultiDayAppointments(this.viewType),
allDay: supportAllDayAppointment,
resources: resources,
firstDayOfWeek: this.firstDayOfWeek,
checkIntersectViewport: checkIntersectViewport
})
});
return this.filterLoadedAppointments({
filterOptions: filterOptions,
groupCount: this.groupCount
}, preparedItems)
}
filterPreparedItems(_ref3, preparedItems) {
var {
filterOptions: filterOptions,
groupCount: groupCount
} = _ref3;
var combinedFilters = [];
var itemsToFilter = preparedItems || this.preparedItems;
var needPreFilter = groupCount > 0;
if (needPreFilter) {
itemsToFilter = itemsToFilter.filter(_ref4 => {
var {
rawAppointment: rawAppointment
} = _ref4;
for (var i = 0; i < filterOptions.length; ++i) {
var {
resources: resources
} = filterOptions[i];
if (this._filterAppointmentByResources(rawAppointment, resources)) {
return true
}
}
})
}
filterOptions.forEach(option => {
combinedFilters.length && combinedFilters.push("or");
var filter = this._createAppointmentFilter(option);
combinedFilters.push(filter)
});
return query(itemsToFilter).filter(combinedFilters).toArray()
}
hasAllDayAppointments() {
return this.filterAllDayAppointments({
viewStartDayHour: this.viewStartDayHour,
viewEndDayHour: this.viewEndDayHour
}).length > 0
}
_getPrerenderFilterResources(groupIndex) {
var cellGroup = this.viewDataProvider.getCellsGroup(groupIndex);
return getResourcesDataByGroups(this.loadedResources, this.resources, [cellGroup])
}
}