@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 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 (32kb [br](https://en.wikipedia.org/wiki/Brotli) compressed)
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.5.1/event-calendar.min.css">
202
- <script src="https://cdn.jsdelivr.net/npm/@event-calendar/build@2.5.1/event-calendar.min.js"></script>
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
@@ -456,6 +456,7 @@
456
456
  .ec-ghost {
457
457
  opacity: 0.5;
458
458
  user-select: none;
459
+ pointer-events: none;
459
460
  }
460
461
 
461
462
  .ec-bg-events {
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 createEventContent(chunk, displayEventEnd, eventContent, theme, _intlEventTime, _view) {
452
- let timeText = _intlEventTime.formatRange(
453
- chunk.start,
454
- displayEventEnd && chunk.event.display !== 'pointer'
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 intl = is_function($format)
606
- ? {formatRange: $format}
607
- : new Intl.DateTimeFormat($locale, $format);
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) => intl.formatRange(toLocalDate(start), toLocalDate(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[16]);
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(31, $_bodyEl = value));
2306
- component_subscribe($$self, _interaction, value => $$invalidate(33, $_interaction = value));
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(34, $_events = value));
2309
- component_subscribe($$self, _queue, value => $$invalidate(32, $_queue = value));
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
- setTimeout(recheckScrollable);
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(17, plugins = $$props.plugins);
2410
- if ('options' in $$props) $$invalidate(18, options = $$props.options);
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*/ 262144) {
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: 17,
2468
- options: 18,
2469
- setOption: 19,
2470
- getOption: 20,
2471
- refetchEvents: 21,
2472
- getEvents: 22,
2473
- getEventById: 23,
2474
- addEvent: 24,
2475
- updateEvent: 25,
2476
- removeEventById: 26,
2477
- getView: 27,
2478
- unselect: 28,
2479
- dateFromPoint: 29,
2480
- destroy: 30
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[19];
2553
+ return this.$$.ctx[20];
2489
2554
  }
2490
2555
 
2491
2556
  get getOption() {
2492
- return this.$$.ctx[20];
2557
+ return this.$$.ctx[21];
2493
2558
  }
2494
2559
 
2495
2560
  get refetchEvents() {
2496
- return this.$$.ctx[21];
2561
+ return this.$$.ctx[22];
2497
2562
  }
2498
2563
 
2499
2564
  get getEvents() {
2500
- return this.$$.ctx[22];
2565
+ return this.$$.ctx[23];
2501
2566
  }
2502
2567
 
2503
2568
  get getEventById() {
2504
- return this.$$.ctx[23];
2569
+ return this.$$.ctx[24];
2505
2570
  }
2506
2571
 
2507
2572
  get addEvent() {
2508
- return this.$$.ctx[24];
2573
+ return this.$$.ctx[25];
2509
2574
  }
2510
2575
 
2511
2576
  get updateEvent() {
2512
- return this.$$.ctx[25];
2577
+ return this.$$.ctx[26];
2513
2578
  }
2514
2579
 
2515
2580
  get removeEventById() {
2516
- return this.$$.ctx[26];
2581
+ return this.$$.ctx[27];
2517
2582
  }
2518
2583
 
2519
2584
  get getView() {
2520
- return this.$$.ctx[27];
2585
+ return this.$$.ctx[28];
2521
2586
  }
2522
2587
 
2523
2588
  get unselect() {
2524
- return this.$$.ctx[28];
2589
+ return this.$$.ctx[29];
2525
2590
  }
2526
2591
 
2527
2592
  get dateFromPoint() {
2528
- return this.$$.ctx[29];
2593
+ return this.$$.ctx[30];
2529
2594
  }
2530
2595
 
2531
2596
  get destroy() {
2532
- return this.$$.ctx[30];
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@event-calendar/core",
3
- "version": "2.5.1",
3
+ "version": "2.6.1",
4
4
  "title": "Event Calendar Core package",
5
5
  "description": "Full-sized drag & drop event calendar with resource view",
6
6
  "keywords": [
@@ -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
- setTimeout(recheckScrollable);
116
+ });
117
+
118
+ afterUpdate(() => {
119
+ flushDebounce($_queue2);
120
+ task(recheckScrollable, null, _tasks);
116
121
  });
117
122
 
118
123
  function recheckScrollable() {
@@ -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 createEventContent(chunk, displayEventEnd, eventContent, theme, _intlEventTime, _view) {
134
- let timeText = _intlEventTime.formatRange(
135
- chunk.start,
136
- displayEventEnd && chunk.event.display !== 'pointer'
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 intl = is_function($format)
19
- ? {formatRange: $format}
20
- : new Intl.DateTimeFormat($locale, $format);
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) => intl.formatRange(toLocalDate(start), toLocalDate(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
+ }
@@ -20,6 +20,7 @@ export function createOptions(plugins) {
20
20
  displayEventEnd: true,
21
21
  duration: {weeks: 1},
22
22
  events: [],
23
+ eventAllUpdated: undefined,
23
24
  eventBackgroundColor: undefined,
24
25
  eventTextColor: undefined,
25
26
  eventClassNames: undefined,
@@ -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);
@@ -458,6 +458,7 @@
458
458
  .ec-ghost {
459
459
  opacity: .5;
460
460
  user-select: none;
461
+ pointer-events: none;
461
462
  }
462
463
 
463
464
  .ec-bg-events {