UNPKG

@event-calendar/core

Version:

Full-sized drag & drop event calendar with resource & timeline views

224 lines (207 loc) 7.28 kB
import {getAbortSignal, tick, untrack} from 'svelte'; import { assign, cloneDate, createDate, createEvents, createResources, datesEqual, empty, isArray, isFunction, setMidnight, toISOString, toLocalDate, toViewWithLocalDates } from '#lib'; import {arrayProxy} from './proxy.svelte.js'; export function switchView(mainState) { return () => { // Dependencies let {options: {view}} = mainState; untrack(() => { let initComponent = mainState.setViewOptions(view); mainState.extensions = {}; mainState.features = []; mainState.viewComponent = initComponent(mainState); }); }; } export function loadEvents(mainState, loadingInvoker) { return () => { // Dependencies let {activeRange, fetchedRange: {events: fetchedRange}, viewDates, options: {events, eventSources, lazyFetching}} = mainState; untrack(() => { load( eventSources.map(source => isFunction(source.events) ? source.events : source), events, createEvents, result => mainState.events = arrayProxy(result), activeRange, fetchedRange, viewDates, true, lazyFetching, loadingInvoker ); }); }; } export function loadResources(mainState, loadingInvoker) { return () => { // Dependencies let {activeRange, fetchedRange: {resources: fetchedRange}, viewDates, options: {lazyFetching, refetchResourcesOnNavigate, resources}} = mainState; untrack(() => { load( isArray(resources) ? [] : [resources], resources, createResources, result => mainState.resources = arrayProxy(result), activeRange, fetchedRange, viewDates, refetchResourcesOnNavigate, lazyFetching, loadingInvoker ); }); }; } function load(sources, defaultResult, parseResult, applyResult, activeRange, fetchedRange, viewDates, refetchOnNavigate, lazyFetching, loading) { if (empty(viewDates)) { return; } if (empty(sources)) { applyResult(defaultResult); return; } // Do not fetch if new range is within the previous one if ( (refetchOnNavigate || !fetchedRange.start) && ( !lazyFetching || !fetchedRange.start || fetchedRange.start > activeRange.start || fetchedRange.end < activeRange.end ) ) { let result = []; // Prepare handlers let failure = e => loading.stop(); let success = data => { result = result.concat(parseResult(data)); applyResult(result); loading.stop(); }; // Prepare other stuff let startStr = toISOString(activeRange.start) let endStr = toISOString(activeRange.end); // Loop over event sources for (let source of sources) { loading.start(); if (isFunction(source)) { // Source as a function let result = source(refetchOnNavigate ? { start: toLocalDate(activeRange.start), end: toLocalDate(activeRange.end), startStr, endStr } : {}, success, failure); if (result !== undefined) { Promise.resolve(result).then(success, failure); } } else { // Source as a JSON feed // Prepare params let params = isFunction(source.extraParams) ? source.extraParams() : assign({}, source.extraParams); if (refetchOnNavigate) { params.start = startStr; params.end = endStr; } params = new URLSearchParams(params); // Prepare fetch let url = source.url, headers = {}, body; if (['GET', 'HEAD'].includes(source.method)) { url += (url.includes('?') ? '&' : '?') + params; } else { headers['content-type'] = 'application/x-www-form-urlencoded;charset=UTF-8'; body = String(params); // Safari 10.1 doesn't convert to string automatically } // Do the fetch fetch(url, { method: source.method, headers, body, signal: getAbortSignal(), credentials: 'same-origin' }) .then(response => response.json()) .then(success) .catch(failure); } } // Save current range for future requests assign(fetchedRange, activeRange); } } export function createLoadingInvoker(mainState) { let counter = 0; function invoke(value) { let {options: {loading}} = mainState; if (isFunction(loading)) { loading(value); } } return { start: () => ++counter === 1 && invoke(true), stop: () => --counter === 0 && invoke(false) }; } export function setNowAndToday(mainState) { return () => { // Now and today let interval = setInterval(() => { let now = createDate(); let today = setMidnight(cloneDate(now)); mainState.now = now; if (!datesEqual(mainState.today, today)) { mainState.today = today; } }, 1000); return () => clearInterval(interval); } } export function runDatesSet(mainState) { return () => { // Dependencies let {activeRange, options: {datesSet}} = mainState; untrack(() => { if (isFunction(datesSet)) { datesSet({ start: toLocalDate(activeRange.start), end: toLocalDate(activeRange.end), startStr: toISOString(activeRange.start), endStr: toISOString(activeRange.end), view: toViewWithLocalDates(mainState.view) }); } }); } } export function runEventAllUpdated(mainState) { let timer; return () => { // Dependencies let {filteredEvents, options: {eventAllUpdated}} = mainState; untrack(() => { if (isFunction(eventAllUpdated)) { if (!timer) { timer = setTimeout(() => { timer = null; eventAllUpdated({view: toViewWithLocalDates(mainState.view)}); }); } } }); } } export function runViewDidMount(mainState) { return () => { // Dependencies let {options: {view, viewDidMount}} = mainState; untrack(() => { if (isFunction(viewDidMount)) { tick().then(() => viewDidMount({ view: toViewWithLocalDates(mainState.view) })); } }); }; }