UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

558 lines (557 loc) • 23.5 kB
/** * 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]) } }