UNPKG

@event-calendar/core

Version:

Full-sized drag & drop event calendar with resource & timeline views

1,540 lines 218 kB
/*! * EventCalendar v4.5.1 * https://github.com/vkurko/calendar */ import { tick, getContext, untrack, setContext, onMount, mount, unmount } from "svelte"; import * as $ from "svelte/internal/client"; import "svelte/internal/disclose-version"; import { derived, writable, readable, get } from "svelte/store"; import "svelte/internal/flags/legacy"; import { SvelteSet } from "svelte/reactivity"; function keyEnter(fn) { return function(e) { return e.key === "Enter" || e.key === " " && !e.preventDefault() ? fn.call(this, e) : void 0; }; } function setContent(node, content) { let actions = { update(content2) { if (typeof content2 == "string") { node.innerText = content2; } else if (content2 == null ? void 0 : content2.domNodes) { node.replaceChildren(...content2.domNodes); } else if (content2 == null ? void 0 : content2.html) { node.innerHTML = content2.html; } } }; actions.update(content); return actions; } function outsideEvent(node, type) { const handlePointerDown = (jsEvent) => { if (node && !node.contains(jsEvent.target)) { node.dispatchEvent( new CustomEvent(type + "outside", { detail: { jsEvent } }) ); } }; document.addEventListener(type, handlePointerDown, true); return { destroy() { document.removeEventListener(type, handlePointerDown, true); } }; } function observeResize(node, callback) { let resizeObserver = new ResizeObserver((entries2) => { for (let entry of entries2) { callback(entry); } }); resizeObserver.observe(node); return { destroy() { resizeObserver.unobserve(node); } }; } const DAY_IN_SECONDS = 86400; function createDate(input = void 0) { if (input !== void 0) { return input instanceof Date ? _fromLocalDate(input) : _fromISOString(input); } return _fromLocalDate(/* @__PURE__ */ new Date()); } function createDuration(input) { if (typeof input === "number") { input = { seconds: input }; } else if (typeof input === "string") { let seconds = 0, exp = 2; for (let part of input.split(":", 3)) { seconds += parseInt(part, 10) * Math.pow(60, exp--); } input = { seconds }; } else if (input instanceof Date) { input = { hours: input.getUTCHours(), minutes: input.getUTCMinutes(), seconds: input.getUTCSeconds() }; } let weeks = input.weeks || input.week || 0; return { years: input.years || input.year || 0, months: input.months || input.month || 0, days: weeks * 7 + (input.days || input.day || 0), seconds: (input.hours || input.hour || 0) * 60 * 60 + (input.minutes || input.minute || 0) * 60 + (input.seconds || input.second || 0), inWeeks: !!weeks }; } function cloneDate(date) { return new Date(date.getTime()); } function addDuration(date, duration, x = 1) { date.setUTCFullYear(date.getUTCFullYear() + x * duration.years); let month = date.getUTCMonth() + x * duration.months; date.setUTCMonth(month); month %= 12; if (month < 0) { month += 12; } while (date.getUTCMonth() !== month) { subtractDay(date); } date.setUTCDate(date.getUTCDate() + x * duration.days); date.setUTCSeconds(date.getUTCSeconds() + x * duration.seconds); return date; } function subtractDuration(date, duration, x = 1) { return addDuration(date, duration, -x); } function addDay(date, x = 1) { date.setUTCDate(date.getUTCDate() + x); return date; } function subtractDay(date, x = 1) { return addDay(date, -x); } function setMidnight(date) { date.setUTCHours(0, 0, 0, 0); return date; } function toLocalDate(date) { return new Date( date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds() ); } function toISOString(date, len = 19) { return date.toISOString().substring(0, len); } function datesEqual(date1, ...dates2) { return dates2.every((date2) => date1.getTime() === date2.getTime()); } function nextClosestDay(date, day) { let diff2 = day - date.getUTCDay(); date.setUTCDate(date.getUTCDate() + (diff2 >= 0 ? diff2 : diff2 + 7)); return date; } function prevClosestDay(date, day) { let diff2 = day - date.getUTCDay(); date.setUTCDate(date.getUTCDate() + (diff2 <= 0 ? diff2 : diff2 - 7)); return date; } function noTimePart(date) { return typeof date === "string" && date.length <= 10; } function copyTime(toDate, fromDate) { toDate.setUTCHours(fromDate.getUTCHours(), fromDate.getUTCMinutes(), fromDate.getUTCSeconds(), 0); return toDate; } function toSeconds(duration) { return duration.seconds; } function nextDate(date, duration) { addDuration(date, duration); return date; } function prevDate(date, duration, hiddenDays) { subtractDuration(date, duration); if (hiddenDays.length && hiddenDays.length < 7) { while (hiddenDays.includes(date.getUTCDay())) { subtractDay(date); } } return date; } function getWeekNumber(date, firstDay) { date = cloneDate(date); if (firstDay == 0) { date.setUTCDate(date.getUTCDate() + 6 - date.getUTCDay()); } else { date.setUTCDate(date.getUTCDate() + 4 - (date.getUTCDay() || 7)); } let yearStart = new Date(Date.UTC(date.getUTCFullYear(), 0, 1)); return Math.ceil(((date - yearStart) / 1e3 / DAY_IN_SECONDS + 1) / 7); } function _fromLocalDate(date) { return new Date(Date.UTC( date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds() )); } function _fromISOString(str) { const parts = str.match(/\d+/g); return new Date(Date.UTC( Number(parts[0]), Number(parts[1]) - 1, Number(parts[2]), Number(parts[3] || 0), Number(parts[4] || 0), Number(parts[5] || 0) )); } function assign(...args) { return Object.assign(...args); } function keys(object) { return Object.keys(object); } function entries(object) { return Object.entries(object); } function floor(value) { return Math.floor(value); } function ceil(value) { return Math.ceil(value); } function min(...args) { return Math.min(...args); } function max(...args) { return Math.max(...args); } function symbol() { return Symbol("ec"); } function isArray(value) { return Array.isArray(value); } function isFunction(value) { return typeof value === "function"; } function run(fn) { return fn(); } function runAll(fns) { fns.forEach(run); } function noop() { } const identity = (x) => x; function stopPropagation(fn) { return function(event) { event.stopPropagation(); if (fn) { fn.call(this, event); } }; } function debounce(fn, handle, queueStore) { queueStore.update((queue) => queue.set(handle, fn)); } function flushDebounce(queue) { runAll(queue); queue.clear(); } function task(fn, handle, tasks) { handle ?? (handle = fn); if (!tasks.has(handle)) { tasks.set(handle, setTimeout(() => { tasks.delete(handle); fn(); })); } } let payloadProp = symbol(); function setPayload(obj, payload) { obj[payloadProp] = payload; } function hasPayload(obj) { return !!(obj == null ? void 0 : obj[payloadProp]); } function getPayload(obj) { return obj[payloadProp]; } function createElement(tag, className, content, attrs = []) { let el = document.createElement(tag); el.className = className; if (typeof content == "string") { el.innerText = content; } else if (content.domNodes) { el.replaceChildren(...content.domNodes); } else if (content.html) { el.innerHTML = content.html; } for (let attr of attrs) { el.setAttribute(...attr); } return el; } function hasYScroll(el) { return el.scrollHeight > el.clientHeight; } function rect(el) { return el.getBoundingClientRect(); } function ancestor(el, up) { while (up--) { el = el.parentElement; } return el; } function height(el) { return rect(el).height; } function getElementWithPayload(x, y, root2 = document, processed = []) { processed.push(root2); for (let el of root2.elementsFromPoint(x, y)) { if (hasPayload(el)) { return el; } if (el.shadowRoot && !processed.includes(el.shadowRoot)) { let shadowEl = getElementWithPayload(x, y, el.shadowRoot, processed); if (shadowEl) { return shadowEl; } } } return null; } function listen(node, event, handler, options) { node.addEventListener(event, handler, options); return () => node.removeEventListener(event, handler, options); } function createView(view2, _viewTitle, _currentRange, _activeRange) { return { type: view2, title: _viewTitle, currentStart: _currentRange.start, currentEnd: _currentRange.end, activeStart: _activeRange.start, activeEnd: _activeRange.end, calendar: void 0 }; } function toViewWithLocalDates(view2) { view2 = assign({}, view2); view2.currentStart = toLocalDate(view2.currentStart); view2.currentEnd = toLocalDate(view2.currentEnd); view2.activeStart = toLocalDate(view2.activeStart); view2.activeEnd = toLocalDate(view2.activeEnd); return view2; } function listView(view2) { return view2.startsWith("list"); } function timelineView(view2) { return view2.includes("Timeline"); } let eventId = 1; function createEvents(input) { return input.map((event) => { let result = { id: "id" in event ? String(event.id) : `{generated-${eventId++}}`, resourceIds: toArrayProp(event, "resourceId").map(String), allDay: event.allDay ?? (noTimePart(event.start) && noTimePart(event.end)), start: createDate(event.start), end: createDate(event.end), title: event.title ?? "", editable: event.editable, startEditable: event.startEditable, durationEditable: event.durationEditable, display: event.display ?? "auto", extendedProps: event.extendedProps ?? {}, backgroundColor: event.backgroundColor ?? event.color, textColor: event.textColor, classNames: toArrayProp(event, "className"), styles: toArrayProp(event, "style") }; if (result.allDay) { setMidnight(result.start); let end = cloneDate(result.end); setMidnight(result.end); if (!datesEqual(result.end, end) || datesEqual(result.end, result.start)) { addDay(result.end); } } return result; }); } function toArrayProp(input, propName) { let result = input[propName + "s"] ?? input[propName] ?? []; return isArray(result) ? result : [result]; } function createEventSources(input) { return input.map((source) => ({ events: source.events, url: source.url && source.url.trimEnd("&") || "", method: source.method && source.method.toUpperCase() || "GET", extraParams: source.extraParams || {} })); } function createEventChunk(event, start, end) { let chunk = { start: event.start > start ? event.start : start, end: event.end < end ? event.end : end, event }; chunk.zeroDuration = datesEqual(chunk.start, chunk.end); return chunk; } function sortEventChunks(chunks) { chunks.sort((a, b) => a.start - b.start || b.event.allDay - a.event.allDay); } function createEventContent(chunk, displayEventEnd, eventContent, theme, _intlEventTime, _view) { let timeText = _intlEventTime.formatRange( chunk.start, displayEventEnd && chunk.event.display !== "pointer" && !chunk.zeroDuration ? copyTime(cloneDate(chunk.start), chunk.end) : chunk.start ); let content; if (eventContent) { content = isFunction(eventContent) ? eventContent({ event: toEventWithLocalDates(chunk.event), timeText, view: toViewWithLocalDates(_view) }) : eventContent; } if (content === void 0) { let domNodes; switch (chunk.event.display) { case "background": domNodes = []; break; case "pointer": domNodes = [createTimeElement(timeText, chunk, theme)]; break; default: domNodes = [ ...chunk.event.allDay ? [] : [createTimeElement(timeText, chunk, theme)], createElement("h4", theme.eventTitle, chunk.event.title) ]; } content = { domNodes }; } return [timeText, content]; } function createTimeElement(timeText, chunk, theme) { return createElement( "time", theme.eventTime, timeText, [["datetime", toISOString(chunk.start)]] ); } function createEventClasses(eventClassNames, event, _view) { let result = event.classNames; if (eventClassNames) { if (isFunction(eventClassNames)) { eventClassNames = eventClassNames({ event: toEventWithLocalDates(event), view: toViewWithLocalDates(_view) }); } result = [ ...isArray(eventClassNames) ? eventClassNames : [eventClassNames], ...result ]; } return result; } function toEventWithLocalDates(event) { return _cloneEvent(event, toLocalDate); } function cloneEvent(event) { return _cloneEvent(event, cloneDate); } function _cloneEvent(event, dateFn) { event = assign({}, event); event.start = dateFn(event.start); event.end = dateFn(event.end); return event; } function prepareEventChunks$1(chunks, hiddenDays) { let longChunks = {}; if (chunks.length) { sortEventChunks(chunks); let prevChunk; for (let chunk of chunks) { let dates = []; let date = setMidnight(cloneDate(chunk.start)); while (chunk.end > date) { if (!hiddenDays.includes(date.getUTCDay())) { dates.push(cloneDate(date)); if (dates.length > 1) { let key = date.getTime(); if (longChunks[key]) { longChunks[key].chunks.push(chunk); } else { longChunks[key] = { sorted: false, chunks: [chunk] }; } } } addDay(date); } if (dates.length) { chunk.date = dates[0]; chunk.days = dates.length; chunk.dates = dates; if (chunk.start < dates[0]) { chunk.start = dates[0]; } let maxEnd = addDay(cloneDate(dates.at(-1))); if (chunk.end > maxEnd) { chunk.end = maxEnd; } } else { chunk.date = setMidnight(cloneDate(chunk.start)); chunk.days = 1; chunk.dates = [chunk.date]; } if (prevChunk && datesEqual(prevChunk.date, chunk.date)) { chunk.prev = prevChunk; } prevChunk = chunk; } } return longChunks; } function repositionEvent$1(chunk, longChunks, height2) { chunk.top = 0; if (chunk.prev) { chunk.top = chunk.prev.bottom + 1; } chunk.bottom = chunk.top + height2; let margin = 1; let key = chunk.date.getTime(); if (longChunks[key]) { if (!longChunks[key].sorted) { longChunks[key].chunks.sort((a, b) => a.top - b.top); longChunks[key].sorted = true; } for (let longChunk of longChunks[key].chunks) { if (chunk.top < longChunk.bottom && chunk.bottom > longChunk.top) { let offset = longChunk.bottom - chunk.top + 1; margin += offset; chunk.top += offset; chunk.bottom += offset; } } } return margin; } function runReposition(refs, data) { var _a; refs.length = data.length; let result = []; for (let ref of refs) { result.push((_a = ref == null ? void 0 : ref.reposition) == null ? void 0 : _a.call(ref)); } return result; } function eventIntersects(event, start, end, resources) { if (event.start < end && event.end > start) { if (resources) { if (!isArray(resources)) { resources = [resources]; } return resources.some((resource) => event.resourceIds.includes(resource.id)); } return true; } return false; } function helperEvent(display) { return previewEvent(display) || ghostEvent(display) || pointerEvent(display); } function bgEvent(display) { return display === "background"; } function previewEvent(display) { return display === "preview"; } function ghostEvent(display) { return display === "ghost"; } function pointerEvent(display) { return display === "pointer"; } function btnTextDay(text) { return btnText(text, "day"); } function btnTextWeek(text) { return btnText(text, "week"); } function btnTextMonth(text) { return btnText(text, "month"); } function btnTextYear(text) { return btnText(text, "year"); } function btnText(text, period) { return { ...text, next: "Next " + period, prev: "Previous " + period }; } function themeView(view2) { return (theme) => ({ ...theme, view: view2 }); } function createDateRange(input) { let start, end; if (input) { ({ start, end } = input); if (start) { start = setMidnight(createDate(start)); } if (end) { end = setMidnight(createDate(end)); } } return { start, end }; } function outsideRange(date, range) { return range.start && date < range.start || range.end && date > range.end; } function limitToRange(date, range) { if (range.start && date < range.start) { date = range.start; } if (range.end && date > range.end) { date = range.end; } return date; } function createResources(input) { let result = []; _createResources(input, 0, result); return result; } function _createResources(input, level, flat) { let result = []; for (let item of input) { let resource = createResource(item); result.push(resource); flat.push(resource); let payload = { level, children: [], expanded: true, hidden: false }; setPayload(resource, payload); if (item.children) { payload.children = _createResources(item.children, level + 1, flat); } } return result; } function createResource(input) { return { id: String(input.id), title: input.title || "", eventBackgroundColor: input.eventBackgroundColor, eventTextColor: input.eventTextColor, extendedProps: input.extendedProps ?? {} }; } function resourceBackgroundColor(event, resources) { var _a; return (_a = findResource(event, resources)) == null ? void 0 : _a.eventBackgroundColor; } function resourceTextColor(event, resources) { var _a; return (_a = findResource(event, resources)) == null ? void 0 : _a.eventTextColor; } function findResource(event, resources) { return resources.find((resource) => event.resourceIds.includes(resource.id)); } function intl(locale, format) { return derived([locale, format], ([$locale, $format]) => { let intl2 = isFunction($format) ? { format: $format } : new Intl.DateTimeFormat($locale, $format); return { format: (date) => intl2.format(toLocalDate(date)) }; }); } function intlRange(locale, format) { return derived([locale, format], ([$locale, $format]) => { let formatRange; if (isFunction($format)) { formatRange = $format; } else { let intl2 = new Intl.DateTimeFormat($locale, $format); formatRange = (start, end) => { if (start <= end) { return intl2.formatRange(start, end); } else { let parts = intl2.formatRangeToParts(end, start); let result = ""; let sources = ["startRange", "endRange"]; let processed = [false, false]; for (let part of parts) { let i = sources.indexOf(part.source); if (i >= 0) { if (!processed[i]) { result += _getParts(sources[1 - i], parts); processed[i] = true; } } else { result += part.value; } } return result; } }; } return { formatRange: (start, end) => formatRange(toLocalDate(start), toLocalDate(end)) }; }); } function _getParts(source, parts) { let result = ""; for (let part of parts) { if (part.source == source) { result += part.value; } } return result; } function viewResources(state) { return derived( [state.resources, state.filterResourcesWithEvents, state._filteredEvents, state._activeRange], ([$resources, $filterResourcesWithEvents, $_filteredEvents, $_activeRange]) => { let result = $resources.filter((resource) => !getPayload(resource).hidden); if ($filterResourcesWithEvents) { result = $resources.filter((resource) => { for (let event of $_filteredEvents) { if (event.display !== "background" && event.resourceIds.includes(resource.id) && event.start < $_activeRange.end && event.end > $_activeRange.start) { return true; } } return false; }); } if (!result.length) { result = createResources([{}]); } return result; } ); } function createTimes(date, $slotDuration, $slotLabelInterval, $_slotTimeLimits, $_intlSlotLabel) { date = cloneDate(date); let times2 = []; let end = cloneDate(date); addDuration(date, $_slotTimeLimits.min); addDuration(end, $_slotTimeLimits.max); if ($slotLabelInterval === void 0) { $slotLabelInterval = $slotDuration.seconds < 3600 ? createDuration($slotDuration.seconds * 2) : $slotDuration; } let label = cloneDate(date); while (date < end) { times2.push([ toISOString(date), $_intlSlotLabel.format(date), date >= label ]); while ($slotLabelInterval.seconds && date >= label) { addDuration(label, $slotLabelInterval); } addDuration(date, $slotDuration); } return times2; } function createSlotTimeLimits($slotMinTime, $slotMaxTime, $flexibleSlotTimeLimits, $_viewDates, $_filteredEvents) { let min$1 = createDuration($slotMinTime); let max$1 = createDuration($slotMaxTime); if ($flexibleSlotTimeLimits) { let minMin = createDuration(min(toSeconds(min$1), max(0, toSeconds(max$1) - DAY_IN_SECONDS))); let maxMax = createDuration(max(toSeconds(max$1), toSeconds(minMin) + DAY_IN_SECONDS)); let filter = isFunction($flexibleSlotTimeLimits == null ? void 0 : $flexibleSlotTimeLimits.eventFilter) ? $flexibleSlotTimeLimits.eventFilter : (event) => !bgEvent(event.display); loop: for (let date of $_viewDates) { let start = addDuration(cloneDate(date), min$1); let end = addDuration(cloneDate(date), max$1); let minStart = addDuration(cloneDate(date), minMin); let maxEnd = addDuration(cloneDate(date), maxMax); for (let event of $_filteredEvents) { if (!event.allDay && filter(event) && event.start < maxEnd && event.end > minStart) { if (event.start < start) { let seconds = max((event.start - date) / 1e3, toSeconds(minMin)); if (seconds < toSeconds(min$1)) { min$1.seconds = seconds; } } if (event.end > end) { let seconds = min((event.end - date) / 1e3, toSeconds(maxMax)); if (seconds > toSeconds(max$1)) { max$1.seconds = seconds; } } if (toSeconds(min$1) === toSeconds(minMin) && toSeconds(max$1) === toSeconds(maxMax)) { break loop; } } } } } return { min: min$1, max: max$1 }; } function createOptions(plugins) { var _a; let options = { allDayContent: void 0, allDaySlot: true, buttonText: { today: "today" }, customButtons: {}, date: /* @__PURE__ */ new Date(), datesSet: void 0, dayHeaderFormat: { weekday: "short", month: "numeric", day: "numeric" }, dayHeaderAriaLabelFormat: { dateStyle: "full" }, displayEventEnd: true, duration: { weeks: 1 }, events: [], eventAllUpdated: void 0, eventBackgroundColor: void 0, eventClassNames: void 0, eventClick: void 0, eventColor: void 0, eventContent: void 0, eventDidMount: void 0, eventFilter: void 0, // ec option eventMouseEnter: void 0, eventMouseLeave: void 0, eventSources: [], eventTextColor: void 0, eventTimeFormat: { hour: "numeric", minute: "2-digit" }, filterEventsWithResources: false, filterResourcesWithEvents: false, firstDay: 0, flexibleSlotTimeLimits: false, // ec option headerToolbar: { start: "title", center: "", end: "today prev,next" }, height: void 0, hiddenDays: [], highlightedDates: [], // ec option lazyFetching: true, loading: void 0, locale: void 0, nowIndicator: false, resourceLabelContent: void 0, resourceLabelDidMount: void 0, resources: [], selectable: false, scrollTime: "06:00:00", slotDuration: "00:30:00", slotEventOverlap: true, slotHeight: 24, // ec option slotLabelInterval: void 0, slotLabelFormat: { hour: "numeric", minute: "2-digit" }, slotMaxTime: "24:00:00", slotMinTime: "00:00:00", slotWidth: 72, theme: { allDay: "ec-all-day", active: "ec-active", bgEvent: "ec-bg-event", bgEvents: "ec-bg-events", body: "ec-body", button: "ec-button", buttonGroup: "ec-button-group", calendar: "ec", content: "ec-content", day: "ec-day", dayHead: "ec-day-head", days: "ec-days", disabled: "ec-disabled", event: "ec-event", eventBody: "ec-event-body", eventTime: "ec-event-time", eventTitle: "ec-event-title", events: "ec-events", extra: "ec-extra", handle: "ec-handle", header: "ec-header", hiddenScroll: "ec-hidden-scroll", highlight: "ec-highlight", icon: "ec-icon", line: "ec-line", lines: "ec-lines", minor: "ec-minor", nowIndicator: "ec-now-indicator", otherMonth: "ec-other-month", resource: "ec-resource", sidebar: "ec-sidebar", sidebarTitle: "ec-sidebar-title", today: "ec-today", time: "ec-time", title: "ec-title", toolbar: "ec-toolbar", view: "", weekdays: ["ec-sun", "ec-mon", "ec-tue", "ec-wed", "ec-thu", "ec-fri", "ec-sat"], withScroll: "ec-with-scroll" }, titleFormat: { year: "numeric", month: "short", day: "numeric" }, validRange: void 0, view: void 0, viewDidMount: void 0, views: {} }; for (let plugin of plugins) { (_a = plugin.createOptions) == null ? void 0 : _a.call(plugin, options); } return options; } function createParsers(plugins) { var _a; let parsers = { date: (date) => setMidnight(createDate(date)), duration: createDuration, events: createEvents, eventSources: createEventSources, hiddenDays: (days2) => [...new Set(days2)], highlightedDates: (dates) => dates.map((date) => setMidnight(createDate(date))), resources: createResources, scrollTime: createDuration, slotDuration: createDuration, slotLabelInterval: (input) => input !== void 0 ? createDuration(input) : void 0, slotMaxTime: createDuration, slotMinTime: createDuration, validRange: createDateRange }; for (let plugin of plugins) { (_a = plugin.createParsers) == null ? void 0 : _a.call(plugin, parsers); } return parsers; } function diff(options, prevOptions) { let diff2 = []; for (let key of keys(options)) { if (options[key] !== prevOptions[key]) { diff2.push([key, options[key]]); } } return diff2; } function dayGrid(state) { return derived(state.view, ($view) => $view == null ? void 0 : $view.startsWith("dayGrid")); } function activeRange(state) { return derived( [state._currentRange, state.firstDay, state.slotMaxTime, state._dayGrid], ([$_currentRange, $firstDay, $slotMaxTime, $_dayGrid]) => { let start = cloneDate($_currentRange.start); let end = cloneDate($_currentRange.end); if ($_dayGrid) { prevClosestDay(start, $firstDay); nextClosestDay(end, $firstDay); } else if ($slotMaxTime.days || $slotMaxTime.seconds > DAY_IN_SECONDS) { addDuration(subtractDay(end), $slotMaxTime); let start2 = subtractDay(cloneDate(end)); if (start2 < start) { start = start2; } } return { start, end }; } ); } function currentRange(state) { return derived( [state.date, state.duration, state.firstDay], ([$date, $duration, $firstDay]) => { let start = cloneDate($date), end; if ($duration.months) { start.setUTCDate(1); } else if ($duration.inWeeks) { prevClosestDay(start, $firstDay); } end = addDuration(cloneDate(start), $duration); return { start, end }; } ); } function viewDates(state) { return derived([state._activeRange, state.hiddenDays], ([$_activeRange, $hiddenDays]) => { let dates = []; let date = setMidnight(cloneDate($_activeRange.start)); let end = setMidnight(cloneDate($_activeRange.end)); while (date < end) { if (!$hiddenDays.includes(date.getUTCDay())) { dates.push(cloneDate(date)); } addDay(date); } if (!dates.length && $hiddenDays.length && $hiddenDays.length < 7) { state.date.update((date2) => { while ($hiddenDays.includes(date2.getUTCDay())) { addDay(date2); } return date2; }); dates = get(state._viewDates); } return dates; }); } function viewTitle(state) { return derived( [state.date, state._activeRange, state._intlTitle, state._dayGrid], ([$date, $_activeRange, $_intlTitle, $_dayGrid]) => { return $_dayGrid ? $_intlTitle.formatRange($date, $date) : $_intlTitle.formatRange($_activeRange.start, subtractDay(cloneDate($_activeRange.end))); } ); } function view(state) { return derived([state.view, state._viewTitle, state._currentRange, state._activeRange], (args) => createView(...args)); } function events(state) { let _events = writable([]); let abortController; let fetching = 0; let debounceHandle = {}; derived( [state.events, state.eventSources, state._activeRange, state._fetchedRange, state.lazyFetching, state.loading], (values, set) => debounce(() => { let [$events, $eventSources, $_activeRange, $_fetchedRange, $lazyFetching, $loading] = values; if (!$eventSources.length) { set($events); return; } if (!$_fetchedRange.start || $_fetchedRange.start > $_activeRange.start || $_fetchedRange.end < $_activeRange.end || !$lazyFetching) { if (abortController) { abortController.abort(); } abortController = new AbortController(); if (isFunction($loading) && !fetching) { $loading(true); } let stopLoading = () => { if (--fetching === 0 && isFunction($loading)) { $loading(false); } }; let events2 = []; let failure = (e) => stopLoading(); let success = (data) => { events2 = events2.concat(createEvents(data)); set(events2); stopLoading(); }; let startStr = toISOString($_activeRange.start); let endStr = toISOString($_activeRange.end); for (let source of $eventSources) { if (isFunction(source.events)) { let result = source.events({ start: toLocalDate($_activeRange.start), end: toLocalDate($_activeRange.end), startStr, endStr }, success, failure); if (result !== void 0) { Promise.resolve(result).then(success, failure); } } else { let params = isFunction(source.extraParams) ? source.extraParams() : assign({}, source.extraParams); params.start = startStr; params.end = endStr; params = new URLSearchParams(params); let url = source.url, headers = {}, body; if (["GET", "HEAD"].includes(source.method)) { url += (url.includes("?") ? "&" : "?") + params; } else { headers["content-type"] = "application/x-www-form-urlencoded;charset=UTF-8"; body = String(params); } fetch(url, { method: source.method, headers, body, signal: abortController.signal, credentials: "same-origin" }).then((response) => response.json()).then(success).catch(failure); } ++fetching; } $_fetchedRange.start = $_activeRange.start; $_fetchedRange.end = $_activeRange.end; } }, debounceHandle, state._queue), [] ).subscribe(_events.set); return _events; } function filteredEvents(state) { let view2; state._view.subscribe(($_view) => view2 = $_view); let debounceHandle = {}; return derived( [state._events, state.eventFilter], (values, set) => debounce(() => { let [$_events, $eventFilter] = values; set( isFunction($eventFilter) ? $_events.filter((event, index2, events2) => $eventFilter({ event, index: index2, events: events2, view: view2 })) : $_events ); }, debounceHandle, state._queue), [] ); } function now() { return readable(createDate(), (set) => { let interval = setInterval(() => { set(createDate()); }, 1e3); return () => clearInterval(interval); }); } function today(state) { return derived(state._now, ($_now) => setMidnight(cloneDate($_now))); } class State { constructor(plugins, input) { var _a, _b; plugins = plugins || []; let options = createOptions(plugins); let parsers = createParsers(plugins); options = parseOpts(options, parsers); input = parseOpts(input, parsers); for (let [option, value] of Object.entries(options)) { this[option] = writable(value); } this._queue = writable(/* @__PURE__ */ new Map()); this._tasks = /* @__PURE__ */ new Map(); this._auxiliary = writable([]); this._dayGrid = dayGrid(this); this._currentRange = currentRange(this); this._activeRange = activeRange(this); this._fetchedRange = writable({ start: void 0, end: void 0 }); this._events = events(this); this._now = now(); this._today = today(this); this._intlEventTime = intlRange(this.locale, this.eventTimeFormat); this._intlSlotLabel = intl(this.locale, this.slotLabelFormat); this._intlDayHeader = intl(this.locale, this.dayHeaderFormat); this._intlDayHeaderAL = intl(this.locale, this.dayHeaderAriaLabelFormat); this._intlTitle = intlRange(this.locale, this.titleFormat); this._bodyEl = writable(void 0); this._scrollable = writable(false); this._recheckScrollable = writable(false); this._viewTitle = viewTitle(this); this._viewDates = viewDates(this); this._view = view(this); this._viewComponent = writable(void 0); this._filteredEvents = filteredEvents(this); this._interaction = writable({}); this._iEvents = writable([null, null]); this._iClasses = writable(identity); this._iClass = writable(void 0); this._set = (key, value) => { if (validKey(key, this)) { if (parsers[key]) { value = parsers[key](value); } this[key].set(value); } }; this._get = (key) => validKey(key, this) ? get(this[key]) : void 0; for (let plugin of plugins) { (_a = plugin.createStores) == null ? void 0 : _a.call(plugin, this); } if (input.view) { this.view.set(input.view); } let views = /* @__PURE__ */ new Set([...keys(options.views), ...keys(input.views ?? {})]); for (let view2 of views) { let defOpts = mergeOpts(options, options.views[view2] ?? {}); let opts = mergeOpts(defOpts, input, ((_b = input.views) == null ? void 0 : _b[view2]) ?? {}); let component = opts.component; filterOpts(opts, this); for (let key of keys(opts)) { let { set, _set = set, ...rest } = this[key]; this[key] = { // Set value in all views set: ["buttonText", "theme"].includes(key) ? (value) => { if (isFunction(value)) { let result = value(defOpts[key]); opts[key] = result; set(set === _set ? result : value); } else { opts[key] = value; set(value); } } : (value) => { opts[key] = value; set(value); }, _set, ...rest }; } this.view.subscribe((newView) => { if (newView === view2) { this._viewComponent.set(component); if (isFunction(opts.viewDidMount)) { tick().then(() => opts.viewDidMount({ view: toViewWithLocalDates(get(this._view)) })); } for (let key of keys(opts)) { this[key]._set(opts[key]); } } }); } } } function parseOpts(opts, parsers) { let result = { ...opts }; for (let key of keys(parsers)) { if (key in result) { result[key] = parsers[key](result[key]); } } if (opts.views) { result.views = {}; for (let view2 of keys(opts.views)) { result.views[view2] = parseOpts(opts.views[view2], parsers); } } return result; } function mergeOpts(...args) { let result = {}; for (let opts of args) { let override = {}; for (let key of ["buttonText", "theme"]) { if (isFunction(opts[key])) { override[key] = opts[key](result[key]); } } result = { ...result, ...opts, ...override }; } return result; } function filterOpts(opts, state) { keys(opts).filter((key) => !validKey(key, state) || key === "view").forEach((key) => delete opts[key]); } function validKey(key, state) { return state.hasOwnProperty(key) && key[0] !== "_"; } var root_2$7 = $.from_html(`<h2></h2>`); var root_4$3 = $.from_html(`<button><i></i></button>`); var root_6$1 = $.from_html(`<button><i></i></button>`); var root_8$1 = $.from_html(`<button> </button>`); var root_10$1 = $.from_html(`<button></button>`); var root_12$1 = $.from_html(`<button> </button>`); function Buttons($$anchor, $$props) { $.push($$props, false); const [$$stores, $$cleanup] = $.setup_stores(); const $validRange = () => $.store_get(validRange, "$validRange", $$stores); const $date = () => $.store_get(date, "$date", $$stores); const $duration = () => $.store_get(duration, "$duration", $$stores); const $hiddenDays = () => $.store_get(hiddenDays, "$hiddenDays", $$stores); const $_currentRange = () => $.store_get(_currentRange, "$_currentRange", $$stores); const $_viewDates = () => $.store_get(_viewDates, "$_viewDates", $$stores); const $theme = () => $.store_get(theme, "$theme", $$stores); const $_viewTitle = () => $.store_get(_viewTitle, "$_viewTitle", $$stores); const $buttonText = () => $.store_get(buttonText, "$buttonText", $$stores); const $customButtons = () => $.store_get(customButtons, "$customButtons", $$stores); const $view = () => $.store_get(view2, "$view", $$stores); let buttons = $.prop($$props, "buttons", 8); let { _currentRange, _viewTitle, _viewDates, buttonText, customButtons, date, duration, hiddenDays, theme, validRange, view: view2 } = getContext("state"); let today2 = setMidnight(createDate()); let prevDisabled = $.mutable_source(), nextDisabled = $.mutable_source(), todayDisabled = $.mutable_source(); let running = $.mutable_source(false); function isRunning() { return $.get(running); } function test() { return $_viewDates().every((date2) => outsideRange(date2, $validRange())); } function prev() { $.store_set(date, prevDate($date(), $duration(), $hiddenDays())); } function next() { $.store_set(date, nextDate($date(), $duration())); } $.legacy_pre_effect( () => ($validRange(), $date(), $duration(), $hiddenDays(), $.get(todayDisabled), $_currentRange(), tick), () => { if (!isRunning()) { $.set(running, true); $.set(prevDisabled, false); $.set(nextDisabled, false); if ($validRange().start) { let currentDate = cloneDate($date()); $.store_set(date, prevDate($date(), $duration(), $hiddenDays())); $.set(prevDisabled, test()); $.store_set(date, currentDate); } if ($validRange().end) { let currentDate = cloneDate($date()); $.store_set(date, nextDate($date(), $duration())); $.set(nextDisabled, test()); $.store_set(date, currentDate); } $.set(todayDisabled, today2 >= $_currentRange().start && today2 < $_currentRange().end); if (!$.get(todayDisabled) && ($validRange().start || $validRange().end)) { let currentDate = cloneDate($date()); $.store_set(date, cloneDate(today2)); $.set(todayDisabled, test()); $.store_set(date, currentDate); } tick().then(() => $.set(running, false)); } } ); $.legacy_pre_effect_reset(); $.init(); var fragment = $.comment(); var node = $.first_child(fragment); $.each(node, 1, buttons, $.index, ($$anchor2, button) => { var fragment_1 = $.comment(); var node_1 = $.first_child(fragment_1); { var consequent = ($$anchor3) => { var h2 = root_2$7(); $.action(h2, ($$node, $$action_arg) => setContent == null ? void 0 : setContent($$node, $$action_arg), $_viewTitle); $.template_effect(() => $.set_class(h2, 1, ($theme(), $.untrack(() => $theme().title)))); $.append($$anchor3, h2); }; var alternate_4 = ($$anchor3) => { var fragment_2 = $.comment(); var node_2 = $.first_child(fragment_2); { var consequent_1 = ($$anchor4) => { var button_1 = root_4$3(); var i = $.child(button_1); $.reset(button_1); $.template_effect(() => { $.set_class(button_1, 1, `${($theme(), $.untrack(() => $theme().button)) ?? ""} ec-${$.get(button) ?? ""}`); $.set_attribute(button_1, "aria-label", ($buttonText(), $.untrack(() => $buttonText().prev))); $.set_attribute(button_1, "title", ($buttonText(), $.untrack(() => $buttonText().prev))); button_1.disabled = $.get(prevDisabled); $.set_class(i, 1, `${($theme(), $.untrack(() => $theme().icon)) ?? ""} ec-${$.get(button) ?? ""}`); }); $.event("click", button_1, prev); $.append($$anchor4, button_1); }; var alternate_3 = ($$anchor4) => { var fragment_3 = $.comment(); var node_3 = $.first_child(fragment_3); { var consequent_2 = ($$anchor5) => { var button_2 = root_6$1(); var i_1 = $.child(button_2); $.reset(button_2); $.template_effect(() => { $.set_class(button_2, 1, `${($theme(), $.untrack(() => $theme().button)) ?? ""} ec-${$.get(button) ?? ""}`); $.set_attribute(button_2, "aria-label", ($buttonText(), $.untrack(() => $buttonText().next))); $.set_attribute(button_2, "title", ($buttonText(), $.untrack(() => $buttonText().next))); button_2.disabled = $.get(nextDisabled); $.set_class(i_1, 1, `${($theme(), $.untrack(() => $theme().icon)) ?? ""} ec-${$.get(button) ?? ""}`); }); $.event("click", button_2, next); $.append($$anchor5, button_2); }; var alternate_2 = ($$anchor5) => { var fragment_4 = $.comment(); var node_4 = $.first_child(fragment_4); { var consequent_3 = ($$anchor6) => { var button_3 = root_8$1(); var text = $.child(button_3, true); $.reset(button_3); $.template_effect(() => { $.set_class(button_3, 1, `${($theme(), $.untrack(() => $theme().button)) ?? ""} ec-${$.get(button) ?? ""}`); button_3.disabled = $.get(todayDisabled); $.set_text(text, ($buttonText(), $.get(button), $.untrack(() => $buttonText()[$.get(button)]))); }); $.event("click", button_3, () => $.store_set(date, cloneDate(today2))); $.append($$anchor6, button_3); }; var alternate_1 = ($$anchor6) => { var fragment_5 = $.comment(); var node_5 = $.first_child(fragment_5); { var consequent_4 = ($$anchor7) => { var button_4 = root_10$1(); $.effect(() => $.event("click", button_4, function(...$$args) { var _a; (_a = $customButtons()[$.get(button)].click) == null ? void 0 : _a.apply(this, $$args); })); $.action(button_4, ($$node, $$action_arg) => setContent == null ? void 0 : setContent($$node, $$action_arg), () => $customButtons()[$.get(button)].text); $.template_effect(() => $.set_class(button_4, 1, `${($theme(), $.untrack(() => $theme().button)) ?? ""} ec-${$.get(button) ?? ""}${($customButtons(), $.get(button), $theme(), $.untrack(() => $customButtons()[$.get(button)].active ? " " + $theme().active : "")) ?? ""}`)); $.append($$anchor7, button_4); }; var alternate = ($$anchor7) => { var fragment_6 = $.comment(); var node_6 = $.first_child(fragment_6); { var consequent_5 = ($$anchor8) => { var button_5 = root_12$1(); var text_1 = $.child(button_5, true); $.reset(button_5); $.template_effect(() => { $.set_class(button_5, 1, `${($theme(), $.untrack(() => $theme().button)) ?? ""}${($view(), $.get(button), $theme(), $.untrack(() => $view() === $.get(button) ? " " + $theme().active : "")) ?? ""} ec-${$.get(button) ?? ""}`); $.set_text(text_1, ($buttonText(), $.get(button), $.untrack(() => $buttonText()[$.get(button)]))); }); $.event("click", button_5, () => $.store_set(view2, $.get(button))); $.append($$anchor8, button_5); }; $.if( node_6, ($$render) => { if ($.get(button) != "") $$render(consequent_5); }, true ); } $.append($$anchor7, fragment_6); }; $.if( node_5, ($$render) => { if ($customButtons(), $.get(button), $.untrack(() => $customButtons()[$.get(button)])) $$render(consequent_4); else $$render(alternate, false); }, true ); } $.append($$anchor6, fragment_5); }; $.if( node_4, ($$render) => { if ($.get(button) == "today") $$render(consequent_3); else $$render(alternate_1, false); }, true ); } $.append($$anchor5, fragment_4); }; $.if( node_3, ($$render) => { if ($.get(button) == "next") $$render(consequent_2); else $$render(alternate_2, false); }, true ); } $.append($$anchor4, fragment_3); }; $.if( node_2, ($$render) => { if ($.get(button) == "prev") $$render(consequent_1); else $$render(alternate_3, false); }, true ); } $.append($$anchor3, fragment_2); }; $.if(node_1, ($$render) => { if ($.get(button) == "title") $$render(consequent); else $$render(alternate_4, false); }); } $.append($$anchor2, fragment_1); }); $.append($$anchor, fragment); $.pop(); $$cleanup(); } var root_3$5 = $.from_html(`<div><!></div>`); var root_1$d = $.from_html(`<div></div>`); var root$r = $.from_html(`<nav></nav>`); function Toolbar($$anchor, $$props) { $.push($$props, true); const [$$stores, $$cleanup] = $.setup_stores(); const $headerToolbar = () => $.store_get(headerToolbar, "$headerToolbar", $$stores); const $theme = () => $.store_get(theme, "$theme", $$stores); let { headerToolbar, theme } = getContext("state"); let sections = $.derived(() => { var _a; let sections2 = {}; for (let key of ["start", "center", "end"]) { sections2[key] = ((_a = $headerToolbar()[key]) == null ? void 0 : _a.split(" ").map((group) => group.split(","))) ?? []; } return sections2; }); var nav = root$r(); $.each(nav, 21, () => keys($.get(sections)), $.index, ($$anchor2, key) => { var div = root_1$d(); $.each(div, 21, () => $.get(sections)[$.get(key)], $.index, ($$anchor3, buttons) => { var fragment = $.comment(); var node = $.first_child(fragment); { var consequent = ($$anchor4) => { var div_1 = root_3$5(); var node_1 = $.child(div_1); Buttons(node_1, { get buttons() { return $.get(buttons); } }); $.reset(di