@sunsama/event-calendar 0.13.1 → 0.13.4

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 (89) hide show
  1. package/lib/commonjs/components/all-day-events.js +1 -1
  2. package/lib/commonjs/components/all-day-events.js.map +1 -1
  3. package/lib/commonjs/components/edit-event-container.js +9 -1
  4. package/lib/commonjs/components/edit-event-container.js.map +1 -1
  5. package/lib/commonjs/components/event-container.js +2 -1
  6. package/lib/commonjs/components/event-container.js.map +1 -1
  7. package/lib/commonjs/components/timed-event-container.js +1 -1
  8. package/lib/commonjs/components/timed-event-container.js.map +1 -1
  9. package/lib/commonjs/components/zoom-provider.js +21 -18
  10. package/lib/commonjs/components/zoom-provider.js.map +1 -1
  11. package/lib/commonjs/hooks/use-auto-scroll.js +14 -4
  12. package/lib/commonjs/hooks/use-auto-scroll.js.map +1 -1
  13. package/lib/commonjs/hooks/use-events.js +7 -6
  14. package/lib/commonjs/hooks/use-events.js.map +1 -1
  15. package/lib/commonjs/index.js +33 -32
  16. package/lib/commonjs/index.js.map +1 -1
  17. package/lib/commonjs/utils/calendar-layout.js +15 -2
  18. package/lib/commonjs/utils/calendar-layout.js.map +1 -1
  19. package/lib/commonjs/utils/compute-positioning.js +1 -1
  20. package/lib/commonjs/utils/compute-positioning.js.map +1 -1
  21. package/lib/commonjs/utils/date-utils.js +40 -11
  22. package/lib/commonjs/utils/date-utils.js.map +1 -1
  23. package/lib/commonjs/utils/generate-event-layouts.js +9 -6
  24. package/lib/commonjs/utils/generate-event-layouts.js.map +1 -1
  25. package/lib/module/components/all-day-events.js +2 -2
  26. package/lib/module/components/all-day-events.js.map +1 -1
  27. package/lib/module/components/edit-event-container.js +9 -1
  28. package/lib/module/components/edit-event-container.js.map +1 -1
  29. package/lib/module/components/event-container.js +3 -2
  30. package/lib/module/components/event-container.js.map +1 -1
  31. package/lib/module/components/timed-event-container.js +1 -1
  32. package/lib/module/components/timed-event-container.js.map +1 -1
  33. package/lib/module/components/zoom-provider.js +22 -19
  34. package/lib/module/components/zoom-provider.js.map +1 -1
  35. package/lib/module/hooks/use-auto-scroll.js +14 -4
  36. package/lib/module/hooks/use-auto-scroll.js.map +1 -1
  37. package/lib/module/hooks/use-events.js +8 -7
  38. package/lib/module/hooks/use-events.js.map +1 -1
  39. package/lib/module/index.js +33 -32
  40. package/lib/module/index.js.map +1 -1
  41. package/lib/module/utils/calendar-layout.js +15 -2
  42. package/lib/module/utils/calendar-layout.js.map +1 -1
  43. package/lib/module/utils/compute-positioning.js +1 -1
  44. package/lib/module/utils/compute-positioning.js.map +1 -1
  45. package/lib/module/utils/date-utils.js +40 -11
  46. package/lib/module/utils/date-utils.js.map +1 -1
  47. package/lib/module/utils/generate-event-layouts.js +9 -6
  48. package/lib/module/utils/generate-event-layouts.js.map +1 -1
  49. package/lib/typescript/commonjs/components/all-day-events.d.ts.map +1 -1
  50. package/lib/typescript/commonjs/components/edit-event-container.d.ts.map +1 -1
  51. package/lib/typescript/commonjs/components/event-container.d.ts +2 -1
  52. package/lib/typescript/commonjs/components/event-container.d.ts.map +1 -1
  53. package/lib/typescript/commonjs/components/timed-event-container.d.ts.map +1 -1
  54. package/lib/typescript/commonjs/components/zoom-provider.d.ts.map +1 -1
  55. package/lib/typescript/commonjs/hooks/use-auto-scroll.d.ts.map +1 -1
  56. package/lib/typescript/commonjs/hooks/use-events.d.ts.map +1 -1
  57. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  58. package/lib/typescript/commonjs/utils/calendar-layout.d.ts +3 -1
  59. package/lib/typescript/commonjs/utils/calendar-layout.d.ts.map +1 -1
  60. package/lib/typescript/commonjs/utils/compute-positioning.d.ts.map +1 -1
  61. package/lib/typescript/commonjs/utils/date-utils.d.ts.map +1 -1
  62. package/lib/typescript/commonjs/utils/generate-event-layouts.d.ts.map +1 -1
  63. package/lib/typescript/module/components/all-day-events.d.ts.map +1 -1
  64. package/lib/typescript/module/components/edit-event-container.d.ts.map +1 -1
  65. package/lib/typescript/module/components/event-container.d.ts +2 -1
  66. package/lib/typescript/module/components/event-container.d.ts.map +1 -1
  67. package/lib/typescript/module/components/timed-event-container.d.ts.map +1 -1
  68. package/lib/typescript/module/components/zoom-provider.d.ts.map +1 -1
  69. package/lib/typescript/module/hooks/use-auto-scroll.d.ts.map +1 -1
  70. package/lib/typescript/module/hooks/use-events.d.ts.map +1 -1
  71. package/lib/typescript/module/index.d.ts.map +1 -1
  72. package/lib/typescript/module/utils/calendar-layout.d.ts +3 -1
  73. package/lib/typescript/module/utils/calendar-layout.d.ts.map +1 -1
  74. package/lib/typescript/module/utils/compute-positioning.d.ts.map +1 -1
  75. package/lib/typescript/module/utils/date-utils.d.ts.map +1 -1
  76. package/lib/typescript/module/utils/generate-event-layouts.d.ts.map +1 -1
  77. package/package.json +1 -1
  78. package/src/components/all-day-events.tsx +8 -4
  79. package/src/components/edit-event-container.tsx +11 -1
  80. package/src/components/event-container.tsx +14 -5
  81. package/src/components/timed-event-container.tsx +3 -0
  82. package/src/components/zoom-provider.tsx +24 -22
  83. package/src/hooks/use-auto-scroll.ts +19 -6
  84. package/src/hooks/use-events.tsx +12 -8
  85. package/src/index.tsx +68 -34
  86. package/src/utils/calendar-layout.ts +15 -4
  87. package/src/utils/compute-positioning.ts +4 -3
  88. package/src/utils/date-utils.ts +52 -26
  89. package/src/utils/generate-event-layouts.ts +12 -19
@@ -1,4 +1,3 @@
1
- import { max } from "lodash";
2
1
  import { EventExtend } from "../types";
3
2
 
4
3
  export class CalendarLayout {
@@ -9,6 +8,8 @@ export class CalendarLayout {
9
8
  visibleX: Set<number>;
10
9
  enableWeekBreaks: boolean;
11
10
  startOfWeekXOffset: number;
11
+ private cachedHeight: number = 0;
12
+ private cachedHeightDirty: boolean = true;
12
13
 
13
14
  constructor({
14
15
  visibleX,
@@ -36,6 +37,7 @@ export class CalendarLayout {
36
37
  this.array2d[x] = column;
37
38
  }
38
39
  column[y] = value;
40
+ this.cachedHeightDirty = true;
39
41
  }
40
42
  // assign value to the line of cells from (x, y) to (x + w, y)
41
43
  setRange(x: number, y: number, duration: number, value: any) {
@@ -71,9 +73,18 @@ export class CalendarLayout {
71
73
  }
72
74
  // find the height of the 2d array
73
75
  height() {
74
- return this.array2d.length > 0
75
- ? max(this.array2d.map((col) => (col && col.length) || 0))
76
- : 0;
76
+ if (!this.cachedHeightDirty) {
77
+ return this.cachedHeight;
78
+ }
79
+ let maxHeight = 0;
80
+ for (const col of this.array2d) {
81
+ if (col && col.length > maxHeight) {
82
+ maxHeight = col.length;
83
+ }
84
+ }
85
+ this.cachedHeight = maxHeight;
86
+ this.cachedHeightDirty = false;
87
+ return this.cachedHeight;
77
88
  }
78
89
  // get the event and associated view data for the given cell
79
90
  getViewAt(x: number, y: number) {
@@ -14,9 +14,10 @@ const computePositioning = <T extends CalendarEvent>({
14
14
  timezone,
15
15
  }: ComputePositioning<T>): EventPosition => {
16
16
  const startDateMoment = moment.tz(collisionObject.event.start, timezone);
17
- const durationMinutes = moment
18
- .tz(collisionObject.event.end, timezone)
19
- .diff(startDateMoment, "minutes");
17
+ const durationMinutes =
18
+ (new Date(collisionObject.event.end).getTime() -
19
+ new Date(collisionObject.event.start).getTime()) /
20
+ 60000;
20
21
 
21
22
  let width = 100;
22
23
  let margin = 0;
@@ -2,6 +2,28 @@ import moment, { type Moment } from "moment-timezone";
2
2
  import { isDate, range, size } from "lodash";
3
3
  import { CalendarEvent, PrefabHour } from "../types";
4
4
 
5
+ // Cached Intl.DateTimeFormat instances for fast timezone-aware day/hour extraction.
6
+ // Avoids creating Moment instances in hot paths like isAllDayOrSpansMidnight.
7
+ let _cachedTz = "";
8
+ let _dayFormatter: Intl.DateTimeFormat;
9
+ let _hourFormatter: Intl.DateTimeFormat;
10
+
11
+ function ensureFormatters(timezone: string) {
12
+ if (timezone !== _cachedTz) {
13
+ _cachedTz = timezone;
14
+ _dayFormatter = new Intl.DateTimeFormat("en-CA", { timeZone: timezone });
15
+ _hourFormatter = new Intl.DateTimeFormat("en-GB", {
16
+ hour: "2-digit",
17
+ hour12: false,
18
+ timeZone: timezone,
19
+ });
20
+ }
21
+ }
22
+
23
+ function toDate(date: Date | string): Date {
24
+ return typeof date === "string" ? new Date(date) : date;
25
+ }
26
+
5
27
  export const generatePrefabHours = (
6
28
  timeFormat: string = "HH:mm"
7
29
  ): PrefabHour[] => {
@@ -62,15 +84,19 @@ export const isAllDayOrSpansMidnight = <T extends CalendarEvent>(
62
84
  return true;
63
85
  }
64
86
 
65
- // Does the range start/end span midnight in the given timezone?
66
- const startMoment = moment.tz(start, timezone);
67
- const endMoment = moment.tz(end, timezone);
87
+ ensureFormatters(timezone);
68
88
 
69
- // Handle special case where range ends at midnight exactly, in which case spansMidnight should return false
70
- return !startMoment.isSame(
71
- endMoment.hour() === 0 ? endMoment.subtract(1, "minute") : endMoment,
72
- "day"
73
- );
89
+ const endDateObj = toDate(end);
90
+
91
+ // If end hour is 0 in user's timezone, subtract 1 minute so events
92
+ // ending exactly at midnight aren't considered as spanning midnight
93
+ const endHour = parseInt(_hourFormatter.format(endDateObj), 10);
94
+ const effectiveEnd =
95
+ endHour === 0 ? new Date(endDateObj.getTime() - 60_000) : endDateObj;
96
+
97
+ const startDay = _dayFormatter.format(toDate(start));
98
+ const endDay = _dayFormatter.format(effectiveEnd);
99
+ return startDay !== endDay;
74
100
  };
75
101
 
76
102
  // Returns the count of unique dates in the provided timezone
@@ -79,17 +105,20 @@ export const getDurationInDays = <T extends CalendarEvent>(
79
105
  timezone: string
80
106
  ) => {
81
107
  // the event duration in days calculation depends on if the event is all day
82
- return calendarEvent.isAllDay
83
- ? moment
108
+ if (calendarEvent.isAllDay) {
109
+ return (
110
+ moment
84
111
  .tz(calendarEvent.end, timezone)
85
112
  .diff(moment.tz(calendarEvent.start, timezone), "days") + 1
86
- : size(
87
- daysInRange({
88
- startDate: calendarEvent.start,
89
- endDate: calendarEvent.end,
90
- timezone,
91
- })
92
- );
113
+ );
114
+ }
115
+ return size(
116
+ daysInRange({
117
+ startDate: calendarEvent.start,
118
+ endDate: calendarEvent.end,
119
+ timezone,
120
+ })
121
+ );
93
122
  };
94
123
 
95
124
  // Returns an array of days (e.g. ['2022-01-02']) in a given date range.
@@ -102,23 +131,20 @@ export const daysInRange = ({
102
131
  endDate: Date | string;
103
132
  timezone: string;
104
133
  }) => {
105
- const countOfDaysInRange = moment
106
- .tz(endDate, timezone)
107
- .diff(moment.tz(startDate, timezone), "days");
108
- const startDay = moment.tz(startDate, timezone).format("YYYY-MM-DD");
134
+ const startMoment = moment.tz(startDate, timezone);
135
+ const endMoment = moment.tz(endDate, timezone);
136
+ const countOfDaysInRange = endMoment.diff(startMoment, "days");
109
137
  const days = [];
138
+ const maxIterations = Math.min(30, Math.abs(countOfDaysInRange));
110
139
  // Make sure we loop at a max of 30 times here as we had events that were scheduled for all day long for
111
140
  // 1000 years in the future and this was causing the app to crash
112
141
  for (
113
142
  let countOfDaysAfterStart = 0;
114
- countOfDaysAfterStart <= Math.min(30, Math.abs(countOfDaysInRange));
143
+ countOfDaysAfterStart <= maxIterations;
115
144
  countOfDaysAfterStart++
116
145
  ) {
117
146
  days.push(
118
- moment
119
- .tz(startDay, timezone)
120
- .add(countOfDaysAfterStart, "day")
121
- .format("YYYY-MM-DD")
147
+ startMoment.clone().add(countOfDaysAfterStart, "day").format("YYYY-MM-DD")
122
148
  );
123
149
  }
124
150
  return days;
@@ -76,16 +76,13 @@ export const generateEventLayouts = <T extends CalendarEvent>({
76
76
  ...(calendarViewInterval === "month" ? partDayEventsSorted : []),
77
77
  ];
78
78
 
79
- const visibleDayCount = moment
80
- .tz(endCalendarDate, timezone)
81
- .diff(moment.tz(startCalendarDate, timezone), "days");
79
+ const startCalendarMoment = moment.tz(startCalendarDate, timezone);
80
+ const endCalendarMoment = moment.tz(endCalendarDate, timezone);
81
+ const visibleDayCount = endCalendarMoment.diff(startCalendarMoment, "days");
82
82
  const visibleDays: string[] = [];
83
83
  for (let dayCount = 0; dayCount <= visibleDayCount; dayCount++) {
84
84
  visibleDays.push(
85
- moment
86
- .tz(startCalendarDate, timezone)
87
- .add(dayCount, "day")
88
- .format("YYYY-MM-DD")
85
+ startCalendarMoment.clone().add(dayCount, "day").format("YYYY-MM-DD")
89
86
  );
90
87
  }
91
88
 
@@ -103,15 +100,14 @@ export const generateEventLayouts = <T extends CalendarEvent>({
103
100
  } = {};
104
101
 
105
102
  calendarViewDateRanges.forEach((date) => {
103
+ const basisMoment = moment.tz(date.basisDate, timezone);
106
104
  const calendarLayoutOptions = {
107
105
  visibleX: date.dayIndexes,
108
106
  enableWeekBreaks: calendarViewInterval === "month",
109
- startOfWeekXOffset: moment
110
- .tz(date.basisDate, timezone)
111
- .diff(
112
- startOfUserWeek(startDayOfWeekOffset, date.basisDate!, timezone),
113
- "days"
114
- ),
107
+ startOfWeekXOffset: basisMoment.diff(
108
+ startOfUserWeek(startDayOfWeekOffset, date.basisDate!, timezone),
109
+ "days"
110
+ ),
115
111
  };
116
112
  const calendarViewDayLayout = _reduce(
117
113
  calendarViewDayEvents,
@@ -119,7 +115,7 @@ export const generateEventLayouts = <T extends CalendarEvent>({
119
115
  const eventStartIndex = moment
120
116
  .tz(event.start, timezone)
121
117
  .startOf("day")
122
- .diff(moment.tz(date.basisDate, timezone), "days");
118
+ .diff(basisMoment, "days");
123
119
  const eventDurationDays = getDurationInDays(event, timezone);
124
120
 
125
121
  calendarLayout.findFitAndInsert(
@@ -139,16 +135,13 @@ export const generateEventLayouts = <T extends CalendarEvent>({
139
135
  calendarDates.forEach((calendarDate) => {
140
136
  const currentDayDate = moment.tz(calendarDate, timezone).startOf("day");
141
137
  const startDate = currentDayDate.toDate();
142
- const endDate = moment.tz(startDate, timezone).endOf("day").toDate();
138
+ const endDate = currentDayDate.clone().endOf("day").toDate();
143
139
  const showHours = !["month"].includes(calendarViewInterval);
144
140
  const dayId = calendarDate;
145
141
  const allDayEventsLayout: AllDayEventLayoutType<T>[] = [];
146
142
  let partDayEventsLayout: PartDayEventLayoutType<T>[] = [];
147
143
 
148
- const x = moment(startDate).diff(
149
- moment.tz(date.basisDate, timezone),
150
- "days"
151
- );
144
+ const x = currentDayDate.diff(basisMoment, "days");
152
145
  for (let y = 0; y < calendarViewDayRowHeight; ++y) {
153
146
  const {
154
147
  event,