- Clicking a day highlights it and shows a panel below the calendar - Panel lists all events for that day (recurring + one-off + brunch) - Click same day again or X button to dismiss - Navigating month clears selection
474 lines
17 KiB
HTML
474 lines
17 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<title>Axis — Events</title>
|
||
<link rel="stylesheet" href="style.css" />
|
||
</head>
|
||
<body>
|
||
|
||
<!-- ===================================================
|
||
ADMIN: Edit the events array below to update the
|
||
events page. Each object supports:
|
||
name (string) — event title
|
||
date (string) — "YYYY-MM-DD"
|
||
time (string) — display string e.g. "9 PM"
|
||
description (string) — short blurb
|
||
tag (string) — optional label e.g. "Live Music"
|
||
==================================================== -->
|
||
<script id="events-data" type="application/json">
|
||
[
|
||
{
|
||
"name": "Trivia Night",
|
||
"date": "2026-03-17",
|
||
"time": "8 PM – 10 PM",
|
||
"description": "Teams of up to 6. Prizes include bar tabs and Axis merchandise. Arrive early to grab a table.",
|
||
"tag": "Social"
|
||
},
|
||
{
|
||
"name": "Live Music",
|
||
"date": "2026-03-20",
|
||
"time": "9 PM",
|
||
"description": "Local and touring acts across folk, rock, soul, and jazz. No cover charge, ever.",
|
||
"tag": "Live Music"
|
||
},
|
||
{
|
||
"name": "Trivia Night",
|
||
"date": "2026-03-24",
|
||
"time": "8 PM – 10 PM",
|
||
"description": "Teams of up to 6. Prizes include bar tabs and Axis merchandise. Arrive early to grab a table.",
|
||
"tag": "Social"
|
||
},
|
||
{
|
||
"name": "Live Music",
|
||
"date": "2026-03-27",
|
||
"time": "9 PM",
|
||
"description": "Local and touring acts across folk, rock, soul, and jazz. No cover charge, ever.",
|
||
"tag": "Live Music"
|
||
},
|
||
{
|
||
"name": "Live Music",
|
||
"date": "2026-03-30",
|
||
"time": "9 PM",
|
||
"description": "Local and touring acts across folk, rock, soul, and jazz. No cover charge, ever.",
|
||
"tag": "Live Music"
|
||
},
|
||
{
|
||
"name": "Trivia Night",
|
||
"date": "2026-03-31",
|
||
"time": "8 PM – 10 PM",
|
||
"description": "Teams of up to 6. Prizes include bar tabs and Axis merchandise. Arrive early to grab a table.",
|
||
"tag": "Social"
|
||
},
|
||
{
|
||
"name": "Live Music",
|
||
"date": "2026-04-03",
|
||
"time": "9 PM",
|
||
"description": "Local and touring acts across folk, rock, soul, and jazz. No cover charge, ever.",
|
||
"tag": "Live Music"
|
||
},
|
||
{
|
||
"name": "Trivia Night",
|
||
"date": "2026-04-07",
|
||
"time": "8 PM – 10 PM",
|
||
"description": "Teams of up to 6. Prizes include bar tabs and Axis merchandise. Arrive early to grab a table.",
|
||
"tag": "Social"
|
||
},
|
||
{
|
||
"name": "Board Games & Chess Night",
|
||
"date": "2026-04-09",
|
||
"time": "7 PM",
|
||
"description": "The 2nd Thursday of every month. Bring your A-game. Classic board games, chess sets, and cold drinks. All skill levels welcome.",
|
||
"tag": "Social"
|
||
},
|
||
{
|
||
"name": "Live Music",
|
||
"date": "2026-04-10",
|
||
"time": "9 PM",
|
||
"description": "Local and touring acts across folk, rock, soul, and jazz. No cover charge, ever.",
|
||
"tag": "Live Music"
|
||
},
|
||
{
|
||
"name": "Trivia Night",
|
||
"date": "2026-04-14",
|
||
"time": "8 PM – 10 PM",
|
||
"description": "Teams of up to 6. Prizes include bar tabs and Axis merchandise. Arrive early to grab a table.",
|
||
"tag": "Social"
|
||
},
|
||
{
|
||
"name": "Live Music",
|
||
"date": "2026-04-17",
|
||
"time": "9 PM",
|
||
"description": "Local and touring acts across folk, rock, soul, and jazz. No cover charge, ever.",
|
||
"tag": "Live Music"
|
||
},
|
||
{
|
||
"name": "Live Music",
|
||
"date": "2026-04-20",
|
||
"time": "9 PM",
|
||
"description": "Local and touring acts across folk, rock, soul, and jazz. No cover charge, ever.",
|
||
"tag": "Live Music"
|
||
},
|
||
{
|
||
"name": "Trivia Night",
|
||
"date": "2026-04-21",
|
||
"time": "8 PM – 10 PM",
|
||
"description": "Teams of up to 6. Prizes include bar tabs and Axis merchandise. Arrive early to grab a table.",
|
||
"tag": "Social"
|
||
},
|
||
{
|
||
"name": "Live Music",
|
||
"date": "2026-04-24",
|
||
"time": "9 PM",
|
||
"description": "Local and touring acts across folk, rock, soul, and jazz. No cover charge, ever.",
|
||
"tag": "Live Music"
|
||
},
|
||
{
|
||
"name": "Trivia Night",
|
||
"date": "2026-04-28",
|
||
"time": "8 PM – 10 PM",
|
||
"description": "Teams of up to 6. Prizes include bar tabs and Axis merchandise. Arrive early to grab a table.",
|
||
"tag": "Social"
|
||
},
|
||
{
|
||
"name": "Live Music",
|
||
"date": "2026-04-27",
|
||
"time": "9 PM",
|
||
"description": "Local and touring acts across folk, rock, soul, and jazz. No cover charge, ever.",
|
||
"tag": "Live Music"
|
||
},
|
||
{
|
||
"name": "Board Games & Chess Night",
|
||
"date": "2026-05-14",
|
||
"time": "7 PM",
|
||
"description": "The 2nd Thursday of every month. Bring your A-game. Classic board games, chess sets, and cold drinks. All skill levels welcome.",
|
||
"tag": "Social"
|
||
}
|
||
]
|
||
</script>
|
||
<script>
|
||
// Make events accessible to calendar
|
||
window._axisEvents = JSON.parse(document.getElementById('events-data').textContent);
|
||
</script>
|
||
|
||
<!-- ===== Navbar ===== -->
|
||
<nav class="navbar">
|
||
<a href="index.html" class="navbar__brand">Axis</a>
|
||
<button class="navbar__toggle" aria-label="Toggle menu" onclick="toggleNav()">
|
||
<span></span><span></span><span></span>
|
||
</button>
|
||
<ul class="navbar__links" id="nav-links">
|
||
<li><a href="index.html">Home</a></li>
|
||
<li><a href="menu.html">Menu</a></li>
|
||
<li><a href="events.html" class="active">Events</a></li>
|
||
</ul>
|
||
</nav>
|
||
|
||
<!-- ===== Hero ===== -->
|
||
<header class="page-hero">
|
||
<p class="page-hero__eyebrow">What's On</p>
|
||
<h1 class="page-hero__title">Upcoming Events</h1>
|
||
<p class="page-hero__sub">Live music · Trivia · Board games · Special nights</p>
|
||
</header>
|
||
|
||
<!-- ===== Calendar Widget ===== -->
|
||
<div class="container">
|
||
<div class="cal-widget" id="cal-widget"></div>
|
||
<div class="cal-day-detail" id="cal-day-detail" style="display:none"></div>
|
||
</div>
|
||
|
||
<!-- ===== Main Content ===== -->
|
||
<main>
|
||
<div class="container">
|
||
<div class="events-grid" id="events-container">
|
||
<!-- Populated by JS below -->
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
<!-- ===== Footer ===== -->
|
||
<footer>
|
||
<p><span>Axis</span> — 3048 Dundas Street West — Open Tue–Sun 5pm–2am — Brunch Thu–Sun 9am–3pm</p>
|
||
<p style="margin-top:0.4rem;">Events subject to change. Follow us for updates.</p>
|
||
</footer>
|
||
|
||
<script>
|
||
/* ---- Mobile nav ---- */
|
||
function toggleNav() {
|
||
document.getElementById('nav-links').classList.toggle('open');
|
||
}
|
||
|
||
/* ---- Calendar Widget ---- */
|
||
(function () {
|
||
const calEl = document.getElementById('cal-widget');
|
||
const detailEl = document.getElementById('cal-day-detail');
|
||
const MONTH_NAMES = ['January','February','March','April','May','June','July','August','September','October','November','December'];
|
||
const DAY_NAMES = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
|
||
const today = new Date();
|
||
let viewYear = today.getFullYear();
|
||
let viewMonth = today.getMonth();
|
||
let selectedKey = null; // "YYYY-M-D"
|
||
|
||
/* -- helpers -- */
|
||
function is2ndThursday(y, m, d) {
|
||
if (new Date(y, m, d).getDay() !== 4) return false;
|
||
let count = 0;
|
||
for (let i = 1; i <= d; i++) {
|
||
if (new Date(y, m, i).getDay() === 4) count++;
|
||
}
|
||
return count === 2;
|
||
}
|
||
|
||
function getDots(y, m, d) {
|
||
const dow = new Date(y, m, d).getDay();
|
||
const dots = [];
|
||
if (dow === 2) dots.push({ color: 'var(--gold)', label: 'Trivia Night' });
|
||
if (dow === 1 || dow === 5) dots.push({ color: '#e07020', label: 'Live Music' });
|
||
if (is2ndThursday(y, m, d)) dots.push({ color: '#8844cc', label: 'Board Games & Chess Night' });
|
||
return dots;
|
||
}
|
||
|
||
function isBrunchDay(dow) {
|
||
return dow === 0 || dow === 4 || dow === 5 || dow === 6;
|
||
}
|
||
|
||
/* -- build event list for a given day -- */
|
||
function getEventsForDay(y, m, d) {
|
||
const dow = new Date(y, m, d).getDay();
|
||
const dateStr = y + '-' + String(m + 1).padStart(2,'0') + '-' + String(d).padStart(2,'0');
|
||
const items = [];
|
||
|
||
// Brunch
|
||
if (isBrunchDay(dow)) {
|
||
items.push({ name: 'Brunch', time: '9 AM – 3 PM', tag: 'Brunch', color: '#2a7a4a' });
|
||
}
|
||
// Trivia
|
||
if (dow === 2) {
|
||
items.push({ name: 'Trivia Night', time: '8 PM – 10 PM', tag: 'Social', color: 'var(--gold)',
|
||
desc: 'Teams of up to 6. Prizes include bar tabs and Axis merchandise.' });
|
||
}
|
||
// Live Music
|
||
if (dow === 1 || dow === 5) {
|
||
items.push({ name: 'Live Music', time: '9 PM', tag: 'Live Music', color: '#e07020',
|
||
desc: 'Local and touring acts — folk, rock, soul, and jazz. No cover.' });
|
||
}
|
||
// Board Games
|
||
if (is2ndThursday(y, m, d)) {
|
||
items.push({ name: 'Board Games & Chess Night', time: '7 PM', tag: 'Social', color: '#8844cc',
|
||
desc: 'Classic board games, chess sets, and cold drinks. All skill levels welcome.' });
|
||
}
|
||
// One-off events from JSON
|
||
if (window._axisEvents) {
|
||
window._axisEvents.forEach(function(e) {
|
||
if (e.date === dateStr) {
|
||
// avoid duplicating recurring events already added above
|
||
const alreadyAdded = items.some(function(i) { return i.name === e.name; });
|
||
if (!alreadyAdded) {
|
||
items.push({ name: e.name, time: e.time, tag: e.tag, desc: e.description, color: '#c8922a' });
|
||
}
|
||
}
|
||
});
|
||
}
|
||
return items;
|
||
}
|
||
|
||
/* -- show day detail panel -- */
|
||
function showDayDetail(y, m, d) {
|
||
const key = y + '-' + m + '-' + d;
|
||
// Toggle off if same day clicked again
|
||
if (selectedKey === key) {
|
||
selectedKey = null;
|
||
detailEl.style.display = 'none';
|
||
render();
|
||
return;
|
||
}
|
||
selectedKey = key;
|
||
render(); // re-render to update selected highlight
|
||
|
||
const items = getEventsForDay(y, m, d);
|
||
const dow = new Date(y, m, d).getDay();
|
||
const label = DAY_NAMES[dow] + ', ' + MONTH_NAMES[m] + ' ' + d;
|
||
|
||
let inner = '<div class="cal-detail-header">';
|
||
inner += '<span class="cal-detail-title">' + label + '</span>';
|
||
inner += '<button class="cal-detail-close" onclick="closeDayDetail()" aria-label="Close">×</button>';
|
||
inner += '</div>';
|
||
|
||
if (items.length === 0) {
|
||
inner += '<p class="cal-detail-empty">No events scheduled for this day.</p>';
|
||
} else {
|
||
inner += '<ul class="cal-detail-list">';
|
||
items.forEach(function(item) {
|
||
inner += '<li class="cal-detail-item">';
|
||
inner += '<span class="cal-detail-dot" style="background:' + item.color + '"></span>';
|
||
inner += '<div class="cal-detail-info">';
|
||
inner += '<div class="cal-detail-name">' + escHtml(item.name) + '</div>';
|
||
if (item.time) inner += '<div class="cal-detail-time">' + escHtml(item.time) + '</div>';
|
||
if (item.desc) inner += '<div class="cal-detail-desc">' + escHtml(item.desc) + '</div>';
|
||
inner += '</div></li>';
|
||
});
|
||
inner += '</ul>';
|
||
}
|
||
|
||
detailEl.innerHTML = inner;
|
||
detailEl.style.display = 'block';
|
||
// Smooth scroll to detail panel
|
||
detailEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||
}
|
||
|
||
window.closeDayDetail = function() {
|
||
selectedKey = null;
|
||
detailEl.style.display = 'none';
|
||
render();
|
||
};
|
||
|
||
/* -- render calendar -- */
|
||
function render() {
|
||
const firstDow = new Date(viewYear, viewMonth, 1).getDay();
|
||
const daysInMonth = new Date(viewYear, viewMonth + 1, 0).getDate();
|
||
|
||
let html = '<div class="cal-header">';
|
||
html += '<button class="cal-nav-btn" id="cal-prev">‹</button>';
|
||
html += '<span class="cal-month-label">' + MONTH_NAMES[viewMonth] + ' ' + viewYear + '</span>';
|
||
html += '<button class="cal-nav-btn" id="cal-next">›</button>';
|
||
html += '</div>';
|
||
|
||
html += '<div class="cal-grid">';
|
||
['Sun','Mon','Tue','Wed','Thu','Fri','Sat'].forEach(function (d) {
|
||
html += '<div class="cal-dow-header">' + d + '</div>';
|
||
});
|
||
|
||
for (let i = 0; i < firstDow; i++) {
|
||
html += '<div class="cal-cell cal-cell--empty"></div>';
|
||
}
|
||
|
||
for (let d = 1; d <= daysInMonth; d++) {
|
||
const dow = new Date(viewYear, viewMonth, d).getDay();
|
||
const isToday = (today.getFullYear() === viewYear && today.getMonth() === viewMonth && today.getDate() === d);
|
||
const isSelected = (selectedKey === viewYear + '-' + viewMonth + '-' + d);
|
||
const brunch = isBrunchDay(dow);
|
||
const dots = getDots(viewYear, viewMonth, d);
|
||
|
||
let cls = 'cal-cell cal-cell--clickable';
|
||
if (brunch) cls += ' cal-cell--brunch';
|
||
if (isToday) cls += ' cal-cell--today';
|
||
if (isSelected) cls += ' cal-cell--selected';
|
||
|
||
const vy = viewYear, vm = viewMonth, vd = d;
|
||
html += '<div class="' + cls + '" onclick="window._calClick(' + vy + ',' + vm + ',' + vd + ')">';
|
||
html += '<span class="cal-day-num">' + d + '</span>';
|
||
if (dots.length > 0) {
|
||
html += '<div class="cal-dots">';
|
||
dots.forEach(function (dot) {
|
||
html += '<span class="cal-dot" style="background:' + dot.color + '" title="' + dot.label + '"></span>';
|
||
});
|
||
html += '</div>';
|
||
}
|
||
html += '</div>';
|
||
}
|
||
|
||
html += '</div>';
|
||
|
||
html += '<div class="cal-legend">';
|
||
html += '<span class="cal-legend-item"><span class="cal-dot" style="background:var(--gold)"></span> Trivia Night (Tue)</span>';
|
||
html += '<span class="cal-legend-item"><span class="cal-dot" style="background:#e07020"></span> Live Music (Mon & Fri)</span>';
|
||
html += '<span class="cal-legend-item"><span class="cal-dot" style="background:#8844cc"></span> Board Games & Chess (2nd Thu)</span>';
|
||
html += '<span class="cal-legend-item"><span class="cal-legend-brunch-swatch"></span> Brunch (Thu–Sun)</span>';
|
||
html += '</div>';
|
||
|
||
calEl.innerHTML = html;
|
||
|
||
document.getElementById('cal-prev').addEventListener('click', function () {
|
||
viewMonth--;
|
||
if (viewMonth < 0) { viewMonth = 11; viewYear--; }
|
||
if (selectedKey) { selectedKey = null; detailEl.style.display = 'none'; }
|
||
render();
|
||
});
|
||
document.getElementById('cal-next').addEventListener('click', function () {
|
||
viewMonth++;
|
||
if (viewMonth > 11) { viewMonth = 0; viewYear++; }
|
||
if (selectedKey) { selectedKey = null; detailEl.style.display = 'none'; }
|
||
render();
|
||
});
|
||
}
|
||
|
||
window._calClick = showDayDetail;
|
||
render();
|
||
})();
|
||
|
||
/* ---- Render events ---- */
|
||
(function () {
|
||
const DAYS = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
|
||
const MONTHS = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
|
||
|
||
const raw = document.getElementById('events-data').textContent;
|
||
const events = JSON.parse(raw);
|
||
const container = document.getElementById('events-container');
|
||
|
||
if (!events || events.length === 0) {
|
||
container.innerHTML = '<p class="no-events">No upcoming events at this time.<br>Check back soon.</p>';
|
||
return;
|
||
}
|
||
|
||
events.sort((a, b) => new Date(a.date) - new Date(b.date));
|
||
|
||
const today = new Date();
|
||
today.setHours(0, 0, 0, 0);
|
||
|
||
const upcoming = events.filter(e => {
|
||
const [y, m, d] = e.date.split('-').map(Number);
|
||
const dt = new Date(y, m - 1, d);
|
||
return dt >= today;
|
||
});
|
||
|
||
if (upcoming.length === 0) {
|
||
container.innerHTML = '<p class="no-events">No upcoming events at this time.<br>Check back soon.</p>';
|
||
return;
|
||
}
|
||
|
||
upcoming.forEach(event => {
|
||
const [y, m, d] = event.date.split('-').map(Number);
|
||
const dt = new Date(y, m - 1, d);
|
||
const mon = MONTHS[dt.getMonth()];
|
||
const day = dt.getDate();
|
||
const dow = DAYS[dt.getDay()];
|
||
|
||
const tagHTML = event.tag
|
||
? `<span class="event-card__tag">${escHtml(event.tag)}</span>`
|
||
: '';
|
||
|
||
const card = document.createElement('article');
|
||
card.className = 'event-card';
|
||
card.innerHTML = `
|
||
<div class="event-card__date-block">
|
||
<div class="event-card__cal">
|
||
<div class="event-card__cal-month">${mon}</div>
|
||
<div class="event-card__cal-day">${day}</div>
|
||
</div>
|
||
<div class="event-card__datetime">
|
||
<div class="event-card__dow">${dow}</div>
|
||
<div class="event-card__time">${escHtml(event.time)}</div>
|
||
</div>
|
||
</div>
|
||
<h2 class="event-card__name">${escHtml(event.name)}</h2>
|
||
<p class="event-card__desc">${escHtml(event.description)}</p>
|
||
${tagHTML}
|
||
`;
|
||
container.appendChild(card);
|
||
});
|
||
})();
|
||
|
||
function escHtml(str) {
|
||
return String(str)
|
||
.replace(/&/g, '&')
|
||
.replace(/</g, '<')
|
||
.replace(/>/g, '>')
|
||
.replace(/"/g, '"')
|
||
.replace(/'/g, ''');
|
||
}
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|