@event-calendar/core 5.4.2 → 5.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@event-calendar/core",
3
- "version": "5.4.2",
3
+ "version": "5.5.1",
4
4
  "title": "Event Calendar Core package",
5
5
  "description": "Full-sized drag & drop event calendar with resource & timeline views",
6
6
  "keywords": [
@@ -32,6 +32,6 @@
32
32
  "#components": "./src/lib/components/index.js"
33
33
  },
34
34
  "dependencies": {
35
- "svelte": "^5.53.10"
35
+ "svelte": "^5.54.0"
36
36
  }
37
37
  }
@@ -11,6 +11,7 @@
11
11
  ariaHidden = false,
12
12
  disabled = false,
13
13
  highlight = false,
14
+ cssSpan = false,
14
15
  children
15
16
  } = $props();
16
17
 
@@ -25,6 +26,7 @@
25
26
  highlight && theme.highlight,
26
27
  disabled && theme.disabled
27
28
  ]}
29
+ style:--ec-col-group-span={cssSpan ? colSpan : undefined}
28
30
  role="{ariaHidden ? null : 'columnheader'}"
29
31
  aria-colspan="{ariaHidden || colSpan <= 1 ? null : colSpan}"
30
32
  aria-colindex="{ariaHidden ? null : colIndex}"
package/src/lib/date.js CHANGED
@@ -1,4 +1,4 @@
1
- import {isDate} from './utils.js';
1
+ import {isDate, isFunction} from './utils.js';
2
2
 
3
3
  export const DAY_IN_SECONDS = 86400;
4
4
 
@@ -182,6 +182,16 @@ export function getWeekNumber(date, firstDay) {
182
182
  return Math.ceil((((date - yearStart) / 1000 / DAY_IN_SECONDS) + 1) / 7);
183
183
  }
184
184
 
185
+ export function createWeekNumberContent(week, weekNumberContent, date) {
186
+ if (weekNumberContent) {
187
+ return isFunction(weekNumberContent)
188
+ ? weekNumberContent({date: toLocalDate(date), week})
189
+ : weekNumberContent;
190
+ }
191
+
192
+ return 'W' + String(week).padStart(2, '0');
193
+ }
194
+
185
195
  /**
186
196
  * Private functions
187
197
  */
@@ -1,7 +1,7 @@
1
1
  <script>
2
2
  import {getContext} from 'svelte';
3
3
  import {
4
- contentFrom, getWeekNumber, isFunction, keyEnter, toISOString, toLocalDate, stopPropagation
4
+ contentFrom, getWeekNumber, isFunction, keyEnter, toISOString, stopPropagation, createWeekNumberContent
5
5
  } from '#lib';
6
6
  import {BaseDay} from '#components';
7
7
 
@@ -19,20 +19,10 @@
19
19
 
20
20
  // Week numbers
21
21
  let showWeekNumber = $derived(weekNumbers && dayStart.getUTCDay() === (firstDay ? 1 : 0));
22
- let weekNumber = $derived.by(() => {
23
- let weekNumber;
24
- if (showWeekNumber) {
25
- let week = getWeekNumber(dayStart, firstDay);
26
- if (weekNumberContent) {
27
- weekNumber = isFunction(weekNumberContent)
28
- ? weekNumberContent({date: toLocalDate(dayStart), week})
29
- : weekNumberContent;
30
- } else {
31
- weekNumber = 'W' + String(week).padStart(2, '0');
32
- }
33
- }
34
- return weekNumber;
35
- });
22
+ let weekNumber = $derived(showWeekNumber
23
+ ? createWeekNumberContent(getWeekNumber(dayStart, firstDay), weekNumberContent, dayStart)
24
+ : undefined
25
+ );
36
26
 
37
27
  // More link
38
28
  let dayHiddenChunks = $derived(hiddenChunks.get(dayStart.getTime()));
@@ -23,8 +23,7 @@ export default {
23
23
  uniform: 'ec-uniform',
24
24
  dayFoot: 'ec-day-foot',
25
25
  otherMonth: 'ec-other-month',
26
- popup: 'ec-popup',
27
- weekNumber: 'ec-week-number'
26
+ popup: 'ec-popup'
28
27
  });
29
28
  assign(options.views, {
30
29
  dayGridDay: {
@@ -12,10 +12,10 @@ export default {
12
12
  view: 'listWeek'
13
13
  });
14
14
  assign(options.buttonText, {
15
- listDay: 'list',
16
- listWeek: 'list',
17
- listMonth: 'list',
18
- listYear: 'list'
15
+ listDay: 'day',
16
+ listWeek: 'week',
17
+ listMonth: 'month',
18
+ listYear: 'year'
19
19
  });
20
20
  assign(options.theme, {
21
21
  daySide: 'ec-day-side',
@@ -27,7 +27,8 @@ export default {
27
27
  buttonText: btnTextDay,
28
28
  component: initViewComponent,
29
29
  duration: {days: 1},
30
- theme: themeView('ec-list ec-day-view')
30
+ theme: themeView('ec-list ec-day-view'),
31
+ titleFormat: {year: 'numeric', month: 'long', day: 'numeric'}
31
32
  },
32
33
  listWeek: {
33
34
  buttonText: btnTextWeek,
@@ -39,13 +40,15 @@ export default {
39
40
  buttonText: btnTextMonth,
40
41
  component: initViewComponent,
41
42
  duration: {months: 1},
42
- theme: themeView('ec-list ec-month-view')
43
+ theme: themeView('ec-list ec-month-view'),
44
+ titleFormat: {year: 'numeric', month: 'long'}
43
45
  },
44
46
  listYear: {
45
47
  buttonText: btnTextYear,
46
48
  component: initViewComponent,
47
49
  duration: {years: 1},
48
- theme: themeView('ec-list ec-year-view')
50
+ theme: themeView('ec-list ec-year-view'),
51
+ titleFormat: {year: 'numeric'}
49
52
  }
50
53
  });
51
54
  }
@@ -15,8 +15,8 @@ export default {
15
15
  view: 'resourceTimeGridWeek'
16
16
  });
17
17
  assign(options.buttonText, {
18
- resourceTimeGridDay: 'resources',
19
- resourceTimeGridWeek: 'resources'
18
+ resourceTimeGridDay: 'day',
19
+ resourceTimeGridWeek: 'week'
20
20
  });
21
21
  assign(options.theme, {
22
22
  colGroup: 'ec-col-group'
@@ -1,7 +1,8 @@
1
1
  <script>
2
2
  import {getContext, setContext, tick} from 'svelte';
3
3
  import {
4
- max, resizeObserver, runReposition, contentFrom, toSeconds, datesEqual, min, isRtl, empty, length
4
+ max, resizeObserver, runReposition, contentFrom, toSeconds, datesEqual, min, isRtl, empty, length, toISOString,
5
+ createWeekNumberContent
5
6
  } from '#lib';
6
7
  import {getSlotTimeLimits} from './lib.js';
7
8
  import ViewState from './state.svelte.js';
@@ -16,9 +17,11 @@
16
17
  let viewState = new ViewState(mainState);
17
18
  setContext('view-state', viewState);
18
19
 
19
- let {mainEl, today, viewDates, options: {columnWidth, nowIndicator, scrollTime, slotDuration, slotHeight, slotWidth, theme}} = $derived(mainState);
20
- let {chunks, bgChunks, iChunks, daySlots, dayTimeLimits, grid, monthView, nestedResources, sidebarWidth,
21
- slotLabelPeriodicity, viewResources} = $derived(viewState);
20
+ let {mainEl, today, viewDates, options: {
21
+ columnWidth, nowIndicator, scrollTime, slotDuration, slotHeight, slotWidth, theme, weekNumberContent
22
+ }} = $derived(mainState);
23
+ let {chunks, bgChunks, iChunks, daySlots, dayTimeLimits, grid, extraHeads, intlMonthHeader, monthView,
24
+ nestedResources, sidebarWidth, slotLabelPeriodicity, viewResources} = $derived(viewState);
22
25
 
23
26
  let headerHeight = $state(0);
24
27
 
@@ -84,6 +87,26 @@
84
87
  <header bind:offsetHeight={headerHeight} class="{theme.header}">
85
88
  <aside class="{theme.sidebar}" bind:offsetWidth={viewState.sidebarWidth}></aside>
86
89
  <div class="{theme.grid}" role="row">
90
+ {#if length(extraHeads.months) > 1}
91
+ {#each extraHeads.months as {date, gridColumn, span}}
92
+ <ColHead className={theme.colGroup} colIndex={gridColumn} colSpan={span} cssSpan weekday={false}>
93
+ <time
94
+ datetime="{toISOString(date, 10)}"
95
+ {@attach contentFrom(intlMonthHeader.format(date))}
96
+ ></time>
97
+ </ColHead>
98
+ {/each}
99
+ {/if}
100
+ {#if length(extraHeads.weeks) > 1}
101
+ {#each extraHeads.weeks as {number, date, gridColumn, span}}
102
+ <ColHead className={theme.colGroup} colIndex={gridColumn} colSpan={span} cssSpan weekday={false}>
103
+ <span
104
+ class="{theme.weekNumber}"
105
+ {@attach contentFrom(createWeekNumberContent(number, weekNumberContent, date))}
106
+ ></span>
107
+ </ColHead>
108
+ {/each}
109
+ {/if}
87
110
  {#each grid[0] as {dayStart: date, disabled, highlight}, i}
88
111
  <ColHead {date} colIndex={1 + i} {disabled} {highlight}>
89
112
  <DayHeader {date}/>
@@ -1,7 +1,7 @@
1
1
  import {untrack} from 'svelte';
2
2
  import {
3
- addDay, addDuration, bgEvent, cloneDate, createSlots, createSlotTimeLimits, datesEqual, getPayload, outsideRange,
4
- toSeconds
3
+ addDay, addDuration, bgEvent, cloneDate, createSlots, createSlotTimeLimits, datesEqual, empty, getPayload,
4
+ getWeekNumber, outsideRange, toSeconds
5
5
  } from '#lib';
6
6
  import {createChunks, prepareChunks} from './lib.js';
7
7
 
@@ -42,6 +42,45 @@ export function grid(mainState, viewState) {
42
42
  };
43
43
  }
44
44
 
45
+ export function extraHeads(mainState, viewState) {
46
+ return () => {
47
+ // Dependencies
48
+ let {features, options: {firstDay, weekNumbers}} = mainState;
49
+ let {grid} = viewState;
50
+
51
+ let months = [];
52
+ let weeks = [];
53
+
54
+ untrack(() => {
55
+ let month;
56
+ let week;
57
+ if (!empty(grid)) {
58
+ for (let {dayStart, gridColumn} of grid[0]) {
59
+ if (features.includes('month')) {
60
+ if (month && month.date.getUTCMonth() === dayStart.getUTCMonth()) {
61
+ ++month.span;
62
+ } else {
63
+ month = {date: dayStart, gridColumn, span: 1};
64
+ months.push(month);
65
+ }
66
+ }
67
+ if (weekNumbers) {
68
+ let number = getWeekNumber(dayStart, firstDay);
69
+ if (week && week.number === number) {
70
+ ++week.span;
71
+ } else {
72
+ week = {number, date: dayStart, gridColumn, span: 1};
73
+ weeks.push(week);
74
+ }
75
+ }
76
+ }
77
+ }
78
+ });
79
+
80
+ return {months, weeks};
81
+ };
82
+ }
83
+
45
84
  export function eventChunks(mainState, viewState) {
46
85
  return () => {
47
86
  // Dependencies
@@ -1,4 +1,4 @@
1
- import {assign, btnTextDay, btnTextMonth, btnTextWeek, getPayload, themeView} from '#lib';
1
+ import {assign, btnTextDay, btnTextMonth, btnTextWeek, btnTextYear, getPayload, themeView} from '#lib';
2
2
  import {setExtensions} from '../time-grid/lib.js';
3
3
  import {createTRROptions, createTRRParsers} from '../time-grid/options.js';
4
4
  import {createRROptions} from '../resource-time-grid/options.js';
@@ -9,17 +9,21 @@ export default {
9
9
  createTRROptions(options);
10
10
  createRROptions(options);
11
11
  assign(options, {
12
- resourceExpand: undefined,
13
- slotWidth: 32,
12
+ monthHeaderFormat: { // ec option
13
+ month: 'long'
14
+ },
15
+ resourceExpand: undefined, // ec option
16
+ slotWidth: 32, // ec option
14
17
  // Common options
15
18
  view: 'resourceTimelineWeek'
16
19
  });
17
20
  assign(options.buttonText, {
18
21
  expand: 'Expand',
19
22
  collapse: 'Collapse',
20
- resourceTimelineDay: 'timeline',
21
- resourceTimelineWeek: 'timeline',
22
- resourceTimelineMonth: 'timeline'
23
+ resourceTimelineDay: 'day',
24
+ resourceTimelineWeek: 'week',
25
+ resourceTimelineMonth: 'month',
26
+ resourceTimelineYear: 'year'
23
27
  });
24
28
  assign(options.icons, {
25
29
  collapse: {html: '&minus;'},
@@ -59,6 +63,19 @@ export default {
59
63
  slotDuration: {days: 1},
60
64
  theme: themeView('ec-resource ec-timeline ec-month-view'),
61
65
  titleFormat: {year: 'numeric', month: 'long'}
66
+ },
67
+ resourceTimelineYear: {
68
+ buttonText: btnTextYear,
69
+ component: initMonthViewComponent,
70
+ displayEventEnd: false,
71
+ dayHeaderFormat: {
72
+ weekday: 'short',
73
+ day: 'numeric'
74
+ },
75
+ duration: {years: 1},
76
+ slotDuration: {days: 1},
77
+ theme: themeView('ec-resource ec-timeline ec-year-view'),
78
+ titleFormat: {year: 'numeric'}
62
79
  }
63
80
  });
64
81
  },
@@ -70,11 +87,15 @@ export default {
70
87
 
71
88
  function initViewComponent(mainState) {
72
89
  setExtensions(mainState);
73
- return initMonthViewComponent(mainState);
90
+ return _initViewComponent(mainState);
74
91
  }
75
92
 
76
93
  function initMonthViewComponent(mainState) {
77
- mainState.features = ['timeline'];
94
+ return _initViewComponent(mainState, ['month']);
95
+ }
96
+
97
+ function _initViewComponent(mainState, extraFeatures = []) {
98
+ mainState.features = ['timeline', ...extraFeatures];
78
99
  mainState.extensions.viewResources = resources => resources.filter(resource => !getPayload(resource).hidden);
79
100
  return View;
80
101
  }
@@ -1,6 +1,9 @@
1
+ import {intl} from '#lib';
1
2
  import {TRRState} from '../time-grid/state.svelte.js';
2
3
  import {RRState} from '../resource-time-grid/state.svelte.js';
3
- import {daySlots, dayTimeLimits, eventChunks, grid, iEventChunks, monthView, nestedResources} from './derived.js';
4
+ import {
5
+ daySlots, dayTimeLimits, eventChunks, extraHeads, grid, iEventChunks, monthView, nestedResources
6
+ } from './derived.js';
4
7
 
5
8
  export default class ViewState extends RRState(TRRState()) {
6
9
  constructor(mainState) {
@@ -8,6 +11,8 @@ export default class ViewState extends RRState(TRRState()) {
8
11
  this.dayTimeLimits = $derived.by(dayTimeLimits(mainState)); // flexible time limits per day
9
12
  this.daySlots = $derived.by(daySlots(mainState, this));
10
13
  this.grid = $derived.by(grid(mainState, this));
14
+ this.extraHeads = $derived.by(extraHeads(mainState, this));
15
+ this.intlMonthHeader = $derived.by(intl(mainState, 'monthHeaderFormat'));
11
16
  this.monthView = $derived.by(monthView(mainState));
12
17
  let {chunks, bgChunks} = $derived.by(eventChunks(mainState, this));
13
18
  this.chunks = $derived(chunks);
@@ -13,7 +13,10 @@ export function currentRange(mainState) {
13
13
 
14
14
  untrack(() => {
15
15
  start = cloneDate(date);
16
- if (duration.months) {
16
+ if (duration.years) {
17
+ start.setUTCMonth(0);
18
+ start.setUTCDate(1);
19
+ } else if (duration.months) {
17
20
  start.setUTCDate(1);
18
21
  } else if (duration.inWeeks) {
19
22
  // First day of week
@@ -90,6 +90,7 @@ function createOptions(plugins) {
90
90
  toolbar: 'ec-toolbar',
91
91
  view: '',
92
92
  weekdays: ['ec-sun', 'ec-mon', 'ec-tue', 'ec-wed', 'ec-thu', 'ec-fri', 'ec-sat'],
93
+ weekNumber: 'ec-week-number'
93
94
  },
94
95
  titleFormat: {
95
96
  year: 'numeric',
@@ -1,5 +1,5 @@
1
1
  import {SvelteMap} from 'svelte/reactivity';
2
- import {createDate, identity, intl, intlRange, setMidnight} from '#lib';
2
+ import {cloneDate, createDate, identity, intl, intlRange, isArray, setMidnight} from '#lib';
3
3
  import {optionsState} from './options.js';
4
4
  import {
5
5
  createLoadingInvoker, loadEvents, loadResources, runDatesSet, runEventAllUpdated, runViewDidMount, setNowAndToday,
@@ -27,8 +27,8 @@ export default class State {
27
27
  this.filteredEvents = $derived.by(filteredEvents(this));
28
28
  this.mainEl = $state();
29
29
  this.now = $state(createDate());
30
- this.resources = $state.raw(arrayProxy(this.options.resources));
31
- this.today = $state(setMidnight(createDate()));
30
+ this.resources = $state.raw(arrayProxy(isArray(this.options.resources) ? this.options.resources : []));
31
+ this.today = $state(setMidnight(cloneDate(this.now)));
32
32
  this.intlEventTime = $derived.by(intlRange(this, 'eventTimeFormat'));
33
33
  this.intlDayHeader = $derived.by(intl(this, 'dayHeaderFormat'));
34
34
  this.intlDayHeaderAL = $derived.by(intl(this, 'dayHeaderAriaLabelFormat'));
@@ -26,7 +26,7 @@
26
26
  100% var(--ec-slot-height);
27
27
  }
28
28
 
29
- .ec-timeline:not(.ec-month-view) .ec-body & {
29
+ .ec-timeline:not(.ec-month-view, .ec-year-view) .ec-body & {
30
30
  --ec-last-line-color: transparent;
31
31
  --ec-direction: to left;
32
32
  [dir="rtl"] & {
@@ -42,7 +42,7 @@
42
42
  flex-grow: 1;
43
43
  }
44
44
 
45
- .ec-timeline.ec-month-view & {
45
+ .ec-timeline:is(.ec-month-view, .ec-year-view) & {
46
46
  grid-template-columns: max-content repeat(var(--ec-grid-cols), var(--ec-col-width));
47
47
  }
48
48