import { EventStore , filterEventStoreDefs } from './structs/event-store' ; import { DateSpan } from './structs/date-span' ; import { rangeContainsRange , rangesIntersect , DateRange , OpenDateRange } from './datelib/date-range' ; import { EventApi } from './api/EventApi' ; import { compileEventUis } from './component/event-rendering' ; import { excludeInstances } from './reducers/eventStore' ; import { EventInteractionState } from './interactions/event-interaction-state' ; import { SplittableProps } from './component/event-splitting' ; import { mapHash } from './util/object' ; import { CalendarContext } from './CalendarContext' ; import { buildDateSpanApiWithContext } from './calendar-utils' ; import { Constraint } from './structs/constraint' ; import { expandRecurring } from './structs/recurring-event' ; import { DateProfile } from './DateProfileGenerator' ; export function isInteractionValid ( interaction , dateProfile , context ) { let { instances } = interaction . mutatedEvents ; for ( let instanceId in instances ) { if ( ! rangeContainsRange ( dateProfile . validRange , instances [ instanceId ] . range ) ) { return false ; } } return isNewPropsValid ( { eventDrag : interaction } , context ) ; } export function isDateSelectionValid ( dateSelection , dateProfile , context ) { if ( ! rangeContainsRange ( dateProfile . validRange , dateSelection . range ) ) { return false ; } return isNewPropsValid ( { dateSelection } , context ) ; } function isNewPropsValid ( newProps , context ) { let calendarState = context . getCurrentData ( ) ; let props = { businessHours : calendarState . businessHours , dateSelection : '' , eventStore : calendarState . eventStore , eventUiBases : calendarState . eventUiBases , eventSelection : '' , eventDrag : null , eventResize : null , ... newProps , } ; return ( context . pluginHooks . isPropsValid || isPropsValid ) ( props , context ) ; } export function isPropsValid ( state , context , dateSpanMeta = { } , filterConfig ? ) { if ( state . eventDrag && ! isInteractionPropsValid ( state , context , dateSpanMeta , filterConfig ) ) { return false ; } if ( state . dateSelection && ! isDateSelectionPropsValid ( state , context , dateSpanMeta , filterConfig ) ) { return false ; } return true ; } function isInteractionPropsValid ( state , context , dateSpanMeta , filterConfig ) { let currentState = context . getCurrentData ( ) ; let interaction = state . eventDrag ; let subjectEventStore = interaction . mutatedEvents ; let subjectDefs = subjectEventStore . defs ; let subjectInstances = subjectEventStore . instances ; let subjectConfigs = compileEventUis ( subjectDefs , interaction . isEvent ? state . eventUiBases : { '' : currentState . selectionConfig } ) ; if ( filterConfig ) { subjectConfigs = mapHash ( subjectConfigs , filterConfig ) ; } let otherEventStore = excludeInstances ( state . eventStore , interaction . affectedEvents . instances ) ; let otherDefs = otherEventStore . defs ; let otherInstances = otherEventStore . instances ; let otherConfigs = compileEventUis ( otherDefs , state . eventUiBases ) ; for ( let subjectInstanceId in subjectInstances ) { let subjectInstance = subjectInstances [ subjectInstanceId ] ; let subjectRange = subjectInstance . range ; let subjectConfig = subjectConfigs [ subjectInstance . defId ] ; let subjectDef = subjectDefs [ subjectInstance . defId ] ; if ( ! allConstraintsPass ( subjectConfig . constraints , subjectRange , otherEventStore , state . businessHours , context ) ) { return false ; } let { eventOverlap } = context . options ; let eventOverlapFunc = typeof eventOverlap === 'function' ? eventOverlap : null ; for ( let otherInstanceId in otherInstances ) { let otherInstance = otherInstances [ otherInstanceId ] ; if ( rangesIntersect ( subjectRange , otherInstance . range ) ) { let otherOverlap = otherConfigs [ otherInstance . defId ] . overlap ; if ( otherOverlap === false && interaction . isEvent ) { return false ; } if ( subjectConfig . overlap === false ) { return false ; } if ( eventOverlapFunc && ! eventOverlapFunc ( new EventApi ( context , otherDefs [ otherInstance . defId ] , otherInstance ) , new EventApi ( context , subjectDef , subjectInstance ) ) ) { return false ; } } } let calendarEventStore = currentState . eventStore ; for ( let subjectAllow of subjectConfig . allows ) { let subjectDateSpan = { ... dateSpanMeta , range : subjectInstance . range , allDay : subjectDef . allDay , } ; let origDef = calendarEventStore . defs [ subjectDef . defId ] ; let origInstance = calendarEventStore . instances [ subjectInstanceId ] ; let eventApi ; if ( origDef ) { eventApi = new EventApi ( context , origDef , origInstance ) ; } else { eventApi = new EventApi ( context , subjectDef ) ; } if ( ! subjectAllow ( buildDateSpanApiWithContext ( subjectDateSpan , context ) , eventApi ) ) { return false ; } } } return true ; } function isDateSelectionPropsValid ( state , context , dateSpanMeta , filterConfig ) { let relevantEventStore = state . eventStore ; let relevantDefs = relevantEventStore . defs ; let relevantInstances = relevantEventStore . instances ; let selection = state . dateSelection ; let selectionRange = selection . range ; let { selectionConfig } = context . getCurrentData ( ) ; if ( filterConfig ) { selectionConfig = filterConfig ( selectionConfig ) ; } if ( ! allConstraintsPass ( selectionConfig . constraints , selectionRange , relevantEventStore , state . businessHours , context ) ) { return false ; } let { selectOverlap } = context . options ; let selectOverlapFunc = typeof selectOverlap === 'function' ? selectOverlap : null ; for ( let relevantInstanceId in relevantInstances ) { let relevantInstance = relevantInstances [ relevantInstanceId ] ; if ( rangesIntersect ( selectionRange , relevantInstance . range ) ) { if ( selectionConfig . overlap === false ) { return false ; } if ( selectOverlapFunc && ! selectOverlapFunc ( new EventApi ( context , relevantDefs [ relevantInstance . defId ] , relevantInstance ) , null ) ) { return false ; } } } for ( let selectionAllow of selectionConfig . allows ) { let fullDateSpan = { ... dateSpanMeta , ... selection } ; if ( ! selectionAllow ( buildDateSpanApiWithContext ( fullDateSpan , context ) , null ) ) { return false ; } } return true ; } function allConstraintsPass ( constraints , subjectRange , otherEventStore , businessHoursUnexpanded , context ) { for ( let constraint of constraints ) { if ( ! anyRangesContainRange ( constraintToRanges ( constraint , subjectRange , otherEventStore , businessHoursUnexpanded , context ) , subjectRange ) ) { return false ; } } return true ; } function constraintToRanges ( constraint , subjectRange , otherEventStore , businessHoursUnexpanded , context ) { if ( constraint === 'businessHours' ) { return eventStoreToRanges ( expandRecurring ( businessHoursUnexpanded , subjectRange , context ) ) ; } if ( typeof constraint === 'string' ) { return eventStoreToRanges ( filterEventStoreDefs ( otherEventStore , ( eventDef ) => eventDef . groupId === constraint ) ) ; } if ( typeof constraint === 'object' && constraint ) { return eventStoreToRanges ( expandRecurring ( constraint , subjectRange , context ) ) ; } return [ ] ; } function eventStoreToRanges ( eventStore ) { let { instances } = eventStore ; let ranges = [ ] ; for ( let instanceId in instances ) { ranges . push ( instances [ instanceId ] . range ) ; } return ranges ; } function anyRangesContainRange ( outerRanges , innerRange ) { for ( let outerRange of outerRanges ) { if ( rangeContainsRange ( outerRange , innerRange ) ) { return true ; } } return false ; }