This commit is contained in:
parent
2c41a374ec
commit
6387b4d92a
5 changed files with 633 additions and 500 deletions
266
css/site.css
266
css/site.css
|
|
@ -216,3 +216,269 @@ body.custom-background { background-color: #ffffff; }
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: #5c5c56;
|
color: #5c5c56;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Static events calendar. */
|
||||||
|
.events-calendar-page .entry-content {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.familyfed-calendar {
|
||||||
|
display: grid;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-toolbar,
|
||||||
|
.calendar-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-toolbar {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-title {
|
||||||
|
margin: 0;
|
||||||
|
color: #333333;
|
||||||
|
font-size: 28px;
|
||||||
|
line-height: 1.2;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-nav-button,
|
||||||
|
.calendar-secondary-button {
|
||||||
|
min-height: 38px;
|
||||||
|
border: 1px solid #d8d8d0;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #333333;
|
||||||
|
background: #ffffff;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-nav-button {
|
||||||
|
width: 42px;
|
||||||
|
font-size: 26px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-secondary-button {
|
||||||
|
padding: 8px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-nav-button:hover,
|
||||||
|
.calendar-nav-button:focus,
|
||||||
|
.calendar-secondary-button:hover,
|
||||||
|
.calendar-secondary-button:focus {
|
||||||
|
border-color: #bf4d28;
|
||||||
|
color: #bf4d28;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-search {
|
||||||
|
display: grid;
|
||||||
|
gap: 5px;
|
||||||
|
min-width: min(100%, 280px);
|
||||||
|
color: #444444;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-search input {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 38px;
|
||||||
|
margin: 0;
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-layout {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) minmax(240px, 300px);
|
||||||
|
gap: 18px;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-grid-wrap,
|
||||||
|
.calendar-panel {
|
||||||
|
border: 1px solid #eeeeee;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-weekdays,
|
||||||
|
.calendar-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(7, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-weekdays {
|
||||||
|
border-bottom: 1px solid #eeeeee;
|
||||||
|
background: #fbfbf8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-weekdays span {
|
||||||
|
padding: 10px 8px;
|
||||||
|
color: #5c5c56;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-align: center;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: auto 1fr;
|
||||||
|
gap: 6px;
|
||||||
|
min-height: 112px;
|
||||||
|
padding: 8px;
|
||||||
|
border: 0;
|
||||||
|
border-right: 1px solid #eeeeee;
|
||||||
|
border-bottom: 1px solid #eeeeee;
|
||||||
|
color: #333333;
|
||||||
|
background: #ffffff;
|
||||||
|
text-align: left;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day:nth-child(7n) {
|
||||||
|
border-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day:hover,
|
||||||
|
.calendar-day:focus {
|
||||||
|
outline: 2px solid #bf4d28;
|
||||||
|
outline-offset: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day-muted {
|
||||||
|
color: #8c8c86;
|
||||||
|
background: #fbfbf8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day-today .calendar-day-number {
|
||||||
|
border-color: #d5e6b3;
|
||||||
|
color: #365d26;
|
||||||
|
background: #f2f7e8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day-selected {
|
||||||
|
box-shadow: inset 0 0 0 2px #bf4d28;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day-number {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 28px;
|
||||||
|
min-height: 28px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day-events {
|
||||||
|
display: grid;
|
||||||
|
gap: 4px;
|
||||||
|
align-content: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-event-pill,
|
||||||
|
.calendar-event-more {
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 3px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.25;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-event-pill {
|
||||||
|
color: #365d26;
|
||||||
|
background: #f2f7e8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-event-more {
|
||||||
|
color: #5c5c56;
|
||||||
|
background: #f7f7f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-panel {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-panel h3 {
|
||||||
|
margin: 0 0 12px;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-event-list {
|
||||||
|
display: grid;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-event-card {
|
||||||
|
padding: 12px;
|
||||||
|
border: 1px solid #eeeeee;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #fbfbf8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-event-card h4 {
|
||||||
|
margin: 0 0 6px;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-event-card p {
|
||||||
|
margin: 0 0 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-event-date {
|
||||||
|
color: #5c5c56;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-empty {
|
||||||
|
margin: 0;
|
||||||
|
color: #5c5c56;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.calendar-layout {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.calendar-title {
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-actions {
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-actions > * {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-weekdays span {
|
||||||
|
padding: 8px 2px;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day {
|
||||||
|
min-height: 78px;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-event-pill,
|
||||||
|
.calendar-event-more {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,14 @@
|
||||||
<p>We’re conveniently located in Dublin City Centre, directly across from the James Joyce Center. Feel free to drop by anytime, since most days someone is around to offer you a cup of tea and a friendly chat about our beliefs.</p>
|
<p>We’re conveniently located in Dublin City Centre, directly across from the James Joyce Center. Feel free to drop by anytime, since most days someone is around to offer you a cup of tea and a friendly chat about our beliefs.</p>
|
||||||
<p>Our address is:</p>
|
<p>Our address is:</p>
|
||||||
<p><strong>19 North Great Georges St.</strong><br /><strong> Dublin 1, Ireland</strong></p>
|
<p><strong>19 North Great Georges St.</strong><br /><strong> Dublin 1, Ireland</strong></p>
|
||||||
<p>Feel free to email us any questions you may have or to book an appointment to hear our lecture series of the Divine Principle or to organize a religious event at our church center: <a href="mailto:directors@familyfed.ie">directors@familyfed.ie</a></p>
|
<p>Feel free to email us any questions you may have or to book an appointment to hear our lecture series of the Divine Principle or to organize a religious event at our church center.</p>
|
||||||
|
<ul>
|
||||||
|
<li>Directors: <a href="mailto:directors@familyfed.ie">directors@familyfed.ie</a></li>
|
||||||
|
<li>General enquiries: <a href="mailto:info@familyfed.ie">info@familyfed.ie</a></li>
|
||||||
|
<li>Media: <a href="mailto:media@familyfed.ie">media@familyfed.ie</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<form id="contact-form" class="contact-form" action="mailto:directors@familyfed.ie" method="post" enctype="text/plain">
|
<form id="contact-form" class="contact-form" action="mailto:directors@familyfed.ie,info@familyfed.ie,media@familyfed.ie" method="post" enctype="text/plain">
|
||||||
<div class="contact-field">
|
<div class="contact-field">
|
||||||
<label for="contact-name">Name</label>
|
<label for="contact-name">Name</label>
|
||||||
<input id="contact-name" name="name" type="text" autocomplete="name" required />
|
<input id="contact-name" name="name" type="text" autocomplete="name" required />
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -47,7 +47,7 @@ import articleHtml from "../content/pages/contact.html?raw";
|
||||||
status.textContent = "Opening your email app with the message ready to send.";
|
status.textContent = "Opening your email app with the message ready to send.";
|
||||||
}
|
}
|
||||||
|
|
||||||
window.location.href = `mailto:directors@familyfed.ie?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
|
window.location.href = `mailto:directors@familyfed.ie,info@familyfed.ie,media@familyfed.ie?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -9,41 +9,317 @@ import mainHtml from "../content/pages/events.html?raw";
|
||||||
bodyClass="archive post-type-archive post-type-archive-tribe_events custom-background tribe-events-page-template tribe-no-js tribe-filter-live metaslider-plugin parabola-image-three caption-light meta-light parabola_triagles parabola-menu-left"
|
bodyClass="archive post-type-archive post-type-archive-tribe_events custom-background tribe-events-page-template tribe-no-js tribe-filter-live metaslider-plugin parabola-image-three caption-light meta-light parabola_triagles parabola-menu-left"
|
||||||
pathname="/events.html"
|
pathname="/events.html"
|
||||||
canonical="/events.html"
|
canonical="/events.html"
|
||||||
robots="noindex, follow, max-image-preview:large"
|
|
||||||
>
|
>
|
||||||
<Fragment slot="head">
|
|
||||||
<link rel="stylesheet" id="tec-variables-skeleton-css" href="/assets/vendor/events-calendar/common/src/resources/css/variables-skeleton.min.css?ver=6.3.2" type="text/css" media="all" />
|
|
||||||
<link rel="stylesheet" id="tec-variables-full-css" href="/assets/vendor/events-calendar/common/src/resources/css/variables-full.min.css?ver=6.3.2" type="text/css" media="all" />
|
|
||||||
<link rel="stylesheet" id="tribe-common-skeleton-style-css" href="/assets/vendor/events-calendar/common/src/resources/css/common-skeleton.min.css?ver=6.3.2" type="text/css" media="all" />
|
|
||||||
<link rel="stylesheet" id="tribe-common-full-style-css" href="/assets/vendor/events-calendar/common/src/resources/css/common-full.min.css?ver=6.3.2" type="text/css" media="all" />
|
|
||||||
<link rel="stylesheet" id="tribe-events-views-v2-bootstrap-datepicker-styles-css" href="/assets/vendor/events-calendar/vendor/bootstrap-datepicker/css/bootstrap-datepicker.standalone.min.css?ver=6.8.2.1" type="text/css" media="all" />
|
|
||||||
<link rel="stylesheet" id="tribe-tooltipster-css-css" href="/assets/vendor/events-calendar/common/vendor/tooltipster/tooltipster.bundle.min.css?ver=6.3.2" type="text/css" media="all" />
|
|
||||||
<link rel="stylesheet" id="tribe-events-views-v2-skeleton-css" href="/assets/vendor/events-calendar/src/resources/css/views-skeleton.min.css?ver=6.8.2.1" type="text/css" media="all" />
|
|
||||||
<link rel="stylesheet" id="tribe-events-views-v2-full-css" href="/assets/vendor/events-calendar/src/resources/css/views-full.min.css?ver=6.8.2.1" type="text/css" media="all" />
|
|
||||||
<link rel="stylesheet" id="tribe-events-views-v2-print-css" href="/assets/vendor/events-calendar/src/resources/css/views-print.min.css?ver=6.8.2.1" type="text/css" media="print" />
|
|
||||||
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/common/src/resources/js/tribe-common.min.js?ver=6.3.2" id="tribe-common-js"></script>
|
|
||||||
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/breakpoints.min.js?ver=6.8.2.1" id="tribe-events-views-v2-breakpoints-js"></script>
|
|
||||||
</Fragment>
|
|
||||||
<Fragment set:html={mainHtml} />
|
<Fragment set:html={mainHtml} />
|
||||||
<Fragment slot="scripts">
|
<script is:inline slot="scripts">
|
||||||
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/viewport.min.js?ver=6.8.2.1" id="tribe-events-views-v2-viewport-js"></script>
|
(() => {
|
||||||
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/accordion.min.js?ver=6.8.2.1" id="tribe-events-views-v2-accordion-js"></script>
|
const calendar = document.querySelector("[data-calendar]");
|
||||||
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/view-selector.min.js?ver=6.8.2.1" id="tribe-events-views-v2-view-selector-js"></script>
|
|
||||||
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/vendor/bootstrap-datepicker/js/bootstrap-datepicker.min.js?ver=6.8.2.1" id="tribe-events-views-v2-bootstrap-datepicker-js"></script>
|
if (!calendar) {
|
||||||
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/datepicker.min.js?ver=6.8.2.1" id="tribe-events-views-v2-datepicker-js"></script>
|
return;
|
||||||
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/ical-links.min.js?ver=6.8.2.1" id="tribe-events-views-v2-ical-links-js"></script>
|
}
|
||||||
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/events-bar-inputs.min.js?ver=6.8.2.1" id="tribe-events-views-v2-events-bar-inputs-js"></script>
|
|
||||||
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/events-bar.min.js?ver=6.8.2.1" id="tribe-events-views-v2-events-bar-js"></script>
|
const recurringEvents = [
|
||||||
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/common/vendor/tooltipster/tooltipster.bundle.min.js?ver=6.3.2" id="tribe-tooltipster-js"></script>
|
{
|
||||||
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/tooltip.min.js?ver=6.8.2.1" id="tribe-events-views-v2-tooltip-js"></script>
|
id: "sunday-service",
|
||||||
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/navigation-scroll.min.js?ver=6.8.2.1" id="tribe-events-views-v2-navigation-scroll-js"></script>
|
title: "Sunday Service",
|
||||||
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/multiday-events.min.js?ver=6.8.2.1" id="tribe-events-views-v2-multiday-events-js"></script>
|
startTime: "11:00",
|
||||||
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/month-mobile-events.min.js?ver=6.8.2.1" id="tribe-events-views-v2-month-mobile-events-js"></script>
|
endTime: "12:00",
|
||||||
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/month-grid.min.js?ver=6.8.2.1" id="tribe-events-views-v2-month-grid-js"></script>
|
location: "19 North Great Georges St., Dublin 1, Ireland",
|
||||||
<script is:inline type="text/javascript" src="/assets/vendor/events-calendar/common/src/resources/js/utils/query-string.min.js?ver=6.3.2" id="tribe-query-string-js"></script>
|
details: "Weekly Sunday Service video upload and community worship.",
|
||||||
<script is:inline src="/assets/vendor/events-calendar/common/src/resources/js/underscore-before.js"></script>
|
url: "/services.html",
|
||||||
<script is:inline type="text/javascript" src="/assets/vendor/underscore.min.js?ver=1.13.4" id="underscore-js"></script>
|
weekday: 0,
|
||||||
<script is:inline src="/assets/vendor/events-calendar/common/src/resources/js/underscore-after.js"></script>
|
},
|
||||||
<script is:inline defer type="text/javascript" src="/assets/vendor/events-calendar/src/resources/js/views/manager.min.js?ver=6.8.2.1" id="tribe-events-views-v2-manager-js"></script>
|
];
|
||||||
</Fragment>
|
|
||||||
|
const monthTitle = calendar.querySelector("[data-calendar-panel-title]");
|
||||||
|
const grid = calendar.querySelector("[data-calendar-grid]");
|
||||||
|
const list = calendar.querySelector("[data-calendar-list]");
|
||||||
|
const title = calendar.querySelector("#calendar-month-title");
|
||||||
|
const search = calendar.querySelector("[data-calendar-search]");
|
||||||
|
const today = new Date();
|
||||||
|
let visibleMonth = new Date(today.getFullYear(), today.getMonth(), 1);
|
||||||
|
let selectedDate = toDateKey(today);
|
||||||
|
|
||||||
|
const monthFormatter = new Intl.DateTimeFormat("en-IE", { month: "long", year: "numeric" });
|
||||||
|
const dayFormatter = new Intl.DateTimeFormat("en-IE", { weekday: "long", day: "numeric", month: "long", year: "numeric" });
|
||||||
|
|
||||||
|
function pad(value) {
|
||||||
|
return String(value).padStart(2, "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
function toDateKey(date) {
|
||||||
|
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fromDateKey(key) {
|
||||||
|
const [year, month, day] = key.split("-").map(Number);
|
||||||
|
return new Date(year, month - 1, day);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addDays(date, days) {
|
||||||
|
const next = new Date(date);
|
||||||
|
next.setDate(next.getDate() + days);
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isSameMonth(date, month) {
|
||||||
|
return date.getFullYear() === month.getFullYear() && date.getMonth() === month.getMonth();
|
||||||
|
}
|
||||||
|
|
||||||
|
function monthStartGridDate(month) {
|
||||||
|
const first = new Date(month.getFullYear(), month.getMonth(), 1);
|
||||||
|
const mondayIndex = (first.getDay() + 6) % 7;
|
||||||
|
return addDays(first, -mondayIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
function monthEndGridDate(month) {
|
||||||
|
const last = new Date(month.getFullYear(), month.getMonth() + 1, 0);
|
||||||
|
const sundayIndex = (7 - last.getDay()) % 7;
|
||||||
|
return addDays(last, sundayIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
function eventMatchesQuery(event) {
|
||||||
|
const query = String(search?.value || "").trim().toLowerCase();
|
||||||
|
|
||||||
|
if (!query) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [event.title, event.location, event.details]
|
||||||
|
.join(" ")
|
||||||
|
.toLowerCase()
|
||||||
|
.includes(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
function eventsBetween(start, end) {
|
||||||
|
const events = [];
|
||||||
|
|
||||||
|
for (let date = new Date(start); date <= end; date = addDays(date, 1)) {
|
||||||
|
for (const recurringEvent of recurringEvents) {
|
||||||
|
if (date.getDay() !== recurringEvent.weekday) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dateKey = toDateKey(date);
|
||||||
|
events.push({
|
||||||
|
...recurringEvent,
|
||||||
|
id: `${recurringEvent.id}-${dateKey}`,
|
||||||
|
date: dateKey,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return events.filter(eventMatchesQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatEventTime(event) {
|
||||||
|
return `${event.startTime} - ${event.endTime}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderGrid() {
|
||||||
|
const start = monthStartGridDate(visibleMonth);
|
||||||
|
const end = monthEndGridDate(visibleMonth);
|
||||||
|
const events = eventsBetween(start, end);
|
||||||
|
const eventsByDate = new Map();
|
||||||
|
|
||||||
|
for (const event of events) {
|
||||||
|
const dayEvents = eventsByDate.get(event.date) || [];
|
||||||
|
dayEvents.push(event);
|
||||||
|
eventsByDate.set(event.date, dayEvents);
|
||||||
|
}
|
||||||
|
|
||||||
|
title.textContent = monthFormatter.format(visibleMonth);
|
||||||
|
grid.innerHTML = "";
|
||||||
|
|
||||||
|
for (let date = new Date(start); date <= end; date = addDays(date, 1)) {
|
||||||
|
const dateKey = toDateKey(date);
|
||||||
|
const dayEvents = eventsByDate.get(dateKey) || [];
|
||||||
|
const cell = document.createElement("button");
|
||||||
|
cell.type = "button";
|
||||||
|
cell.className = "calendar-day";
|
||||||
|
cell.dataset.date = dateKey;
|
||||||
|
cell.setAttribute("role", "gridcell");
|
||||||
|
cell.setAttribute("aria-label", `${dayFormatter.format(date)}${dayEvents.length ? `, ${dayEvents.length} event` : ""}`);
|
||||||
|
|
||||||
|
if (!isSameMonth(date, visibleMonth)) {
|
||||||
|
cell.classList.add("calendar-day-muted");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dateKey === toDateKey(today)) {
|
||||||
|
cell.classList.add("calendar-day-today");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dateKey === selectedDate) {
|
||||||
|
cell.classList.add("calendar-day-selected");
|
||||||
|
cell.setAttribute("aria-selected", "true");
|
||||||
|
}
|
||||||
|
|
||||||
|
const number = document.createElement("span");
|
||||||
|
number.className = "calendar-day-number";
|
||||||
|
number.textContent = String(date.getDate());
|
||||||
|
cell.append(number);
|
||||||
|
|
||||||
|
const eventStack = document.createElement("span");
|
||||||
|
eventStack.className = "calendar-day-events";
|
||||||
|
for (const event of dayEvents.slice(0, 2)) {
|
||||||
|
const pill = document.createElement("span");
|
||||||
|
pill.className = "calendar-event-pill";
|
||||||
|
pill.textContent = event.title;
|
||||||
|
eventStack.append(pill);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dayEvents.length > 2) {
|
||||||
|
const more = document.createElement("span");
|
||||||
|
more.className = "calendar-event-more";
|
||||||
|
more.textContent = `+${dayEvents.length - 2} more`;
|
||||||
|
eventStack.append(more);
|
||||||
|
}
|
||||||
|
|
||||||
|
cell.append(eventStack);
|
||||||
|
cell.addEventListener("click", () => {
|
||||||
|
selectedDate = dateKey;
|
||||||
|
render();
|
||||||
|
});
|
||||||
|
grid.append(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderList() {
|
||||||
|
const selected = fromDateKey(selectedDate);
|
||||||
|
const monthStart = new Date(visibleMonth.getFullYear(), visibleMonth.getMonth(), 1);
|
||||||
|
const monthEnd = new Date(visibleMonth.getFullYear(), visibleMonth.getMonth() + 1, 0);
|
||||||
|
const selectedEvents = eventsBetween(selected, selected);
|
||||||
|
const upcomingEvents = eventsBetween(monthStart, monthEnd)
|
||||||
|
.filter((event) => event.date >= toDateKey(today))
|
||||||
|
.slice(0, 8);
|
||||||
|
const events = selectedEvents.length ? selectedEvents : upcomingEvents;
|
||||||
|
|
||||||
|
monthTitle.textContent = selectedEvents.length
|
||||||
|
? dayFormatter.format(selected)
|
||||||
|
: "Upcoming Events";
|
||||||
|
list.innerHTML = "";
|
||||||
|
|
||||||
|
if (!events.length) {
|
||||||
|
const empty = document.createElement("p");
|
||||||
|
empty.className = "calendar-empty";
|
||||||
|
empty.textContent = "No matching events for this view.";
|
||||||
|
list.append(empty);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const event of events) {
|
||||||
|
const item = document.createElement("article");
|
||||||
|
item.className = "calendar-event-card";
|
||||||
|
const eventDate = fromDateKey(event.date);
|
||||||
|
|
||||||
|
const date = document.createElement("p");
|
||||||
|
date.className = "calendar-event-date";
|
||||||
|
date.textContent = dayFormatter.format(eventDate);
|
||||||
|
|
||||||
|
const heading = document.createElement("h4");
|
||||||
|
heading.textContent = event.title;
|
||||||
|
|
||||||
|
const time = document.createElement("p");
|
||||||
|
time.textContent = formatEventTime(event);
|
||||||
|
|
||||||
|
const location = document.createElement("p");
|
||||||
|
location.textContent = event.location;
|
||||||
|
|
||||||
|
const details = document.createElement("p");
|
||||||
|
details.textContent = event.details;
|
||||||
|
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = event.url;
|
||||||
|
link.textContent = "Sunday Services";
|
||||||
|
|
||||||
|
item.append(date, heading, time, location, details, link);
|
||||||
|
list.append(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function icsDate(dateKey, time) {
|
||||||
|
const [year, month, day] = dateKey.split("-");
|
||||||
|
const [hours, minutes] = time.split(":");
|
||||||
|
return `${year}${month}${day}T${hours}${minutes}00`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeIcs(value) {
|
||||||
|
return String(value)
|
||||||
|
.replace(/\\/g, "\\\\")
|
||||||
|
.replace(/;/g, "\\;")
|
||||||
|
.replace(/,/g, "\\,")
|
||||||
|
.replace(/\n/g, "\\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
function exportCalendar() {
|
||||||
|
const start = new Date(today.getFullYear(), today.getMonth(), 1);
|
||||||
|
const end = new Date(today.getFullYear() + 1, today.getMonth() + 1, 0);
|
||||||
|
const events = eventsBetween(start, end);
|
||||||
|
const stamp = new Date().toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
|
||||||
|
const lines = [
|
||||||
|
"BEGIN:VCALENDAR",
|
||||||
|
"VERSION:2.0",
|
||||||
|
"PRODID:-//FFWPU Ireland//Events Calendar//EN",
|
||||||
|
"CALSCALE:GREGORIAN",
|
||||||
|
"METHOD:PUBLISH",
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const event of events) {
|
||||||
|
lines.push(
|
||||||
|
"BEGIN:VEVENT",
|
||||||
|
`UID:${event.id}@familyfed.ie`,
|
||||||
|
`DTSTAMP:${stamp}`,
|
||||||
|
`DTSTART:${icsDate(event.date, event.startTime)}`,
|
||||||
|
`DTEND:${icsDate(event.date, event.endTime)}`,
|
||||||
|
`SUMMARY:${escapeIcs(event.title)}`,
|
||||||
|
`DESCRIPTION:${escapeIcs(event.details)}`,
|
||||||
|
`LOCATION:${escapeIcs(event.location)}`,
|
||||||
|
`URL:https://familyfed.bcgen.ie${event.url}`,
|
||||||
|
"END:VEVENT",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.push("END:VCALENDAR");
|
||||||
|
|
||||||
|
const blob = new Blob([lines.join("\r\n")], { type: "text/calendar;charset=utf-8" });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = url;
|
||||||
|
link.download = "familyfed-events.ics";
|
||||||
|
document.body.append(link);
|
||||||
|
link.click();
|
||||||
|
link.remove();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
function render() {
|
||||||
|
renderGrid();
|
||||||
|
renderList();
|
||||||
|
}
|
||||||
|
|
||||||
|
calendar.querySelector("[data-calendar-prev]")?.addEventListener("click", () => {
|
||||||
|
visibleMonth = new Date(visibleMonth.getFullYear(), visibleMonth.getMonth() - 1, 1);
|
||||||
|
selectedDate = toDateKey(new Date(visibleMonth.getFullYear(), visibleMonth.getMonth(), 1));
|
||||||
|
render();
|
||||||
|
});
|
||||||
|
|
||||||
|
calendar.querySelector("[data-calendar-next]")?.addEventListener("click", () => {
|
||||||
|
visibleMonth = new Date(visibleMonth.getFullYear(), visibleMonth.getMonth() + 1, 1);
|
||||||
|
selectedDate = toDateKey(new Date(visibleMonth.getFullYear(), visibleMonth.getMonth(), 1));
|
||||||
|
render();
|
||||||
|
});
|
||||||
|
|
||||||
|
calendar.querySelector("[data-calendar-today]")?.addEventListener("click", () => {
|
||||||
|
visibleMonth = new Date(today.getFullYear(), today.getMonth(), 1);
|
||||||
|
selectedDate = toDateKey(today);
|
||||||
|
render();
|
||||||
|
});
|
||||||
|
|
||||||
|
calendar.querySelector("[data-calendar-export]")?.addEventListener("click", exportCalendar);
|
||||||
|
search?.addEventListener("input", render);
|
||||||
|
|
||||||
|
render();
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
</SiteLayout>
|
</SiteLayout>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue