@event-calendar/core 4.7.0 → 5.0.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.
Files changed (93) hide show
  1. package/README.md +29 -51
  2. package/dist/index.css +627 -731
  3. package/dist/index.js +2500 -3191
  4. package/package.json +2 -2
  5. package/src/Auxiliary.svelte +2 -13
  6. package/src/Buttons.svelte +62 -52
  7. package/src/Calendar.svelte +9 -3
  8. package/src/lib/a11y.js +2 -2
  9. package/src/lib/attachments.js +61 -0
  10. package/src/lib/chunks.js +117 -0
  11. package/src/lib/components/BaseDay.svelte +50 -0
  12. package/src/lib/components/BaseEvent.svelte +3 -3
  13. package/src/lib/components/ColHead.svelte +34 -0
  14. package/src/lib/components/DayHeader.svelte +14 -0
  15. package/src/lib/components/InteractableEvent.svelte +1 -3
  16. package/src/lib/components/index.js +3 -0
  17. package/src/lib/date.js +1 -1
  18. package/src/lib/dom.js +0 -4
  19. package/src/lib/events.js +10 -134
  20. package/src/lib/index.js +3 -2
  21. package/src/lib/{times.js → slots.js} +14 -19
  22. package/src/lib/stores.js +0 -33
  23. package/src/lib/utils.js +11 -2
  24. package/src/lib/view.js +0 -4
  25. package/src/plugins/day-grid/Day.svelte +36 -129
  26. package/src/plugins/day-grid/Event.svelte +42 -41
  27. package/src/plugins/day-grid/Popup.svelte +65 -48
  28. package/src/plugins/day-grid/View.svelte +76 -4
  29. package/src/plugins/day-grid/index.js +5 -5
  30. package/src/plugins/day-grid/lib.js +61 -0
  31. package/src/plugins/day-grid/stores.js +2 -20
  32. package/src/plugins/interaction/Action.svelte +40 -45
  33. package/src/plugins/interaction/Auxiliary.svelte +4 -4
  34. package/src/plugins/interaction/Pointer.svelte +8 -12
  35. package/src/plugins/interaction/Resizer.svelte +2 -2
  36. package/src/plugins/interaction/lib/utils.js +1 -5
  37. package/src/plugins/list/Day.svelte +8 -24
  38. package/src/plugins/list/View.svelte +39 -2
  39. package/src/plugins/resource-time-grid/Label.svelte +2 -2
  40. package/src/plugins/resource-time-grid/View.svelte +38 -82
  41. package/src/plugins/resource-time-grid/index.js +18 -10
  42. package/src/plugins/resource-time-grid/lib.js +31 -0
  43. package/src/plugins/resource-time-grid/options.js +10 -0
  44. package/src/plugins/resource-time-grid/stores.js +34 -0
  45. package/src/plugins/resource-timeline/Day.svelte +10 -73
  46. package/src/plugins/resource-timeline/Event.svelte +14 -23
  47. package/src/plugins/resource-timeline/Header.svelte +5 -5
  48. package/src/plugins/resource-timeline/Label.svelte +4 -12
  49. package/src/plugins/resource-timeline/NowIndicator.svelte +33 -28
  50. package/src/plugins/resource-timeline/View.svelte +129 -14
  51. package/src/plugins/resource-timeline/index.js +26 -23
  52. package/src/plugins/resource-timeline/lib.js +115 -118
  53. package/src/plugins/resource-timeline/stores.js +11 -7
  54. package/src/plugins/time-grid/AllDayEvent.svelte +31 -0
  55. package/src/plugins/time-grid/Day.svelte +11 -99
  56. package/src/plugins/time-grid/Event.svelte +18 -20
  57. package/src/plugins/time-grid/NowIndicator.svelte +32 -10
  58. package/src/plugins/time-grid/View.svelte +127 -35
  59. package/src/plugins/time-grid/index.js +10 -8
  60. package/src/plugins/time-grid/lib.js +142 -0
  61. package/src/plugins/time-grid/options.js +57 -0
  62. package/src/plugins/time-grid/stores.js +41 -8
  63. package/src/storage/options.js +4 -39
  64. package/src/storage/state.js +1 -4
  65. package/src/storage/stores.js +42 -11
  66. package/src/styles/days.css +91 -0
  67. package/src/styles/events.css +180 -0
  68. package/src/styles/index.css +126 -0
  69. package/src/styles/now-indicator.css +35 -0
  70. package/src/styles/popup.css +30 -0
  71. package/src/styles/sidebar.css +59 -0
  72. package/src/styles/slots.css +42 -0
  73. package/src/styles/theme.css +68 -0
  74. package/src/styles/toolbar.css +80 -0
  75. package/src/lib/actions.js +0 -52
  76. package/src/plugins/day-grid/Body.svelte +0 -54
  77. package/src/plugins/day-grid/Header.svelte +0 -20
  78. package/src/plugins/day-grid/Week.svelte +0 -60
  79. package/src/plugins/list/Body.svelte +0 -44
  80. package/src/plugins/resource-timeline/Body.svelte +0 -67
  81. package/src/plugins/resource-timeline/Days.svelte +0 -72
  82. package/src/plugins/resource-timeline/Sidebar.svelte +0 -35
  83. package/src/plugins/time-grid/Body.svelte +0 -43
  84. package/src/plugins/time-grid/Section.svelte +0 -29
  85. package/src/plugins/time-grid/all-day/Day.svelte +0 -65
  86. package/src/plugins/time-grid/all-day/Event.svelte +0 -37
  87. package/src/plugins/time-grid/all-day/Week.svelte +0 -65
  88. package/src/plugins/time-grid/utils.js +0 -58
  89. package/src/styles/day-grid.scss +0 -51
  90. package/src/styles/index.scss +0 -553
  91. package/src/styles/theme.scss +0 -95
  92. package/src/styles/time-grid.scss +0 -83
  93. package/src/styles/timeline.scss +0 -152
@@ -1,39 +1,44 @@
1
1
  <script>
2
2
  import {getContext} from 'svelte';
3
- import {addDuration, cloneDate, datesEqual} from '#lib';
4
- import {getSlotTimeLimits} from './lib.js';
3
+ import {datesEqual, toSeconds, intersectionObserver} from '#lib';
5
4
 
6
- let {slotDuration, slotWidth, theme, _bodyHeight, _bodyWidth, _bodyScrollLeft,
7
- _headerHeight, _dayTimeLimits, _now, _today, _viewDates} = getContext('state');
5
+ let {grid} = $props();
8
6
 
9
- // Style
10
- let left = $derived.by(() => {
11
- let offset = 0;
12
- for (let i = 0; i < $_viewDates.length; ++i) {
13
- let slotTimeLimits = getSlotTimeLimits($_dayTimeLimits, $_viewDates[i]);
14
- if (datesEqual($_viewDates[i], $_today)) {
15
- let dayStart = addDuration(cloneDate($_viewDates[i]), slotTimeLimits.min);
16
- let dayEnd = addDuration(cloneDate($_viewDates[i]), slotTimeLimits.max);
17
- if ($_now >= dayStart && $_now <= dayEnd) {
18
- offset += ($_now - dayStart) / 1000;
19
- break;
20
- } else {
21
- return null;
22
- }
23
- } else {
24
- offset += slotTimeLimits.max.seconds - slotTimeLimits.min.seconds;
7
+ let {_mainEl, _now, _today, _sidebarWidth, slotDuration, slotWidth, theme} = getContext('state');
8
+
9
+ // Layout
10
+ let {gridColumn, start, end} = $derived.by(() => {
11
+ for (let day of grid[0]) {
12
+ if (datesEqual(day.dayStart, $_today)) {
13
+ return day;
25
14
  }
26
15
  }
27
- let step = $slotDuration.seconds;
28
- return offset / step * $slotWidth - $_bodyScrollLeft;
16
+ return {};
17
+ });
18
+ let left = $derived.by(() => {
19
+ if ($_now < start || $_now > end) {
20
+ return null;
21
+ }
22
+ let step = toSeconds($slotDuration);
23
+ return ($_now - start) / 1000 / step * $slotWidth;
24
+ });
25
+
26
+ // Observe intersections
27
+ let observerOptions = $derived({
28
+ root: $_mainEl,
29
+ rootMargin: `0px 0px 0px -${$_sidebarWidth + 1}px`,
30
+ threshold: 0.0,
29
31
  });
32
+ function onIntersect(el, entry) {
33
+ el.classList.toggle($theme.hidden, !entry.isIntersecting);
34
+ }
30
35
  </script>
31
36
 
32
- {#if left !== null && left >= 3 && left <= $_bodyWidth - 3}
33
- <div
34
- class="{$theme.nowIndicator}"
35
- style:top="{$_headerHeight+2}px"
36
- style:left="{left}px"
37
- style:height="{$_bodyHeight-1}px"
37
+ {#if gridColumn && left !== null}
38
+ <div {@attach intersectionObserver(onIntersect, observerOptions)}
39
+ class="{$theme.nowIndicator}"
40
+ style:grid-column="{gridColumn + 1}"
41
+ style:grid-row="2 / span {grid.length}"
42
+ style:inset-inline-start="{left}px"
38
43
  ></div>
39
44
  {/if}
@@ -1,20 +1,135 @@
1
1
  <script>
2
- import {getContext} from 'svelte';
3
- import Sidebar from './Sidebar.svelte';
4
- import Header from './Header.svelte';
5
- import Body from './Body.svelte';
2
+ import {getContext, tick} from 'svelte';
3
+ import {max, resizeObserver, runReposition, contentFrom, toSeconds, datesEqual, min} from "#lib";
4
+ import {createGrid, createEventChunks, createIEventChunks, getSlotTimeLimits} from './lib.js';
5
+ import {ColHead, DayHeader} from '#components';
6
+ import Day from './Day.svelte';
7
+ import Event from './Event.svelte';
8
+ import Label from './Label.svelte';
9
+ import Expander from './Expander.svelte';
6
10
  import NowIndicator from './NowIndicator.svelte';
7
11
 
8
- let {nowIndicator, theme} = getContext('state');
12
+ let {_daySlots, _dayTimeLimits, _filteredEvents, _iEvents, _mainEl, _monthView, _nestedResources, _sidebarWidth,
13
+ _slotLabelPeriodicity, _today, _viewResources, _viewDates, columnWidth, highlightedDates, nowIndicator, scrollTime,
14
+ slotDuration, slotHeight, slotWidth, theme, validRange} = getContext('state');
15
+
16
+ let headerHeight = $state(0);
17
+
18
+ let grid = $derived(createGrid($_viewDates, $_viewResources, $_dayTimeLimits, $validRange, $highlightedDates));
19
+ let {chunks, bgChunks} = $derived(createEventChunks($_filteredEvents, grid));
20
+ let iChunks = $derived(createIEventChunks($_iEvents, grid));
21
+
22
+ // Handle scrollTime
23
+ $effect(() => {
24
+ $_viewDates;
25
+ $scrollTime;
26
+ tick().then(scrollToTime);
27
+ });
28
+ function scrollToTime() {
29
+ if ($_monthView) {
30
+ return;
31
+ }
32
+ let scrollLeft = 0;
33
+ let gaps = 0;
34
+ let todayOutOfView = $_today < $_viewDates[0] || $_today > $_viewDates.at(-1);
35
+ for (let date of $_viewDates) {
36
+ let slotTimeLimits = getSlotTimeLimits($_dayTimeLimits, date);
37
+ if (todayOutOfView || datesEqual(date, $_today)) {
38
+ scrollLeft += max(
39
+ min(toSeconds($scrollTime), toSeconds(slotTimeLimits.max)) - toSeconds(slotTimeLimits.min),
40
+ 0
41
+ );
42
+ break;
43
+ } else {
44
+ scrollLeft += toSeconds(slotTimeLimits.max) - toSeconds(slotTimeLimits.min);
45
+ ++ gaps;
46
+ }
47
+ }
48
+ $_mainEl.scrollLeft = scrollLeft / toSeconds($slotDuration) * $slotWidth + gaps;
49
+ }
50
+
51
+ // Events reposition
52
+ let refs = [];
53
+ function reposition() {
54
+ runReposition(refs, chunks);
55
+ }
56
+ $effect(reposition);
9
57
  </script>
10
58
 
11
- <div class="{$theme.container}">
12
- <Sidebar/>
13
- <div class="{$theme.main}">
14
- <Header/>
15
- <Body/>
16
- {#if $nowIndicator}
17
- <NowIndicator/>
18
- {/if}
59
+ <section
60
+ bind:this={$_mainEl}
61
+ class="{$theme.main}"
62
+ style:--ec-grid-cols="{grid[0].length}"
63
+ style:--ec-grid-rows="{grid.length}"
64
+ style:--ec-col-width="{$columnWidth ?? 'minmax(4em, 1fr)'}"
65
+ style:--ec-slot-label-periodicity="{$_slotLabelPeriodicity}"
66
+ style:--ec-slot-height="{$slotHeight}px"
67
+ style:--ec-slot-width="{$slotWidth}px"
68
+ style:--ec-header-height="{headerHeight}px"
69
+ style:--ec-sidebar-width="{$_sidebarWidth}px"
70
+ {@attach resizeObserver(reposition)}
71
+ >
72
+ <header bind:offsetHeight={headerHeight} class="{$theme.header}">
73
+ <aside class="{$theme.sidebar}" bind:offsetWidth={$_sidebarWidth}></aside>
74
+ <div class="{$theme.grid}" role="row">
75
+ {#each grid[0] as {dayStart: date, disabled, highlight}, i}
76
+ <ColHead {date} colIndex={1 + i} {disabled} {highlight}>
77
+ <DayHeader {date}/>
78
+ </ColHead>
79
+ {/each}
80
+ {#if !$_monthView}
81
+ {#each grid[0] as {dayStart: date, disabled, highlight}}
82
+ <ColHead {date} className={$theme.slots} {disabled} {highlight} ariaHidden>
83
+ {#each $_daySlots[date.getTime()] as slot}
84
+ <div
85
+ class="{$theme.slot}"
86
+ style:--ec-slot-label-periodicity={slot[2]}
87
+ >
88
+ <time
89
+ datetime="{slot[0]}"
90
+ {@attach contentFrom(slot[1])}
91
+ ></time>
92
+ </div>
93
+ {/each}
94
+ </ColHead>
95
+ {/each}
96
+ {/if}
97
+ </div>
98
+ </header>
99
+
100
+ <div class="{$theme.body}" role="rowgroup">
101
+ <aside class="{$theme.sidebar}">
102
+ {#each $_viewResources as resource}
103
+ <div class="{$theme.rowHead}" role="rowheader">
104
+ {#if $_nestedResources}
105
+ <Expander {resource} />
106
+ {/if}
107
+ <Label {resource}/>
108
+ </div>
109
+ {/each}
110
+ </aside>
111
+ <div class="{$theme.grid}" role="row">
112
+ {#each grid as days}
113
+ {#each days as day}
114
+ <Day {day}/>
115
+ {/each}
116
+ {/each}
117
+ </div>
118
+ <div class="{$theme.events}">
119
+ {#each chunks as chunk, i}
120
+ <!-- svelte-ignore binding_property_non_reactive -->
121
+ <Event bind:this={refs[i]} {chunk}/>
122
+ {/each}
123
+ {#each bgChunks as chunk}
124
+ <Event {chunk}/>
125
+ {/each}
126
+ {#each iChunks as chunk}
127
+ <Event {chunk}/>
128
+ {/each}
129
+ </div>
19
130
  </div>
20
- </div>
131
+
132
+ {#if $nowIndicator && !$_monthView}
133
+ <NowIndicator {grid} />
134
+ {/if}
135
+ </section>
@@ -1,18 +1,23 @@
1
- import {writable} from 'svelte/store';
2
- import {btnTextDay, btnTextMonth, btnTextWeek, themeView, viewResources} from '#lib';
3
- import {dayTimeLimits, dayTimes, nestedResources} from './stores.js';
1
+ import {btnTextDay, btnTextMonth, btnTextWeek, themeView} from '#lib';
2
+ import {dayTimeLimits, daySlots, nestedResources, monthView} from './stores.js';
3
+ import {createTRROptions, createTRRParsers} from '../time-grid/options.js';
4
+ import {createTRRStores} from '../time-grid/stores.js';
5
+ import {createRROptions} from '../resource-time-grid/options.js';
6
+ import {createRRStores} from '../resource-time-grid/stores.js';
4
7
  import View from './View.svelte';
5
8
 
6
9
  export default {
7
10
  createOptions(options) {
11
+ createTRROptions(options);
12
+ createRROptions(options);
13
+ options.slotWidth = 16;
8
14
  // Common options
9
15
  options.buttonText.resourceTimelineDay = 'timeline';
10
16
  options.buttonText.resourceTimelineWeek = 'timeline';
11
17
  options.buttonText.resourceTimelineMonth = 'timeline';
12
18
  options.theme.expander = 'ec-expander';
13
- options.theme.main = 'ec-main';
14
- options.theme.times = 'ec-times';
15
- options.theme.container = 'ec-container';
19
+ options.theme.rowHead = 'ec-row-head';
20
+ options.theme.slots = 'ec-slots';
16
21
  options.view = 'resourceTimelineWeek';
17
22
  options.views.resourceTimelineDay = {
18
23
  buttonText: btnTextDay,
@@ -20,8 +25,9 @@ export default {
20
25
  displayEventEnd: false,
21
26
  dayHeaderFormat: {weekday: 'long'},
22
27
  duration: {days: 1},
23
- slotDuration: '01:00',
24
- theme: themeView('ec-timeline ec-resource-day-view'),
28
+ slotLabelInterval: '01:00',
29
+ slotDuration: '00:15',
30
+ theme: themeView('ec-resource ec-timeline ec-day-view'),
25
31
  titleFormat: {year: 'numeric', month: 'long', day: 'numeric'}
26
32
  };
27
33
  options.views.resourceTimelineWeek = {
@@ -29,8 +35,9 @@ export default {
29
35
  component: View,
30
36
  displayEventEnd: false,
31
37
  duration: {weeks: 1},
32
- slotDuration: '01:00',
33
- theme: themeView('ec-timeline ec-resource-week-view')
38
+ slotLabelInterval: '01:00',
39
+ slotDuration: '00:15',
40
+ theme: themeView('ec-resource ec-timeline ec-week-view')
34
41
  };
35
42
  options.views.resourceTimelineMonth = {
36
43
  buttonText: btnTextMonth,
@@ -42,25 +49,21 @@ export default {
42
49
  },
43
50
  duration: {months: 1},
44
51
  slotDuration: {days: 1},
45
- theme: themeView('ec-timeline ec-resource-month-view'),
52
+ theme: themeView('ec-resource ec-timeline ec-month-view'),
46
53
  titleFormat: {year: 'numeric', month: 'long'}
47
54
  };
48
55
  },
49
56
 
57
+ createParsers(parsers) {
58
+ createTRRParsers(parsers);
59
+ },
60
+
50
61
  createStores(state) {
51
- if (!('_viewResources' in state)) {
52
- state._viewResources = viewResources(state);
53
- }
54
- state._bodyHeight = writable(0);
55
- state._bodyWidth = writable(0);
56
- state._bodyScrollLeft = writable(0);
57
- state._headerEl = writable(undefined);
58
- state._headerHeight = writable(0);
62
+ createTRRStores(state);
63
+ createRRStores(state);
59
64
  state._dayTimeLimits = dayTimeLimits(state); // flexible time limits per day
60
- state._dayTimes = dayTimes(state);
61
- state._daysHs = writable(new Map()); // days row heights
65
+ state._daySlots = daySlots(state);
66
+ state._monthView = monthView(state);
62
67
  state._nestedResources = nestedResources(state);
63
- state._resHs = writable(new Map()); // resource row heights
64
- state._sidebarEl = writable(undefined);
65
68
  }
66
69
  }
@@ -1,139 +1,136 @@
1
- import {addDay, addDuration, cloneDate, createDuration, datesEqual, sortEventChunks, toSeconds} from '#lib';
1
+ import {
2
+ addDay, addDuration, assign, bgEvent, cloneDate, createDuration, createEventChunk, datesEqual, eventIntersects, max,
3
+ min, outsideRange
4
+ } from '#lib';
2
5
 
3
- export function prepareEventChunks(chunks, $_viewDates, $_dayTimeLimits, $slotDuration, $eventOrder) {
4
- let longChunks = {};
5
- let filteredChunks = [];
6
-
7
- if (chunks.length) {
8
- sortEventChunks(chunks, $eventOrder);
6
+ export function createGrid($_viewDates, $_viewResources, $_dayTimeLimits, $validRange, $highlightedDates) {
7
+ let grid = [];
8
+ let gridRow = 1
9
+ for (let resource of $_viewResources) {
10
+ let days = [];
11
+ let gridColumn = 1;
12
+ for (let date of $_viewDates) {
13
+ let slotTimeLimits = $_dayTimeLimits[date.getTime()];
14
+ days.push({
15
+ gridColumn,
16
+ gridRow,
17
+ resource,
18
+ start: addDuration(cloneDate(date), slotTimeLimits.min),
19
+ end: addDuration(cloneDate(date), slotTimeLimits.max),
20
+ dayStart: date,
21
+ dayEnd: addDay(cloneDate(date)),
22
+ disabled: outsideRange(date, $validRange),
23
+ highlight: $highlightedDates.some(d => datesEqual(d, date))
24
+ });
25
+ ++ gridColumn;
26
+ }
27
+ grid.push(days);
28
+ ++ gridRow;
29
+ }
30
+ return grid;
31
+ }
9
32
 
10
- let step = toSeconds($slotDuration);
11
- let prevChunk;
12
- for (let chunk of chunks) {
13
- let prevDayEnd;
14
- if (step) {
15
- let slots = 0;
16
- for (let i = 0; i < $_viewDates.length; ++i) {
17
- let slotTimeLimits = getSlotTimeLimits($_dayTimeLimits, $_viewDates[i]);
18
- let dayStart = addDuration(cloneDate($_viewDates[i]), slotTimeLimits.min);
19
- let dayEnd = addDuration(cloneDate($_viewDates[i]), slotTimeLimits.max);
20
- if (!chunk.date) {
21
- if (chunk.start < dayEnd && chunk.end > dayStart) {
22
- // The first day is found
23
- chunk.date = $_viewDates[i];
24
- if (chunk.start < dayStart) {
25
- // Adjust chunk start
26
- chunk.start = dayStart;
27
- }
28
- // Calculate offset
29
- chunk.offset = (chunk.start - dayStart) / 1000 / step;
30
- // Calculate slots
31
- if (chunk.end > dayEnd) {
32
- slots += dayEnd - chunk.start;
33
- } else {
34
- slots += chunk.end - chunk.start || step * 1000;
35
- break;
36
- }
37
- }
38
- } else {
39
- if (chunk.end <= dayStart) {
40
- // Adjust chunk end
41
- chunk.end = prevDayEnd;
42
- break;
43
- }
44
- // The chunk is long one
45
- let key = $_viewDates[i].getTime();
46
- if (longChunks[key]) {
47
- longChunks[key].push(chunk);
48
- } else {
49
- longChunks[key] = [chunk];
50
- }
51
- // Calculate slots
52
- if (chunk.end > dayEnd) {
53
- slots += dayEnd - dayStart;
54
- } else {
55
- slots += chunk.end - dayStart;
56
- break;
57
- }
58
- }
59
- prevDayEnd = dayEnd;
60
- }
61
- chunk.slots = slots / 1000 / step;
33
+ export function createEventChunks($_filteredEvents, grid) {
34
+ let chunks = [];
35
+ let bgChunks = [];
36
+ for (let event of $_filteredEvents) {
37
+ for (let days of grid) {
38
+ if (bgEvent(event.display)) {
39
+ bgChunks = bgChunks.concat(createChunks(event, days));
62
40
  } else {
63
- // Month view
64
- let days = 0;
65
- for (let i = 0; i < $_viewDates.length; ++i) {
66
- let dayStart = $_viewDates[i];
67
- let dayEnd = addDay(cloneDate(dayStart));
68
- if (!chunk.date) {
69
- if (chunk.start < dayEnd) {
70
- // The first day is found
71
- chunk.date = dayStart;
72
- if (chunk.start < dayStart) {
73
- // Adjust chunk start
74
- chunk.start = dayStart;
75
- }
76
- ++days;
77
- }
78
- } else {
79
- if (chunk.end <= dayStart) {
80
- // Adjust chunk end
81
- chunk.end = prevDayEnd;
82
- break;
83
- }
84
- // The chunk is long one
85
- let key = dayStart.getTime();
86
- if (longChunks[key]) {
87
- longChunks[key].push(chunk);
88
- } else {
89
- longChunks[key] = [chunk];
90
- }
91
- ++days;
92
- }
93
- prevDayEnd = dayEnd;
94
- }
95
- chunk.days = days;
41
+ chunks = chunks.concat(createChunks(event, days));
96
42
  }
43
+ }
44
+ }
45
+ prepareChunks(chunks);
97
46
 
98
- if (!chunk.date) {
99
- // Chunk is outside the slot time limits, so skip it
100
- continue;
101
- }
47
+ return {chunks, bgChunks};
48
+ }
102
49
 
103
- if (prevChunk && datesEqual(prevChunk.date, chunk.date)) {
104
- chunk.prev = prevChunk;
50
+ export function createIEventChunks($_iEvents, grid) {
51
+ let iChunks = [];
52
+ for (let event of $_iEvents) {
53
+ if (!event) {
54
+ continue;
55
+ }
56
+ for (let days of grid) {
57
+ iChunks = iChunks.concat(createChunks(event, days));
58
+ }
59
+ }
60
+
61
+ return iChunks;
62
+ }
63
+
64
+ function createChunks(event, days) {
65
+ let dates = [];
66
+ let firstStart;
67
+ let lastEnd;
68
+ let gridColumn;
69
+ let gridRow;
70
+ let left;
71
+ let width = 0;
72
+ for (let {gridColumn: column, gridRow: row, resource, dayStart, start, end, disabled} of days) {
73
+ if (!disabled && eventIntersects(event, start, end, resource)) {
74
+ if (!dates.length) {
75
+ firstStart = start;
76
+ gridColumn = column;
77
+ gridRow = row;
78
+ left = max(event.start - start, 0) / 1000;
105
79
  }
106
- prevChunk = chunk;
107
- filteredChunks.push(chunk);
80
+ dates.push(dayStart);
81
+ lastEnd = end;
82
+ width += (min(end, event.end) - max(start, event.start)) / 1000;
108
83
  }
109
84
  }
85
+ if (dates.length) {
86
+ let chunk = createEventChunk(event, firstStart, lastEnd);
87
+ // Chunk layout
88
+ assign(chunk, {gridColumn, gridRow, dates, left, width});
110
89
 
111
- return [filteredChunks, longChunks];
90
+ return [chunk];
91
+ }
92
+
93
+ return [];
94
+ }
95
+
96
+ export function prepareChunks(chunks) {
97
+ let dayChunks = {};
98
+ for (let chunk of chunks) {
99
+ let {gridColumn, gridRow} = chunk;
100
+ // Prepare day chunks
101
+ for (let i = 0; i < chunk.dates.length; ++ i) {
102
+ let key = `${gridRow}_${gridColumn + i}`;
103
+ if (dayChunks[key]) {
104
+ dayChunks[key].push(chunk);
105
+ } else {
106
+ dayChunks[key] = [chunk];
107
+ }
108
+ }
109
+ let key = `${gridRow}_${gridColumn}`;
110
+ chunk.day = dayChunks[key];
111
+ }
112
112
  }
113
113
 
114
- export function repositionEvent(chunk, dayChunks, longChunks, height, allDay) {
115
- chunk.top = 0;
116
- chunk.bottom = height;
117
- let margin = 1;
118
- let key = chunk.date.getTime();
119
- longChunks = longChunks?.[key] ?? [];
120
- let chunks = [...dayChunks, ...longChunks];
121
- chunks.sort((a, b) => (a.top ?? 0) - (b.top ?? 0) || a.start - b.start || b.event.allDay - a.event.allDay);
122
- for (let dayChunk of chunks) {
123
- if (dayChunk === chunk) {
114
+ export function repositionEvent(chunk, height, monthView) {
115
+ let top = 1;
116
+ let bottom = top + height;
117
+ let dayChunks = chunk.day;
118
+ dayChunks.sort((a, b) => (a.top ?? Number.POSITIVE_INFINITY) - (b.top ?? Number.POSITIVE_INFINITY));
119
+ for (let dayChunk of dayChunks) {
120
+ if (dayChunk === chunk || !('top' in dayChunk)) {
124
121
  continue;
125
122
  }
126
- if ((allDay || chunk.start < dayChunk.end && chunk.end > dayChunk.start) && chunk.top < dayChunk.bottom && chunk.bottom > dayChunk.top) {
127
- let offset = dayChunk.bottom - chunk.top + 1;
128
- margin += offset;
129
- chunk.top += offset;
130
- chunk.bottom += offset;
123
+ if ((monthView || chunk.start < dayChunk.end && chunk.end > dayChunk.start) && top < dayChunk.bottom && bottom > dayChunk.top) {
124
+ let offset = dayChunk.bottom - top + 1;
125
+ top += offset;
126
+ bottom += offset;
131
127
  }
132
128
  }
129
+ assign(chunk, {top, bottom});
133
130
 
134
- return margin;
131
+ return top;
135
132
  }
136
133
 
137
134
  export function getSlotTimeLimits($_dayTimeLimits, date) {
138
- return $_dayTimeLimits[date.getTime()] ?? {min: createDuration(0), max: createDuration(0)};
135
+ return $_dayTimeLimits[date.getTime()] ?? {min: createDuration(0), max: createDuration('24:00:00')};
139
136
  }
@@ -1,5 +1,5 @@
1
1
  import {derived} from 'svelte/store';
2
- import {createSlotTimeLimits, createTimes, getPayload} from '#lib';
2
+ import {createSlotTimeLimits, createSlots, getPayload, toSeconds} from '#lib';
3
3
 
4
4
  // slotTimeLimits per day
5
5
  export function dayTimeLimits(state) {
@@ -22,15 +22,15 @@ export function dayTimeLimits(state) {
22
22
  );
23
23
  }
24
24
 
25
- export function dayTimes(state) {
25
+ export function daySlots(state) {
26
26
  return derived(
27
- [state._viewDates, state.slotDuration, state.slotLabelInterval, state._dayTimeLimits, state._intlSlotLabel],
28
- ([$_viewDates, $slotDuration, $slotLabelInterval, $_dayTimeLimits, $_intlSlotLabel]) => {
27
+ [state._viewDates, state.slotDuration, state._slotLabelPeriodicity, state._dayTimeLimits, state._intlSlotLabel],
28
+ ([$_viewDates, $slotDuration, $_slotLabelPeriodicity, $_dayTimeLimits, $_intlSlotLabel]) => {
29
29
  let dayTimes = {};
30
30
  for (let date of $_viewDates) {
31
- let time = date.getTime();
32
- dayTimes[time] = time in $_dayTimeLimits
33
- ? createTimes(date, $slotDuration, $slotLabelInterval, $_dayTimeLimits[time], $_intlSlotLabel)
31
+ let key = date.getTime();
32
+ dayTimes[key] = key in $_dayTimeLimits
33
+ ? createSlots(date, $slotDuration, $_slotLabelPeriodicity, $_dayTimeLimits[key], $_intlSlotLabel)
34
34
  : [];
35
35
  }
36
36
 
@@ -42,3 +42,7 @@ export function dayTimes(state) {
42
42
  export function nestedResources(state) {
43
43
  return derived(state.resources, $resources => $resources.some(resource => getPayload(resource).children.length));
44
44
  }
45
+
46
+ export function monthView(state) {
47
+ return derived(state.slotDuration, $slotDuration => !toSeconds($slotDuration));
48
+ }
@@ -0,0 +1,31 @@
1
+ <script>
2
+ import {height, repositionEvent} from '#lib';
3
+ import {InteractableEvent} from '#components';
4
+
5
+ let {chunk} = $props();
6
+
7
+ let el = $state();
8
+ let margin = $state(0);
9
+ let event = $derived(chunk.event);
10
+
11
+ // Style
12
+ let styles = $derived(style => {
13
+ style['grid-column'] = `${chunk.gridColumn} / span ${chunk.dates.length}`;
14
+ if (margin || event._margin) {
15
+ style['margin-block-start'] = `${event._margin ?? margin}px`;
16
+ }
17
+ return style;
18
+ });
19
+
20
+ export function reposition() {
21
+ margin = repositionEvent(chunk, height(el));
22
+ }
23
+ </script>
24
+
25
+ <InteractableEvent
26
+ bind:el
27
+ {chunk}
28
+ {styles}
29
+ axis="x"
30
+ forceMargin={margin}
31
+ />