trud-calendar-core 0.1.3 → 0.4.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.
package/dist/index.cjs CHANGED
@@ -7,6 +7,7 @@ var DEFAULT_LABELS = {
7
7
  week: "Week",
8
8
  day: "Day",
9
9
  agenda: "Agenda",
10
+ year: "Year",
10
11
  allDay: "all-day",
11
12
  noEvents: "No events in this period",
12
13
  more: (n) => `+${n} more`
@@ -19,9 +20,10 @@ var DEFAULT_VIEW = "month";
19
20
  var HOURS_IN_DAY = 24;
20
21
  var MINUTES_IN_HOUR = 60;
21
22
  var MINUTES_IN_DAY = HOURS_IN_DAY * MINUTES_IN_HOUR;
22
- var VIEWS = ["month", "week", "day", "agenda"];
23
+ var VIEWS = ["month", "week", "day", "agenda", "year"];
23
24
  var DEFAULT_DAY_START_HOUR = 0;
24
25
  var DEFAULT_DAY_END_HOUR = 24;
26
+ var DEFAULT_SNAP_DURATION = 15;
25
27
 
26
28
  // src/utils/date.ts
27
29
  function parseDate(iso) {
@@ -134,8 +136,27 @@ function getVisibleRange(date, view, weekStartsOn = 0) {
134
136
  return { start: date, end: date };
135
137
  case "agenda":
136
138
  return { start: date, end: addDays(date, 30) };
139
+ case "year": {
140
+ const d = parseDate(date);
141
+ const yearStart = `${d.getFullYear()}-01-01`;
142
+ const yearEnd = `${d.getFullYear()}-12-31`;
143
+ return { start: yearStart, end: yearEnd };
144
+ }
137
145
  }
138
146
  }
147
+ function getISOWeekNumber(date) {
148
+ const d = parseDate(date);
149
+ const target = new Date(d.getFullYear(), d.getMonth(), d.getDate());
150
+ const dayNum = target.getDay() || 7;
151
+ target.setDate(target.getDate() + 4 - dayNum);
152
+ const yearStart = new Date(target.getFullYear(), 0, 1);
153
+ return Math.ceil(((target.getTime() - yearStart.getTime()) / 864e5 + 1) / 7);
154
+ }
155
+ function filterHiddenDays(days, hiddenDays) {
156
+ if (hiddenDays.length === 0) return days;
157
+ const hiddenSet = new Set(hiddenDays);
158
+ return days.filter((d) => !hiddenSet.has(parseDate(d).getDay()));
159
+ }
139
160
  function getTimeOfDay(datetime) {
140
161
  const d = parseDate(datetime);
141
162
  return d.getHours() + d.getMinutes() / 60;
@@ -192,6 +213,8 @@ function formatToolbarTitle(date, view, locale = "en-US") {
192
213
  }).format(d);
193
214
  case "agenda":
194
215
  return getFormatter(locale, { month: "long", year: "numeric" }).format(d);
216
+ case "year":
217
+ return getFormatter(locale, { year: "numeric" }).format(d);
195
218
  }
196
219
  }
197
220
  function formatWeekdayShort(date, locale = "en-US") {
@@ -244,6 +267,8 @@ function navigateByView(date, view, direction) {
244
267
  return addDays(date, direction);
245
268
  case "agenda":
246
269
  return addMonths(date, direction);
270
+ case "year":
271
+ return addMonths(date, 12 * direction);
247
272
  }
248
273
  }
249
274
  function calendarReducer(state, action) {
@@ -300,14 +325,17 @@ function isMultiDayEvent(event) {
300
325
  function partitionEvents(events) {
301
326
  const allDay = [];
302
327
  const timed = [];
328
+ const background = [];
303
329
  for (const event of events) {
304
- if (event.allDay || isMultiDayEvent(event)) {
330
+ if (event.display === "background") {
331
+ background.push(event);
332
+ } else if (event.allDay || isMultiDayEvent(event)) {
305
333
  allDay.push(event);
306
334
  } else {
307
335
  timed.push(event);
308
336
  }
309
337
  }
310
- return { allDay, timed };
338
+ return { allDay, timed, background };
311
339
  }
312
340
  function segmentMultiDayEvent(event, rangeStart, rangeEnd) {
313
341
  const eventStartDate = event.start.slice(0, 10);
@@ -465,9 +493,9 @@ function yPositionToFractionalHour(clientY, columnRect, dayStart, dayEnd) {
465
493
  function normalizeRange(a, b) {
466
494
  return a <= b ? { start: a, end: b } : { start: b, end: a };
467
495
  }
468
- function computeDropPosition(day, clientY, columnRect, dayStartHour, dayEndHour, durationMs) {
496
+ function computeDropPosition(day, clientY, columnRect, dayStartHour, dayEndHour, durationMs, snapMinutes = 15) {
469
497
  const fractionalHour = yPositionToFractionalHour(clientY, columnRect, dayStartHour, dayEndHour);
470
- const snapped = snapToIncrement(fractionalHour, 15);
498
+ const snapped = snapToIncrement(fractionalHour, snapMinutes);
471
499
  const clamped = Math.max(dayStartHour, Math.min(dayEndHour - durationMs / 36e5, snapped));
472
500
  const newStart = fractionalHourToDateTime(day, clamped);
473
501
  const newStartMs = new Date(newStart).getTime();
@@ -787,6 +815,101 @@ function scrollToViewportRange(scrollTop, containerHeight, totalHeight, dayStart
787
815
  };
788
816
  }
789
817
 
818
+ // src/utils/ical.ts
819
+ function toICalDate(dateTime) {
820
+ return dateTime.replace(/[-:]/g, "").replace("T", "T");
821
+ }
822
+ function escapeICalText(text) {
823
+ return text.replace(/\\/g, "\\\\").replace(/;/g, "\\;").replace(/,/g, "\\,").replace(/\n/g, "\\n");
824
+ }
825
+ function eventsToICal(events, calendarName = "trud-calendar") {
826
+ const lines = [
827
+ "BEGIN:VCALENDAR",
828
+ "VERSION:2.0",
829
+ `PRODID:-//${calendarName}//EN`,
830
+ "CALSCALE:GREGORIAN",
831
+ "METHOD:PUBLISH"
832
+ ];
833
+ for (const event of events) {
834
+ lines.push("BEGIN:VEVENT");
835
+ lines.push(`UID:${event.id}`);
836
+ if (event.allDay) {
837
+ lines.push(`DTSTART;VALUE=DATE:${event.start.slice(0, 10).replace(/-/g, "")}`);
838
+ lines.push(`DTEND;VALUE=DATE:${event.end.slice(0, 10).replace(/-/g, "")}`);
839
+ } else {
840
+ lines.push(`DTSTART:${toICalDate(event.start)}`);
841
+ lines.push(`DTEND:${toICalDate(event.end)}`);
842
+ }
843
+ lines.push(`SUMMARY:${escapeICalText(event.title)}`);
844
+ if (event.recurrence) {
845
+ const parts = [`FREQ=${event.recurrence.freq.toUpperCase()}`];
846
+ if (event.recurrence.interval && event.recurrence.interval > 1) {
847
+ parts.push(`INTERVAL=${event.recurrence.interval}`);
848
+ }
849
+ if (event.recurrence.count) {
850
+ parts.push(`COUNT=${event.recurrence.count}`);
851
+ }
852
+ if (event.recurrence.until) {
853
+ parts.push(`UNTIL=${event.recurrence.until.replace(/-/g, "")}T235959`);
854
+ }
855
+ if (event.recurrence.byDay && event.recurrence.byDay.length > 0) {
856
+ parts.push(`BYDAY=${event.recurrence.byDay.join(",")}`);
857
+ }
858
+ if (event.recurrence.byMonthDay && event.recurrence.byMonthDay.length > 0) {
859
+ parts.push(`BYMONTHDAY=${event.recurrence.byMonthDay.join(",")}`);
860
+ }
861
+ if (event.recurrence.bySetPos) {
862
+ parts.push(`BYSETPOS=${event.recurrence.bySetPos}`);
863
+ }
864
+ lines.push(`RRULE:${parts.join(";")}`);
865
+ }
866
+ if (event.exDates && event.exDates.length > 0) {
867
+ const exDateValues = event.exDates.map((d) => d.replace(/-/g, "")).join(",");
868
+ lines.push(`EXDATE;VALUE=DATE:${exDateValues}`);
869
+ }
870
+ lines.push("END:VEVENT");
871
+ }
872
+ lines.push("END:VCALENDAR");
873
+ return lines.join("\r\n");
874
+ }
875
+ function downloadICal(events, filename = "calendar.ics", calendarName) {
876
+ const content = eventsToICal(events, calendarName);
877
+ const blob = new Blob([content], { type: "text/calendar;charset=utf-8" });
878
+ const url = URL.createObjectURL(blob);
879
+ const a = document.createElement("a");
880
+ a.href = url;
881
+ a.download = filename;
882
+ a.click();
883
+ URL.revokeObjectURL(url);
884
+ }
885
+
886
+ // src/utils/resources.ts
887
+ function flattenResources(resources) {
888
+ const result = [];
889
+ for (const resource of resources) {
890
+ result.push(resource);
891
+ if (resource.children && resource.children.length > 0) {
892
+ result.push(...flattenResources(resource.children));
893
+ }
894
+ }
895
+ return result;
896
+ }
897
+ function getEventsForResource(events, resourceId) {
898
+ return events.filter((e) => e.resourceId === resourceId);
899
+ }
900
+ function groupEventsByResource(events, resources) {
901
+ const map = /* @__PURE__ */ new Map();
902
+ for (const resource of resources) {
903
+ map.set(resource.id, []);
904
+ }
905
+ for (const event of events) {
906
+ if (event.resourceId && map.has(event.resourceId)) {
907
+ map.get(event.resourceId).push(event);
908
+ }
909
+ }
910
+ return map;
911
+ }
912
+
790
913
  // src/utils/undo.ts
791
914
  var DEFAULT_MAX_HISTORY = 30;
792
915
  function createUndoStack(initial) {
@@ -839,6 +962,7 @@ exports.DEFAULT_DAY_END_HOUR = DEFAULT_DAY_END_HOUR;
839
962
  exports.DEFAULT_DAY_START_HOUR = DEFAULT_DAY_START_HOUR;
840
963
  exports.DEFAULT_LABELS = DEFAULT_LABELS;
841
964
  exports.DEFAULT_LOCALE = DEFAULT_LOCALE;
965
+ exports.DEFAULT_SNAP_DURATION = DEFAULT_SNAP_DURATION;
842
966
  exports.DEFAULT_VIEW = DEFAULT_VIEW;
843
967
  exports.HOURS_IN_DAY = HOURS_IN_DAY;
844
968
  exports.MINUTES_IN_DAY = MINUTES_IN_DAY;
@@ -858,11 +982,15 @@ exports.createInitialState = createInitialState;
858
982
  exports.createUndoStack = createUndoStack;
859
983
  exports.dateInRange = dateInRange;
860
984
  exports.daysBetween = daysBetween;
985
+ exports.downloadICal = downloadICal;
861
986
  exports.eachDayOfRange = eachDayOfRange;
862
987
  exports.endOfMonth = endOfMonth;
988
+ exports.eventsToICal = eventsToICal;
863
989
  exports.expandRecurringEvents = expandRecurringEvents;
864
990
  exports.filterEventsInRange = filterEventsInRange;
991
+ exports.filterHiddenDays = filterHiddenDays;
865
992
  exports.filterVisibleEvents = filterVisibleEvents;
993
+ exports.flattenResources = flattenResources;
866
994
  exports.formatAgendaDate = formatAgendaDate;
867
995
  exports.formatDayNumber = formatDayNumber;
868
996
  exports.formatMonthDay = formatMonthDay;
@@ -876,13 +1004,16 @@ exports.generateOccurrences = generateOccurrences;
876
1004
  exports.getDurationHours = getDurationHours;
877
1005
  exports.getEventSegments = getEventSegments;
878
1006
  exports.getEventsForDay = getEventsForDay;
1007
+ exports.getEventsForResource = getEventsForResource;
879
1008
  exports.getHourLabels = getHourLabels;
1009
+ exports.getISOWeekNumber = getISOWeekNumber;
880
1010
  exports.getMonthViewRange = getMonthViewRange;
881
1011
  exports.getTimeOfDay = getTimeOfDay;
882
1012
  exports.getVisibleRange = getVisibleRange;
883
1013
  exports.getWeekDays = getWeekDays;
884
1014
  exports.getWeekViewRange = getWeekViewRange;
885
1015
  exports.groupEventsByDate = groupEventsByDate;
1016
+ exports.groupEventsByResource = groupEventsByResource;
886
1017
  exports.isAfter = isAfter;
887
1018
  exports.isBefore = isBefore;
888
1019
  exports.isMultiDayEvent = isMultiDayEvent;