@vicacha-devs/strapi-plugin-calendar 0.0.0-stage → 1.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.
- package/LICENSE.md +21 -0
- package/README.md +163 -2
- package/dist/admin/CalendarPage-CVqowKst.cjs +1015 -0
- package/dist/admin/CalendarPage-Dq6YfSXF.js +1015 -0
- package/dist/admin/SettingsPage-7SecVQEi.js +2088 -0
- package/dist/admin/SettingsPage-D0aCDb9w.cjs +2088 -0
- package/dist/admin/ar-O6QL_nJZ.js +331 -0
- package/dist/admin/ar-yKsEpX_y.cjs +331 -0
- package/dist/admin/de-BpDxjctz.js +331 -0
- package/dist/admin/de-ryVtwWOz.cjs +331 -0
- package/dist/admin/en-BHYSUqXv.cjs +331 -0
- package/dist/admin/en-BoRq-9QD.js +331 -0
- package/dist/admin/es-BZ7WX8Ev.js +331 -0
- package/dist/admin/es-T5v93hXW.cjs +331 -0
- package/dist/admin/fr-BnDr9DC8.js +331 -0
- package/dist/admin/fr-DTdTwx7e.cjs +331 -0
- package/dist/admin/index-DO0QbnXX.cjs +3223 -0
- package/dist/admin/index-USqxqyAm.js +3216 -0
- package/dist/admin/index.cjs +9 -0
- package/dist/admin/index.js +9 -0
- package/dist/admin/index.mjs +9 -0
- package/dist/admin/it-BkJNzy27.cjs +331 -0
- package/dist/admin/it-DkdUInap.js +331 -0
- package/dist/admin/nl-C2NFORLS.js +331 -0
- package/dist/admin/nl-CjnlbeUM.cjs +331 -0
- package/dist/admin/pl-BJArPgCQ.cjs +331 -0
- package/dist/admin/pl-mv5eXA8W.js +331 -0
- package/dist/admin/pt-CSYqSGnz.cjs +331 -0
- package/dist/admin/pt-WpvZGj3b.js +331 -0
- package/dist/admin/src/components/CalendarSidePanel.d.ts +13 -0
- package/dist/admin/src/components/Initializer.d.ts +6 -0
- package/dist/admin/src/components/PluginIcon.d.ts +3 -0
- package/dist/admin/src/components/RRuleInput.d.ts +20 -0
- package/dist/admin/src/components/calendar/CalendarToolbar.d.ts +32 -0
- package/dist/admin/src/components/calendar/FullCalendarCanvas.d.ts +21 -0
- package/dist/admin/src/components/calendar/FullCalendarDayCell.d.ts +11 -0
- package/dist/admin/src/components/calendar/OccurrenceDetailDrawer.d.ts +15 -0
- package/dist/admin/src/components/calendar/QuickCreatePanel.d.ts +29 -0
- package/dist/admin/src/components/calendar/QuickCreateSubcomponents.d.ts +40 -0
- package/dist/admin/src/components/calendar/calendarPageHelpers.d.ts +52 -0
- package/dist/admin/src/components/calendar/calendarThemeStyles.d.ts +2 -0
- package/dist/admin/src/components/settings/CalendarThemeSection.d.ts +9 -0
- package/dist/admin/src/components/settings/CalendarViewsSection.d.ts +10 -0
- package/dist/admin/src/components/settings/CollectionSourcesSection.d.ts +12 -0
- package/dist/admin/src/components/settings/CompanionSuiteSettingsSection.d.ts +29 -0
- package/dist/admin/src/components/settings/SecondaryCalendarsSection.d.ts +11 -0
- package/dist/admin/src/fields/rruleField.d.ts +19 -0
- package/dist/admin/src/index.d.ts +18 -0
- package/dist/admin/src/pages/CalendarPage.d.ts +3 -0
- package/dist/admin/src/pages/SettingsPage.d.ts +3 -0
- package/dist/admin/src/pages/calendarPageSupport.d.ts +8 -0
- package/dist/admin/tr-BhZKvFoK.js +331 -0
- package/dist/admin/tr-zUVxuw9X.cjs +331 -0
- package/dist/package.json +3 -0
- package/dist/server/index.cjs +2585 -0
- package/dist/server/index.js +2585 -0
- package/dist/server/index.mjs +2585 -0
- package/dist/server/src/bootstrap.d.ts +5 -0
- package/dist/server/src/config/index.d.ts +7 -0
- package/dist/server/src/controllers/admin.d.ts +15 -0
- package/dist/server/src/controllers/content-api.d.ts +10 -0
- package/dist/server/src/controllers/index.d.ts +21 -0
- package/dist/server/src/destroy.d.ts +5 -0
- package/dist/server/src/index.d.ts +85 -0
- package/dist/server/src/register.d.ts +5 -0
- package/dist/server/src/routes/admin.d.ts +17 -0
- package/dist/server/src/routes/content-api.d.ts +10 -0
- package/dist/server/src/routes/index.d.ts +28 -0
- package/dist/server/src/services/builtinSources.d.ts +3 -0
- package/dist/server/src/services/ical.d.ts +6 -0
- package/dist/server/src/services/index.d.ts +23 -0
- package/dist/server/src/services/occurrence.d.ts +6 -0
- package/dist/server/src/services/occurrenceDates.d.ts +7 -0
- package/dist/server/src/services/quickCreateHelpers.d.ts +20 -0
- package/dist/server/src/services/registry.d.ts +19 -0
- package/dist/server/src/services/settings.d.ts +6 -0
- package/dist/server/src/types.d.ts +98 -0
- package/dist/shared/calendarSystems.d.ts +41 -0
- package/dist/shared/icalGenerator.d.ts +18 -0
- package/dist/shared/index.cjs +1612 -0
- package/dist/shared/index.d.ts +6 -0
- package/dist/shared/index.js +1612 -0
- package/dist/shared/index.mjs +1612 -0
- package/dist/shared/localization.d.ts +14 -0
- package/dist/shared/pluginId.d.ts +2 -0
- package/dist/shared/rruleEngine.d.ts +22 -0
- package/dist/shared/themePalettes.d.ts +27 -0
- package/dist/shared/themePresets.d.ts +32 -0
- package/dist/shared/types.d.ts +311 -0
- package/package.json +120 -3
|
@@ -0,0 +1,1612 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const SUPPORTED_INTL_CALENDARS = [
|
|
4
|
+
{ id: "gregory", intlCalendar: "gregory", defaultLabel: "Gregorian" },
|
|
5
|
+
{ id: "iso8601", intlCalendar: "iso8601", defaultLabel: "ISO 8601" },
|
|
6
|
+
{
|
|
7
|
+
id: "islamic-umalqura",
|
|
8
|
+
intlCalendar: "islamic-umalqura",
|
|
9
|
+
defaultLabel: "Hijri (Umm al-Qura)"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
id: "islamic-civil",
|
|
13
|
+
intlCalendar: "islamic-civil",
|
|
14
|
+
defaultLabel: "Hijri (Civil)"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: "islamic-tbla",
|
|
18
|
+
intlCalendar: "islamic-tbla",
|
|
19
|
+
defaultLabel: "Hijri (Tabular)"
|
|
20
|
+
},
|
|
21
|
+
{ id: "hebrew", intlCalendar: "hebrew", defaultLabel: "Hebrew" },
|
|
22
|
+
{
|
|
23
|
+
id: "persian",
|
|
24
|
+
intlCalendar: "persian",
|
|
25
|
+
defaultLabel: "Persian (Solar Hijri)"
|
|
26
|
+
},
|
|
27
|
+
{ id: "buddhist", intlCalendar: "buddhist", defaultLabel: "Buddhist" },
|
|
28
|
+
{ id: "japanese", intlCalendar: "japanese", defaultLabel: "Japanese" },
|
|
29
|
+
{ id: "chinese", intlCalendar: "chinese", defaultLabel: "Chinese Lunar" },
|
|
30
|
+
{ id: "indian", intlCalendar: "indian", defaultLabel: "Indian National" },
|
|
31
|
+
{ id: "coptic", intlCalendar: "coptic", defaultLabel: "Coptic" },
|
|
32
|
+
{ id: "ethiopic", intlCalendar: "ethiopic", defaultLabel: "Ethiopic" }
|
|
33
|
+
];
|
|
34
|
+
const customFormatters = /* @__PURE__ */ new Map();
|
|
35
|
+
function registerCalendarSystemFormatter(calendarId, formatter) {
|
|
36
|
+
if (!calendarId || typeof formatter !== "function") {
|
|
37
|
+
throw new Error("Both calendarId and formatter function are required.");
|
|
38
|
+
}
|
|
39
|
+
customFormatters.set(calendarId, formatter);
|
|
40
|
+
}
|
|
41
|
+
function unregisterCalendarSystemFormatter(calendarId) {
|
|
42
|
+
customFormatters.delete(calendarId);
|
|
43
|
+
}
|
|
44
|
+
function getRegisteredCalendarFormatters() {
|
|
45
|
+
return customFormatters;
|
|
46
|
+
}
|
|
47
|
+
function applyDayOffset(date, dayOffset) {
|
|
48
|
+
if (!dayOffset || !Number.isFinite(dayOffset)) {
|
|
49
|
+
return date;
|
|
50
|
+
}
|
|
51
|
+
const offsetDays = Math.trunc(dayOffset);
|
|
52
|
+
return new Date(date.getTime() + offsetDays * 864e5);
|
|
53
|
+
}
|
|
54
|
+
function parseDigits(raw) {
|
|
55
|
+
const n = Number.parseInt(raw.replace(/[^0-9]/g, ""), 10);
|
|
56
|
+
return Number.isFinite(n) ? n : 0;
|
|
57
|
+
}
|
|
58
|
+
const INTL_PART_FIELD_MAP = {
|
|
59
|
+
year: "year",
|
|
60
|
+
relatedYear: "year",
|
|
61
|
+
month: "month",
|
|
62
|
+
day: "day"
|
|
63
|
+
};
|
|
64
|
+
function parseIntlPartsToYmd(parts) {
|
|
65
|
+
const ymd = { year: 0, month: 0, day: 0 };
|
|
66
|
+
for (const part of parts) {
|
|
67
|
+
const key = INTL_PART_FIELD_MAP[String(part.type)];
|
|
68
|
+
if (!key) continue;
|
|
69
|
+
const val = parseDigits(part.value);
|
|
70
|
+
if (val > 0) ymd[key] = val;
|
|
71
|
+
}
|
|
72
|
+
return ymd.month >= 1 && ymd.day >= 1 ? ymd : null;
|
|
73
|
+
}
|
|
74
|
+
const intlFormatterCache = /* @__PURE__ */ new Map();
|
|
75
|
+
function getCachedIntlFormatter(locale, options) {
|
|
76
|
+
const key = `${locale}|${options.year ?? ""}|${options.month ?? ""}|${options.day ?? ""}|${options.timeZone ?? "UTC"}`;
|
|
77
|
+
let fmt = intlFormatterCache.get(key);
|
|
78
|
+
if (!fmt) {
|
|
79
|
+
if (intlFormatterCache.size >= 200) {
|
|
80
|
+
const oldestKey = intlFormatterCache.keys().next().value;
|
|
81
|
+
if (oldestKey !== void 0) intlFormatterCache.delete(oldestKey);
|
|
82
|
+
}
|
|
83
|
+
fmt = new Intl.DateTimeFormat(locale, options);
|
|
84
|
+
intlFormatterCache.set(key, fmt);
|
|
85
|
+
}
|
|
86
|
+
return fmt;
|
|
87
|
+
}
|
|
88
|
+
function extractCalendarParts(date, intlCalendar, dayOffset = 0) {
|
|
89
|
+
if (Number.isNaN(date.getTime())) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const effectiveDate = applyDayOffset(date, dayOffset);
|
|
93
|
+
const calendarTag = (intlCalendar || "gregory").trim().toLowerCase();
|
|
94
|
+
try {
|
|
95
|
+
const formatter = getCachedIntlFormatter(`en-u-ca-${calendarTag}-nu-latn`, {
|
|
96
|
+
year: "numeric",
|
|
97
|
+
month: "numeric",
|
|
98
|
+
day: "numeric",
|
|
99
|
+
timeZone: "UTC"
|
|
100
|
+
});
|
|
101
|
+
const parsed = parseIntlPartsToYmd(formatter.formatToParts(effectiveDate));
|
|
102
|
+
if (parsed) return parsed;
|
|
103
|
+
} catch {
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
year: effectiveDate.getUTCFullYear(),
|
|
107
|
+
month: effectiveDate.getUTCMonth() + 1,
|
|
108
|
+
day: effectiveDate.getUTCDate()
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function resolveCalendarLocaleTag(systemLocale, fallbackLocale) {
|
|
112
|
+
const rawLocale = systemLocale && systemLocale !== "auto" ? systemLocale : fallbackLocale && fallbackLocale !== "auto" ? fallbackLocale : "en";
|
|
113
|
+
return rawLocale.split("-u-")[0];
|
|
114
|
+
}
|
|
115
|
+
function tryFormatWithCustomFormatter(effectiveDate, system, effectiveLocale, compact) {
|
|
116
|
+
const customFn = customFormatters.get(system.id) ?? customFormatters.get(system.intlCalendar);
|
|
117
|
+
if (!customFn) return null;
|
|
118
|
+
try {
|
|
119
|
+
return customFn(effectiveDate, {
|
|
120
|
+
locale: effectiveLocale,
|
|
121
|
+
system,
|
|
122
|
+
compact
|
|
123
|
+
});
|
|
124
|
+
} catch {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function formatWithIntlFallback(effectiveDate, isoOrDateKey, intlLocale, compact) {
|
|
129
|
+
try {
|
|
130
|
+
const formatter = getCachedIntlFormatter(
|
|
131
|
+
intlLocale,
|
|
132
|
+
compact ? { day: "numeric", month: "short", timeZone: "UTC" } : { day: "numeric", month: "long", year: "numeric", timeZone: "UTC" }
|
|
133
|
+
);
|
|
134
|
+
return formatter.format(effectiveDate);
|
|
135
|
+
} catch {
|
|
136
|
+
try {
|
|
137
|
+
const fallback = getCachedIntlFormatter("en-u-ca-gregory", {
|
|
138
|
+
day: "numeric",
|
|
139
|
+
month: "short",
|
|
140
|
+
year: "numeric",
|
|
141
|
+
timeZone: "UTC"
|
|
142
|
+
});
|
|
143
|
+
return fallback.format(effectiveDate);
|
|
144
|
+
} catch {
|
|
145
|
+
return isoOrDateKey.slice(0, 10);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function formatDateInCalendarSystem(isoOrDateKey, system, locale = "en", options) {
|
|
150
|
+
const rawDate = /^\d{4}-\d{2}-\d{2}$/.test(isoOrDateKey) ? /* @__PURE__ */ new Date(`${isoOrDateKey}T12:00:00.000Z`) : new Date(isoOrDateKey);
|
|
151
|
+
if (Number.isNaN(rawDate.getTime())) {
|
|
152
|
+
return "";
|
|
153
|
+
}
|
|
154
|
+
const effectiveDate = applyDayOffset(rawDate, system.dayOffset);
|
|
155
|
+
const effectiveLocale = resolveCalendarLocaleTag(system.locale, locale);
|
|
156
|
+
const compact = Boolean(options?.compact);
|
|
157
|
+
const customResult = tryFormatWithCustomFormatter(
|
|
158
|
+
effectiveDate,
|
|
159
|
+
system,
|
|
160
|
+
effectiveLocale,
|
|
161
|
+
compact
|
|
162
|
+
);
|
|
163
|
+
if (customResult !== null) {
|
|
164
|
+
return customResult;
|
|
165
|
+
}
|
|
166
|
+
const calendarTag = (system.intlCalendar || "gregory").trim();
|
|
167
|
+
return formatWithIntlFallback(
|
|
168
|
+
effectiveDate,
|
|
169
|
+
isoOrDateKey,
|
|
170
|
+
`${effectiveLocale}-u-ca-${calendarTag}`,
|
|
171
|
+
compact
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
function readTrimmedString(val) {
|
|
175
|
+
return typeof val === "string" ? val.trim() : "";
|
|
176
|
+
}
|
|
177
|
+
function extractDocAltDatesMap(doc) {
|
|
178
|
+
if (doc.alternateDates && typeof doc.alternateDates === "object" && !Array.isArray(doc.alternateDates)) {
|
|
179
|
+
return doc.alternateDates;
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
function resolveSystemOverrideText(sys, doc, sourceAltFields, docAltDatesMap) {
|
|
184
|
+
const fieldName = sourceAltFields[sys.id] || sys.overrideField;
|
|
185
|
+
const rawFieldOverride = fieldName ? readTrimmedString(doc[fieldName]) : "";
|
|
186
|
+
const rawJsonOverride = docAltDatesMap ? readTrimmedString(docAltDatesMap[sys.id]) : "";
|
|
187
|
+
return rawFieldOverride || rawJsonOverride;
|
|
188
|
+
}
|
|
189
|
+
function buildSingleSecondaryCalendarEntry(args) {
|
|
190
|
+
const {
|
|
191
|
+
sys,
|
|
192
|
+
occurrenceDate,
|
|
193
|
+
rawDateObj,
|
|
194
|
+
isBaseOccurrence,
|
|
195
|
+
doc,
|
|
196
|
+
sourceAltFields,
|
|
197
|
+
docAltDatesMap,
|
|
198
|
+
locale
|
|
199
|
+
} = args;
|
|
200
|
+
const rawOverride = resolveSystemOverrideText(
|
|
201
|
+
sys,
|
|
202
|
+
doc,
|
|
203
|
+
sourceAltFields,
|
|
204
|
+
docAltDatesMap
|
|
205
|
+
);
|
|
206
|
+
const parts = extractCalendarParts(
|
|
207
|
+
rawDateObj,
|
|
208
|
+
sys.intlCalendar,
|
|
209
|
+
sys.dayOffset ?? 0
|
|
210
|
+
);
|
|
211
|
+
const label = sys.label || sys.id;
|
|
212
|
+
if (rawOverride && isBaseOccurrence) {
|
|
213
|
+
return {
|
|
214
|
+
calendarId: sys.id,
|
|
215
|
+
label,
|
|
216
|
+
formatted: rawOverride,
|
|
217
|
+
compact: rawOverride,
|
|
218
|
+
intlCalendar: sys.intlCalendar,
|
|
219
|
+
year: parts?.year,
|
|
220
|
+
month: parts?.month,
|
|
221
|
+
day: parts?.day,
|
|
222
|
+
isOverridden: true
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
const formatted = formatDateInCalendarSystem(occurrenceDate, sys, locale, {
|
|
226
|
+
compact: false
|
|
227
|
+
});
|
|
228
|
+
if (!formatted) return null;
|
|
229
|
+
const compact = formatDateInCalendarSystem(occurrenceDate, sys, locale, {
|
|
230
|
+
compact: true
|
|
231
|
+
});
|
|
232
|
+
return {
|
|
233
|
+
calendarId: sys.id,
|
|
234
|
+
label,
|
|
235
|
+
formatted,
|
|
236
|
+
compact: compact || formatted,
|
|
237
|
+
intlCalendar: sys.intlCalendar,
|
|
238
|
+
year: parts?.year,
|
|
239
|
+
month: parts?.month,
|
|
240
|
+
day: parts?.day,
|
|
241
|
+
isOverridden: false
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
function appendDirectSourceFieldOverrides(result, sourceAltFields, doc) {
|
|
245
|
+
for (const [calendarId, fieldName] of Object.entries(sourceAltFields)) {
|
|
246
|
+
if (result[calendarId] || !fieldName) continue;
|
|
247
|
+
const rawVal = readTrimmedString(doc[fieldName]);
|
|
248
|
+
if (rawVal) {
|
|
249
|
+
result[calendarId] = {
|
|
250
|
+
calendarId,
|
|
251
|
+
label: calendarId,
|
|
252
|
+
formatted: rawVal,
|
|
253
|
+
compact: rawVal,
|
|
254
|
+
isOverridden: true
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
function appendJsonMapOverrides(result, docAltDatesMap) {
|
|
260
|
+
for (const [calendarId, val] of Object.entries(docAltDatesMap)) {
|
|
261
|
+
if (result[calendarId]) continue;
|
|
262
|
+
const trimmed = readTrimmedString(val);
|
|
263
|
+
if (trimmed) {
|
|
264
|
+
result[calendarId] = {
|
|
265
|
+
calendarId,
|
|
266
|
+
label: calendarId,
|
|
267
|
+
formatted: trimmed,
|
|
268
|
+
compact: trimmed,
|
|
269
|
+
isOverridden: true
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
function populateSecondarySystems(args) {
|
|
275
|
+
for (const sys of args.secondaryCalendars) {
|
|
276
|
+
if (!sys?.enabled || !sys.id) continue;
|
|
277
|
+
const entry = buildSingleSecondaryCalendarEntry({ ...args, sys });
|
|
278
|
+
if (entry) args.result[sys.id] = entry;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
function resolveOccurrenceAlternateDates(params) {
|
|
282
|
+
const { occurrenceDate, baseDateKey, isRecurring, doc, source } = params;
|
|
283
|
+
const result = {};
|
|
284
|
+
const sourceAltFields = source.fields.alternateDateFields ?? {};
|
|
285
|
+
const isBaseOccurrence = !isRecurring || occurrenceDate === baseDateKey;
|
|
286
|
+
const docAltDatesMap = extractDocAltDatesMap(doc);
|
|
287
|
+
populateSecondarySystems({
|
|
288
|
+
result,
|
|
289
|
+
secondaryCalendars: params.secondaryCalendars ?? [],
|
|
290
|
+
occurrenceDate,
|
|
291
|
+
rawDateObj: /* @__PURE__ */ new Date(`${occurrenceDate}T12:00:00.000Z`),
|
|
292
|
+
isBaseOccurrence,
|
|
293
|
+
doc,
|
|
294
|
+
sourceAltFields,
|
|
295
|
+
docAltDatesMap,
|
|
296
|
+
locale: params.locale ?? "en"
|
|
297
|
+
});
|
|
298
|
+
if (isBaseOccurrence) {
|
|
299
|
+
appendDirectSourceFieldOverrides(result, sourceAltFields, doc);
|
|
300
|
+
if (docAltDatesMap) appendJsonMapOverrides(result, docAltDatesMap);
|
|
301
|
+
}
|
|
302
|
+
return result;
|
|
303
|
+
}
|
|
304
|
+
function escapeIcsText(value) {
|
|
305
|
+
if (!value) return "";
|
|
306
|
+
return String(value).replace(/\\/g, "\\\\").replace(/;/g, "\\;").replace(/,/g, "\\,").replace(/\r?\n/g, "\\n");
|
|
307
|
+
}
|
|
308
|
+
const utf8Encoder = new TextEncoder();
|
|
309
|
+
function foldIcsLine(line) {
|
|
310
|
+
if (utf8Encoder.encode(line).byteLength <= 75) {
|
|
311
|
+
return line;
|
|
312
|
+
}
|
|
313
|
+
const chunks = [];
|
|
314
|
+
let current = "";
|
|
315
|
+
let currentBytes = 0;
|
|
316
|
+
for (const codePoint of line) {
|
|
317
|
+
const cpBytes = utf8Encoder.encode(codePoint).byteLength;
|
|
318
|
+
if (currentBytes + cpBytes > 75) {
|
|
319
|
+
chunks.push(current);
|
|
320
|
+
current = ` ${codePoint}`;
|
|
321
|
+
currentBytes = 1 + cpBytes;
|
|
322
|
+
} else {
|
|
323
|
+
current += codePoint;
|
|
324
|
+
currentBytes += cpBytes;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (current) {
|
|
328
|
+
chunks.push(current);
|
|
329
|
+
}
|
|
330
|
+
return chunks.join("\r\n");
|
|
331
|
+
}
|
|
332
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
333
|
+
function toIcsTimestamp(iso, allDay = false) {
|
|
334
|
+
const date = new Date(iso);
|
|
335
|
+
if (Number.isNaN(date.getTime())) {
|
|
336
|
+
return "19700101T000000Z";
|
|
337
|
+
}
|
|
338
|
+
const y = date.getUTCFullYear();
|
|
339
|
+
const m = pad(date.getUTCMonth() + 1);
|
|
340
|
+
const d = pad(date.getUTCDate());
|
|
341
|
+
if (allDay) {
|
|
342
|
+
return `${y}${m}${d}`;
|
|
343
|
+
}
|
|
344
|
+
const hh = pad(date.getUTCHours());
|
|
345
|
+
const mm = pad(date.getUTCMinutes());
|
|
346
|
+
const ss = pad(date.getUTCSeconds());
|
|
347
|
+
return `${y}${m}${d}T${hh}${mm}${ss}Z`;
|
|
348
|
+
}
|
|
349
|
+
function toExclusiveAllDayEndStamp(startIso, endIso) {
|
|
350
|
+
const startDate = new Date(startIso);
|
|
351
|
+
const endDate = new Date(endIso);
|
|
352
|
+
if (Number.isNaN(startDate.getTime())) {
|
|
353
|
+
return "19700102";
|
|
354
|
+
}
|
|
355
|
+
const startMidnight = Date.UTC(
|
|
356
|
+
startDate.getUTCFullYear(),
|
|
357
|
+
startDate.getUTCMonth(),
|
|
358
|
+
startDate.getUTCDate()
|
|
359
|
+
);
|
|
360
|
+
const endMidnight = Number.isNaN(endDate.getTime()) ? startMidnight : Date.UTC(
|
|
361
|
+
endDate.getUTCFullYear(),
|
|
362
|
+
endDate.getUTCMonth(),
|
|
363
|
+
endDate.getUTCDate()
|
|
364
|
+
);
|
|
365
|
+
const isAlreadyExclusiveMidnight = !Number.isNaN(endDate.getTime()) && endMidnight > startMidnight && endDate.getUTCHours() === 0 && endDate.getUTCMinutes() === 0 && endDate.getUTCSeconds() === 0;
|
|
366
|
+
const exclusiveDate = new Date(
|
|
367
|
+
isAlreadyExclusiveMidnight ? endMidnight : Math.max(startMidnight, endMidnight) + 864e5
|
|
368
|
+
);
|
|
369
|
+
return toIcsTimestamp(exclusiveDate.toISOString(), true);
|
|
370
|
+
}
|
|
371
|
+
function buildCalendarLinks(params) {
|
|
372
|
+
const startStamp = toIcsTimestamp(params.startDate, params.allDay);
|
|
373
|
+
const endStamp = params.allDay ? toExclusiveAllDayEndStamp(params.startDate, params.endDate) : toIcsTimestamp(params.endDate, false);
|
|
374
|
+
const googleParams = new URLSearchParams({
|
|
375
|
+
action: "TEMPLATE",
|
|
376
|
+
text: params.title,
|
|
377
|
+
dates: `${startStamp}/${endStamp}`
|
|
378
|
+
});
|
|
379
|
+
if (params.summary) googleParams.set("details", params.summary);
|
|
380
|
+
if (params.location) googleParams.set("location", params.location);
|
|
381
|
+
const outlookParams = new URLSearchParams({
|
|
382
|
+
path: "/calendar/action/compose",
|
|
383
|
+
rru: "addevent",
|
|
384
|
+
subject: params.title,
|
|
385
|
+
startdt: params.startDate,
|
|
386
|
+
enddt: params.endDate
|
|
387
|
+
});
|
|
388
|
+
if (params.summary) outlookParams.set("body", params.summary);
|
|
389
|
+
if (params.location) outlookParams.set("location", params.location);
|
|
390
|
+
const yahooParams = new URLSearchParams({
|
|
391
|
+
v: "60",
|
|
392
|
+
title: params.title,
|
|
393
|
+
st: startStamp,
|
|
394
|
+
et: endStamp
|
|
395
|
+
});
|
|
396
|
+
if (params.summary) yahooParams.set("desc", params.summary);
|
|
397
|
+
if (params.location) yahooParams.set("in_loc", params.location);
|
|
398
|
+
const querySuffix = params.occurrenceDate ? `?occurrenceDate=${encodeURIComponent(params.occurrenceDate)}` : "";
|
|
399
|
+
return {
|
|
400
|
+
google: `https://calendar.google.com/calendar/render?${googleParams.toString()}`,
|
|
401
|
+
outlook: `https://outlook.live.com/calendar/0/deeplink/compose?${outlookParams.toString()}`,
|
|
402
|
+
yahoo: `https://calendar.yahoo.com/?${yahooParams.toString()}`,
|
|
403
|
+
icsPath: `/api/calendar/${encodeURIComponent(params.documentId)}/invite.ics${querySuffix}`
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
function generateIcsCalendar(params) {
|
|
407
|
+
const nowStamp = toIcsTimestamp((/* @__PURE__ */ new Date()).toISOString());
|
|
408
|
+
const lines = [
|
|
409
|
+
"BEGIN:VCALENDAR",
|
|
410
|
+
"VERSION:2.0",
|
|
411
|
+
"PRODID:-//Vicacha Devs//Strapi Plugin Calendar//EN",
|
|
412
|
+
"CALSCALE:GREGORIAN",
|
|
413
|
+
"METHOD:PUBLISH",
|
|
414
|
+
`X-WR-CALNAME:${escapeIcsText(params.calendarTitle)}`,
|
|
415
|
+
`X-WR-TIMEZONE:${escapeIcsText(params.timezone)}`
|
|
416
|
+
];
|
|
417
|
+
for (const occ of params.occurrences) {
|
|
418
|
+
lines.push("BEGIN:VEVENT");
|
|
419
|
+
lines.push(`UID:${escapeIcsText(occ.occurrenceId)}@strapi-plugin-calendar`);
|
|
420
|
+
lines.push(`DTSTAMP:${nowStamp}`);
|
|
421
|
+
if (occ.allDay) {
|
|
422
|
+
lines.push(`DTSTART;VALUE=DATE:${toIcsTimestamp(occ.startDate, true)}`);
|
|
423
|
+
lines.push(
|
|
424
|
+
`DTEND;VALUE=DATE:${toExclusiveAllDayEndStamp(occ.startDate, occ.endDate)}`
|
|
425
|
+
);
|
|
426
|
+
} else {
|
|
427
|
+
lines.push(`DTSTART:${toIcsTimestamp(occ.startDate, false)}`);
|
|
428
|
+
lines.push(`DTEND:${toIcsTimestamp(occ.endDate, false)}`);
|
|
429
|
+
}
|
|
430
|
+
lines.push(`SUMMARY:${escapeIcsText(occ.title)}`);
|
|
431
|
+
if (occ.summary) {
|
|
432
|
+
lines.push(`DESCRIPTION:${escapeIcsText(occ.summary)}`);
|
|
433
|
+
}
|
|
434
|
+
if (occ.location) {
|
|
435
|
+
lines.push(`LOCATION:${escapeIcsText(occ.location)}`);
|
|
436
|
+
}
|
|
437
|
+
if (occ.category) {
|
|
438
|
+
lines.push(`CATEGORIES:${escapeIcsText(occ.category)}`);
|
|
439
|
+
}
|
|
440
|
+
if (occ.alternateDates) {
|
|
441
|
+
for (const alt of Object.values(occ.alternateDates)) {
|
|
442
|
+
const safeCalId = alt.calendarId.replace(/[^A-Za-z0-9-]/g, "").toUpperCase();
|
|
443
|
+
if (!safeCalId) continue;
|
|
444
|
+
lines.push(`X-ALT-DATE-${safeCalId}:${escapeIcsText(alt.formatted)}`);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
const icsStatus = occ.status === "cancelled" ? "CANCELLED" : occ.status === "postponed" ? "TENTATIVE" : "CONFIRMED";
|
|
448
|
+
lines.push(`STATUS:${icsStatus}`);
|
|
449
|
+
lines.push("END:VEVENT");
|
|
450
|
+
}
|
|
451
|
+
lines.push("END:VCALENDAR");
|
|
452
|
+
return `${lines.map(foldIcsLine).join("\r\n")}\r
|
|
453
|
+
`;
|
|
454
|
+
}
|
|
455
|
+
const RTL_LANGUAGE_PREFIXES = /* @__PURE__ */ new Set([
|
|
456
|
+
"ar",
|
|
457
|
+
"he",
|
|
458
|
+
"iw",
|
|
459
|
+
"fa",
|
|
460
|
+
"ur",
|
|
461
|
+
"ps",
|
|
462
|
+
"sd",
|
|
463
|
+
"ug",
|
|
464
|
+
"yi"
|
|
465
|
+
]);
|
|
466
|
+
const SUPPORTED_CALENDAR_LOCALES = [
|
|
467
|
+
{
|
|
468
|
+
code: "en",
|
|
469
|
+
label: "English",
|
|
470
|
+
nativeName: "English",
|
|
471
|
+
direction: "ltr",
|
|
472
|
+
week: { dow: 0, doy: 4 },
|
|
473
|
+
buttonText: {
|
|
474
|
+
prev: "Prev",
|
|
475
|
+
next: "Next",
|
|
476
|
+
today: "Today",
|
|
477
|
+
year: "Year",
|
|
478
|
+
month: "Month",
|
|
479
|
+
week: "Week",
|
|
480
|
+
day: "Day",
|
|
481
|
+
list: "Agenda"
|
|
482
|
+
},
|
|
483
|
+
weekText: "Wk",
|
|
484
|
+
allDayText: "all-day",
|
|
485
|
+
moreLinkText: "more",
|
|
486
|
+
noEventsText: "No events to display"
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
code: "es",
|
|
490
|
+
label: "Spanish",
|
|
491
|
+
nativeName: "Español",
|
|
492
|
+
direction: "ltr",
|
|
493
|
+
week: { dow: 1, doy: 4 },
|
|
494
|
+
buttonText: {
|
|
495
|
+
prev: "Ant",
|
|
496
|
+
next: "Sig",
|
|
497
|
+
today: "Hoy",
|
|
498
|
+
year: "Año",
|
|
499
|
+
month: "Mes",
|
|
500
|
+
week: "Semana",
|
|
501
|
+
day: "Día",
|
|
502
|
+
list: "Agenda"
|
|
503
|
+
},
|
|
504
|
+
weekText: "Sm",
|
|
505
|
+
allDayText: "todo el día",
|
|
506
|
+
moreLinkText: "más",
|
|
507
|
+
noEventsText: "No hay eventos para mostrar"
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
code: "fr",
|
|
511
|
+
label: "French",
|
|
512
|
+
nativeName: "Français",
|
|
513
|
+
direction: "ltr",
|
|
514
|
+
week: { dow: 1, doy: 4 },
|
|
515
|
+
buttonText: {
|
|
516
|
+
prev: "Préc",
|
|
517
|
+
next: "Suiv",
|
|
518
|
+
today: "Aujourd'hui",
|
|
519
|
+
year: "Année",
|
|
520
|
+
month: "Mois",
|
|
521
|
+
week: "Semaine",
|
|
522
|
+
day: "Jour",
|
|
523
|
+
list: "Planning"
|
|
524
|
+
},
|
|
525
|
+
weekText: "Sem.",
|
|
526
|
+
allDayText: "toute la journée",
|
|
527
|
+
moreLinkText: "en plus",
|
|
528
|
+
noEventsText: "Aucun événement à afficher"
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
code: "de",
|
|
532
|
+
label: "German",
|
|
533
|
+
nativeName: "Deutsch",
|
|
534
|
+
direction: "ltr",
|
|
535
|
+
week: { dow: 1, doy: 4 },
|
|
536
|
+
buttonText: {
|
|
537
|
+
prev: "Zurück",
|
|
538
|
+
next: "Weiter",
|
|
539
|
+
today: "Heute",
|
|
540
|
+
year: "Jahr",
|
|
541
|
+
month: "Monat",
|
|
542
|
+
week: "Woche",
|
|
543
|
+
day: "Tag",
|
|
544
|
+
list: "Terminübersicht"
|
|
545
|
+
},
|
|
546
|
+
weekText: "KW",
|
|
547
|
+
allDayText: "ganztägig",
|
|
548
|
+
moreLinkText: "weitere",
|
|
549
|
+
noEventsText: "Keine Ereignisse anzuzeigen"
|
|
550
|
+
},
|
|
551
|
+
{
|
|
552
|
+
code: "pt",
|
|
553
|
+
label: "Portuguese",
|
|
554
|
+
nativeName: "Português",
|
|
555
|
+
direction: "ltr",
|
|
556
|
+
week: { dow: 0, doy: 4 },
|
|
557
|
+
buttonText: {
|
|
558
|
+
prev: "Ant",
|
|
559
|
+
next: "Próx",
|
|
560
|
+
today: "Hoje",
|
|
561
|
+
year: "Ano",
|
|
562
|
+
month: "Mês",
|
|
563
|
+
week: "Semana",
|
|
564
|
+
day: "Dia",
|
|
565
|
+
list: "Agenda"
|
|
566
|
+
},
|
|
567
|
+
weekText: "Sem",
|
|
568
|
+
allDayText: "dia inteiro",
|
|
569
|
+
moreLinkText: "mais",
|
|
570
|
+
noEventsText: "Não há eventos para mostrar"
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
code: "it",
|
|
574
|
+
label: "Italian",
|
|
575
|
+
nativeName: "Italiano",
|
|
576
|
+
direction: "ltr",
|
|
577
|
+
week: { dow: 1, doy: 4 },
|
|
578
|
+
buttonText: {
|
|
579
|
+
prev: "Prec",
|
|
580
|
+
next: "Succ",
|
|
581
|
+
today: "Oggi",
|
|
582
|
+
year: "Anno",
|
|
583
|
+
month: "Mese",
|
|
584
|
+
week: "Settimana",
|
|
585
|
+
day: "Giorno",
|
|
586
|
+
list: "Agenda"
|
|
587
|
+
},
|
|
588
|
+
weekText: "Set",
|
|
589
|
+
allDayText: "tutto il giorno",
|
|
590
|
+
moreLinkText: "altri",
|
|
591
|
+
noEventsText: "Non ci sono eventi da visualizzare"
|
|
592
|
+
},
|
|
593
|
+
{
|
|
594
|
+
code: "nl",
|
|
595
|
+
label: "Dutch",
|
|
596
|
+
nativeName: "Nederlands",
|
|
597
|
+
direction: "ltr",
|
|
598
|
+
week: { dow: 1, doy: 4 },
|
|
599
|
+
buttonText: {
|
|
600
|
+
prev: "Vorige",
|
|
601
|
+
next: "Volgende",
|
|
602
|
+
today: "Vandaag",
|
|
603
|
+
year: "Jaar",
|
|
604
|
+
month: "Maand",
|
|
605
|
+
week: "Week",
|
|
606
|
+
day: "Dag",
|
|
607
|
+
list: "Lijst"
|
|
608
|
+
},
|
|
609
|
+
weekText: "Wk",
|
|
610
|
+
allDayText: "hele dag",
|
|
611
|
+
moreLinkText: "extra",
|
|
612
|
+
noEventsText: "Geen evenementen om te laten zien"
|
|
613
|
+
},
|
|
614
|
+
{
|
|
615
|
+
code: "pl",
|
|
616
|
+
label: "Polish",
|
|
617
|
+
nativeName: "Polski",
|
|
618
|
+
direction: "ltr",
|
|
619
|
+
week: { dow: 1, doy: 4 },
|
|
620
|
+
buttonText: {
|
|
621
|
+
prev: "Poprzedni",
|
|
622
|
+
next: "Następny",
|
|
623
|
+
today: "Dziś",
|
|
624
|
+
year: "Rok",
|
|
625
|
+
month: "Miesiąc",
|
|
626
|
+
week: "Tydzień",
|
|
627
|
+
day: "Dzień",
|
|
628
|
+
list: "Plan dnia"
|
|
629
|
+
},
|
|
630
|
+
weekText: "Tydz",
|
|
631
|
+
allDayText: "cały dzień",
|
|
632
|
+
moreLinkText: "więcej",
|
|
633
|
+
noEventsText: "Brak wydarzeń do wyświetlenia"
|
|
634
|
+
},
|
|
635
|
+
{
|
|
636
|
+
code: "tr",
|
|
637
|
+
label: "Turkish",
|
|
638
|
+
nativeName: "Türkçe",
|
|
639
|
+
direction: "ltr",
|
|
640
|
+
week: { dow: 1, doy: 7 },
|
|
641
|
+
buttonText: {
|
|
642
|
+
prev: "geri",
|
|
643
|
+
next: "ileri",
|
|
644
|
+
today: "bugün",
|
|
645
|
+
year: "Yıl",
|
|
646
|
+
month: "Ay",
|
|
647
|
+
week: "Hafta",
|
|
648
|
+
day: "Gün",
|
|
649
|
+
list: "Ajanda"
|
|
650
|
+
},
|
|
651
|
+
weekText: "Hf",
|
|
652
|
+
allDayText: "tüm gün",
|
|
653
|
+
moreLinkText: "daha fazla",
|
|
654
|
+
noEventsText: "Gösterilecek etkinlik yok"
|
|
655
|
+
},
|
|
656
|
+
{
|
|
657
|
+
code: "ar",
|
|
658
|
+
label: "Arabic",
|
|
659
|
+
nativeName: "العربية",
|
|
660
|
+
direction: "rtl",
|
|
661
|
+
week: { dow: 6, doy: 12 },
|
|
662
|
+
buttonText: {
|
|
663
|
+
prev: "السابق",
|
|
664
|
+
next: "التالي",
|
|
665
|
+
today: "اليوم",
|
|
666
|
+
year: "سنة",
|
|
667
|
+
month: "شهر",
|
|
668
|
+
week: "أسبوع",
|
|
669
|
+
day: "يوم",
|
|
670
|
+
list: "أجندة"
|
|
671
|
+
},
|
|
672
|
+
weekText: "أسبوع",
|
|
673
|
+
allDayText: "اليوم كله",
|
|
674
|
+
moreLinkText: "أخرى",
|
|
675
|
+
noEventsText: "أي أحداث لعرض"
|
|
676
|
+
}
|
|
677
|
+
];
|
|
678
|
+
function normalizeLocaleCode(locale) {
|
|
679
|
+
if (!locale || typeof locale !== "string") {
|
|
680
|
+
return "en";
|
|
681
|
+
}
|
|
682
|
+
const cleaned = locale.trim().toLowerCase().replace("_", "-");
|
|
683
|
+
if (!cleaned || cleaned === "auto") {
|
|
684
|
+
return "en";
|
|
685
|
+
}
|
|
686
|
+
const exact = SUPPORTED_CALENDAR_LOCALES.find((l) => l.code === cleaned);
|
|
687
|
+
if (exact) {
|
|
688
|
+
return exact.code;
|
|
689
|
+
}
|
|
690
|
+
const baseLang = cleaned.split("-")[0];
|
|
691
|
+
const baseMatch = SUPPORTED_CALENDAR_LOCALES.find((l) => l.code === baseLang);
|
|
692
|
+
return baseMatch ? baseMatch.code : baseLang || "en";
|
|
693
|
+
}
|
|
694
|
+
function isRtlLocale(locale) {
|
|
695
|
+
const normalized = normalizeLocaleCode(locale);
|
|
696
|
+
const baseLang = normalized.split("-")[0];
|
|
697
|
+
return RTL_LANGUAGE_PREFIXES.has(baseLang);
|
|
698
|
+
}
|
|
699
|
+
function getLocalizedCalendarBundle(locale) {
|
|
700
|
+
const code = normalizeLocaleCode(locale);
|
|
701
|
+
return SUPPORTED_CALENDAR_LOCALES.find((l) => l.code === code) ?? SUPPORTED_CALENDAR_LOCALES[0];
|
|
702
|
+
}
|
|
703
|
+
function resolveFullCalendarLocale(uiLocale, defaultLocaleSetting, runtimeOverrideLocale) {
|
|
704
|
+
if (runtimeOverrideLocale && runtimeOverrideLocale !== "auto" && runtimeOverrideLocale.trim()) {
|
|
705
|
+
return normalizeLocaleCode(runtimeOverrideLocale);
|
|
706
|
+
}
|
|
707
|
+
if (defaultLocaleSetting && defaultLocaleSetting !== "auto" && defaultLocaleSetting.trim()) {
|
|
708
|
+
return normalizeLocaleCode(defaultLocaleSetting);
|
|
709
|
+
}
|
|
710
|
+
return normalizeLocaleCode(uiLocale || "en");
|
|
711
|
+
}
|
|
712
|
+
function resolveCalendarDirection(locale, directionSetting) {
|
|
713
|
+
if (directionSetting === "ltr" || directionSetting === "rtl") {
|
|
714
|
+
return directionSetting;
|
|
715
|
+
}
|
|
716
|
+
return isRtlLocale(locale) ? "rtl" : "ltr";
|
|
717
|
+
}
|
|
718
|
+
function resolveLocaleFirstDay(locale, configuredFirstDay) {
|
|
719
|
+
if (typeof configuredFirstDay === "number" && configuredFirstDay >= 0 && configuredFirstDay <= 6) {
|
|
720
|
+
return configuredFirstDay;
|
|
721
|
+
}
|
|
722
|
+
const bundle = getLocalizedCalendarBundle(locale);
|
|
723
|
+
return bundle.week.dow;
|
|
724
|
+
}
|
|
725
|
+
function buildFullCalendarTimeFormat(timeFormat24h = true) {
|
|
726
|
+
if (timeFormat24h) {
|
|
727
|
+
return {
|
|
728
|
+
hour: "2-digit",
|
|
729
|
+
minute: "2-digit",
|
|
730
|
+
meridiem: false,
|
|
731
|
+
hour12: false
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
return {
|
|
735
|
+
hour: "numeric",
|
|
736
|
+
minute: "2-digit",
|
|
737
|
+
meridiem: "short",
|
|
738
|
+
hour12: true
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
const PLUGIN_ID = "calendar";
|
|
742
|
+
const RRULE_CUSTOM_FIELD_UID = "plugin::calendar.rrule";
|
|
743
|
+
const VALID_FREQUENCIES = [
|
|
744
|
+
"NONE",
|
|
745
|
+
"DAILY",
|
|
746
|
+
"WEEKLY",
|
|
747
|
+
"MONTHLY",
|
|
748
|
+
"YEARLY"
|
|
749
|
+
];
|
|
750
|
+
const JS_DAY_TO_RRULE_DAY = [
|
|
751
|
+
"SU",
|
|
752
|
+
"MO",
|
|
753
|
+
"TU",
|
|
754
|
+
"WE",
|
|
755
|
+
"TH",
|
|
756
|
+
"FR",
|
|
757
|
+
"SA"
|
|
758
|
+
];
|
|
759
|
+
const DAY_LABELS = {
|
|
760
|
+
MO: "Mon",
|
|
761
|
+
TU: "Tue",
|
|
762
|
+
WE: "Wed",
|
|
763
|
+
TH: "Thu",
|
|
764
|
+
FR: "Fri",
|
|
765
|
+
SA: "Sat",
|
|
766
|
+
SU: "Sun"
|
|
767
|
+
};
|
|
768
|
+
const FREQ_UNITS = {
|
|
769
|
+
NONE: ["", "days"],
|
|
770
|
+
DAILY: ["Every day", "days"],
|
|
771
|
+
WEEKLY: ["Every week", "weeks"],
|
|
772
|
+
MONTHLY: ["Every month", "months"],
|
|
773
|
+
YEARLY: ["Every year", "years"]
|
|
774
|
+
};
|
|
775
|
+
function parseBoundedInt(val, min, max) {
|
|
776
|
+
const n = Number.parseInt(val, 10);
|
|
777
|
+
return Number.isFinite(n) && n >= min && n <= max ? n : void 0;
|
|
778
|
+
}
|
|
779
|
+
function parseUntilDate(val) {
|
|
780
|
+
if (/^\d{8}(?:T\d{6}Z?)?$/.test(val)) {
|
|
781
|
+
return `${val.slice(0, 4)}-${val.slice(4, 6)}-${val.slice(6, 8)}`;
|
|
782
|
+
}
|
|
783
|
+
return /^\d{4}-\d{2}-\d{2}/.test(val) ? val.slice(0, 10) : void 0;
|
|
784
|
+
}
|
|
785
|
+
function parseByDayList(val) {
|
|
786
|
+
return val.split(",").map((d) => d.trim().slice(-2)).filter((d) => JS_DAY_TO_RRULE_DAY.includes(d));
|
|
787
|
+
}
|
|
788
|
+
const intAssigner = (key, min, max) => (p, v) => {
|
|
789
|
+
const n = parseBoundedInt(v, min, max);
|
|
790
|
+
if (n !== void 0) p[key] = n;
|
|
791
|
+
};
|
|
792
|
+
const RRULE_PART_ASSIGNERS = {
|
|
793
|
+
RSCALE: (p, v) => {
|
|
794
|
+
if (v) p.rscale = v.toLowerCase();
|
|
795
|
+
},
|
|
796
|
+
FREQ: (p, v) => {
|
|
797
|
+
if (VALID_FREQUENCIES.includes(v)) {
|
|
798
|
+
p.freq = v;
|
|
799
|
+
}
|
|
800
|
+
},
|
|
801
|
+
INTERVAL: intAssigner("interval", 1, 365),
|
|
802
|
+
BYDAY: (p, v) => {
|
|
803
|
+
p.byDay = parseByDayList(v);
|
|
804
|
+
},
|
|
805
|
+
BYMONTH: intAssigner("byMonth", 1, 13),
|
|
806
|
+
BYMONTHDAY: intAssigner("byMonthDay", 1, 31),
|
|
807
|
+
COUNT: intAssigner("count", 1, 1e3),
|
|
808
|
+
UNTIL: (p, v) => {
|
|
809
|
+
const u = parseUntilDate(v);
|
|
810
|
+
if (u !== void 0) p.until = u;
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
function parseRRule(rawRule) {
|
|
814
|
+
const defaultParsed = { freq: "NONE", interval: 1, byDay: [] };
|
|
815
|
+
if (!rawRule || typeof rawRule !== "string") return defaultParsed;
|
|
816
|
+
const cleaned = rawRule.trim().replace(/^RRULE:/i, "");
|
|
817
|
+
if (!cleaned) return defaultParsed;
|
|
818
|
+
const parsed = { ...defaultParsed };
|
|
819
|
+
for (const part of cleaned.split(";")) {
|
|
820
|
+
const [rawKey, rawVal] = part.split("=");
|
|
821
|
+
if (!rawKey || !rawVal) continue;
|
|
822
|
+
RRULE_PART_ASSIGNERS[rawKey.trim().toUpperCase()]?.(
|
|
823
|
+
parsed,
|
|
824
|
+
rawVal.trim().toUpperCase()
|
|
825
|
+
);
|
|
826
|
+
}
|
|
827
|
+
return parsed;
|
|
828
|
+
}
|
|
829
|
+
function buildRRule(parsed) {
|
|
830
|
+
if (!parsed || parsed.freq === "NONE") return "";
|
|
831
|
+
const rscale = parsed.rscale;
|
|
832
|
+
const segments = [];
|
|
833
|
+
if (rscale && rscale !== "gregory" && rscale !== "iso8601") {
|
|
834
|
+
segments.push(`RSCALE=${rscale.toUpperCase()}`);
|
|
835
|
+
}
|
|
836
|
+
segments.push(
|
|
837
|
+
`FREQ=${parsed.freq}`,
|
|
838
|
+
`INTERVAL=${Math.max(1, Math.floor(parsed.interval || 1))}`
|
|
839
|
+
);
|
|
840
|
+
if (parsed.freq === "WEEKLY" && Array.isArray(parsed.byDay)) {
|
|
841
|
+
const validDays = parsed.byDay.filter(
|
|
842
|
+
(d) => JS_DAY_TO_RRULE_DAY.includes(d)
|
|
843
|
+
);
|
|
844
|
+
if (validDays.length > 0) segments.push(`BYDAY=${validDays.join(",")}`);
|
|
845
|
+
}
|
|
846
|
+
if (parsed.freq === "YEARLY" && typeof parsed.byMonth === "number" && parsed.byMonth >= 1 && parsed.byMonth <= 13) {
|
|
847
|
+
segments.push(`BYMONTH=${Math.floor(parsed.byMonth)}`);
|
|
848
|
+
}
|
|
849
|
+
if ((parsed.freq === "MONTHLY" || parsed.freq === "YEARLY") && typeof parsed.byMonthDay === "number" && parsed.byMonthDay >= 1 && parsed.byMonthDay <= 31) {
|
|
850
|
+
segments.push(`BYMONTHDAY=${Math.floor(parsed.byMonthDay)}`);
|
|
851
|
+
}
|
|
852
|
+
if (typeof parsed.count === "number" && parsed.count >= 1) {
|
|
853
|
+
segments.push(`COUNT=${Math.floor(parsed.count)}`);
|
|
854
|
+
} else if (parsed.until && /^\d{4}-\d{2}-\d{2}$/.test(parsed.until)) {
|
|
855
|
+
segments.push(`UNTIL=${parsed.until.replace(/-/g, "")}T235959Z`);
|
|
856
|
+
}
|
|
857
|
+
return segments.join(";");
|
|
858
|
+
}
|
|
859
|
+
function formatRRuleSuffix(parsed) {
|
|
860
|
+
if (parsed.freq === "WEEKLY" && parsed.byDay.length > 0) {
|
|
861
|
+
return ` on ${parsed.byDay.map((d) => DAY_LABELS[d]).join(", ")}`;
|
|
862
|
+
}
|
|
863
|
+
if (parsed.freq === "YEARLY" && parsed.byMonth && parsed.byMonthDay) {
|
|
864
|
+
return ` on month ${parsed.byMonth}, day ${parsed.byMonthDay}`;
|
|
865
|
+
}
|
|
866
|
+
if (parsed.freq === "MONTHLY" && parsed.byMonthDay) {
|
|
867
|
+
return ` on day ${parsed.byMonthDay}`;
|
|
868
|
+
}
|
|
869
|
+
return "";
|
|
870
|
+
}
|
|
871
|
+
function describeRRule(rawRule) {
|
|
872
|
+
const parsed = parseRRule(rawRule);
|
|
873
|
+
if (parsed.freq === "NONE") return "Does not repeat (Single occurrence)";
|
|
874
|
+
const rscalePrefix = parsed.rscale && parsed.rscale !== "gregory" ? `[${parsed.rscale.toUpperCase()}] ` : "";
|
|
875
|
+
const [singular, plural] = FREQ_UNITS[parsed.freq] || ["Every year", "years"];
|
|
876
|
+
const intervalText = parsed.interval === 1 ? singular : `Every ${parsed.interval} ${plural}`;
|
|
877
|
+
const limit = parsed.count ? `, ${parsed.count} times` : parsed.until ? `, until ${parsed.until}` : "";
|
|
878
|
+
return `${rscalePrefix}${intervalText}${formatRRuleSuffix(parsed)}${limit}`;
|
|
879
|
+
}
|
|
880
|
+
function normalizeRRuleObject(obj, defaultTimezone) {
|
|
881
|
+
const rrule = typeof obj.rrule === "string" ? obj.rrule : "";
|
|
882
|
+
const parsed = parseRRule(rrule);
|
|
883
|
+
const timezone = typeof obj.timezone === "string" && obj.timezone ? obj.timezone : defaultTimezone;
|
|
884
|
+
const rscale = typeof obj.rscale === "string" ? obj.rscale : parsed.rscale;
|
|
885
|
+
const exdates = Array.isArray(obj.exdates) ? obj.exdates.filter((d) => typeof d === "string") : [];
|
|
886
|
+
const overrides = obj.overrides && typeof obj.overrides === "object" ? obj.overrides : {};
|
|
887
|
+
return {
|
|
888
|
+
rrule,
|
|
889
|
+
timezone,
|
|
890
|
+
rscale,
|
|
891
|
+
allDay: Boolean(obj.allDay),
|
|
892
|
+
exdates,
|
|
893
|
+
overrides
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
function parseRRuleFieldValue(raw, defaultTimezone = "UTC") {
|
|
897
|
+
const fallback = normalizeRRuleObject({}, defaultTimezone);
|
|
898
|
+
if (!raw) return fallback;
|
|
899
|
+
if (typeof raw === "string") {
|
|
900
|
+
const trimmed = raw.trim();
|
|
901
|
+
if (trimmed.startsWith("{")) {
|
|
902
|
+
try {
|
|
903
|
+
const obj = JSON.parse(trimmed);
|
|
904
|
+
return normalizeRRuleObject(obj, defaultTimezone);
|
|
905
|
+
} catch {
|
|
906
|
+
return fallback;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
const parsed = parseRRule(trimmed);
|
|
910
|
+
return { ...fallback, rrule: trimmed, rscale: parsed.rscale };
|
|
911
|
+
}
|
|
912
|
+
return typeof raw === "object" ? normalizeRRuleObject(raw, defaultTimezone) : fallback;
|
|
913
|
+
}
|
|
914
|
+
const toDateKeyUTC = (date) => date.toISOString().slice(0, 10);
|
|
915
|
+
function buildEffectiveSlot(candidateDate, dateKey, ctx, isRecurring) {
|
|
916
|
+
if (ctx.exdatesSet.has(dateKey)) return null;
|
|
917
|
+
const override = ctx.overridesMap[dateKey];
|
|
918
|
+
if (override?.cancelled) return null;
|
|
919
|
+
const effStart = override?.startDate ? new Date(override.startDate) : candidateDate;
|
|
920
|
+
const effEnd = override?.endDate ? new Date(override.endDate) : new Date(effStart.getTime() + ctx.durationMs);
|
|
921
|
+
if (effEnd.getTime() < ctx.winStartMs || effStart.getTime() > ctx.winEndMs)
|
|
922
|
+
return null;
|
|
923
|
+
return {
|
|
924
|
+
occurrenceDate: dateKey,
|
|
925
|
+
startDate: effStart.toISOString(),
|
|
926
|
+
endDate: effEnd.toISOString(),
|
|
927
|
+
isRecurring,
|
|
928
|
+
isOverridden: Boolean(override),
|
|
929
|
+
...isRecurring ? { rscale: ctx.effectiveRScale } : {},
|
|
930
|
+
override
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
function matchesNonGregorianFrequency(candidateDate, ctx) {
|
|
934
|
+
const calParts = extractCalendarParts(candidateDate, ctx.effectiveRScale);
|
|
935
|
+
if (!calParts || !ctx.baseParts) return false;
|
|
936
|
+
const interval = Math.max(1, ctx.parsed.interval);
|
|
937
|
+
if (ctx.parsed.freq === "MONTHLY") {
|
|
938
|
+
const monthsDiff = (calParts.year - ctx.baseParts.year) * 12 + (calParts.month - ctx.baseParts.month);
|
|
939
|
+
return monthsDiff >= 0 && monthsDiff % interval === 0 && calParts.day === ctx.targetDayOfMonth;
|
|
940
|
+
}
|
|
941
|
+
const yearsDiff = calParts.year - ctx.baseParts.year;
|
|
942
|
+
return yearsDiff >= 0 && yearsDiff % interval === 0 && calParts.month === ctx.targetMonth && calParts.day === ctx.targetDayOfMonth;
|
|
943
|
+
}
|
|
944
|
+
function matchesGregorianFrequency(candidateDate, dayOffset, ctx) {
|
|
945
|
+
const { freq, interval } = ctx.parsed;
|
|
946
|
+
if (freq === "DAILY") return dayOffset % interval === 0;
|
|
947
|
+
if (freq === "WEEKLY") {
|
|
948
|
+
const rruleDay = JS_DAY_TO_RRULE_DAY[candidateDate.getUTCDay()];
|
|
949
|
+
const startWeekDay = (ctx.startObj.getUTCDay() + 6) % 7;
|
|
950
|
+
const weekIdx = Math.floor((dayOffset + startWeekDay) / 7);
|
|
951
|
+
return weekIdx % interval === 0 && ctx.targetWeekdays.includes(rruleDay);
|
|
952
|
+
}
|
|
953
|
+
if (freq === "MONTHLY") {
|
|
954
|
+
const monthsDiff = (candidateDate.getUTCFullYear() - ctx.startObj.getUTCFullYear()) * 12 + (candidateDate.getUTCMonth() - ctx.startObj.getUTCMonth());
|
|
955
|
+
return monthsDiff >= 0 && monthsDiff % interval === 0 && candidateDate.getUTCDate() === ctx.targetDayOfMonth;
|
|
956
|
+
}
|
|
957
|
+
const yearsDiff = candidateDate.getUTCFullYear() - ctx.startObj.getUTCFullYear();
|
|
958
|
+
return yearsDiff >= 0 && yearsDiff % interval === 0 && candidateDate.getUTCMonth() + 1 === ctx.targetMonth && candidateDate.getUTCDate() === ctx.targetDayOfMonth;
|
|
959
|
+
}
|
|
960
|
+
function matchesCandidateRule(candidateDate, dayOffset, ctx) {
|
|
961
|
+
const isMonthOrYear = ctx.parsed.freq === "MONTHLY" || ctx.parsed.freq === "YEARLY";
|
|
962
|
+
if (ctx.isNonGregorianRScale && ctx.effectiveRScale && isMonthOrYear) {
|
|
963
|
+
return matchesNonGregorianFrequency(candidateDate, ctx);
|
|
964
|
+
}
|
|
965
|
+
return matchesGregorianFrequency(candidateDate, dayOffset, ctx);
|
|
966
|
+
}
|
|
967
|
+
function resolveEndDate(startObj, baseEndDate) {
|
|
968
|
+
if (baseEndDate) {
|
|
969
|
+
const parsedEnd = new Date(baseEndDate);
|
|
970
|
+
if (!Number.isNaN(parsedEnd.getTime())) return parsedEnd;
|
|
971
|
+
}
|
|
972
|
+
return new Date(startObj.getTime() + 60 * 60 * 1e3);
|
|
973
|
+
}
|
|
974
|
+
function resolveCalendarTargets(startObj, parsed, effectiveRScale) {
|
|
975
|
+
const isNonGregorianRScale = Boolean(effectiveRScale) && effectiveRScale !== "gregory" && effectiveRScale !== "iso8601";
|
|
976
|
+
const baseParts = isNonGregorianRScale && effectiveRScale ? extractCalendarParts(startObj, effectiveRScale) : null;
|
|
977
|
+
const targetMonth = parsed.byMonth ?? (baseParts ? baseParts.month : startObj.getUTCMonth() + 1);
|
|
978
|
+
const targetDayOfMonth = parsed.byMonthDay ?? (baseParts ? baseParts.day : startObj.getUTCDate());
|
|
979
|
+
const targetWeekdays = parsed.byDay.length > 0 ? parsed.byDay : [JS_DAY_TO_RRULE_DAY[startObj.getUTCDay()]];
|
|
980
|
+
return {
|
|
981
|
+
isNonGregorianRScale,
|
|
982
|
+
baseParts,
|
|
983
|
+
targetMonth,
|
|
984
|
+
targetDayOfMonth,
|
|
985
|
+
targetWeekdays
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
function createSlotContext(params) {
|
|
989
|
+
const { startObj, baseEndDate, recurrence, windowStart, windowEnd } = params;
|
|
990
|
+
const endObj = resolveEndDate(startObj, baseEndDate);
|
|
991
|
+
const parsed = parseRRule(recurrence?.rrule);
|
|
992
|
+
const effectiveRScale = recurrence?.rscale || parsed.rscale;
|
|
993
|
+
const targets = resolveCalendarTargets(startObj, parsed, effectiveRScale);
|
|
994
|
+
const untilMs = parsed.until ? (/* @__PURE__ */ new Date(`${parsed.until}T23:59:59.999Z`)).getTime() : Number.POSITIVE_INFINITY;
|
|
995
|
+
return {
|
|
996
|
+
startObj,
|
|
997
|
+
durationMs: Math.max(0, endObj.getTime() - startObj.getTime()),
|
|
998
|
+
winStartMs: new Date(windowStart).getTime(),
|
|
999
|
+
winEndMs: new Date(windowEnd).getTime(),
|
|
1000
|
+
untilMs,
|
|
1001
|
+
maxCount: parsed.count ?? params.maxOccurrences,
|
|
1002
|
+
maxOccurrences: params.maxOccurrences,
|
|
1003
|
+
parsed,
|
|
1004
|
+
exdatesSet: new Set(recurrence?.exdates ?? []),
|
|
1005
|
+
overridesMap: recurrence?.overrides ?? {},
|
|
1006
|
+
effectiveRScale,
|
|
1007
|
+
...targets,
|
|
1008
|
+
anchorMidnightMs: Date.UTC(
|
|
1009
|
+
startObj.getUTCFullYear(),
|
|
1010
|
+
startObj.getUTCMonth(),
|
|
1011
|
+
startObj.getUTCDate()
|
|
1012
|
+
),
|
|
1013
|
+
timeOffsetMs: startObj.getUTCHours() * 36e5 + startObj.getUTCMinutes() * 6e4 + startObj.getUTCSeconds() * 1e3
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
function scanRecurringSlots(ctx) {
|
|
1017
|
+
const results = [];
|
|
1018
|
+
let generatedCount = 0;
|
|
1019
|
+
const daysToWinEnd = Math.ceil((ctx.winEndMs - ctx.anchorMidnightMs) / 864e5) + 1;
|
|
1020
|
+
const maxDayOffset = Math.min(Math.max(daysToWinEnd, 0), 36500);
|
|
1021
|
+
const startDayOffset = ctx.parsed.count === void 0 && !ctx.isNonGregorianRScale ? Math.max(
|
|
1022
|
+
0,
|
|
1023
|
+
Math.floor(
|
|
1024
|
+
(ctx.winStartMs - ctx.durationMs - ctx.anchorMidnightMs) / 864e5
|
|
1025
|
+
) - 1
|
|
1026
|
+
) : 0;
|
|
1027
|
+
for (let dayOffset = startDayOffset; dayOffset <= maxDayOffset; dayOffset++) {
|
|
1028
|
+
if (generatedCount >= ctx.maxCount || results.length >= ctx.maxOccurrences)
|
|
1029
|
+
break;
|
|
1030
|
+
const candidateDate = new Date(
|
|
1031
|
+
ctx.anchorMidnightMs + dayOffset * 864e5 + ctx.timeOffsetMs
|
|
1032
|
+
);
|
|
1033
|
+
const candidateMs = candidateDate.getTime();
|
|
1034
|
+
if (candidateMs > ctx.untilMs || candidateMs > ctx.winEndMs) break;
|
|
1035
|
+
if (!matchesCandidateRule(candidateDate, dayOffset, ctx)) continue;
|
|
1036
|
+
generatedCount++;
|
|
1037
|
+
const slot = buildEffectiveSlot(
|
|
1038
|
+
candidateDate,
|
|
1039
|
+
toDateKeyUTC(candidateDate),
|
|
1040
|
+
ctx,
|
|
1041
|
+
true
|
|
1042
|
+
);
|
|
1043
|
+
if (slot) results.push(slot);
|
|
1044
|
+
}
|
|
1045
|
+
return results;
|
|
1046
|
+
}
|
|
1047
|
+
function expandRuleSlots(params) {
|
|
1048
|
+
const startObj = new Date(params.baseStartDate);
|
|
1049
|
+
if (Number.isNaN(startObj.getTime())) return [];
|
|
1050
|
+
const ctx = createSlotContext({
|
|
1051
|
+
...params,
|
|
1052
|
+
startObj,
|
|
1053
|
+
maxOccurrences: params.maxOccurrences ?? 366
|
|
1054
|
+
});
|
|
1055
|
+
if (ctx.parsed.freq === "NONE") {
|
|
1056
|
+
const slot = buildEffectiveSlot(
|
|
1057
|
+
startObj,
|
|
1058
|
+
toDateKeyUTC(startObj),
|
|
1059
|
+
ctx,
|
|
1060
|
+
false
|
|
1061
|
+
);
|
|
1062
|
+
return slot ? [slot] : [];
|
|
1063
|
+
}
|
|
1064
|
+
return scanRecurringSlots(ctx);
|
|
1065
|
+
}
|
|
1066
|
+
const CALENDAR_COLOR_PALETTES = {
|
|
1067
|
+
default: {
|
|
1068
|
+
id: "default",
|
|
1069
|
+
label: "Theme Default",
|
|
1070
|
+
lightPrimary: "#0284C7",
|
|
1071
|
+
darkPrimary: "#38BDF8",
|
|
1072
|
+
lightHeaderBg: "#E0F2FE",
|
|
1073
|
+
lightHeaderText: "#0C4A6E",
|
|
1074
|
+
lightTodayBg: "#F0F9FF",
|
|
1075
|
+
lightBadgeBg: "#E0F2FE",
|
|
1076
|
+
lightBadgeText: "#0369A1",
|
|
1077
|
+
lightBadgeBorder: "#7DD3FC",
|
|
1078
|
+
darkHeaderBg: "#1E293B",
|
|
1079
|
+
darkHeaderText: "#E2E8F0",
|
|
1080
|
+
darkTodayBg: "#172554",
|
|
1081
|
+
darkBadgeBg: "#1E293B",
|
|
1082
|
+
darkBadgeText: "#7DD3FC",
|
|
1083
|
+
darkBadgeBorder: "#0369A1"
|
|
1084
|
+
},
|
|
1085
|
+
purple: {
|
|
1086
|
+
id: "purple",
|
|
1087
|
+
label: "Monarch Purple",
|
|
1088
|
+
lightPrimary: "#6750A4",
|
|
1089
|
+
darkPrimary: "#D0BCFF",
|
|
1090
|
+
lightHeaderBg: "#F3EDF7",
|
|
1091
|
+
lightHeaderText: "#381E72",
|
|
1092
|
+
lightTodayBg: "#F7F2FA",
|
|
1093
|
+
lightBadgeBg: "#EADDFF",
|
|
1094
|
+
lightBadgeText: "#4F378B",
|
|
1095
|
+
lightBadgeBorder: "#D0BCFF",
|
|
1096
|
+
darkHeaderBg: "#211F26",
|
|
1097
|
+
darkHeaderText: "#E6E0E9",
|
|
1098
|
+
darkTodayBg: "#2B223C",
|
|
1099
|
+
darkBadgeBg: "#381E72",
|
|
1100
|
+
darkBadgeText: "#EADDFF",
|
|
1101
|
+
darkBadgeBorder: "#4F378B"
|
|
1102
|
+
},
|
|
1103
|
+
blue: {
|
|
1104
|
+
id: "blue",
|
|
1105
|
+
label: "Forma / Pulse Blue",
|
|
1106
|
+
lightPrimary: "#0F6CBD",
|
|
1107
|
+
darkPrimary: "#60A5FA",
|
|
1108
|
+
lightHeaderBg: "#EFF6FC",
|
|
1109
|
+
lightHeaderText: "#0A4478",
|
|
1110
|
+
lightTodayBg: "#F0F7FF",
|
|
1111
|
+
lightBadgeBg: "#DBEAFE",
|
|
1112
|
+
lightBadgeText: "#1E40AF",
|
|
1113
|
+
lightBadgeBorder: "#93C5FD",
|
|
1114
|
+
darkHeaderBg: "#172130",
|
|
1115
|
+
darkHeaderText: "#DBEAFE",
|
|
1116
|
+
darkTodayBg: "#172554",
|
|
1117
|
+
darkBadgeBg: "#1E3A8A",
|
|
1118
|
+
darkBadgeText: "#BFDBFE",
|
|
1119
|
+
darkBadgeBorder: "#2563EB"
|
|
1120
|
+
},
|
|
1121
|
+
indigo: {
|
|
1122
|
+
id: "indigo",
|
|
1123
|
+
label: "Breezy Indigo",
|
|
1124
|
+
lightPrimary: "#4F46E5",
|
|
1125
|
+
darkPrimary: "#818CF8",
|
|
1126
|
+
lightHeaderBg: "#EEF2FF",
|
|
1127
|
+
lightHeaderText: "#312E81",
|
|
1128
|
+
lightTodayBg: "#EEF2FF",
|
|
1129
|
+
lightBadgeBg: "#E0E7FF",
|
|
1130
|
+
lightBadgeText: "#3730A3",
|
|
1131
|
+
lightBadgeBorder: "#A5B4FC",
|
|
1132
|
+
darkHeaderBg: "#1E1B4B",
|
|
1133
|
+
darkHeaderText: "#E0E7FF",
|
|
1134
|
+
darkTodayBg: "#1E1B4B",
|
|
1135
|
+
darkBadgeBg: "#312E81",
|
|
1136
|
+
darkBadgeText: "#C7D2FE",
|
|
1137
|
+
darkBadgeBorder: "#4F46E5"
|
|
1138
|
+
},
|
|
1139
|
+
emerald: {
|
|
1140
|
+
id: "emerald",
|
|
1141
|
+
label: "Emerald Green",
|
|
1142
|
+
lightPrimary: "#059669",
|
|
1143
|
+
darkPrimary: "#34D399",
|
|
1144
|
+
lightHeaderBg: "#ECFDF5",
|
|
1145
|
+
lightHeaderText: "#065F46",
|
|
1146
|
+
lightTodayBg: "#F0FDF4",
|
|
1147
|
+
lightBadgeBg: "#D1FAE5",
|
|
1148
|
+
lightBadgeText: "#065F46",
|
|
1149
|
+
lightBadgeBorder: "#6EE7B7",
|
|
1150
|
+
darkHeaderBg: "#064E3B",
|
|
1151
|
+
darkHeaderText: "#D1FAE5",
|
|
1152
|
+
darkTodayBg: "#064E3B",
|
|
1153
|
+
darkBadgeBg: "#065F46",
|
|
1154
|
+
darkBadgeText: "#A7F3D0",
|
|
1155
|
+
darkBadgeBorder: "#059669"
|
|
1156
|
+
},
|
|
1157
|
+
rose: {
|
|
1158
|
+
id: "rose",
|
|
1159
|
+
label: "Pulse Rose",
|
|
1160
|
+
lightPrimary: "#E11D48",
|
|
1161
|
+
darkPrimary: "#FB7185",
|
|
1162
|
+
lightHeaderBg: "#FFF1F2",
|
|
1163
|
+
lightHeaderText: "#881337",
|
|
1164
|
+
lightTodayBg: "#FFF1F2",
|
|
1165
|
+
lightBadgeBg: "#FFE4E6",
|
|
1166
|
+
lightBadgeText: "#9F1239",
|
|
1167
|
+
lightBadgeBorder: "#FDA4AF",
|
|
1168
|
+
darkHeaderBg: "#3B0A19",
|
|
1169
|
+
darkHeaderText: "#FFE4E6",
|
|
1170
|
+
darkTodayBg: "#4C0519",
|
|
1171
|
+
darkBadgeBg: "#881337",
|
|
1172
|
+
darkBadgeText: "#FECDD3",
|
|
1173
|
+
darkBadgeBorder: "#E11D48"
|
|
1174
|
+
},
|
|
1175
|
+
amber: {
|
|
1176
|
+
id: "amber",
|
|
1177
|
+
label: "Monarch Amber",
|
|
1178
|
+
lightPrimary: "#D97706",
|
|
1179
|
+
darkPrimary: "#FBBF24",
|
|
1180
|
+
lightHeaderBg: "#FFFBEB",
|
|
1181
|
+
lightHeaderText: "#78350F",
|
|
1182
|
+
lightTodayBg: "#FEF3C7",
|
|
1183
|
+
lightBadgeBg: "#FEF3C7",
|
|
1184
|
+
lightBadgeText: "#92400E",
|
|
1185
|
+
lightBadgeBorder: "#FCD34D",
|
|
1186
|
+
darkHeaderBg: "#3B2506",
|
|
1187
|
+
darkHeaderText: "#FEF3C7",
|
|
1188
|
+
darkTodayBg: "#451A03",
|
|
1189
|
+
darkBadgeBg: "#78350F",
|
|
1190
|
+
darkBadgeText: "#FDE68A",
|
|
1191
|
+
darkBadgeBorder: "#D97706"
|
|
1192
|
+
}
|
|
1193
|
+
};
|
|
1194
|
+
function detectFromHexLuminance(strapiNeutral0) {
|
|
1195
|
+
if (typeof strapiNeutral0 !== "string" || !strapiNeutral0.startsWith("#")) {
|
|
1196
|
+
return null;
|
|
1197
|
+
}
|
|
1198
|
+
const hex = strapiNeutral0.slice(1).trim();
|
|
1199
|
+
const r = hex.length === 3 ? Number.parseInt(hex[0] + hex[0], 16) : Number.parseInt(hex.slice(0, 2), 16);
|
|
1200
|
+
if (Number.isNaN(r)) return null;
|
|
1201
|
+
return r < 128 ? "dark" : "light";
|
|
1202
|
+
}
|
|
1203
|
+
function detectFromBrowserEnvironment() {
|
|
1204
|
+
if (typeof window === "undefined") return "light";
|
|
1205
|
+
try {
|
|
1206
|
+
const storedTheme = window.localStorage?.getItem("STRAPI_THEME");
|
|
1207
|
+
if (storedTheme === "dark" || storedTheme === "light") {
|
|
1208
|
+
return storedTheme;
|
|
1209
|
+
}
|
|
1210
|
+
} catch {
|
|
1211
|
+
}
|
|
1212
|
+
return window.matchMedia?.("(prefers-color-scheme: dark)")?.matches ? "dark" : "light";
|
|
1213
|
+
}
|
|
1214
|
+
function detectEffectiveColorScheme(colorScheme = "auto", strapiNeutral0) {
|
|
1215
|
+
if (colorScheme === "dark" || colorScheme === "light") {
|
|
1216
|
+
return colorScheme;
|
|
1217
|
+
}
|
|
1218
|
+
return detectFromHexLuminance(strapiNeutral0) ?? detectFromBrowserEnvironment();
|
|
1219
|
+
}
|
|
1220
|
+
const CALENDAR_THEME_PRESETS = {
|
|
1221
|
+
breeze: {
|
|
1222
|
+
id: "breeze",
|
|
1223
|
+
label: "Breezy (Modern Default)",
|
|
1224
|
+
description: "FullCalendar Breezy style — airy sky & slate grid, soft-tinted event pills, and rounded-lg container",
|
|
1225
|
+
category: "modern",
|
|
1226
|
+
fontFamily: "'Inter Variable', Inter, system-ui, sans-serif",
|
|
1227
|
+
containerRadius: "10px",
|
|
1228
|
+
containerShadow: "0 1px 3px rgba(15, 23, 42, 0.06)",
|
|
1229
|
+
eventRadius: "6px",
|
|
1230
|
+
eventStyle: "tinted",
|
|
1231
|
+
dayNumberAlign: "start",
|
|
1232
|
+
todayHeaderAccent: "pill",
|
|
1233
|
+
primaryColor: "#0284C7",
|
|
1234
|
+
headerBg: "#F8FAFC",
|
|
1235
|
+
headerText: "#0F172A",
|
|
1236
|
+
cellBg: "#FFFFFF",
|
|
1237
|
+
cellAltBg: "#F8FAFC",
|
|
1238
|
+
cellHoverBg: "#F0F9FF",
|
|
1239
|
+
cellText: "#111827",
|
|
1240
|
+
cellMutedText: "#6B7280",
|
|
1241
|
+
borderColor: "#E5E7EB",
|
|
1242
|
+
todayBg: "#F0F9FF",
|
|
1243
|
+
todayBorder: "#0284C7",
|
|
1244
|
+
todayNumberBg: "#0284C7",
|
|
1245
|
+
todayNumberText: "#FFFFFF",
|
|
1246
|
+
badgeBg: "#E0F2FE",
|
|
1247
|
+
badgeText: "#0369A1",
|
|
1248
|
+
badgeBorder: "#7DD3FC"
|
|
1249
|
+
},
|
|
1250
|
+
monarch: {
|
|
1251
|
+
id: "monarch",
|
|
1252
|
+
label: "Monarch (Material 3)",
|
|
1253
|
+
description: "FullCalendar Monarch style — pill-shaped headers, centered day badges, tonal surfaces & rounded-xl frame",
|
|
1254
|
+
category: "modern",
|
|
1255
|
+
fontFamily: "'Roboto Variable', Roboto, system-ui, sans-serif",
|
|
1256
|
+
containerRadius: "14px",
|
|
1257
|
+
containerShadow: "0 2px 8px rgba(103, 80, 164, 0.08)",
|
|
1258
|
+
eventRadius: "6px",
|
|
1259
|
+
eventStyle: "solid",
|
|
1260
|
+
dayNumberAlign: "center",
|
|
1261
|
+
todayHeaderAccent: "pill",
|
|
1262
|
+
primaryColor: "#6750A4",
|
|
1263
|
+
headerBg: "#F7F2FA",
|
|
1264
|
+
headerText: "#1D1B20",
|
|
1265
|
+
cellBg: "#FFFFFF",
|
|
1266
|
+
cellAltBg: "#FAF8FC",
|
|
1267
|
+
cellHoverBg: "#F3EDF7",
|
|
1268
|
+
cellText: "#0C090C",
|
|
1269
|
+
cellMutedText: "#79747E",
|
|
1270
|
+
borderColor: "#DEDCE1",
|
|
1271
|
+
todayBg: "#F7F2FA",
|
|
1272
|
+
todayBorder: "#6750A4",
|
|
1273
|
+
todayNumberBg: "#895264",
|
|
1274
|
+
todayNumberText: "#FFFFFF",
|
|
1275
|
+
badgeBg: "#EADDFF",
|
|
1276
|
+
badgeText: "#4F378B",
|
|
1277
|
+
badgeBorder: "#D0BCFF"
|
|
1278
|
+
},
|
|
1279
|
+
forma: {
|
|
1280
|
+
id: "forma",
|
|
1281
|
+
label: "Forma (Fluent Enterprise)",
|
|
1282
|
+
description: "FullCalendar Forma style — architectural 6px left-accent event bars, top-accent Today bar & crisp 4px corners",
|
|
1283
|
+
category: "modern",
|
|
1284
|
+
fontFamily: "'Noto Sans Variable', 'Noto Sans', system-ui, sans-serif",
|
|
1285
|
+
containerRadius: "4px",
|
|
1286
|
+
containerShadow: "0 1px 2px rgba(0, 0, 0, 0.06)",
|
|
1287
|
+
eventRadius: "3px",
|
|
1288
|
+
eventStyle: "accent-bar",
|
|
1289
|
+
dayNumberAlign: "start",
|
|
1290
|
+
todayHeaderAccent: "top-bar",
|
|
1291
|
+
primaryColor: "#0F6CBD",
|
|
1292
|
+
headerBg: "#FFFFFF",
|
|
1293
|
+
headerText: "#030712",
|
|
1294
|
+
cellBg: "#FFFFFF",
|
|
1295
|
+
cellAltBg: "#F9FAFB",
|
|
1296
|
+
cellHoverBg: "#EFF6FC",
|
|
1297
|
+
cellText: "#030712",
|
|
1298
|
+
cellMutedText: "#6B7280",
|
|
1299
|
+
borderColor: "#E0E0E0",
|
|
1300
|
+
todayBg: "#F5F9FF",
|
|
1301
|
+
todayBorder: "#0F6CBD",
|
|
1302
|
+
todayNumberBg: "#0F6CBD",
|
|
1303
|
+
todayNumberText: "#FFFFFF",
|
|
1304
|
+
badgeBg: "#EFF6FC",
|
|
1305
|
+
badgeText: "#0F6CBD",
|
|
1306
|
+
badgeBorder: "#B4D6FA"
|
|
1307
|
+
},
|
|
1308
|
+
pulse: {
|
|
1309
|
+
id: "pulse",
|
|
1310
|
+
label: "Pulse (Apple / macOS)",
|
|
1311
|
+
description: "FullCalendar Pulse style — right-aligned day numbers, crimson Today circle, elevated controls & dotted minor slots",
|
|
1312
|
+
category: "modern",
|
|
1313
|
+
fontFamily: "ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif",
|
|
1314
|
+
containerRadius: "8px",
|
|
1315
|
+
containerShadow: "0 1px 3px rgba(0, 0, 0, 0.12)",
|
|
1316
|
+
eventRadius: "6px",
|
|
1317
|
+
eventStyle: "solid",
|
|
1318
|
+
dayNumberAlign: "end",
|
|
1319
|
+
todayHeaderAccent: "pill",
|
|
1320
|
+
primaryColor: "#117AFF",
|
|
1321
|
+
headerBg: "#FFFFFF",
|
|
1322
|
+
headerText: "#232D3E",
|
|
1323
|
+
cellBg: "#FFFFFF",
|
|
1324
|
+
cellAltBg: "#F9FAFB",
|
|
1325
|
+
cellHoverBg: "#F0F7FF",
|
|
1326
|
+
cellText: "#232D3E",
|
|
1327
|
+
cellMutedText: "#545C6C",
|
|
1328
|
+
borderColor: "#E5E7EB",
|
|
1329
|
+
todayBg: "#FFF5F5",
|
|
1330
|
+
todayBorder: "#FD453B",
|
|
1331
|
+
todayNumberBg: "#FD453B",
|
|
1332
|
+
todayNumberText: "#FFFFFF",
|
|
1333
|
+
badgeBg: "#EFF6FF",
|
|
1334
|
+
badgeText: "#1D4ED8",
|
|
1335
|
+
badgeBorder: "#BFDBFE"
|
|
1336
|
+
},
|
|
1337
|
+
classic: {
|
|
1338
|
+
id: "classic",
|
|
1339
|
+
label: "Classic (FullCalendar Standard)",
|
|
1340
|
+
description: "Traditional FullCalendar v6/v7 studio grid with slate headers, warm yellow Today cell tint & solid events",
|
|
1341
|
+
category: "modern",
|
|
1342
|
+
fontFamily: "ui-sans-serif, system-ui, sans-serif",
|
|
1343
|
+
containerRadius: "6px",
|
|
1344
|
+
containerShadow: "none",
|
|
1345
|
+
eventRadius: "4px",
|
|
1346
|
+
eventStyle: "solid",
|
|
1347
|
+
dayNumberAlign: "end",
|
|
1348
|
+
todayHeaderAccent: "tint",
|
|
1349
|
+
primaryColor: "#3788D8",
|
|
1350
|
+
headerBg: "#F8FAFC",
|
|
1351
|
+
headerText: "#1E293B",
|
|
1352
|
+
cellBg: "#FFFFFF",
|
|
1353
|
+
cellAltBg: "#F8FAFC",
|
|
1354
|
+
cellHoverBg: "#F1F5F9",
|
|
1355
|
+
cellText: "#030712",
|
|
1356
|
+
cellMutedText: "#6B7280",
|
|
1357
|
+
borderColor: "#DDDDDD",
|
|
1358
|
+
todayBg: "#FEFCE8",
|
|
1359
|
+
todayBorder: "#EAB308",
|
|
1360
|
+
todayNumberBg: "#334155",
|
|
1361
|
+
todayNumberText: "#FFFFFF",
|
|
1362
|
+
badgeBg: "#F1F5F9",
|
|
1363
|
+
badgeText: "#334155",
|
|
1364
|
+
badgeBorder: "#CBD5E1"
|
|
1365
|
+
},
|
|
1366
|
+
shadcn: {
|
|
1367
|
+
id: "shadcn",
|
|
1368
|
+
label: "Shadcn UI (Zinc Minimal)",
|
|
1369
|
+
description: "Design-system theme matching Shadcn UI — ultra-clean 1px zinc borders, monochrome primary & tinted badges",
|
|
1370
|
+
category: "design-system",
|
|
1371
|
+
fontFamily: "'Inter Variable', Inter, system-ui, sans-serif",
|
|
1372
|
+
containerRadius: "8px",
|
|
1373
|
+
containerShadow: "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
|
|
1374
|
+
eventRadius: "5px",
|
|
1375
|
+
eventStyle: "tinted",
|
|
1376
|
+
dayNumberAlign: "start",
|
|
1377
|
+
todayHeaderAccent: "pill",
|
|
1378
|
+
primaryColor: "#18181B",
|
|
1379
|
+
headerBg: "#FAFAFA",
|
|
1380
|
+
headerText: "#09090B",
|
|
1381
|
+
cellBg: "#FFFFFF",
|
|
1382
|
+
cellAltBg: "#FAFAFA",
|
|
1383
|
+
cellHoverBg: "#F4F4F5",
|
|
1384
|
+
cellText: "#09090B",
|
|
1385
|
+
cellMutedText: "#71717A",
|
|
1386
|
+
borderColor: "#E4E4E7",
|
|
1387
|
+
todayBg: "#F4F4F5",
|
|
1388
|
+
todayBorder: "#18181B",
|
|
1389
|
+
todayNumberBg: "#18181B",
|
|
1390
|
+
todayNumberText: "#FAFAFA",
|
|
1391
|
+
badgeBg: "#F4F4F5",
|
|
1392
|
+
badgeText: "#18181B",
|
|
1393
|
+
badgeBorder: "#D4D4D8"
|
|
1394
|
+
},
|
|
1395
|
+
mui: {
|
|
1396
|
+
id: "mui",
|
|
1397
|
+
label: "Material UI (MUI Paper)",
|
|
1398
|
+
description: "Design-system theme matching Material UI — Paper surface elevation, Roboto uppercase headers & Material blue",
|
|
1399
|
+
category: "design-system",
|
|
1400
|
+
fontFamily: "'Roboto Variable', Roboto, Helvetica, Arial, sans-serif",
|
|
1401
|
+
containerRadius: "8px",
|
|
1402
|
+
containerShadow: "0px 2px 4px -1px rgba(0,0,0,0.08), 0px 4px 5px 0px rgba(0,0,0,0.05)",
|
|
1403
|
+
eventRadius: "4px",
|
|
1404
|
+
eventStyle: "solid",
|
|
1405
|
+
dayNumberAlign: "center",
|
|
1406
|
+
todayHeaderAccent: "pill",
|
|
1407
|
+
primaryColor: "#1976D2",
|
|
1408
|
+
headerBg: "#F5F7FA",
|
|
1409
|
+
headerText: "#1E293B",
|
|
1410
|
+
cellBg: "#FFFFFF",
|
|
1411
|
+
cellAltBg: "#FAFAFA",
|
|
1412
|
+
cellHoverBg: "#E3F2FD",
|
|
1413
|
+
cellText: "#212121",
|
|
1414
|
+
cellMutedText: "#757575",
|
|
1415
|
+
borderColor: "#E0E0E0",
|
|
1416
|
+
todayBg: "#E3F2FD",
|
|
1417
|
+
todayBorder: "#1976D2",
|
|
1418
|
+
todayNumberBg: "#1976D2",
|
|
1419
|
+
todayNumberText: "#FFFFFF",
|
|
1420
|
+
badgeBg: "#E3F2FD",
|
|
1421
|
+
badgeText: "#1565C0",
|
|
1422
|
+
badgeBorder: "#90CAF9"
|
|
1423
|
+
},
|
|
1424
|
+
emerald: {
|
|
1425
|
+
id: "emerald",
|
|
1426
|
+
label: "Emerald Mosque",
|
|
1427
|
+
description: "Lush emerald & teal palette inspired by traditional Islamic architecture",
|
|
1428
|
+
category: "heritage",
|
|
1429
|
+
fontFamily: "'Inter Variable', Inter, system-ui, sans-serif",
|
|
1430
|
+
containerRadius: "10px",
|
|
1431
|
+
containerShadow: "0 1px 3px rgba(13, 148, 136, 0.08)",
|
|
1432
|
+
eventRadius: "6px",
|
|
1433
|
+
eventStyle: "solid",
|
|
1434
|
+
dayNumberAlign: "start",
|
|
1435
|
+
todayHeaderAccent: "pill",
|
|
1436
|
+
primaryColor: "#0D9488",
|
|
1437
|
+
headerBg: "#CCFBF1",
|
|
1438
|
+
headerText: "#115E59",
|
|
1439
|
+
cellBg: "#FFFFFF",
|
|
1440
|
+
cellAltBg: "#F0FDFA",
|
|
1441
|
+
cellHoverBg: "#CCFBF1",
|
|
1442
|
+
cellText: "#0F172A",
|
|
1443
|
+
cellMutedText: "#475569",
|
|
1444
|
+
borderColor: "#99F6E4",
|
|
1445
|
+
todayBg: "#F0FDFA",
|
|
1446
|
+
todayBorder: "#0D9488",
|
|
1447
|
+
todayNumberBg: "#0D9488",
|
|
1448
|
+
todayNumberText: "#FFFFFF",
|
|
1449
|
+
badgeBg: "#CCFBF1",
|
|
1450
|
+
badgeText: "#0F766E",
|
|
1451
|
+
badgeBorder: "#5EEAD4"
|
|
1452
|
+
},
|
|
1453
|
+
sand: {
|
|
1454
|
+
id: "sand",
|
|
1455
|
+
label: "Warm Sand & Gold",
|
|
1456
|
+
description: "Warm parchment & amber tones for high-comfort reading",
|
|
1457
|
+
category: "heritage",
|
|
1458
|
+
fontFamily: "'Noto Sans Variable', 'Noto Sans', system-ui, sans-serif",
|
|
1459
|
+
containerRadius: "10px",
|
|
1460
|
+
containerShadow: "0 1px 3px rgba(180, 83, 9, 0.08)",
|
|
1461
|
+
eventRadius: "6px",
|
|
1462
|
+
eventStyle: "solid",
|
|
1463
|
+
dayNumberAlign: "start",
|
|
1464
|
+
todayHeaderAccent: "pill",
|
|
1465
|
+
primaryColor: "#B45309",
|
|
1466
|
+
headerBg: "#FEF3C7",
|
|
1467
|
+
headerText: "#78350F",
|
|
1468
|
+
cellBg: "#FFFBEB",
|
|
1469
|
+
cellAltBg: "#FEF9C3",
|
|
1470
|
+
cellHoverBg: "#FEF3C7",
|
|
1471
|
+
cellText: "#1C1917",
|
|
1472
|
+
cellMutedText: "#78716C",
|
|
1473
|
+
borderColor: "#FDE68A",
|
|
1474
|
+
todayBg: "#FEF3C7",
|
|
1475
|
+
todayBorder: "#D97706",
|
|
1476
|
+
todayNumberBg: "#B45309",
|
|
1477
|
+
todayNumberText: "#FFFFFF",
|
|
1478
|
+
badgeBg: "#FEF3C7",
|
|
1479
|
+
badgeText: "#92400E",
|
|
1480
|
+
badgeBorder: "#FCD34D"
|
|
1481
|
+
},
|
|
1482
|
+
midnight: {
|
|
1483
|
+
id: "midnight",
|
|
1484
|
+
label: "Midnight Slate",
|
|
1485
|
+
description: "Deep dark-mode slate grid with luminous sky accents",
|
|
1486
|
+
category: "heritage",
|
|
1487
|
+
fontFamily: "'Inter Variable', Inter, system-ui, sans-serif",
|
|
1488
|
+
containerRadius: "10px",
|
|
1489
|
+
containerShadow: "0 4px 12px rgba(2, 6, 23, 0.35)",
|
|
1490
|
+
eventRadius: "6px",
|
|
1491
|
+
eventStyle: "tinted",
|
|
1492
|
+
dayNumberAlign: "start",
|
|
1493
|
+
todayHeaderAccent: "pill",
|
|
1494
|
+
primaryColor: "#38BDF8",
|
|
1495
|
+
headerBg: "#1E293B",
|
|
1496
|
+
headerText: "#E2E8F0",
|
|
1497
|
+
cellBg: "#0F172A",
|
|
1498
|
+
cellAltBg: "#162032",
|
|
1499
|
+
cellHoverBg: "#1E293B",
|
|
1500
|
+
cellText: "#F8FAFC",
|
|
1501
|
+
cellMutedText: "#94A3B8",
|
|
1502
|
+
borderColor: "#334155",
|
|
1503
|
+
todayBg: "#172554",
|
|
1504
|
+
todayBorder: "#38BDF8",
|
|
1505
|
+
todayNumberBg: "#0284C7",
|
|
1506
|
+
todayNumberText: "#FFFFFF",
|
|
1507
|
+
badgeBg: "#1E293B",
|
|
1508
|
+
badgeText: "#7DD3FC",
|
|
1509
|
+
badgeBorder: "#0369A1"
|
|
1510
|
+
}
|
|
1511
|
+
};
|
|
1512
|
+
function applyDarkSurfaceOverride(base, themeKey) {
|
|
1513
|
+
return {
|
|
1514
|
+
...base,
|
|
1515
|
+
headerBg: themeKey === "monarch" ? "#141218" : "#111827",
|
|
1516
|
+
headerText: "#F3F4F6",
|
|
1517
|
+
cellBg: "#030712",
|
|
1518
|
+
cellAltBg: "#0B0F19",
|
|
1519
|
+
cellHoverBg: "#1F2937",
|
|
1520
|
+
cellText: "#F9FAFB",
|
|
1521
|
+
cellMutedText: "#9CA3AF",
|
|
1522
|
+
borderColor: themeKey === "forma" ? "#2A2A2A" : "#1F2937",
|
|
1523
|
+
todayBg: "#172554",
|
|
1524
|
+
todayBorder: base.primaryColor,
|
|
1525
|
+
todayNumberBg: base.primaryColor,
|
|
1526
|
+
todayNumberText: "#FFFFFF",
|
|
1527
|
+
badgeBg: "#1E293B",
|
|
1528
|
+
badgeText: "#E2E8F0",
|
|
1529
|
+
badgeBorder: "#334155"
|
|
1530
|
+
};
|
|
1531
|
+
}
|
|
1532
|
+
function selectPaletteColors(palette, isDark) {
|
|
1533
|
+
if (isDark) {
|
|
1534
|
+
return {
|
|
1535
|
+
primary: palette.darkPrimary,
|
|
1536
|
+
headerBg: palette.darkHeaderBg,
|
|
1537
|
+
headerText: palette.darkHeaderText,
|
|
1538
|
+
todayBg: palette.darkTodayBg,
|
|
1539
|
+
badgeBg: palette.darkBadgeBg,
|
|
1540
|
+
badgeText: palette.darkBadgeText,
|
|
1541
|
+
badgeBorder: palette.darkBadgeBorder
|
|
1542
|
+
};
|
|
1543
|
+
}
|
|
1544
|
+
return {
|
|
1545
|
+
primary: palette.lightPrimary,
|
|
1546
|
+
headerBg: palette.lightHeaderBg,
|
|
1547
|
+
headerText: palette.lightHeaderText,
|
|
1548
|
+
todayBg: palette.lightTodayBg,
|
|
1549
|
+
badgeBg: palette.lightBadgeBg,
|
|
1550
|
+
badgeText: palette.lightBadgeText,
|
|
1551
|
+
badgeBorder: palette.lightBadgeBorder
|
|
1552
|
+
};
|
|
1553
|
+
}
|
|
1554
|
+
function applyPaletteOverride(resolved, paletteKey, isDark) {
|
|
1555
|
+
const palette = CALENDAR_COLOR_PALETTES[paletteKey];
|
|
1556
|
+
if (!palette) return resolved;
|
|
1557
|
+
const c = selectPaletteColors(palette, isDark);
|
|
1558
|
+
return {
|
|
1559
|
+
...resolved,
|
|
1560
|
+
primaryColor: c.primary,
|
|
1561
|
+
headerBg: c.headerBg,
|
|
1562
|
+
headerText: c.headerText,
|
|
1563
|
+
todayBg: c.todayBg,
|
|
1564
|
+
todayBorder: c.primary,
|
|
1565
|
+
todayNumberBg: c.primary,
|
|
1566
|
+
todayNumberText: isDark && paletteKey === "purple" ? "#381E72" : "#FFFFFF",
|
|
1567
|
+
badgeBg: c.badgeBg,
|
|
1568
|
+
badgeText: c.badgeText,
|
|
1569
|
+
badgeBorder: c.badgeBorder
|
|
1570
|
+
};
|
|
1571
|
+
}
|
|
1572
|
+
function resolveCalendarTheme(themeKey = "breeze", colorScheme = "auto", paletteKey = "default", strapiNeutral0) {
|
|
1573
|
+
const base = CALENDAR_THEME_PRESETS[themeKey] ?? CALENDAR_THEME_PRESETS.breeze;
|
|
1574
|
+
const effectiveScheme = detectEffectiveColorScheme(
|
|
1575
|
+
colorScheme,
|
|
1576
|
+
strapiNeutral0
|
|
1577
|
+
);
|
|
1578
|
+
const isDark = effectiveScheme === "dark" || themeKey === "midnight";
|
|
1579
|
+
const surfaced = isDark && themeKey !== "midnight" ? applyDarkSurfaceOverride(base, themeKey) : { ...base };
|
|
1580
|
+
return paletteKey !== "default" ? applyPaletteOverride(surfaced, paletteKey, isDark) : surfaced;
|
|
1581
|
+
}
|
|
1582
|
+
exports.CALENDAR_COLOR_PALETTES = CALENDAR_COLOR_PALETTES;
|
|
1583
|
+
exports.CALENDAR_THEME_PRESETS = CALENDAR_THEME_PRESETS;
|
|
1584
|
+
exports.PLUGIN_ID = PLUGIN_ID;
|
|
1585
|
+
exports.RRULE_CUSTOM_FIELD_UID = RRULE_CUSTOM_FIELD_UID;
|
|
1586
|
+
exports.SUPPORTED_CALENDAR_LOCALES = SUPPORTED_CALENDAR_LOCALES;
|
|
1587
|
+
exports.SUPPORTED_INTL_CALENDARS = SUPPORTED_INTL_CALENDARS;
|
|
1588
|
+
exports.applyDayOffset = applyDayOffset;
|
|
1589
|
+
exports.buildCalendarLinks = buildCalendarLinks;
|
|
1590
|
+
exports.buildFullCalendarTimeFormat = buildFullCalendarTimeFormat;
|
|
1591
|
+
exports.buildRRule = buildRRule;
|
|
1592
|
+
exports.describeRRule = describeRRule;
|
|
1593
|
+
exports.detectEffectiveColorScheme = detectEffectiveColorScheme;
|
|
1594
|
+
exports.expandRuleSlots = expandRuleSlots;
|
|
1595
|
+
exports.extractCalendarParts = extractCalendarParts;
|
|
1596
|
+
exports.foldIcsLine = foldIcsLine;
|
|
1597
|
+
exports.formatDateInCalendarSystem = formatDateInCalendarSystem;
|
|
1598
|
+
exports.generateIcsCalendar = generateIcsCalendar;
|
|
1599
|
+
exports.getLocalizedCalendarBundle = getLocalizedCalendarBundle;
|
|
1600
|
+
exports.getRegisteredCalendarFormatters = getRegisteredCalendarFormatters;
|
|
1601
|
+
exports.isRtlLocale = isRtlLocale;
|
|
1602
|
+
exports.normalizeLocaleCode = normalizeLocaleCode;
|
|
1603
|
+
exports.parseRRule = parseRRule;
|
|
1604
|
+
exports.parseRRuleFieldValue = parseRRuleFieldValue;
|
|
1605
|
+
exports.registerCalendarSystemFormatter = registerCalendarSystemFormatter;
|
|
1606
|
+
exports.resolveCalendarDirection = resolveCalendarDirection;
|
|
1607
|
+
exports.resolveCalendarTheme = resolveCalendarTheme;
|
|
1608
|
+
exports.resolveFullCalendarLocale = resolveFullCalendarLocale;
|
|
1609
|
+
exports.resolveLocaleFirstDay = resolveLocaleFirstDay;
|
|
1610
|
+
exports.resolveOccurrenceAlternateDates = resolveOccurrenceAlternateDates;
|
|
1611
|
+
exports.toIcsTimestamp = toIcsTimestamp;
|
|
1612
|
+
exports.unregisterCalendarSystemFormatter = unregisterCalendarSystemFormatter;
|