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 +136 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +125 -7
- package/dist/index.d.ts +125 -7
- package/dist/index.js +129 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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.
|
|
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,
|
|
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;
|