@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 +1 -1
- package/index.js +3 -3
- package/package.json +11 -5
- package/src/Buttons.svelte +41 -0
- package/src/Calendar.svelte +91 -0
- package/src/Toolbar.svelte +34 -0
- package/src/index.js +3 -0
- package/src/index.scss +507 -0
- package/src/storage/options.js +150 -0
- package/src/storage/state.js +114 -0
- package/src/storage/stores.js +206 -0
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-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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": [
|
|
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.
|
|
22
|
-
"svelte": "^3.46.
|
|
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
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
|
+
}
|