@event-calendar/core 0.8.0 → 0.8.3

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.
package/index.css CHANGED
@@ -1 +1 @@
1
- .ec-flex{display:flex}.ec-body.ec-month,.ec-days,.ec-day,.ec-day-title,.ec-resource{flex:1 1 0%;min-width:0;max-width:100%}.ec{display:flex;flex-direction:column}.ec ::-webkit-scrollbar{background:#fff}.ec ::-webkit-scrollbar-thumb{border:4px solid #fff;box-shadow:none;background:#dadce0;border-radius:8px;min-height:40px}.ec :hover::-webkit-scrollbar-thumb{background:#bdc1c6}.ec-hidden-scroll{display:none;overflow-y:scroll;visibility:hidden;flex-shrink:0}.ec-with-scroll .ec-hidden-scroll{display:block}.ec-toolbar{flex:0 0 auto;display:flex;justify-content:space-between;align-items:center;margin-bottom:1em}.ec-toolbar>*{margin-bottom:-0.5em}.ec-toolbar>*>*{margin-bottom:.5em}.ec-toolbar>*>*:not(:last-child){margin-right:.75em}.ec-title{margin:0}.ec-button{background-color:#fff;border:1px solid #ced4da;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem}.ec-button:not(:disabled){color:#212529;cursor:pointer}.ec-button:not(:disabled):hover,.ec-button.ec-active{background-color:#ececec;border-color:#b1bbc4}.ec-button-group{display:inline-flex}.ec-button-group .ec-button:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0;margin-left:-1px}.ec-button-group .ec-button:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.ec-icon{display:inline-block;width:1em}.ec-icon.ec-prev:after,.ec-icon.ec-next:after{content:"";position:relative;width:.5em;height:.5em;border-top:2px solid #212529;border-right:2px solid #212529;display:inline-block}.ec-icon.ec-prev:after{transform:rotate(-135deg) translate(-2px, 2px)}.ec-icon.ec-next:after{transform:rotate(45deg) translate(-2px, 2px)}.ec-header,.ec-body,.ec-days,.ec-day{border:1px solid #dadce0}.ec-header{display:flex;flex-shrink:0}.ec-header .ec-resource{flex-direction:column}.ec-header .ec-resource .ec-days{border-top-style:solid}.ec-header .ec-days{border-bottom:none}.ec-header .ec-day{min-height:24px;line-height:24px;text-align:center;overflow:hidden;text-overflow:ellipsis}.ec-body{position:relative;overflow-x:hidden;overflow-y:auto}.ec-body:not(.ec-list){border-top:none}.ec-sidebar{flex:0 0 auto;width:auto;max-width:100%;padding:0 4px 0 8px}.ec-content{display:flex}.ec-month .ec-content{flex-direction:column;height:100%}.ec-month.ec-uniform .ec-content{overflow:hidden}.ec-list .ec-content{flex-direction:column}.ec-resource{display:flex}.ec-days{display:flex;border-style:none none solid}.ec-days:last-child{border-bottom:none}.ec-month .ec-days,.ec-resource .ec-days{flex:1 0 auto}.ec-month.ec-uniform .ec-days{flex:1 1 0%;min-height:0}.ec-day{border-style:none none none solid}.ec-day.ec-today{background-color:#fcf8e3}.ec-day.ec-highlight{background-color:#e5f7fe}.ec-month.ec-body .ec-day{min-height:5em;position:relative}.ec-month.ec-uniform .ec-day{min-height:0}.ec-month .ec-day:first-child{border-left:none}.ec-day.ec-other-month .ec-day-head{opacity:.3}.ec-list .ec-day{flex:1 0 auto;background-color:#fff;border-style:solid none;padding:8px 14px;font-weight:bold;position:-webkit-sticky;position:sticky;top:0;z-index:2}.ec-list .ec-day:first-child{border-top:none}.ec-month .ec-day-head{text-align:right;padding:4px 4px 3px}.ec-month .ec-day-foot{position:absolute;bottom:0;padding:2px;font-size:.85em}.ec-month .ec-day-foot a{cursor:pointer}.ec-list .ec-day-side{float:right}.ec-list .ec-no-events{text-align:center;padding:5em 0}.ec-events{margin:0 6px 0 0}.ec-week .ec-events,.ec-events.ec-preview{position:relative}.ec-event{display:flex;flex-direction:column;padding:2px;color:#fff;box-sizing:border-box;box-shadow:0 0 1px 0 #dadce0;background-color:#039be5;border-radius:3px;font-size:.85em;line-height:1.5;z-index:1}.ec-month .ec-event{position:relative;flex-direction:row}.ec-week .ec-event{position:absolute}.ec-list .ec-event{flex-direction:row;padding:8px 14px;color:inherit;background-color:transparent;border-radius:0}.ec-event.ec-preview{cursor:pointer;position:absolute;z-index:1000;width:100%;-webkit-user-select:none;user-select:none;opacity:.8}.ec-event.ec-pointer{color:inherit;pointer-events:none;-webkit-user-select:none;user-select:none;position:absolute;z-index:0;box-shadow:none;display:none}.ec-day:hover .ec-event.ec-pointer{display:flex}.ec-event-tag{width:4px;border-radius:2px;margin-right:8px}.ec-event-time{overflow:hidden;white-space:nowrap;margin:0 0 1px 0;flex-shrink:0}.ec-month .ec-event-time{margin:0 3px 0 0;max-width:100%;text-overflow:ellipsis}.ec-event-title{overflow:hidden}.ec-month .ec-event-title{white-space:nowrap;text-overflow:ellipsis}.ec-week .ec-event-title{position:-webkit-sticky;position:sticky;top:0}.ec-list .ec-event-title{font-size:1rem}.ec-draggable{cursor:pointer;-webkit-user-select:none;user-select:none;touch-action:none}.ec-ghost{opacity:.5;-webkit-user-select:none;user-select:none;touch-action:none}.ec-bg-events{position:relative}.ec-bg-event{position:absolute;background-color:#dadce0;opacity:.3;width:100%}.ec-hidden-times{visibility:hidden;overflow-y:hidden;height:0}.ec-time,.ec-line{height:24px}.ec-time{position:relative;line-height:24px;top:-12px;text-align:right;white-space:nowrap}.ec-lines{width:8px}.ec-line:not(:first-child):after{content:"";position:absolute;width:100%;border-bottom:1px solid #dadce0;pointer-events:none}.ec-body:not(.ec-compact) .ec-line:nth-child(even):after{border-bottom-style:dotted}.ec-popup{position:absolute;top:0;width:110%;min-width:180px;z-index:1010;padding:8px 10px 14px;background-color:#fff;border-radius:6px;outline:1px solid transparent;box-shadow:0 1px 3px 0 rgba(60,64,67,.3),0 4px 8px 3px rgba(60,64,67,.15)}.ec-popup .ec-day-head{text-align:left;display:flex;justify-content:space-between}.ec-popup .ec-day-head a{cursor:pointer;font-size:1.5em;line-height:.8}.ec-popup .ec-events{margin:0}.ec-extra{position:relative}.ec-now-indicator{position:absolute;z-index:1005;width:100%;border-top:#ea4335 solid 2px}.ec-now-indicator:before{background:#ea4335;border-radius:50%;content:"";position:absolute;height:12px;margin-left:-7px;margin-top:-7px;width:12px}
1
+ .ec-flex{display:flex}.ec-body.ec-month,.ec-days,.ec-day,.ec-day-title,.ec-resource{flex:1 1 0%;min-width:0;max-width:100%}.ec{display:flex;flex-direction:column}.ec ::-webkit-scrollbar{background:#fff}.ec ::-webkit-scrollbar-thumb{border:4px solid #fff;box-shadow:none;background:#dadce0;border-radius:8px;min-height:40px}.ec :hover::-webkit-scrollbar-thumb{background:#bdc1c6}.ec-hidden-scroll{display:none;overflow-y:scroll;visibility:hidden;flex-shrink:0}.ec-with-scroll .ec-hidden-scroll{display:block}.ec-toolbar{flex:0 0 auto;display:flex;justify-content:space-between;align-items:center;margin-bottom:1em}.ec-toolbar>*{margin-bottom:-0.5em}.ec-toolbar>*>*{margin-bottom:.5em}.ec-toolbar>*>*:not(:last-child){margin-right:.75em}.ec-title{margin:0}.ec-button{background-color:#fff;border:1px solid #ced4da;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem}.ec-button:not(:disabled){color:#212529;cursor:pointer}.ec-button:not(:disabled):hover,.ec-button.ec-active{background-color:#ececec;border-color:#b1bbc4}.ec-button-group{display:inline-flex}.ec-button-group .ec-button:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0;margin-left:-1px}.ec-button-group .ec-button:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.ec-icon{display:inline-block;width:1em}.ec-icon.ec-prev:after,.ec-icon.ec-next:after{content:"";position:relative;width:.5em;height:.5em;border-top:2px solid #212529;border-right:2px solid #212529;display:inline-block}.ec-icon.ec-prev:after{transform:rotate(-135deg) translate(-2px, 2px)}.ec-icon.ec-next:after{transform:rotate(45deg) translate(-2px, 2px)}.ec-header,.ec-body,.ec-days,.ec-day{border:1px solid #dadce0}.ec-header{display:flex;flex-shrink:0}.ec-header .ec-resource{flex-direction:column}.ec-header .ec-resource .ec-days{border-top-style:solid}.ec-header .ec-days{border-bottom:none}.ec-header .ec-day{min-height:24px;line-height:24px;text-align:center;overflow:hidden;text-overflow:ellipsis}.ec-body{position:relative;overflow-x:hidden;overflow-y:auto}.ec-body:not(.ec-list){border-top:none}.ec-sidebar{flex:0 0 auto;width:auto;max-width:100%;padding:0 4px 0 8px}.ec-content{display:flex}.ec-month .ec-content{flex-direction:column;height:100%}.ec-month.ec-uniform .ec-content{overflow:hidden}.ec-list .ec-content{flex-direction:column}.ec-resource{display:flex}.ec-days{display:flex;border-style:none none solid}.ec-days:last-child{border-bottom:none}.ec-month .ec-days,.ec-resource .ec-days{flex:1 0 auto}.ec-month.ec-uniform .ec-days{flex:1 1 0%;min-height:0}.ec-day{border-style:none none none solid}.ec-day.ec-today{background-color:#fcf8e3}.ec-day.ec-highlight{background-color:#e5f7fe}.ec-month.ec-body .ec-day{min-height:5em;position:relative}.ec-month.ec-uniform .ec-day{min-height:0}.ec-month .ec-day:first-child{border-left:none}.ec-day.ec-other-month .ec-day-head{opacity:.3}.ec-list .ec-day{flex:1 0 auto;background-color:#fff;border-style:solid none;padding:8px 14px;font-weight:bold;position:-webkit-sticky;position:sticky;top:0;z-index:2}.ec-list .ec-day:first-child{border-top:none}.ec-month .ec-day-head{text-align:right;padding:4px 4px 3px}.ec-month .ec-day-foot{position:absolute;bottom:0;padding:2px;font-size:.85em}.ec-month .ec-day-foot a{cursor:pointer}.ec-list .ec-day-side{float:right}.ec-list .ec-no-events{text-align:center;padding:5em 0}.ec-events{margin:0 6px 0 0}.ec-week .ec-events,.ec-events.ec-preview{position:relative}.ec-event{display:flex;flex-direction:column;padding:2px;color:#fff;box-sizing:border-box;box-shadow:0 0 1px 0 #dadce0;background-color:#039be5;border-radius:3px;font-size:.85em;line-height:1.5;z-index:1}.ec-month .ec-event{position:relative;flex-direction:row}.ec-week .ec-event{position:absolute}.ec-list .ec-event{flex-direction:row;padding:8px 14px;color:inherit;background-color:transparent;border-radius:0}.ec-event.ec-preview{cursor:pointer;position:absolute;z-index:1000;width:100%;-webkit-user-select:none;user-select:none;opacity:.8}.ec-event.ec-pointer{color:inherit;pointer-events:none;-webkit-user-select:none;user-select:none;position:absolute;z-index:0;box-shadow:none;display:none}.ec-day:hover .ec-event.ec-pointer{display:flex}.ec-event-tag{width:4px;border-radius:2px;margin-right:8px}.ec-event-time{overflow:hidden;white-space:nowrap;margin:0 0 1px 0;flex-shrink:0}.ec-month .ec-event-time{margin:0 3px 0 0;max-width:100%;text-overflow:ellipsis}.ec-event-title{overflow:hidden}.ec-month .ec-event-title{white-space:nowrap;text-overflow:ellipsis}.ec-week .ec-event-title{position:-webkit-sticky;position:sticky;top:0}.ec-list .ec-event-title{font-size:1rem}.ec-draggable{cursor:pointer;-webkit-user-select:none;user-select:none;touch-action:none}.ec-ghost{opacity:.5;-webkit-user-select:none;user-select:none;touch-action:none}.ec-bg-events{position:relative}.ec-bg-event{position:absolute;background-color:#dadce0;opacity:.3;width:100%}.ec-hidden-times{visibility:hidden;overflow-y:hidden;height:0}.ec-time,.ec-line{height:24px}.ec-time{position:relative;line-height:24px;top:-12px;text-align:right;white-space:nowrap}.ec-lines{width:8px}.ec-line:not(:first-child):after{content:"";position:absolute;width:100%;border-bottom:1px solid #dadce0;pointer-events:none}.ec-body:not(.ec-compact) .ec-line:nth-child(even):after{border-bottom-style:dotted}.ec-popup{position:absolute;top:0;width:110%;min-width:180px;z-index:1010;padding:8px 10px 14px;background-color:#fff;border-radius:6px;outline:1px solid transparent;box-shadow:0 1px 3px 0 rgba(60,64,67,.3),0 4px 8px 3px rgba(60,64,67,.15)}.ec-popup .ec-day-head{text-align:left;display:flex;justify-content:space-between}.ec-popup .ec-day-head a{cursor:pointer;font-size:1.5em;line-height:.8}.ec-popup .ec-events{margin:0}.ec-extra{position:relative;height:100%;overflow:hidden;margin-left:-6.5px}.ec-now-indicator{position:absolute;z-index:1005;width:100%;border-top:#ea4335 solid 2px;pointer-events:none}.ec-now-indicator:before{background:#ea4335;border-radius:50%;content:"";position:absolute;height:12px;margin-top:-7px;width:12px;pointer-events:none}
package/index.js CHANGED
@@ -439,7 +439,7 @@ function parseOpts(opts, state) {
439
439
  }
440
440
  }
441
441
 
442
- /* packages/core/src/Buttons.svelte generated by Svelte v3.46.3 */
442
+ /* packages/core/src/Buttons.svelte generated by Svelte v3.46.4 */
443
443
 
444
444
  function get_each_context$1(ctx, list, i) {
445
445
  const child_ctx = ctx.slice();
@@ -842,7 +842,7 @@ class Buttons extends SvelteComponent {
842
842
  }
843
843
  }
844
844
 
845
- /* packages/core/src/Toolbar.svelte generated by Svelte v3.46.3 */
845
+ /* packages/core/src/Toolbar.svelte generated by Svelte v3.46.4 */
846
846
 
847
847
  function get_each_context(ctx, list, i) {
848
848
  const child_ctx = ctx.slice();
@@ -1212,7 +1212,7 @@ class Toolbar extends SvelteComponent {
1212
1212
  }
1213
1213
  }
1214
1214
 
1215
- /* packages/core/src/Calendar.svelte generated by Svelte v3.46.3 */
1215
+ /* packages/core/src/Calendar.svelte generated by Svelte v3.46.4 */
1216
1216
 
1217
1217
  function create_fragment(ctx) {
1218
1218
  let div;
package/package.json CHANGED
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "name": "@event-calendar/core",
3
- "version": "0.8.0",
3
+ "version": "0.8.3",
4
4
  "title": "Event Calendar Core package",
5
5
  "description": "Full-sized drag & drop event calendar with resource view",
6
- "keywords": ["calendar", "event", "resource", "full-sized"],
6
+ "keywords": [
7
+ "calendar",
8
+ "event",
9
+ "resource",
10
+ "full-sized"
11
+ ],
7
12
  "homepage": "https://vkurko.github.io/calendar/",
8
13
  "repository": {
9
14
  "type": "git",
@@ -12,13 +17,14 @@
12
17
  },
13
18
  "license": "MIT",
14
19
  "type": "module",
20
+ "svelte": "src/index.js",
15
21
  "exports": {
16
22
  ".": "./index.js",
17
23
  "./index.css": "./index.css",
18
24
  "./package.json": "./package.json"
19
25
  },
20
26
  "dependencies": {
21
- "@event-calendar/common": "~0.8.0",
22
- "svelte": "^3.46.3"
27
+ "@event-calendar/common": "~0.8.3",
28
+ "svelte": "^3.46.4"
23
29
  }
24
- }
30
+ }
@@ -0,0 +1,41 @@
1
+ <script>
2
+ import {getContext} from 'svelte';
3
+ import {createDate, cloneDate, subtractDay, addDuration, subtractDuration, setMidnight} from '@event-calendar/common';
4
+
5
+ export let buttons;
6
+
7
+ let {_currentRange, _viewTitle, buttonText, date, duration, hiddenDays, theme, view} = getContext('state');
8
+
9
+ let today = setMidnight(createDate()), isToday;
10
+
11
+ $: isToday = today >= $_currentRange.start && today < $_currentRange.end || null;
12
+
13
+ function prev() {
14
+ let d = subtractDuration($date, $duration);
15
+ if ($hiddenDays.length && $hiddenDays.length < 7) {
16
+ while ($hiddenDays.includes(d.getUTCDay())) {
17
+ subtractDay(d);
18
+ }
19
+ }
20
+ $date = d;
21
+ }
22
+
23
+ function next() {
24
+ $date = addDuration($date, $duration);
25
+ }
26
+ </script>
27
+
28
+ {#each buttons as button}
29
+ {#if button == ''}
30
+ {:else if button == 'title'}
31
+ <h2 class="{$theme.title}">{$_viewTitle}</h2>
32
+ {:else if button == 'prev'}
33
+ <button class="{$theme.button} ec-{button}" on:click={prev}><i class="{$theme.icon} ec-{button}"></i></button>
34
+ {:else if button === 'next'}
35
+ <button class="{$theme.button} ec-{button}" on:click={next}><i class="{$theme.icon} ec-{button}"></i></button>
36
+ {:else if button === 'today'}
37
+ <button class="{$theme.button} ec-{button}" on:click={() => $date = cloneDate(today)} disabled={isToday}>{$buttonText[button]}</button>
38
+ {:else}
39
+ <button class="{$theme.button}{$view === button ? ' ' + $theme.active : ''} ec-{button}" on:click={() => $view = button}>{$buttonText[button]}</button>
40
+ {/if}
41
+ {/each}
@@ -0,0 +1,91 @@
1
+ <script>
2
+ import '../index.css';
3
+ import {setContext} from 'svelte';
4
+ import {get} from 'svelte/store';
5
+ import {diff} from './storage/options';
6
+ import State from './storage/state';
7
+ import Toolbar from './Toolbar.svelte';
8
+ import {assign, toEventWithLocalDates, toViewWithLocalDates} from '@event-calendar/common';
9
+
10
+ export let plugins = [];
11
+ export let options = {};
12
+
13
+ let state = new State(plugins, options);
14
+ setContext('state', state);
15
+
16
+ let {_viewComponent, _interaction, _events, events, eventSources, height, theme} = state;
17
+
18
+ // Reactively update options that did change
19
+ $: for (let [name, value] of diff(options)) {
20
+ setOption(name, value);
21
+ }
22
+
23
+ export function setOption(name, value) {
24
+ if (state.hasOwnProperty(name)) {
25
+ if (state[name].parse) {
26
+ value = state[name].parse(value);
27
+ }
28
+ state[name].set(value);
29
+ }
30
+ return this;
31
+ }
32
+
33
+ export function getOption(name) {
34
+ return state.hasOwnProperty(name) ? get(state[name]) : undefined;
35
+ }
36
+
37
+ export function refetchEvents() {
38
+ state._fetchedRange.set({start: undefined, end: undefined});
39
+ return this;
40
+ }
41
+
42
+ export function getEventById(id) {
43
+ for (let event of get(state._events)) {
44
+ if (event.id == id) {
45
+ return toEventWithLocalDates(event);
46
+ }
47
+ }
48
+ return null;
49
+ }
50
+
51
+ export function addEvent(event) {
52
+ updateEvents(events => events.concat(state.events.parse([event])));
53
+ return this;
54
+ }
55
+
56
+ export function updateEvent(event) {
57
+ updateEvents(events => {
58
+ for (let e of events) {
59
+ if (e.id == event.id) {
60
+ assign(e, state.events.parse([event])[0]);
61
+ break;
62
+ }
63
+ }
64
+ return events;
65
+ });
66
+ return this;
67
+ }
68
+
69
+ export function removeEventById(id) {
70
+ updateEvents(events => events.filter(event => event.id != id));
71
+ return this;
72
+ }
73
+
74
+ export function getView() {
75
+ return toViewWithLocalDates(state._view.get());
76
+ }
77
+
78
+ function updateEvents(func) {
79
+ if ($eventSources.length) {
80
+ $_events = func($_events);
81
+ } else {
82
+ $events = func($events);
83
+ }
84
+ }
85
+ </script>
86
+
87
+ <div class="{$theme.calendar}" style="height: {$height}">
88
+ <Toolbar/>
89
+ <svelte:component this={$_viewComponent}/>
90
+ <svelte:component this={$_interaction.component}/>
91
+ </div>
@@ -0,0 +1,34 @@
1
+ <script>
2
+ import {getContext} from 'svelte';
3
+ import Buttons from './Buttons.svelte';
4
+
5
+ let {headerToolbar, theme} = getContext('state');
6
+
7
+ let sections = {
8
+ start: [],
9
+ center: [],
10
+ end: []
11
+ };
12
+
13
+ $: {
14
+ for (let key of Object.keys(sections)) {
15
+ sections[key] = $headerToolbar[key].split(' ').map(group => group.split(','));
16
+ }
17
+ }
18
+ </script>
19
+
20
+ <div class="{$theme.toolbar}">
21
+ {#each Object.keys(sections) as key}
22
+ <div>
23
+ {#each sections[key] as buttons}
24
+ {#if buttons.length > 1}
25
+ <div class="{$theme.buttonGroup}">
26
+ <Buttons {buttons}/>
27
+ </div>
28
+ {:else}
29
+ <Buttons {buttons}/>
30
+ {/if}
31
+ {/each}
32
+ </div>
33
+ {/each}
34
+ </div>
package/src/index.js ADDED
@@ -0,0 +1,3 @@
1
+ import Calendar from './Calendar.svelte';
2
+
3
+ export {Calendar as default};
package/src/index.scss ADDED
@@ -0,0 +1,507 @@
1
+ /* Grid */
2
+ .ec-flex {
3
+ display: flex;
4
+ }
5
+
6
+ .ec-body.ec-month,
7
+ .ec-days,
8
+ .ec-day,
9
+ .ec-day-title,
10
+ .ec-resource {
11
+ flex: 1 1 0%; /* % for ie11 */
12
+ min-width: 0;
13
+ max-width: 100%;
14
+ }
15
+
16
+ .ec {
17
+ display: flex;
18
+ flex-direction: column;
19
+
20
+ /* Scrollbar */
21
+ ::-webkit-scrollbar {
22
+ background: #fff;
23
+ }
24
+
25
+ ::-webkit-scrollbar-thumb {
26
+ border: 4px solid #fff;
27
+ box-shadow: none;
28
+ background: #dadce0;
29
+ border-radius: 8px;
30
+ min-height: 40px;
31
+ }
32
+
33
+ :hover::-webkit-scrollbar-thumb {
34
+ background: #bdc1c6;
35
+ }
36
+ }
37
+
38
+ .ec-hidden-scroll {
39
+ display: none;
40
+ overflow-y: scroll;
41
+ visibility: hidden;
42
+ flex-shrink: 0;
43
+
44
+ .ec-with-scroll & {
45
+ display: block;
46
+ }
47
+ }
48
+
49
+ /* Toolbar */
50
+ .ec-toolbar {
51
+ flex: 0 0 auto;
52
+ display: flex;
53
+ justify-content: space-between;
54
+ align-items: center;
55
+ margin-bottom: 1em;
56
+
57
+ > * {
58
+ margin-bottom: -.5em;
59
+
60
+ > * {
61
+ margin-bottom: .5em;
62
+
63
+ &:not(:last-child) {
64
+ margin-right: .75em;
65
+ }
66
+ }
67
+ }
68
+ }
69
+
70
+ .ec-title {
71
+ margin: 0;
72
+ }
73
+
74
+ .ec-button {
75
+ background-color: #fff;
76
+ border: 1px solid #ced4da;
77
+ padding: .375rem .75rem;
78
+ font-size: 1rem;
79
+ line-height: 1.5;
80
+ border-radius: .25rem;
81
+
82
+ &:not(:disabled) {
83
+ color: #212529;
84
+ cursor: pointer;
85
+ }
86
+
87
+ &:not(:disabled):hover,
88
+ &.ec-active {
89
+ background-color: #ececec;
90
+ border-color: #b1bbc4;
91
+ }
92
+ }
93
+
94
+ .ec-button-group {
95
+ display: inline-flex;
96
+
97
+ .ec-button:not(:first-child) {
98
+ border-top-left-radius: 0;
99
+ border-bottom-left-radius: 0;
100
+ margin-left: -1px;
101
+ }
102
+
103
+ .ec-button:not(:last-child) {
104
+ border-top-right-radius: 0;
105
+ border-bottom-right-radius: 0;
106
+ }
107
+ }
108
+
109
+ .ec-icon {
110
+ display: inline-block;
111
+ width: 1em;
112
+
113
+ &.ec-prev:after,
114
+ &.ec-next:after {
115
+ content: '';
116
+ position: relative;
117
+ width: .5em;
118
+ height: .5em;
119
+ border-top: 2px solid #212529;
120
+ border-right: 2px solid #212529;
121
+ display: inline-block;
122
+ }
123
+
124
+ &.ec-prev:after {
125
+ transform: rotate(-135deg) translate(-2px, 2px);
126
+ }
127
+
128
+ &.ec-next:after {
129
+ transform: rotate(45deg) translate(-2px, 2px);
130
+ }
131
+ }
132
+
133
+ /* Header */
134
+ .ec-header,
135
+ .ec-body,
136
+ .ec-days,
137
+ .ec-day {
138
+ border: 1px solid #dadce0;
139
+ }
140
+
141
+ .ec-header {
142
+ display: flex;
143
+ flex-shrink: 0;
144
+
145
+ .ec-resource {
146
+ flex-direction: column;
147
+
148
+ .ec-days {
149
+ border-top-style: solid;
150
+ }
151
+ }
152
+
153
+ .ec-days {
154
+ border-bottom: none;
155
+ }
156
+
157
+ .ec-day {
158
+ min-height: 24px;
159
+ line-height: 24px;
160
+ text-align: center;
161
+ overflow: hidden;
162
+ text-overflow: ellipsis;
163
+ }
164
+ }
165
+
166
+ /* Body */
167
+ .ec-body {
168
+ position: relative;
169
+ overflow-x: hidden;
170
+ overflow-y: auto;
171
+
172
+ &:not(.ec-list) {
173
+ border-top: none;
174
+ }
175
+ }
176
+
177
+ .ec-sidebar {
178
+ flex: 0 0 auto;
179
+ width: auto;
180
+ max-width: 100%;
181
+ padding: 0 4px 0 8px;
182
+ }
183
+
184
+ .ec-content {
185
+ display: flex;
186
+
187
+ .ec-month & {
188
+ flex-direction: column;
189
+ height: 100%; /* ie11 */
190
+ }
191
+
192
+ .ec-month.ec-uniform & {
193
+ overflow: hidden;
194
+ }
195
+
196
+ .ec-list & {
197
+ flex-direction: column;
198
+ }
199
+ }
200
+
201
+ .ec-resource {
202
+ display: flex;
203
+ }
204
+
205
+ .ec-days {
206
+ display: flex;
207
+ border-style: none none solid;
208
+
209
+ &:last-child {
210
+ border-bottom: none;
211
+ }
212
+
213
+ .ec-month &,
214
+ .ec-resource & {
215
+ flex: 1 0 auto; /* ie11 */
216
+ }
217
+
218
+ .ec-month.ec-uniform & {
219
+ flex: 1 1 0%;
220
+ min-height: 0;
221
+ }
222
+ }
223
+
224
+ .ec-day {
225
+ border-style: none none none solid;
226
+
227
+ &.ec-today {
228
+ background-color: #fcf8e3;
229
+ }
230
+
231
+ &.ec-highlight {
232
+ background-color: #e5f7fe;
233
+ }
234
+
235
+ .ec-month.ec-body & {
236
+ min-height: 5em;
237
+ position: relative;
238
+ }
239
+
240
+ .ec-month.ec-uniform & {
241
+ min-height: 0;
242
+ }
243
+
244
+ .ec-month &:first-child {
245
+ border-left: none;
246
+ }
247
+
248
+ &.ec-other-month .ec-day-head {
249
+ opacity: .3;
250
+ }
251
+
252
+ .ec-list & {
253
+ flex: 1 0 auto; /* ie11 */
254
+ background-color: #fff;
255
+ border-style: solid none;
256
+ padding: 8px 14px;
257
+ font-weight: bold;
258
+ position: sticky;
259
+ top: 0;
260
+ z-index: 2;
261
+
262
+ &:first-child {
263
+ border-top: none;
264
+ }
265
+ }
266
+ }
267
+
268
+ .ec-month {
269
+ .ec-day-head {
270
+ text-align: right;
271
+ padding: 4px 4px 3px;
272
+ }
273
+
274
+ .ec-day-foot {
275
+ position: absolute;
276
+ bottom: 0;
277
+ padding: 2px;
278
+ font-size: .85em;
279
+
280
+ a {
281
+ cursor: pointer;
282
+ }
283
+ }
284
+ }
285
+
286
+ .ec-list {
287
+ .ec-day-side {
288
+ float: right;
289
+ }
290
+
291
+ .ec-no-events {
292
+ text-align: center;
293
+ padding: 5em 0;
294
+ }
295
+ }
296
+
297
+ .ec-events {
298
+ margin: 0 6px 0 0;
299
+
300
+ .ec-week &,
301
+ &.ec-preview {
302
+ position: relative;
303
+ }
304
+ }
305
+
306
+ .ec-event {
307
+ display: flex;
308
+ flex-direction: column;
309
+ padding: 2px;
310
+ color: #fff;
311
+ box-sizing: border-box;
312
+ box-shadow: 0 0 1px 0 #dadce0;
313
+ background-color: #039be5;
314
+ border-radius: 3px;
315
+ font-size: .85em;
316
+ line-height: 1.5;
317
+ z-index: 1; // put it above the pointer event (for multi-day events in month view)
318
+
319
+ .ec-month & {
320
+ position: relative;
321
+ flex-direction: row;
322
+ }
323
+
324
+ .ec-week & {
325
+ position: absolute;
326
+ }
327
+
328
+ .ec-list & {
329
+ flex-direction: row;
330
+ padding: 8px 14px;
331
+ color: inherit;
332
+ background-color: transparent;
333
+ border-radius: 0;
334
+ }
335
+
336
+ &.ec-preview {
337
+ cursor: pointer;
338
+ position: absolute;
339
+ z-index: 1000;
340
+ width: 100%;
341
+ user-select: none;
342
+ opacity: .8;
343
+ }
344
+
345
+ &.ec-pointer {
346
+ color: inherit;
347
+ pointer-events: none;
348
+ user-select: none;
349
+ position: absolute;
350
+ z-index: 0;
351
+ box-shadow: none;
352
+ display: none;
353
+ .ec-day:hover & {
354
+ display: flex;
355
+ }
356
+ }
357
+ }
358
+
359
+ .ec-event-tag {
360
+ width: 4px;
361
+ border-radius: 2px;
362
+ margin-right: 8px;
363
+ }
364
+
365
+ .ec-event-time {
366
+ overflow: hidden;
367
+ white-space: nowrap;
368
+ margin: 0 0 1px 0;
369
+ flex-shrink: 0;
370
+
371
+ .ec-month & {
372
+ margin: 0 3px 0 0;
373
+ max-width: 100%;
374
+ text-overflow: ellipsis;
375
+ }
376
+ }
377
+
378
+ .ec-event-title {
379
+ overflow: hidden;
380
+
381
+ .ec-month & {
382
+ white-space: nowrap;
383
+ text-overflow: ellipsis;
384
+ }
385
+
386
+ .ec-week & {
387
+ position: sticky;
388
+ top: 0;
389
+ }
390
+
391
+ .ec-list & {
392
+ font-size: 1rem;
393
+ }
394
+ }
395
+
396
+ .ec-draggable {
397
+ cursor: pointer;
398
+ user-select: none;
399
+ touch-action: none;
400
+ }
401
+
402
+ .ec-ghost {
403
+ opacity: .5;
404
+ user-select: none;
405
+ touch-action: none;
406
+ }
407
+
408
+ .ec-bg-events {
409
+ position: relative;
410
+ }
411
+
412
+ .ec-bg-event {
413
+ position: absolute;
414
+ background-color: #dadce0;
415
+ opacity: 0.3;
416
+ width: 100%;
417
+ }
418
+
419
+ .ec-hidden-times {
420
+ visibility: hidden;
421
+ overflow-y: hidden;
422
+ height: 0;
423
+ }
424
+
425
+ .ec-time,
426
+ .ec-line {
427
+ height: 24px;
428
+ }
429
+
430
+ .ec-time {
431
+ position: relative;
432
+ line-height: 24px;
433
+ top: -12px;
434
+ text-align: right;
435
+ white-space: nowrap;
436
+ }
437
+
438
+ .ec-lines {
439
+ width: 8px;
440
+ }
441
+
442
+ .ec-line:not(:first-child):after {
443
+ content: '';
444
+ position: absolute;
445
+ width: 100%;
446
+ border-bottom: 1px solid #dadce0;
447
+ pointer-events: none;
448
+ }
449
+
450
+ .ec-body:not(.ec-compact) .ec-line:nth-child(even):after {
451
+ border-bottom-style: dotted;
452
+ }
453
+
454
+ .ec-popup {
455
+ position: absolute;
456
+ top: 0;
457
+ width: 110%;
458
+ min-width: 180px;
459
+ z-index: 1010;
460
+ padding: 8px 10px 14px;
461
+ background-color: #fff;
462
+ border-radius: 6px;
463
+ outline: 1px solid transparent;
464
+ box-shadow: 0 1px 3px 0 rgba(60, 64, 67, .3), 0 4px 8px 3px rgba(60, 64, 67, .15);
465
+
466
+ .ec-day-head {
467
+ text-align: left;
468
+ display: flex;
469
+ justify-content: space-between;
470
+
471
+ a {
472
+ cursor: pointer;
473
+ font-size: 1.5em;
474
+ line-height: .8;
475
+ }
476
+ }
477
+
478
+ .ec-events {
479
+ margin: 0;
480
+ }
481
+ }
482
+
483
+ .ec-extra {
484
+ position: relative;
485
+ height: 100%;
486
+ overflow: hidden;
487
+ margin-left: -6.5px;
488
+ }
489
+
490
+ .ec-now-indicator {
491
+ position: absolute;
492
+ z-index: 1005;
493
+ width: 100%;
494
+ border-top: #ea4335 solid 2px;
495
+ pointer-events: none;
496
+
497
+ &:before {
498
+ background: #ea4335;
499
+ border-radius: 50%;
500
+ content: "";
501
+ position: absolute;
502
+ height: 12px;
503
+ margin-top: -7px;
504
+ width: 12px;
505
+ pointer-events: none;
506
+ }
507
+ }
@@ -0,0 +1,150 @@
1
+ import {assign, createDate, createDuration, setMidnight} from '@event-calendar/common';
2
+ import {createEvents, createEventSources} from '@event-calendar/common';
3
+ import {is_function} from 'svelte/internal';
4
+
5
+ export function createOptions(plugins) {
6
+ let options = {
7
+ buttonText: {
8
+ today: 'today',
9
+ },
10
+ date: new Date(),
11
+ dateClick: undefined,
12
+ datesSet: undefined,
13
+ dayHeaderFormat: {
14
+ weekday: 'short',
15
+ month: 'numeric',
16
+ day: 'numeric'
17
+ },
18
+ displayEventEnd: true,
19
+ duration: {weeks: 1},
20
+ events: [],
21
+ eventBackgroundColor: undefined,
22
+ eventClick: undefined,
23
+ eventColor: undefined,
24
+ eventContent: undefined,
25
+ eventDidMount: undefined,
26
+ eventMouseEnter: undefined,
27
+ eventMouseLeave: undefined,
28
+ eventSources: [],
29
+ eventTimeFormat: {
30
+ hour: 'numeric',
31
+ minute: '2-digit'
32
+ },
33
+ firstDay: 0,
34
+ flexibleSlotTimeLimits: false, // ec option
35
+ headerToolbar: {
36
+ start: 'title',
37
+ center: '',
38
+ end: 'today prev,next'
39
+ },
40
+ height: 'auto',
41
+ hiddenDays: [],
42
+ highlightedDates: [], // ec option
43
+ lazyFetching: true,
44
+ loading: undefined,
45
+ locale: undefined,
46
+ monthMode: false,
47
+ nowIndicator: false,
48
+ scrollTime: '06:00:00',
49
+ slotDuration: '00:30:00',
50
+ slotHeight: 24, // ec option
51
+ slotLabelFormat: {
52
+ hour: 'numeric',
53
+ minute: '2-digit'
54
+ },
55
+ slotMaxTime: '24:00:00',
56
+ slotMinTime: '00:00:00',
57
+ theme: {
58
+ active: 'ec-active',
59
+ bgEvent: 'ec-bg-event',
60
+ bgEvents: 'ec-bg-events',
61
+ body: 'ec-body',
62
+ button: 'ec-button',
63
+ buttonGroup: 'ec-button-group',
64
+ calendar: 'ec',
65
+ compact: 'ec-compact',
66
+ content: 'ec-content',
67
+ day: 'ec-day',
68
+ dayHead: 'ec-day-head',
69
+ days: 'ec-days',
70
+ event: 'ec-event',
71
+ eventTime: 'ec-event-time',
72
+ eventTitle: 'ec-event-title',
73
+ events: 'ec-events',
74
+ extra: 'ec-extra',
75
+ handle: 'ec-handle',
76
+ header: 'ec-header',
77
+ hiddenScroll: 'ec-hidden-scroll',
78
+ hiddenTimes: 'ec-hidden-times',
79
+ highlight: 'ec-highlight',
80
+ icon: 'ec-icon',
81
+ line: 'ec-line',
82
+ lines: 'ec-lines',
83
+ nowIndicator: 'ec-now-indicator',
84
+ otherMonth: 'ec-other-month',
85
+ sidebar: 'ec-sidebar',
86
+ today: 'ec-today',
87
+ time: 'ec-time',
88
+ title: 'ec-title',
89
+ toolbar: 'ec-toolbar',
90
+ week: 'ec-week',
91
+ withScroll: 'ec-with-scroll'
92
+ },
93
+ titleFormat: {
94
+ year: 'numeric',
95
+ month: 'short',
96
+ day: 'numeric'
97
+ },
98
+ view: undefined,
99
+ viewDidMount: undefined,
100
+ views: {}
101
+ };
102
+
103
+ for (let plugin of plugins) {
104
+ if ('createOptions' in plugin) {
105
+ plugin.createOptions(options);
106
+ }
107
+ }
108
+
109
+ return options;
110
+ }
111
+
112
+ export function createParsers(options, plugins) {
113
+ let parsers = {
114
+ buttonText: input => is_function(input) ? input(options.buttonText) : input,
115
+ date: date => setMidnight(createDate(date)),
116
+ duration: createDuration,
117
+ events: createEvents,
118
+ eventSources: createEventSources,
119
+ hiddenDays: days => [...new Set(days)],
120
+ highlightedDates: dates => dates.map(createDate),
121
+ scrollTime: createDuration,
122
+ slotDuration: createDuration,
123
+ slotMaxTime: createDuration,
124
+ slotMinTime: createDuration,
125
+ theme: input => is_function(input) ? input(options.theme) : input
126
+ };
127
+
128
+ for (let plugin of plugins) {
129
+ if ('createParsers' in plugin) {
130
+ plugin.createParsers(parsers, options);
131
+ }
132
+ }
133
+
134
+ return parsers;
135
+ }
136
+
137
+ let prev;
138
+ export function diff(options) {
139
+ let diff = [];
140
+ if (prev) {
141
+ for (let name of Object.keys(options)) {
142
+ if (options[name] !== prev[name]) {
143
+ diff.push([name, options[name]]);
144
+ }
145
+ }
146
+ }
147
+ prev = assign({}, options);
148
+
149
+ return diff;
150
+ }
@@ -0,0 +1,114 @@
1
+ import {writable} from 'svelte/store';
2
+ import {is_function, tick, noop, identity} from 'svelte/internal';
3
+ import {createOptions, createParsers} from './options';
4
+ import {
5
+ activeRange,
6
+ currentRange,
7
+ events,
8
+ viewDates,
9
+ viewTitle,
10
+ view as view2 // hack to avoid a runtime error in SvelteKit dev mode (ReferenceError: view is not defined)
11
+ } from './stores';
12
+ import {writable2, intl, intlRange} from '@event-calendar/common';
13
+ import {assign} from '@event-calendar/common';
14
+
15
+ export default class {
16
+ constructor(plugins, input) {
17
+ plugins = plugins || [];
18
+
19
+ // Create options
20
+ let options = createOptions(plugins);
21
+ let parsers = createParsers(options, plugins);
22
+
23
+ // Create stores for options
24
+ for (let [option, value] of Object.entries(options)) {
25
+ this[option] = writable2(value, parsers[option]);
26
+ }
27
+
28
+ // Private stores
29
+ this._currentRange = currentRange(this);
30
+ this._activeRange = activeRange(this);
31
+ this._fetchedRange = writable({start: undefined, end: undefined});
32
+ this._events = events(this);
33
+ this._intlEventTime = intl(this.locale, this.eventTimeFormat);
34
+ this._intlSlotLabel = intl(this.locale, this.slotLabelFormat);
35
+ this._intlDayHeader = intl(this.locale, this.dayHeaderFormat);
36
+ this._titleIntlRange = intlRange(this.locale, this.titleFormat);
37
+ this._scrollable = writable(false);
38
+ this._viewTitle = viewTitle(this);
39
+ this._viewDates = viewDates(this);
40
+ this._view = view2(this);
41
+ this._viewComponent = writable(undefined);
42
+ // Interaction
43
+ this._interaction = writable({});
44
+ this._interactionEvents = writable([null, null]); // drag, pointer
45
+ this._draggable = writable(noop);
46
+ this._classes = writable(identity);
47
+ this._scroll = writable(undefined);
48
+
49
+ // Let plugins create their private stores
50
+ for (let plugin of plugins) {
51
+ if ('createStores' in plugin) {
52
+ plugin.createStores(this);
53
+ }
54
+ }
55
+
56
+ if (input.view) {
57
+ // Set initial view based on input
58
+ this.view.set(input.view);
59
+ }
60
+
61
+ // Set options for each view
62
+ let commonOpts = assign({}, options, input);
63
+ parseOpts(commonOpts, this);
64
+ let views = new Set([...Object.keys(options.views), ...Object.keys(input.views || {})]);
65
+ for (let view of views) {
66
+ let viewOpts = assign({}, options.views[view] || {}, input.views && input.views[view] || {});
67
+ parseOpts(viewOpts, this);
68
+ let opts = assign({}, commonOpts, viewOpts);
69
+ // Change view component when view changes
70
+ this.view.subscribe(newView => {
71
+ if (newView === view) {
72
+ this._viewComponent.set(opts.component);
73
+ if (is_function(opts.viewDidMount)) {
74
+ tick().then(() => opts.viewDidMount(this._view.get()));
75
+ }
76
+ }
77
+ });
78
+ for (let key of Object.keys(opts)) {
79
+ if (this.hasOwnProperty(key) && key[0] !== '_') {
80
+ let {set, _set, ...rest} = this[key];
81
+
82
+ if (!_set) {
83
+ // Original set
84
+ _set = set;
85
+ }
86
+
87
+ this[key] = {
88
+ // Set value in all views
89
+ set: value => {opts[key] = value; set(value);},
90
+ _set,
91
+ ...rest
92
+ };
93
+
94
+ // Change value when view changes
95
+ this.view.subscribe(newView => {
96
+ if (newView === view) {
97
+ _set(opts[key]);
98
+ }
99
+ });
100
+ }
101
+ }
102
+ }
103
+ }
104
+ }
105
+
106
+ function parseOpts(opts, state) {
107
+ for (let key of Object.keys(opts)) {
108
+ if (state.hasOwnProperty(key) && key[0] !== '_') {
109
+ if (state[key].parse) {
110
+ opts[key] = state[key].parse(opts[key]);
111
+ }
112
+ }
113
+ }
114
+ }
@@ -0,0 +1,206 @@
1
+ import {derived, writable} from 'svelte/store';
2
+ import {is_function, noop, tick} from 'svelte/internal';
3
+ import {
4
+ DAY_IN_SECONDS,
5
+ cloneDate,
6
+ addDuration,
7
+ addDay,
8
+ subtractDay,
9
+ toISOString,
10
+ nextClosestDay,
11
+ prevClosestDay,
12
+ setMidnight,
13
+ toLocalDate
14
+ } from '@event-calendar/common';
15
+ import {derived2} from '@event-calendar/common';
16
+ import {createEvents} from '@event-calendar/common';
17
+ import {createView} from '@event-calendar/common';
18
+ import {assign} from '@event-calendar/common';
19
+
20
+ export function activeRange(state) {
21
+ let _activeRange = derived(
22
+ [state._currentRange, state.firstDay, state.monthMode, state.slotMinTime, state.slotMaxTime],
23
+ ([$_currentRange, $firstDay, $monthMode, $slotMinTime, $slotMaxTime]) => {
24
+ let start = cloneDate($_currentRange.start);
25
+ let end = cloneDate($_currentRange.end);
26
+
27
+ if ($monthMode) {
28
+ // First day of week
29
+ prevClosestDay(start, $firstDay);
30
+ nextClosestDay(end, $firstDay);
31
+ } else if ($slotMaxTime.days || $slotMaxTime.seconds > DAY_IN_SECONDS) {
32
+ addDuration(subtractDay(end), $slotMaxTime);
33
+ let start2 = subtractDay(cloneDate(end));
34
+ if (start2 < start) {
35
+ start = start2;
36
+ }
37
+ }
38
+
39
+ return {start, end};
40
+ }
41
+ );
42
+
43
+ let debounce = 0;
44
+ derived([_activeRange, state.datesSet], ([$_activeRange, $datesSet]) => {
45
+ if ($datesSet && !debounce) {
46
+ ++debounce;
47
+ tick().then(() => {
48
+ --debounce;
49
+ $datesSet({
50
+ start: toLocalDate($_activeRange.start),
51
+ end: toLocalDate($_activeRange.end),
52
+ startStr: toISOString($_activeRange.start),
53
+ endStr: toISOString($_activeRange.end)
54
+ });
55
+ });
56
+ }
57
+ }).subscribe(noop);
58
+
59
+ return _activeRange;
60
+ }
61
+
62
+ export function currentRange(state) {
63
+ return derived(
64
+ [state.date, state.duration, state.monthMode, state.firstDay],
65
+ ([$date, $duration, $monthMode, $firstDay]) => {
66
+ let start = cloneDate($date), end;
67
+ if ($monthMode) {
68
+ start.setDate(1);
69
+ } else if ($duration.inWeeks) {
70
+ // First day of week
71
+ prevClosestDay(start, $firstDay);
72
+ }
73
+ end = addDuration(cloneDate(start), $duration);
74
+
75
+ return {start, end};
76
+ }
77
+ );
78
+ }
79
+
80
+ export function viewDates(state) {
81
+ return derived2([state._activeRange, state.hiddenDays], ([$_activeRange, $hiddenDays]) => {
82
+ let dates = [];
83
+ let date = setMidnight(cloneDate($_activeRange.start));
84
+ let end = setMidnight(cloneDate($_activeRange.end));
85
+ while (date < end) {
86
+ if (!$hiddenDays.includes(date.getUTCDay())) {
87
+ dates.push(cloneDate(date));
88
+ }
89
+ addDay(date);
90
+ }
91
+ if (!dates.length && $hiddenDays.length && $hiddenDays.length < 7) {
92
+ // Try to move the date
93
+ state.date.update(date => {
94
+ while ($hiddenDays.includes(date.getUTCDay())) {
95
+ addDay(date);
96
+ }
97
+ return date;
98
+ });
99
+ dates = state._viewDates.get();
100
+ }
101
+
102
+ return dates;
103
+ });
104
+ }
105
+
106
+ export function viewTitle(state) {
107
+ return derived(
108
+ [state.date, state._activeRange, state._titleIntlRange, state.monthMode],
109
+ ([$date, $_activeRange, $_titleIntlRange, $monthMode]) => {
110
+ return $monthMode
111
+ ? $_titleIntlRange.format($date, $date)
112
+ : $_titleIntlRange.format($_activeRange.start, subtractDay(cloneDate($_activeRange.end)));
113
+ }
114
+ );
115
+ }
116
+
117
+ export function view(state) {
118
+ return derived2([state.view, state._viewTitle, state._currentRange, state._activeRange], args => createView(...args));
119
+ }
120
+
121
+ export function events(state) {
122
+ let _events = writable([]);
123
+ let abortController;
124
+ let fetching = 0;
125
+ derived(
126
+ [state.events, state.eventSources, state._activeRange, state._fetchedRange, state.lazyFetching, state.loading],
127
+ (values, set) => tick().then(() => {
128
+ let [$events, $eventSources, $_activeRange, $_fetchedRange, $lazyFetching, $loading] = values;
129
+ if (!$eventSources.length) {
130
+ set($events);
131
+ return;
132
+ }
133
+ // Do not fetch if new range is within the previous one
134
+ if (!$_fetchedRange.start || $_fetchedRange.start > $_activeRange.start || $_fetchedRange.end < $_activeRange.end || !$lazyFetching) {
135
+ if (abortController) {
136
+ // Abort previous request
137
+ abortController.abort();
138
+ }
139
+ // Create new abort controller
140
+ abortController = new AbortController();
141
+ // Call loading hook
142
+ if (is_function($loading) && !fetching) {
143
+ $loading(true);
144
+ }
145
+ let stopLoading = () => {
146
+ if (--fetching === 0 && is_function($loading)) {
147
+ $loading(false);
148
+ }
149
+ };
150
+ let events = [];
151
+ // Prepare handlers
152
+ let failure = e => stopLoading();
153
+ let success = data => {
154
+ events = events.concat(createEvents(data));
155
+ set(events);
156
+ stopLoading();
157
+ };
158
+ // Prepare other stuff
159
+ let startStr = toISOString($_activeRange.start)
160
+ let endStr = toISOString($_activeRange.end);
161
+ // Loop over event sources
162
+ for (let source of $eventSources) {
163
+ if (is_function(source.events)) {
164
+ // Events as a function
165
+ let result = source.events({
166
+ start: toLocalDate($_activeRange.start),
167
+ end: toLocalDate($_activeRange.end),
168
+ startStr,
169
+ endStr
170
+ }, success, failure);
171
+ if (result !== undefined) {
172
+ Promise.resolve(result).then(success, failure);
173
+ }
174
+ } else {
175
+ // Events as a JSON feed
176
+ // Prepare params
177
+ let params = is_function(source.extraParams) ? source.extraParams() : assign({}, source.extraParams);
178
+ params.start = startStr;
179
+ params.end = endStr;
180
+ params = new URLSearchParams(params);
181
+ // Prepare fetch
182
+ let url = source.url, headers = {}, body;
183
+ if (['GET', 'HEAD'].includes(source.method)) {
184
+ url += (url.includes('?') ? '&' : '?') + params;
185
+ } else {
186
+ headers['content-type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
187
+ body = String(params); // Safari 10.1 doesn't convert to string automatically
188
+ }
189
+ // Do the fetch
190
+ fetch(url, {method: source.method, headers, body, signal: abortController.signal, credentials: 'same-origin'})
191
+ .then(response => response.json())
192
+ .then(success)
193
+ .catch(failure);
194
+ }
195
+ ++fetching;
196
+ }
197
+ // Save current range for future requests
198
+ $_fetchedRange.start = $_activeRange.start;
199
+ $_fetchedRange.end = $_activeRange.end;
200
+ }
201
+ }),
202
+ []
203
+ ).subscribe(_events.set);
204
+
205
+ return _events;
206
+ }