@event-calendar/core 2.5.1 → 2.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -5
- package/index.css +1 -0
- package/index.js +181 -116
- package/package.json +1 -1
- package/src/Calendar.svelte +9 -4
- package/src/lib/debounce.js +10 -0
- package/src/lib/events.js +80 -73
- package/src/lib/stores.js +42 -4
- package/src/storage/options.js +1 -0
- package/src/storage/state.js +3 -1
- package/src/styles/index.scss +1 -0
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ See [demo](https://vkurko.github.io/calendar/) and [changelog](CHANGELOG.md).
|
|
|
4
4
|
|
|
5
5
|
Full-sized drag & drop JavaScript event calendar with resource view:
|
|
6
6
|
|
|
7
|
-
* Lightweight (
|
|
7
|
+
* Lightweight (33kb [br](https://en.wikipedia.org/wiki/Brotli) compressed)
|
|
8
8
|
* Zero-dependency (pre-built bundle)
|
|
9
9
|
* Used on over 70,000 websites with [Bookly](https://wordpress.org/plugins/bookly-responsive-appointment-booking-tool/)
|
|
10
10
|
|
|
@@ -36,6 +36,7 @@ Inspired by [FullCalendar](https://fullcalendar.io/), implements similar options
|
|
|
36
36
|
- [duration](#duration)
|
|
37
37
|
- [editable](#editable)
|
|
38
38
|
- [events](#events)
|
|
39
|
+
- [eventAllUpdated](#eventallupdated)
|
|
39
40
|
- [eventBackgroundColor](#eventbackgroundcolor)
|
|
40
41
|
- [eventClassNames](#eventclassnames)
|
|
41
42
|
- [eventClick](#eventclick)
|
|
@@ -45,9 +46,9 @@ Inspired by [FullCalendar](https://fullcalendar.io/), implements similar options
|
|
|
45
46
|
- [eventDragMinDistance](#eventdragmindistance)
|
|
46
47
|
- [eventDragStart](#eventdragstart)
|
|
47
48
|
- [eventDragStop](#eventdragstop)
|
|
48
|
-
- [eventDrop](#eventdrop)
|
|
49
49
|
</td><td>
|
|
50
50
|
|
|
51
|
+
- [eventDrop](#eventdrop)
|
|
51
52
|
- [eventDurationEditable](#eventdurationeditable)
|
|
52
53
|
- [eventLongPressDelay](#eventlongpressdelay)
|
|
53
54
|
- [eventMouseEnter](#eventmouseenter)
|
|
@@ -198,8 +199,8 @@ import '@event-calendar/core/index.css';
|
|
|
198
199
|
### Pre-built browser ready bundle
|
|
199
200
|
Include the following lines of code in the `<head>` section of your page:
|
|
200
201
|
```html
|
|
201
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@event-calendar/build@2.
|
|
202
|
-
<script src="https://cdn.jsdelivr.net/npm/@event-calendar/build@2.
|
|
202
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@event-calendar/build@2.6.1/event-calendar.min.css">
|
|
203
|
+
<script src="https://cdn.jsdelivr.net/npm/@event-calendar/build@2.6.1/event-calendar.min.js"></script>
|
|
203
204
|
```
|
|
204
205
|
|
|
205
206
|
<details>
|
|
@@ -603,6 +604,31 @@ Array of plain objects that will be parsed into [Event](#event-object) objects a
|
|
|
603
604
|
|
|
604
605
|
This option is not used if the `eventSources` option is provided.
|
|
605
606
|
|
|
607
|
+
### eventAllUpdated
|
|
608
|
+
- Type `function`
|
|
609
|
+
- Default `undefined`
|
|
610
|
+
|
|
611
|
+
Callback function that is triggered when all events have finished updating.
|
|
612
|
+
|
|
613
|
+
This is an experimental feature and its behavior may change in the future. The function is called at the end of the cycle of rendering all events. The rendering occurs when new events are added, already displayed events are modified, or events are deleted.
|
|
614
|
+
|
|
615
|
+
```js
|
|
616
|
+
function (info) { }
|
|
617
|
+
```
|
|
618
|
+
`info` is an object with the following properties:
|
|
619
|
+
<table>
|
|
620
|
+
<tr>
|
|
621
|
+
<td>
|
|
622
|
+
|
|
623
|
+
`view`
|
|
624
|
+
</td>
|
|
625
|
+
<td>
|
|
626
|
+
|
|
627
|
+
The current [View](#view-object) object
|
|
628
|
+
</td>
|
|
629
|
+
</tr>
|
|
630
|
+
</table>
|
|
631
|
+
|
|
606
632
|
### eventBackgroundColor
|
|
607
633
|
- Type `string`
|
|
608
634
|
- Default `undefined`
|
|
@@ -1262,7 +1288,27 @@ This option is used instead of the `events` option.
|
|
|
1262
1288
|
</td>
|
|
1263
1289
|
<td>
|
|
1264
1290
|
|
|
1265
|
-
A URL that the calendar will fetch [Event](#event-object) objects from
|
|
1291
|
+
A URL that the calendar will fetch [Event](#event-object) objects from. HTTP requests with the following parameters will be sent to this URL whenever the calendar needs new event data
|
|
1292
|
+
<table>
|
|
1293
|
+
<tr>
|
|
1294
|
+
<td>
|
|
1295
|
+
|
|
1296
|
+
`start`
|
|
1297
|
+
</td>
|
|
1298
|
+
<td>
|
|
1299
|
+
Start date of the range the calendar needs events for
|
|
1300
|
+
</td>
|
|
1301
|
+
</tr>
|
|
1302
|
+
<tr>
|
|
1303
|
+
<td>
|
|
1304
|
+
|
|
1305
|
+
`end`
|
|
1306
|
+
</td>
|
|
1307
|
+
<td>
|
|
1308
|
+
End date of the range the calendar needs events for
|
|
1309
|
+
</td>
|
|
1310
|
+
</tr>
|
|
1311
|
+
</table>
|
|
1266
1312
|
</td>
|
|
1267
1313
|
</tr>
|
|
1268
1314
|
<tr>
|
package/index.css
CHANGED
package/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { run_all, is_function, noop, identity, tick, SvelteComponent, init, safe_not_equal, ensure_array_like, empty, insert, detach, destroy_each, component_subscribe, set_store_value, element, text, attr, append, listen, set_data, action_destroyer, transition_in, group_outros, check_outros, transition_out, space, create_component, mount_component, destroy_component, construct_svelte_component, set_style, get_current_component } from 'svelte/internal';
|
|
2
|
-
import { getContext, setContext, beforeUpdate } from 'svelte';
|
|
2
|
+
import { getContext, setContext, beforeUpdate, afterUpdate } from 'svelte';
|
|
3
3
|
import { derived, get, writable, readable } from 'svelte/store';
|
|
4
4
|
|
|
5
5
|
function keyEnter(fn) {
|
|
@@ -208,6 +208,16 @@ function flushDebounce(queue) {
|
|
|
208
208
|
queue.clear();
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
+
function task(fn, handle, tasks) {
|
|
212
|
+
handle ??= fn;
|
|
213
|
+
if (!tasks.has(handle)) {
|
|
214
|
+
tasks.set(handle, setTimeout(() => {
|
|
215
|
+
tasks.delete(handle);
|
|
216
|
+
fn();
|
|
217
|
+
}));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
211
221
|
function assign(...args) {
|
|
212
222
|
return Object.assign(...args);
|
|
213
223
|
}
|
|
@@ -366,6 +376,82 @@ function sortEventChunks(chunks) {
|
|
|
366
376
|
chunks.sort((a, b) => a.start - b.start || b.event.allDay - a.event.allDay);
|
|
367
377
|
}
|
|
368
378
|
|
|
379
|
+
function createEventContent(chunk, displayEventEnd, eventContent, theme, _intlEventTime, _view) {
|
|
380
|
+
let timeText = _intlEventTime.formatRange(
|
|
381
|
+
chunk.start,
|
|
382
|
+
displayEventEnd && chunk.event.display !== 'pointer'
|
|
383
|
+
? copyTime(cloneDate(chunk.start), chunk.end) // make Intl.formatRange output only the time part
|
|
384
|
+
: chunk.start
|
|
385
|
+
);
|
|
386
|
+
let content;
|
|
387
|
+
|
|
388
|
+
if (eventContent) {
|
|
389
|
+
content = is_function(eventContent)
|
|
390
|
+
? eventContent({
|
|
391
|
+
event: toEventWithLocalDates(chunk.event),
|
|
392
|
+
timeText,
|
|
393
|
+
view: toViewWithLocalDates(_view)
|
|
394
|
+
})
|
|
395
|
+
: eventContent;
|
|
396
|
+
} else {
|
|
397
|
+
let domNodes;
|
|
398
|
+
switch (chunk.event.display) {
|
|
399
|
+
case 'background':
|
|
400
|
+
domNodes = [];
|
|
401
|
+
break;
|
|
402
|
+
case 'pointer':
|
|
403
|
+
domNodes = [createTimeElement(timeText, chunk, theme)];
|
|
404
|
+
break;
|
|
405
|
+
default:
|
|
406
|
+
domNodes = [
|
|
407
|
+
...chunk.event.allDay ? [] : [createTimeElement(timeText, chunk, theme)],
|
|
408
|
+
createElement('h4', theme.eventTitle, chunk.event.title)
|
|
409
|
+
];
|
|
410
|
+
}
|
|
411
|
+
content = {domNodes};
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return [timeText, content];
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function createTimeElement(timeText, chunk, theme) {
|
|
418
|
+
return createElement(
|
|
419
|
+
'time',
|
|
420
|
+
theme.eventTime,
|
|
421
|
+
timeText,
|
|
422
|
+
[['datetime', toISOString(chunk.start)]]
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function createEventClasses(eventClassNames, event, _view) {
|
|
427
|
+
if (eventClassNames) {
|
|
428
|
+
if (is_function(eventClassNames)) {
|
|
429
|
+
eventClassNames = eventClassNames({
|
|
430
|
+
event: toEventWithLocalDates(event),
|
|
431
|
+
view: toViewWithLocalDates(_view)
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
return Array.isArray(eventClassNames) ? eventClassNames : [eventClassNames];
|
|
435
|
+
}
|
|
436
|
+
return [];
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function toEventWithLocalDates(event) {
|
|
440
|
+
return _cloneEvent(event, toLocalDate);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function cloneEvent(event) {
|
|
444
|
+
return _cloneEvent(event, cloneDate);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function _cloneEvent(event, dateFn) {
|
|
448
|
+
event = assign({}, event);
|
|
449
|
+
event.start = dateFn(event.start);
|
|
450
|
+
event.end = dateFn(event.end);
|
|
451
|
+
|
|
452
|
+
return event;
|
|
453
|
+
}
|
|
454
|
+
|
|
369
455
|
/**
|
|
370
456
|
* Prepare event chunks for month view and all-day slot in week view
|
|
371
457
|
*/
|
|
@@ -448,80 +534,11 @@ function repositionEvent(chunk, longChunks, height) {
|
|
|
448
534
|
return margin;
|
|
449
535
|
}
|
|
450
536
|
|
|
451
|
-
function
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
? copyTime(cloneDate(chunk.start), chunk.end) // make Intl.formatRange output only the time part
|
|
456
|
-
: chunk.start
|
|
457
|
-
);
|
|
458
|
-
let content;
|
|
459
|
-
|
|
460
|
-
if (eventContent) {
|
|
461
|
-
content = is_function(eventContent)
|
|
462
|
-
? eventContent({
|
|
463
|
-
event: toEventWithLocalDates(chunk.event),
|
|
464
|
-
timeText,
|
|
465
|
-
view: toViewWithLocalDates(_view)
|
|
466
|
-
})
|
|
467
|
-
: eventContent;
|
|
468
|
-
} else {
|
|
469
|
-
let domNodes;
|
|
470
|
-
switch (chunk.event.display) {
|
|
471
|
-
case 'background':
|
|
472
|
-
domNodes = [];
|
|
473
|
-
break;
|
|
474
|
-
case 'pointer':
|
|
475
|
-
domNodes = [createTimeElement(timeText, chunk, theme)];
|
|
476
|
-
break;
|
|
477
|
-
default:
|
|
478
|
-
domNodes = [
|
|
479
|
-
...chunk.event.allDay ? [] : [createTimeElement(timeText, chunk, theme)],
|
|
480
|
-
createElement('h4', theme.eventTitle, chunk.event.title)
|
|
481
|
-
];
|
|
482
|
-
}
|
|
483
|
-
content = {domNodes};
|
|
537
|
+
function runReposition(refs, data) {
|
|
538
|
+
refs.length = data.length;
|
|
539
|
+
for (let ref of refs) {
|
|
540
|
+
ref?.reposition?.();
|
|
484
541
|
}
|
|
485
|
-
|
|
486
|
-
return [timeText, content];
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
function createTimeElement(timeText, chunk, theme) {
|
|
490
|
-
return createElement(
|
|
491
|
-
'time',
|
|
492
|
-
theme.eventTime,
|
|
493
|
-
timeText,
|
|
494
|
-
[['datetime', toISOString(chunk.start)]]
|
|
495
|
-
);
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
function createEventClasses(eventClassNames, event, _view) {
|
|
499
|
-
if (eventClassNames) {
|
|
500
|
-
if (is_function(eventClassNames)) {
|
|
501
|
-
eventClassNames = eventClassNames({
|
|
502
|
-
event: toEventWithLocalDates(event),
|
|
503
|
-
view: toViewWithLocalDates(_view)
|
|
504
|
-
});
|
|
505
|
-
}
|
|
506
|
-
return Array.isArray(eventClassNames) ? eventClassNames : [eventClassNames];
|
|
507
|
-
}
|
|
508
|
-
return [];
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
function toEventWithLocalDates(event) {
|
|
512
|
-
return _cloneEvent(event, toLocalDate);
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
function cloneEvent(event) {
|
|
516
|
-
return _cloneEvent(event, cloneDate);
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
function _cloneEvent(event, dateFn) {
|
|
520
|
-
event = assign({}, event);
|
|
521
|
-
event.start = dateFn(event.start);
|
|
522
|
-
event.end = dateFn(event.end);
|
|
523
|
-
|
|
524
|
-
return event;
|
|
525
542
|
}
|
|
526
543
|
|
|
527
544
|
/**
|
|
@@ -602,15 +619,53 @@ function intl(locale, format) {
|
|
|
602
619
|
|
|
603
620
|
function intlRange(locale, format) {
|
|
604
621
|
return derived([locale, format], ([$locale, $format]) => {
|
|
605
|
-
let
|
|
606
|
-
|
|
607
|
-
|
|
622
|
+
let formatRange;
|
|
623
|
+
if (is_function($format)) {
|
|
624
|
+
formatRange = $format;
|
|
625
|
+
} else {
|
|
626
|
+
let intl = new Intl.DateTimeFormat($locale, $format);
|
|
627
|
+
formatRange = (start, end) => {
|
|
628
|
+
if (start <= end) {
|
|
629
|
+
return intl.formatRange(start, end);
|
|
630
|
+
} else {
|
|
631
|
+
// In iOS 16 and older, intl.formatRange() throws an exception if the start date is later than the end date.
|
|
632
|
+
// Therefore, we first swap the parameters, and then swap the resulting parts.
|
|
633
|
+
/** @see https://github.com/vkurko/calendar/issues/227 */
|
|
634
|
+
let parts = intl.formatRangeToParts(end, start);
|
|
635
|
+
let result = '';
|
|
636
|
+
let sources = ['startRange', 'endRange'];
|
|
637
|
+
let processed = [false, false];
|
|
638
|
+
for (let part of parts) {
|
|
639
|
+
let i = sources.indexOf(part.source);
|
|
640
|
+
if (i >= 0) {
|
|
641
|
+
if (!processed[i]) {
|
|
642
|
+
result += _getParts(sources[1 - i], parts);
|
|
643
|
+
processed[i] = true;
|
|
644
|
+
}
|
|
645
|
+
} else {
|
|
646
|
+
result += part.value;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
return result;
|
|
650
|
+
}
|
|
651
|
+
};
|
|
652
|
+
}
|
|
608
653
|
return {
|
|
609
|
-
formatRange: (start, end) =>
|
|
654
|
+
formatRange: (start, end) => formatRange(toLocalDate(start), toLocalDate(end))
|
|
610
655
|
};
|
|
611
656
|
});
|
|
612
657
|
}
|
|
613
658
|
|
|
659
|
+
function _getParts(source, parts) {
|
|
660
|
+
let result = '';
|
|
661
|
+
for (let part of parts) {
|
|
662
|
+
if (part.source == source) {
|
|
663
|
+
result += part.value;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
return result;
|
|
667
|
+
}
|
|
668
|
+
|
|
614
669
|
function createOptions(plugins) {
|
|
615
670
|
let options = {
|
|
616
671
|
allDayContent: undefined,
|
|
@@ -631,6 +686,7 @@ function createOptions(plugins) {
|
|
|
631
686
|
displayEventEnd: true,
|
|
632
687
|
duration: {weeks: 1},
|
|
633
688
|
events: [],
|
|
689
|
+
eventAllUpdated: undefined,
|
|
634
690
|
eventBackgroundColor: undefined,
|
|
635
691
|
eventTextColor: undefined,
|
|
636
692
|
eventClassNames: undefined,
|
|
@@ -966,7 +1022,9 @@ class State {
|
|
|
966
1022
|
}
|
|
967
1023
|
|
|
968
1024
|
// Private stores
|
|
969
|
-
this._queue = writable(new Map()); // debounce queue
|
|
1025
|
+
this._queue = writable(new Map()); // debounce queue (beforeUpdate)
|
|
1026
|
+
this._queue2 = writable(new Map()); // debounce queue (afterUpdate)
|
|
1027
|
+
this._tasks = new Map(); // timeout IDs for tasks
|
|
970
1028
|
this._auxiliary = writable([]); // auxiliary components
|
|
971
1029
|
this._dayGrid = dayGrid(this);
|
|
972
1030
|
this._currentRange = currentRange(this);
|
|
@@ -2213,7 +2271,7 @@ function create_fragment(ctx) {
|
|
|
2213
2271
|
current = true;
|
|
2214
2272
|
|
|
2215
2273
|
if (!mounted) {
|
|
2216
|
-
dispose = listen(window, "resize", /*recheckScrollable*/ ctx[
|
|
2274
|
+
dispose = listen(window, "resize", /*recheckScrollable*/ ctx[17]);
|
|
2217
2275
|
mounted = true;
|
|
2218
2276
|
}
|
|
2219
2277
|
},
|
|
@@ -2287,6 +2345,7 @@ function create_fragment(ctx) {
|
|
|
2287
2345
|
function instance($$self, $$props, $$invalidate) {
|
|
2288
2346
|
let $_bodyEl;
|
|
2289
2347
|
let $_scrollable;
|
|
2348
|
+
let $_queue2;
|
|
2290
2349
|
let $_queue;
|
|
2291
2350
|
let $_interaction;
|
|
2292
2351
|
let $_events;
|
|
@@ -2300,13 +2359,14 @@ function instance($$self, $$props, $$invalidate) {
|
|
|
2300
2359
|
let component = get_current_component();
|
|
2301
2360
|
let state = new State(plugins, options);
|
|
2302
2361
|
setContext('state', state);
|
|
2303
|
-
let { _viewComponent, _bodyEl, _interaction, _iClass, _events, _queue, _scrollable, height, theme, view } = state;
|
|
2362
|
+
let { _viewComponent, _bodyEl, _interaction, _iClass, _events, _queue, _queue2, _tasks, _scrollable, height, theme, view } = state;
|
|
2304
2363
|
component_subscribe($$self, _viewComponent, value => $$invalidate(5, $_viewComponent = value));
|
|
2305
|
-
component_subscribe($$self, _bodyEl, value => $$invalidate(
|
|
2306
|
-
component_subscribe($$self, _interaction, value => $$invalidate(
|
|
2364
|
+
component_subscribe($$self, _bodyEl, value => $$invalidate(32, $_bodyEl = value));
|
|
2365
|
+
component_subscribe($$self, _interaction, value => $$invalidate(35, $_interaction = value));
|
|
2307
2366
|
component_subscribe($$self, _iClass, value => $$invalidate(2, $_iClass = value));
|
|
2308
|
-
component_subscribe($$self, _events, value => $$invalidate(
|
|
2309
|
-
component_subscribe($$self, _queue, value => $$invalidate(
|
|
2367
|
+
component_subscribe($$self, _events, value => $$invalidate(36, $_events = value));
|
|
2368
|
+
component_subscribe($$self, _queue, value => $$invalidate(34, $_queue = value));
|
|
2369
|
+
component_subscribe($$self, _queue2, value => $$invalidate(33, $_queue2 = value));
|
|
2310
2370
|
component_subscribe($$self, _scrollable, value => $$invalidate(0, $_scrollable = value));
|
|
2311
2371
|
component_subscribe($$self, height, value => $$invalidate(3, $height = value));
|
|
2312
2372
|
component_subscribe($$self, theme, value => $$invalidate(1, $theme = value));
|
|
@@ -2396,7 +2456,11 @@ function instance($$self, $$props, $$invalidate) {
|
|
|
2396
2456
|
|
|
2397
2457
|
beforeUpdate(() => {
|
|
2398
2458
|
flushDebounce($_queue);
|
|
2399
|
-
|
|
2459
|
+
});
|
|
2460
|
+
|
|
2461
|
+
afterUpdate(() => {
|
|
2462
|
+
flushDebounce($_queue2);
|
|
2463
|
+
task(recheckScrollable, null, _tasks);
|
|
2400
2464
|
});
|
|
2401
2465
|
|
|
2402
2466
|
function recheckScrollable() {
|
|
@@ -2406,12 +2470,12 @@ function instance($$self, $$props, $$invalidate) {
|
|
|
2406
2470
|
}
|
|
2407
2471
|
|
|
2408
2472
|
$$self.$$set = $$props => {
|
|
2409
|
-
if ('plugins' in $$props) $$invalidate(
|
|
2410
|
-
if ('options' in $$props) $$invalidate(
|
|
2473
|
+
if ('plugins' in $$props) $$invalidate(18, plugins = $$props.plugins);
|
|
2474
|
+
if ('options' in $$props) $$invalidate(19, options = $$props.options);
|
|
2411
2475
|
};
|
|
2412
2476
|
|
|
2413
2477
|
$$self.$$.update = () => {
|
|
2414
|
-
if ($$self.$$.dirty[0] & /*options*/
|
|
2478
|
+
if ($$self.$$.dirty[0] & /*options*/ 524288) {
|
|
2415
2479
|
for (let [name, value] of diff(options, prevOptions)) {
|
|
2416
2480
|
setOption(name, value);
|
|
2417
2481
|
}
|
|
@@ -2431,6 +2495,7 @@ function instance($$self, $$props, $$invalidate) {
|
|
|
2431
2495
|
_iClass,
|
|
2432
2496
|
_events,
|
|
2433
2497
|
_queue,
|
|
2498
|
+
_queue2,
|
|
2434
2499
|
_scrollable,
|
|
2435
2500
|
height,
|
|
2436
2501
|
theme,
|
|
@@ -2464,20 +2529,20 @@ class Calendar extends SvelteComponent {
|
|
|
2464
2529
|
create_fragment,
|
|
2465
2530
|
safe_not_equal,
|
|
2466
2531
|
{
|
|
2467
|
-
plugins:
|
|
2468
|
-
options:
|
|
2469
|
-
setOption:
|
|
2470
|
-
getOption:
|
|
2471
|
-
refetchEvents:
|
|
2472
|
-
getEvents:
|
|
2473
|
-
getEventById:
|
|
2474
|
-
addEvent:
|
|
2475
|
-
updateEvent:
|
|
2476
|
-
removeEventById:
|
|
2477
|
-
getView:
|
|
2478
|
-
unselect:
|
|
2479
|
-
dateFromPoint:
|
|
2480
|
-
destroy:
|
|
2532
|
+
plugins: 18,
|
|
2533
|
+
options: 19,
|
|
2534
|
+
setOption: 20,
|
|
2535
|
+
getOption: 21,
|
|
2536
|
+
refetchEvents: 22,
|
|
2537
|
+
getEvents: 23,
|
|
2538
|
+
getEventById: 24,
|
|
2539
|
+
addEvent: 25,
|
|
2540
|
+
updateEvent: 26,
|
|
2541
|
+
removeEventById: 27,
|
|
2542
|
+
getView: 28,
|
|
2543
|
+
unselect: 29,
|
|
2544
|
+
dateFromPoint: 30,
|
|
2545
|
+
destroy: 31
|
|
2481
2546
|
},
|
|
2482
2547
|
null,
|
|
2483
2548
|
[-1, -1]
|
|
@@ -2485,52 +2550,52 @@ class Calendar extends SvelteComponent {
|
|
|
2485
2550
|
}
|
|
2486
2551
|
|
|
2487
2552
|
get setOption() {
|
|
2488
|
-
return this.$$.ctx[
|
|
2553
|
+
return this.$$.ctx[20];
|
|
2489
2554
|
}
|
|
2490
2555
|
|
|
2491
2556
|
get getOption() {
|
|
2492
|
-
return this.$$.ctx[
|
|
2557
|
+
return this.$$.ctx[21];
|
|
2493
2558
|
}
|
|
2494
2559
|
|
|
2495
2560
|
get refetchEvents() {
|
|
2496
|
-
return this.$$.ctx[
|
|
2561
|
+
return this.$$.ctx[22];
|
|
2497
2562
|
}
|
|
2498
2563
|
|
|
2499
2564
|
get getEvents() {
|
|
2500
|
-
return this.$$.ctx[
|
|
2565
|
+
return this.$$.ctx[23];
|
|
2501
2566
|
}
|
|
2502
2567
|
|
|
2503
2568
|
get getEventById() {
|
|
2504
|
-
return this.$$.ctx[
|
|
2569
|
+
return this.$$.ctx[24];
|
|
2505
2570
|
}
|
|
2506
2571
|
|
|
2507
2572
|
get addEvent() {
|
|
2508
|
-
return this.$$.ctx[
|
|
2573
|
+
return this.$$.ctx[25];
|
|
2509
2574
|
}
|
|
2510
2575
|
|
|
2511
2576
|
get updateEvent() {
|
|
2512
|
-
return this.$$.ctx[
|
|
2577
|
+
return this.$$.ctx[26];
|
|
2513
2578
|
}
|
|
2514
2579
|
|
|
2515
2580
|
get removeEventById() {
|
|
2516
|
-
return this.$$.ctx[
|
|
2581
|
+
return this.$$.ctx[27];
|
|
2517
2582
|
}
|
|
2518
2583
|
|
|
2519
2584
|
get getView() {
|
|
2520
|
-
return this.$$.ctx[
|
|
2585
|
+
return this.$$.ctx[28];
|
|
2521
2586
|
}
|
|
2522
2587
|
|
|
2523
2588
|
get unselect() {
|
|
2524
|
-
return this.$$.ctx[
|
|
2589
|
+
return this.$$.ctx[29];
|
|
2525
2590
|
}
|
|
2526
2591
|
|
|
2527
2592
|
get dateFromPoint() {
|
|
2528
|
-
return this.$$.ctx[
|
|
2593
|
+
return this.$$.ctx[30];
|
|
2529
2594
|
}
|
|
2530
2595
|
|
|
2531
2596
|
get destroy() {
|
|
2532
|
-
return this.$$.ctx[
|
|
2597
|
+
return this.$$.ctx[31];
|
|
2533
2598
|
}
|
|
2534
2599
|
}
|
|
2535
2600
|
|
|
2536
|
-
export { DAY_IN_SECONDS, addDay, addDuration, ancestor, assign, bgEvent, btnTextDay, btnTextMonth, btnTextWeek, btnTextYear, cloneDate, cloneEvent, copyTime, createDate, createDuration, createElement, createEventChunk, createEventClasses, createEventContent, createEventSources, createEvents, createView, datesEqual, debounce, Calendar as default, eventIntersects, floor, flushDebounce, getElementWithPayload, getPayload, ghostEvent, hasPayload, hasYScroll, height, helperEvent, intl, intlRange, keyEnter, keys, listView, max, min, nextClosestDay, noTimePart, outsideEvent, pointerEvent, prepareEventChunks, prevClosestDay, previewEvent, rect, repositionEvent, setContent, setMidnight, setPayload, sortEventChunks, subtractDay, subtractDuration, symbol, themeView, toEventWithLocalDates, toISOString, toLocalDate, toViewWithLocalDates };
|
|
2601
|
+
export { DAY_IN_SECONDS, addDay, addDuration, ancestor, assign, bgEvent, btnTextDay, btnTextMonth, btnTextWeek, btnTextYear, cloneDate, cloneEvent, copyTime, createDate, createDuration, createElement, createEventChunk, createEventClasses, createEventContent, createEventSources, createEvents, createView, datesEqual, debounce, Calendar as default, eventIntersects, floor, flushDebounce, getElementWithPayload, getPayload, ghostEvent, hasPayload, hasYScroll, height, helperEvent, intl, intlRange, keyEnter, keys, listView, max, min, nextClosestDay, noTimePart, outsideEvent, pointerEvent, prepareEventChunks, prevClosestDay, previewEvent, rect, repositionEvent, runReposition, setContent, setMidnight, setPayload, sortEventChunks, subtractDay, subtractDuration, symbol, task, themeView, toEventWithLocalDates, toISOString, toLocalDate, toViewWithLocalDates };
|
package/package.json
CHANGED
package/src/Calendar.svelte
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import '../index.css';
|
|
3
|
-
import {setContext, beforeUpdate} from 'svelte';
|
|
3
|
+
import {setContext, beforeUpdate, afterUpdate} from 'svelte';
|
|
4
4
|
import {destroy_component, get_current_component} from 'svelte/internal';
|
|
5
5
|
import {get} from 'svelte/store';
|
|
6
6
|
import {diff} from './storage/options';
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
getPayload,
|
|
18
18
|
flushDebounce,
|
|
19
19
|
hasYScroll,
|
|
20
|
-
listView
|
|
20
|
+
listView,
|
|
21
|
+
task
|
|
21
22
|
} from './lib.js';
|
|
22
23
|
|
|
23
24
|
export let plugins = [];
|
|
@@ -28,7 +29,7 @@
|
|
|
28
29
|
let state = new State(plugins, options);
|
|
29
30
|
setContext('state', state);
|
|
30
31
|
|
|
31
|
-
let {_viewComponent, _bodyEl, _interaction, _iClass, _events, _queue, _scrollable, height, theme, view} = state;
|
|
32
|
+
let {_viewComponent, _bodyEl, _interaction, _iClass, _events, _queue, _queue2, _tasks, _scrollable, height, theme, view} = state;
|
|
32
33
|
|
|
33
34
|
// Reactively update options that did change
|
|
34
35
|
let prevOptions = {...options};
|
|
@@ -112,7 +113,11 @@
|
|
|
112
113
|
|
|
113
114
|
beforeUpdate(() => {
|
|
114
115
|
flushDebounce($_queue);
|
|
115
|
-
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
afterUpdate(() => {
|
|
119
|
+
flushDebounce($_queue2);
|
|
120
|
+
task(recheckScrollable, null, _tasks);
|
|
116
121
|
});
|
|
117
122
|
|
|
118
123
|
function recheckScrollable() {
|
package/src/lib/debounce.js
CHANGED
|
@@ -8,3 +8,13 @@ export function flushDebounce(queue) {
|
|
|
8
8
|
run_all(queue);
|
|
9
9
|
queue.clear();
|
|
10
10
|
}
|
|
11
|
+
|
|
12
|
+
export function task(fn, handle, tasks) {
|
|
13
|
+
handle ??= fn;
|
|
14
|
+
if (!tasks.has(handle)) {
|
|
15
|
+
tasks.set(handle, setTimeout(() => {
|
|
16
|
+
tasks.delete(handle);
|
|
17
|
+
fn();
|
|
18
|
+
}));
|
|
19
|
+
}
|
|
20
|
+
}
|
package/src/lib/events.js
CHANGED
|
@@ -48,6 +48,82 @@ export function sortEventChunks(chunks) {
|
|
|
48
48
|
chunks.sort((a, b) => a.start - b.start || b.event.allDay - a.event.allDay);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
export function createEventContent(chunk, displayEventEnd, eventContent, theme, _intlEventTime, _view) {
|
|
52
|
+
let timeText = _intlEventTime.formatRange(
|
|
53
|
+
chunk.start,
|
|
54
|
+
displayEventEnd && chunk.event.display !== 'pointer'
|
|
55
|
+
? copyTime(cloneDate(chunk.start), chunk.end) // make Intl.formatRange output only the time part
|
|
56
|
+
: chunk.start
|
|
57
|
+
);
|
|
58
|
+
let content;
|
|
59
|
+
|
|
60
|
+
if (eventContent) {
|
|
61
|
+
content = is_function(eventContent)
|
|
62
|
+
? eventContent({
|
|
63
|
+
event: toEventWithLocalDates(chunk.event),
|
|
64
|
+
timeText,
|
|
65
|
+
view: toViewWithLocalDates(_view)
|
|
66
|
+
})
|
|
67
|
+
: eventContent;
|
|
68
|
+
} else {
|
|
69
|
+
let domNodes;
|
|
70
|
+
switch (chunk.event.display) {
|
|
71
|
+
case 'background':
|
|
72
|
+
domNodes = [];
|
|
73
|
+
break;
|
|
74
|
+
case 'pointer':
|
|
75
|
+
domNodes = [createTimeElement(timeText, chunk, theme)];
|
|
76
|
+
break;
|
|
77
|
+
default:
|
|
78
|
+
domNodes = [
|
|
79
|
+
...chunk.event.allDay ? [] : [createTimeElement(timeText, chunk, theme)],
|
|
80
|
+
createElement('h4', theme.eventTitle, chunk.event.title)
|
|
81
|
+
];
|
|
82
|
+
}
|
|
83
|
+
content = {domNodes};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return [timeText, content];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function createTimeElement(timeText, chunk, theme) {
|
|
90
|
+
return createElement(
|
|
91
|
+
'time',
|
|
92
|
+
theme.eventTime,
|
|
93
|
+
timeText,
|
|
94
|
+
[['datetime', toISOString(chunk.start)]]
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function createEventClasses(eventClassNames, event, _view) {
|
|
99
|
+
if (eventClassNames) {
|
|
100
|
+
if (is_function(eventClassNames)) {
|
|
101
|
+
eventClassNames = eventClassNames({
|
|
102
|
+
event: toEventWithLocalDates(event),
|
|
103
|
+
view: toViewWithLocalDates(_view)
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return Array.isArray(eventClassNames) ? eventClassNames : [eventClassNames];
|
|
107
|
+
}
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function toEventWithLocalDates(event) {
|
|
112
|
+
return _cloneEvent(event, toLocalDate);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function cloneEvent(event) {
|
|
116
|
+
return _cloneEvent(event, cloneDate);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function _cloneEvent(event, dateFn) {
|
|
120
|
+
event = assign({}, event);
|
|
121
|
+
event.start = dateFn(event.start);
|
|
122
|
+
event.end = dateFn(event.end);
|
|
123
|
+
|
|
124
|
+
return event;
|
|
125
|
+
}
|
|
126
|
+
|
|
51
127
|
/**
|
|
52
128
|
* Prepare event chunks for month view and all-day slot in week view
|
|
53
129
|
*/
|
|
@@ -130,80 +206,11 @@ export function repositionEvent(chunk, longChunks, height) {
|
|
|
130
206
|
return margin;
|
|
131
207
|
}
|
|
132
208
|
|
|
133
|
-
export function
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
? copyTime(cloneDate(chunk.start), chunk.end) // make Intl.formatRange output only the time part
|
|
138
|
-
: chunk.start
|
|
139
|
-
);
|
|
140
|
-
let content;
|
|
141
|
-
|
|
142
|
-
if (eventContent) {
|
|
143
|
-
content = is_function(eventContent)
|
|
144
|
-
? eventContent({
|
|
145
|
-
event: toEventWithLocalDates(chunk.event),
|
|
146
|
-
timeText,
|
|
147
|
-
view: toViewWithLocalDates(_view)
|
|
148
|
-
})
|
|
149
|
-
: eventContent;
|
|
150
|
-
} else {
|
|
151
|
-
let domNodes;
|
|
152
|
-
switch (chunk.event.display) {
|
|
153
|
-
case 'background':
|
|
154
|
-
domNodes = [];
|
|
155
|
-
break;
|
|
156
|
-
case 'pointer':
|
|
157
|
-
domNodes = [createTimeElement(timeText, chunk, theme)];
|
|
158
|
-
break;
|
|
159
|
-
default:
|
|
160
|
-
domNodes = [
|
|
161
|
-
...chunk.event.allDay ? [] : [createTimeElement(timeText, chunk, theme)],
|
|
162
|
-
createElement('h4', theme.eventTitle, chunk.event.title)
|
|
163
|
-
];
|
|
164
|
-
}
|
|
165
|
-
content = {domNodes};
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
return [timeText, content];
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function createTimeElement(timeText, chunk, theme) {
|
|
172
|
-
return createElement(
|
|
173
|
-
'time',
|
|
174
|
-
theme.eventTime,
|
|
175
|
-
timeText,
|
|
176
|
-
[['datetime', toISOString(chunk.start)]]
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
export function createEventClasses(eventClassNames, event, _view) {
|
|
181
|
-
if (eventClassNames) {
|
|
182
|
-
if (is_function(eventClassNames)) {
|
|
183
|
-
eventClassNames = eventClassNames({
|
|
184
|
-
event: toEventWithLocalDates(event),
|
|
185
|
-
view: toViewWithLocalDates(_view)
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
return Array.isArray(eventClassNames) ? eventClassNames : [eventClassNames];
|
|
209
|
+
export function runReposition(refs, data) {
|
|
210
|
+
refs.length = data.length;
|
|
211
|
+
for (let ref of refs) {
|
|
212
|
+
ref?.reposition?.();
|
|
189
213
|
}
|
|
190
|
-
return [];
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
export function toEventWithLocalDates(event) {
|
|
194
|
-
return _cloneEvent(event, toLocalDate);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
export function cloneEvent(event) {
|
|
198
|
-
return _cloneEvent(event, cloneDate);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
function _cloneEvent(event, dateFn) {
|
|
202
|
-
event = assign({}, event);
|
|
203
|
-
event.start = dateFn(event.start);
|
|
204
|
-
event.end = dateFn(event.end);
|
|
205
|
-
|
|
206
|
-
return event;
|
|
207
214
|
}
|
|
208
215
|
|
|
209
216
|
/**
|
package/src/lib/stores.js
CHANGED
|
@@ -15,11 +15,49 @@ export function intl(locale, format) {
|
|
|
15
15
|
|
|
16
16
|
export function intlRange(locale, format) {
|
|
17
17
|
return derived([locale, format], ([$locale, $format]) => {
|
|
18
|
-
let
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
let formatRange;
|
|
19
|
+
if (is_function($format)) {
|
|
20
|
+
formatRange = $format;
|
|
21
|
+
} else {
|
|
22
|
+
let intl = new Intl.DateTimeFormat($locale, $format);
|
|
23
|
+
formatRange = (start, end) => {
|
|
24
|
+
if (start <= end) {
|
|
25
|
+
return intl.formatRange(start, end);
|
|
26
|
+
} else {
|
|
27
|
+
// In iOS 16 and older, intl.formatRange() throws an exception if the start date is later than the end date.
|
|
28
|
+
// Therefore, we first swap the parameters, and then swap the resulting parts.
|
|
29
|
+
/** @see https://github.com/vkurko/calendar/issues/227 */
|
|
30
|
+
let parts = intl.formatRangeToParts(end, start);
|
|
31
|
+
let result = '';
|
|
32
|
+
let sources = ['startRange', 'endRange'];
|
|
33
|
+
let processed = [false, false];
|
|
34
|
+
for (let part of parts) {
|
|
35
|
+
let i = sources.indexOf(part.source);
|
|
36
|
+
if (i >= 0) {
|
|
37
|
+
if (!processed[i]) {
|
|
38
|
+
result += _getParts(sources[1 - i], parts);
|
|
39
|
+
processed[i] = true;
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
result += part.value;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
21
49
|
return {
|
|
22
|
-
formatRange: (start, end) =>
|
|
50
|
+
formatRange: (start, end) => formatRange(toLocalDate(start), toLocalDate(end))
|
|
23
51
|
};
|
|
24
52
|
});
|
|
25
53
|
}
|
|
54
|
+
|
|
55
|
+
function _getParts(source, parts) {
|
|
56
|
+
let result = '';
|
|
57
|
+
for (let part of parts) {
|
|
58
|
+
if (part.source == source) {
|
|
59
|
+
result += part.value;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return result;
|
|
63
|
+
}
|
package/src/storage/options.js
CHANGED
package/src/storage/state.js
CHANGED
|
@@ -32,7 +32,9 @@ export default class {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
// Private stores
|
|
35
|
-
this._queue = writable(new Map()); // debounce queue
|
|
35
|
+
this._queue = writable(new Map()); // debounce queue (beforeUpdate)
|
|
36
|
+
this._queue2 = writable(new Map()); // debounce queue (afterUpdate)
|
|
37
|
+
this._tasks = new Map(); // timeout IDs for tasks
|
|
36
38
|
this._auxiliary = writable([]); // auxiliary components
|
|
37
39
|
this._dayGrid = dayGrid(this);
|
|
38
40
|
this._currentRange = currentRange(this);
|