@event-calendar/core 0.8.1 → 0.9.0
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/README.md +6 -0
- package/index.css +1 -1
- package/index.js +26 -16
- package/package.json +11 -5
- package/src/Buttons.svelte +41 -0
- package/src/Calendar.svelte +95 -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/README.md
CHANGED
|
@@ -89,6 +89,7 @@ Inspired by [FullCalendar](https://fullcalendar.io/), implements similar options
|
|
|
89
89
|
- [setOption](#setoption-name-value-)
|
|
90
90
|
</td><td>
|
|
91
91
|
|
|
92
|
+
- [getEvents](#getevents)
|
|
92
93
|
- [getEventById](#geteventbyid-id-)
|
|
93
94
|
- [removeEventById](#removeeventbyid-id-)
|
|
94
95
|
- [addEvent](#addevent-event-)
|
|
@@ -1461,6 +1462,11 @@ This method allows you to set new value to any calendar option.
|
|
|
1461
1462
|
// E.g. Change the current date
|
|
1462
1463
|
ec.setOption('date', new Date());
|
|
1463
1464
|
```
|
|
1465
|
+
### getEvents()
|
|
1466
|
+
- Return value `Event[]` Array of [Event](#event-object) objects
|
|
1467
|
+
|
|
1468
|
+
Returns an array of events that the calendar has in memory.
|
|
1469
|
+
|
|
1464
1470
|
### getEventById( id )
|
|
1465
1471
|
- Parameters
|
|
1466
1472
|
- `id` `string|integer` The ID of the event
|
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:
|
|
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:rgba(0,0,0,0);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 rgba(0,0,0,0);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.6 */
|
|
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.6 */
|
|
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.6 */
|
|
1216
1216
|
|
|
1217
1217
|
function create_fragment(ctx) {
|
|
1218
1218
|
let div;
|
|
@@ -1362,9 +1362,9 @@ function instance($$self, $$props, $$invalidate) {
|
|
|
1362
1362
|
let { _viewComponent, _interaction, _events, events, eventSources, height, theme } = state;
|
|
1363
1363
|
component_subscribe($$self, _viewComponent, value => $$invalidate(2, $_viewComponent = value));
|
|
1364
1364
|
component_subscribe($$self, _interaction, value => $$invalidate(3, $_interaction = value));
|
|
1365
|
-
component_subscribe($$self, _events, value => $$invalidate(
|
|
1366
|
-
component_subscribe($$self, events, value => $$invalidate(
|
|
1367
|
-
component_subscribe($$self, eventSources, value => $$invalidate(
|
|
1365
|
+
component_subscribe($$self, _events, value => $$invalidate(23, $_events = value));
|
|
1366
|
+
component_subscribe($$self, events, value => $$invalidate(22, $events = value));
|
|
1367
|
+
component_subscribe($$self, eventSources, value => $$invalidate(24, $eventSources = value));
|
|
1368
1368
|
component_subscribe($$self, height, value => $$invalidate(1, $height = value));
|
|
1369
1369
|
component_subscribe($$self, theme, value => $$invalidate(0, $theme = value));
|
|
1370
1370
|
|
|
@@ -1391,6 +1391,10 @@ function instance($$self, $$props, $$invalidate) {
|
|
|
1391
1391
|
return this;
|
|
1392
1392
|
}
|
|
1393
1393
|
|
|
1394
|
+
function getEvents() {
|
|
1395
|
+
return get(state._events).map(toEventWithLocalDates);
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1394
1398
|
function getEventById(id) {
|
|
1395
1399
|
for (let event of get(state._events)) {
|
|
1396
1400
|
if (event.id == id) {
|
|
@@ -1469,6 +1473,7 @@ function instance($$self, $$props, $$invalidate) {
|
|
|
1469
1473
|
setOption,
|
|
1470
1474
|
getOption,
|
|
1471
1475
|
refetchEvents,
|
|
1476
|
+
getEvents,
|
|
1472
1477
|
getEventById,
|
|
1473
1478
|
addEvent,
|
|
1474
1479
|
updateEvent,
|
|
@@ -1487,11 +1492,12 @@ class Calendar extends SvelteComponent {
|
|
|
1487
1492
|
setOption: 13,
|
|
1488
1493
|
getOption: 14,
|
|
1489
1494
|
refetchEvents: 15,
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
+
getEvents: 16,
|
|
1496
|
+
getEventById: 17,
|
|
1497
|
+
addEvent: 18,
|
|
1498
|
+
updateEvent: 19,
|
|
1499
|
+
removeEventById: 20,
|
|
1500
|
+
getView: 21
|
|
1495
1501
|
});
|
|
1496
1502
|
}
|
|
1497
1503
|
|
|
@@ -1507,25 +1513,29 @@ class Calendar extends SvelteComponent {
|
|
|
1507
1513
|
return this.$$.ctx[15];
|
|
1508
1514
|
}
|
|
1509
1515
|
|
|
1510
|
-
get
|
|
1516
|
+
get getEvents() {
|
|
1511
1517
|
return this.$$.ctx[16];
|
|
1512
1518
|
}
|
|
1513
1519
|
|
|
1514
|
-
get
|
|
1520
|
+
get getEventById() {
|
|
1515
1521
|
return this.$$.ctx[17];
|
|
1516
1522
|
}
|
|
1517
1523
|
|
|
1518
|
-
get
|
|
1524
|
+
get addEvent() {
|
|
1519
1525
|
return this.$$.ctx[18];
|
|
1520
1526
|
}
|
|
1521
1527
|
|
|
1522
|
-
get
|
|
1528
|
+
get updateEvent() {
|
|
1523
1529
|
return this.$$.ctx[19];
|
|
1524
1530
|
}
|
|
1525
1531
|
|
|
1526
|
-
get
|
|
1532
|
+
get removeEventById() {
|
|
1527
1533
|
return this.$$.ctx[20];
|
|
1528
1534
|
}
|
|
1535
|
+
|
|
1536
|
+
get getView() {
|
|
1537
|
+
return this.$$.ctx[21];
|
|
1538
|
+
}
|
|
1529
1539
|
}
|
|
1530
1540
|
|
|
1531
1541
|
export { Calendar as default };
|
package/package.json
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@event-calendar/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
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.
|
|
22
|
-
"svelte": "^3.46.
|
|
27
|
+
"@event-calendar/common": "~0.9.0",
|
|
28
|
+
"svelte": "^3.46.6"
|
|
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,95 @@
|
|
|
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 getEvents() {
|
|
43
|
+
return get(state._events).map(toEventWithLocalDates);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function getEventById(id) {
|
|
47
|
+
for (let event of get(state._events)) {
|
|
48
|
+
if (event.id == id) {
|
|
49
|
+
return toEventWithLocalDates(event);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function addEvent(event) {
|
|
56
|
+
updateEvents(events => events.concat(state.events.parse([event])));
|
|
57
|
+
return this;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function updateEvent(event) {
|
|
61
|
+
updateEvents(events => {
|
|
62
|
+
for (let e of events) {
|
|
63
|
+
if (e.id == event.id) {
|
|
64
|
+
assign(e, state.events.parse([event])[0]);
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return events;
|
|
69
|
+
});
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function removeEventById(id) {
|
|
74
|
+
updateEvents(events => events.filter(event => event.id != id));
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function getView() {
|
|
79
|
+
return toViewWithLocalDates(state._view.get());
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function updateEvents(func) {
|
|
83
|
+
if ($eventSources.length) {
|
|
84
|
+
$_events = func($_events);
|
|
85
|
+
} else {
|
|
86
|
+
$events = func($events);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
</script>
|
|
90
|
+
|
|
91
|
+
<div class="{$theme.calendar}" style="height: {$height}">
|
|
92
|
+
<Toolbar/>
|
|
93
|
+
<svelte:component this={$_viewComponent}/>
|
|
94
|
+
<svelte:component this={$_interaction.component}/>
|
|
95
|
+
</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
|
+
}
|