@event-calendar/core 5.2.4 → 5.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,91 +1,140 @@
1
1
  import {getAbortSignal, tick, untrack} from 'svelte';
2
2
  import {
3
- assign, cloneDate, createDate, createEvents, datesEqual, isFunction, setMidnight, toISOString, toLocalDate,
4
- toViewWithLocalDates
3
+ assign, cloneDate, createDate, createEvents, createResources, datesEqual, empty, isArray, isFunction, setMidnight,
4
+ toISOString, toLocalDate, toViewWithLocalDates
5
5
  } from '#lib';
6
6
 
7
- export function loadEvents(mainState) {
8
- let fetching = 0;
7
+ export function loadEvents(mainState, loadingInvoker) {
9
8
  return () => {
10
9
  // Dependencies
11
- let {activeRange, fetchedRange, viewDates, options: {events, eventSources, lazyFetching, loading}} = mainState;
10
+ let {activeRange, fetchedRange: {events: fetchedRange}, viewDates,
11
+ options: {events, eventSources, lazyFetching}} = mainState;
12
12
 
13
13
  untrack(() => {
14
- if (!viewDates.length) {
15
- return;
16
- }
17
- if (!eventSources.length) {
18
- mainState.events = events;
19
- }
20
- // Do not fetch if new range is within the previous one
21
- if (
22
- !fetchedRange.start ||
23
- fetchedRange.start > activeRange.start ||
24
- fetchedRange.end < activeRange.end ||
25
- !lazyFetching
26
- ) {
27
- // Call loading hook
28
- if (isFunction(loading) && !fetching) {
29
- loading(true);
14
+ load(
15
+ eventSources.map(source => isFunction(source.events) ? source.events : source),
16
+ events,
17
+ createEvents,
18
+ result => mainState.events = result,
19
+ activeRange,
20
+ fetchedRange,
21
+ viewDates,
22
+ true,
23
+ lazyFetching,
24
+ loadingInvoker
25
+ );
26
+ });
27
+ };
28
+ }
29
+
30
+ export function loadResources(mainState, loadingInvoker) {
31
+ return () => {
32
+ // Dependencies
33
+ let {activeRange, fetchedRange: {resources: fetchedRange}, viewDates,
34
+ options: {lazyFetching, refetchResourcesOnNavigate, resources}} = mainState;
35
+
36
+ untrack(() => {
37
+ load(
38
+ isArray(resources) ? [] : [resources],
39
+ resources,
40
+ createResources,
41
+ result => mainState.resources = result,
42
+ activeRange,
43
+ fetchedRange,
44
+ viewDates,
45
+ refetchResourcesOnNavigate,
46
+ lazyFetching,
47
+ loadingInvoker
48
+ );
49
+ });
50
+ };
51
+ }
52
+
53
+ function load(sources, defaultResult, parseResult, applyResult, activeRange, fetchedRange, viewDates, refetchOnNavigate, lazyFetching, loading) {
54
+ if (empty(viewDates)) {
55
+ return;
56
+ }
57
+ if (empty(sources)) {
58
+ applyResult(defaultResult);
59
+ return;
60
+ }
61
+ // Do not fetch if new range is within the previous one
62
+ if (
63
+ (refetchOnNavigate || !fetchedRange.start) &&
64
+ (
65
+ !lazyFetching ||
66
+ !fetchedRange.start ||
67
+ fetchedRange.start > activeRange.start ||
68
+ fetchedRange.end < activeRange.end
69
+ )
70
+ ) {
71
+ let result = [];
72
+ // Prepare handlers
73
+ let failure = e => loading.stop();
74
+ let success = data => {
75
+ result = result.concat(parseResult(data));
76
+ applyResult(result);
77
+ loading.stop();
78
+ };
79
+ // Prepare other stuff
80
+ let startStr = toISOString(activeRange.start)
81
+ let endStr = toISOString(activeRange.end);
82
+ // Loop over event sources
83
+ for (let source of sources) {
84
+ loading.start();
85
+ if (isFunction(source)) {
86
+ // Source as a function
87
+ let result = source(refetchOnNavigate ? {
88
+ start: toLocalDate(activeRange.start),
89
+ end: toLocalDate(activeRange.end),
90
+ startStr,
91
+ endStr
92
+ } : {}, success, failure);
93
+ if (result !== undefined) {
94
+ Promise.resolve(result).then(success, failure);
30
95
  }
31
- let stopLoading = () => {
32
- if (--fetching === 0 && isFunction(loading)) {
33
- loading(false);
34
- }
35
- };
36
- let events = [];
37
- // Prepare handlers
38
- let failure = e => stopLoading();
39
- let success = data => {
40
- events = events.concat(createEvents(data));
41
- mainState.events = events;
42
- stopLoading();
43
- };
44
- // Prepare other stuff
45
- let startStr = toISOString(activeRange.start)
46
- let endStr = toISOString(activeRange.end);
47
- // Loop over event sources
48
- for (let source of eventSources) {
49
- if (isFunction(source.events)) {
50
- // Events as a function
51
- let result = source.events({
52
- start: toLocalDate(activeRange.start),
53
- end: toLocalDate(activeRange.end),
54
- startStr,
55
- endStr
56
- }, success, failure);
57
- if (result !== undefined) {
58
- Promise.resolve(result).then(success, failure);
59
- }
60
- } else {
61
- // Events as a JSON feed
62
- // Prepare params
63
- let params = isFunction(source.extraParams) ? source.extraParams() : assign({}, source.extraParams);
64
- params.start = startStr;
65
- params.end = endStr;
66
- params = new URLSearchParams(params);
67
- // Prepare fetch
68
- let url = source.url, headers = {}, body;
69
- if (['GET', 'HEAD'].includes(source.method)) {
70
- url += (url.includes('?') ? '&' : '?') + params;
71
- } else {
72
- headers['content-type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
73
- body = String(params); // Safari 10.1 doesn't convert to string automatically
74
- }
75
- // Do the fetch
76
- fetch(url, {
77
- method: source.method, headers, body, signal: getAbortSignal(), credentials: 'same-origin'
78
- })
79
- .then(response => response.json())
80
- .then(success)
81
- .catch(failure);
82
- }
83
- ++fetching;
96
+ } else {
97
+ // Source as a JSON feed
98
+ // Prepare params
99
+ let params = isFunction(source.extraParams) ? source.extraParams() : assign({}, source.extraParams);
100
+ if (refetchOnNavigate) {
101
+ params.start = startStr;
102
+ params.end = endStr;
84
103
  }
85
- // Save current range for future requests
86
- assign(fetchedRange, activeRange);
104
+ params = new URLSearchParams(params);
105
+ // Prepare fetch
106
+ let url = source.url, headers = {}, body;
107
+ if (['GET', 'HEAD'].includes(source.method)) {
108
+ url += (url.includes('?') ? '&' : '?') + params;
109
+ } else {
110
+ headers['content-type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
111
+ body = String(params); // Safari 10.1 doesn't convert to string automatically
112
+ }
113
+ // Do the fetch
114
+ fetch(url, {
115
+ method: source.method, headers, body, signal: getAbortSignal(), credentials: 'same-origin'
116
+ })
117
+ .then(response => response.json())
118
+ .then(success)
119
+ .catch(failure);
87
120
  }
88
- });
121
+ }
122
+ // Save current range for future requests
123
+ assign(fetchedRange, activeRange);
124
+ }
125
+ }
126
+
127
+ export function createLoadingInvoker(options) {
128
+ let counter = 0;
129
+ function invoke(value) {
130
+ let {loading} = options;
131
+ if (isFunction(loading)) {
132
+ loading(value);
133
+ }
134
+ }
135
+ return {
136
+ start: () => ++counter === 1 && invoke(true),
137
+ stop: () => --counter === 0 && invoke(false)
89
138
  };
90
139
  }
91
140
 
@@ -1,6 +1,6 @@
1
1
  import {untrack} from 'svelte';
2
2
  import {
3
- createDate, createDateRange, createDuration, createEvents, createEventSources, createResources, entries,
3
+ createDate, createDateRange, createDuration, createEvents, createEventSources, createResources, entries, isArray,
4
4
  isFunction, keys, setMidnight
5
5
  } from '#lib';
6
6
  import {SvelteMap} from "svelte/reactivity";
@@ -55,6 +55,7 @@ function createOptions(plugins) {
55
55
  lazyFetching: true,
56
56
  loading: undefined,
57
57
  locale: undefined,
58
+ refetchResourcesOnNavigate: false,
58
59
  resources: [],
59
60
  selectable: false,
60
61
  theme: {
@@ -110,13 +111,13 @@ function createOptions(plugins) {
110
111
 
111
112
  function createParsers(plugins) {
112
113
  let parsers = {
113
- date: date => setMidnight(createDate(date)),
114
+ date: input => setMidnight(createDate(input)),
114
115
  duration: createDuration,
115
116
  events: createEvents,
116
117
  eventSources: createEventSources,
117
- hiddenDays: days => [...new Set(days)],
118
- highlightedDates: dates => dates.map(date => setMidnight(createDate(date))),
119
- resources: createResources,
118
+ hiddenDays: input => [...new Set(input)],
119
+ highlightedDates: input => input.map(item => setMidnight(createDate(item))),
120
+ resources: input => isArray(input) ? createResources(input) : input,
120
121
  validRange: createDateRange
121
122
  };
122
123
 
@@ -1,7 +1,9 @@
1
1
  import {SvelteMap} from 'svelte/reactivity';
2
2
  import {createDate, identity, intl, intlRange, setMidnight} from '#lib';
3
3
  import {optionsState} from './options.svelte.js';
4
- import {loadEvents, runDatesSet, runEventAllUpdated, runViewDidMount, setNowAndToday} from './effects.js';
4
+ import {
5
+ createLoadingInvoker, loadEvents, loadResources, runDatesSet, runEventAllUpdated, runViewDidMount, setNowAndToday
6
+ } from './effects.js';
5
7
  import {activeRange, currentRange, filteredEvents, view, viewDates, viewTitle} from './derived.js';
6
8
 
7
9
  export default class State {
@@ -18,11 +20,12 @@ export default class State {
18
20
  this.auxComponents = $state([]);
19
21
  this.currentRange = $derived.by(currentRange(this));
20
22
  this.activeRange = $derived.by(activeRange(this));
21
- this.fetchedRange = $state.raw({start: undefined, end: undefined});
23
+ this.fetchedRange = $state({events: {}, resources: {}});
22
24
  this.events = $state.raw([]);
23
25
  this.filteredEvents = $derived.by(filteredEvents(this));
24
26
  this.mainEl = $state();
25
27
  this.now = $state(createDate());
28
+ this.resources = $state.raw([]);
26
29
  this.today = $state(setMidnight(createDate()));
27
30
  this.intlEventTime = $derived.by(intlRange(this, 'eventTimeFormat'));
28
31
  this.intlDayHeader = $derived.by(intl(this, 'dayHeaderFormat'));
@@ -50,8 +53,10 @@ export default class State {
50
53
  }
51
54
 
52
55
  #initEffects() {
56
+ let loading = createLoadingInvoker(this.options);
53
57
  $effect.pre(setNowAndToday(this));
54
- $effect(loadEvents(this));
58
+ $effect(loadEvents(this, loading));
59
+ $effect(loadResources(this, loading));
55
60
  $effect(runDatesSet(this));
56
61
  $effect(runEventAllUpdated(this));
57
62
  $effect(runViewDidMount(this));