diff --git a/blocks/events/events.css b/blocks/events/events.css new file mode 100644 index 0000000..b30da2d --- /dev/null +++ b/blocks/events/events.css @@ -0,0 +1,48 @@ +* { + box-sizing: border-box; +} + +main .section > div { + margin: 0 auto; + padding: 0 15px; +} + +.section.events-container .events-wrapper { + padding-top: 0; +} + +.events .list .filter .select .dropdown-menu.open { + max-height: initial; +} + +.events .list .filter .select.open .dropdown-toggle, +.events .list .filter .select .dropdown-toggle:hover { + color: inherit; +} + +.events label { + display: block; + padding: 0 0 0 37px; + margin: 0 0 20px; + position: relative; + cursor: pointer; +} + +.events input[type="checkbox"].filter-item { + display: none; +} + +.events label span::before { + content: ""; + width: 18px; + height: 18px; + position: absolute; + left: 9px; + background: none; + border: 2px solid #adb3b7; +} + +.events input[type="checkbox"].filter-item:checked+span::before { + background-color: var(--primary-color); + border-color: var(--primary-color); +} \ No newline at end of file diff --git a/blocks/events/events.js b/blocks/events/events.js new file mode 100644 index 0000000..875eee4 --- /dev/null +++ b/blocks/events/events.js @@ -0,0 +1,319 @@ +import { createOptimizedPicture, capitalizeWords, toClassName } from '../../scripts/aem.js'; +import { + div, a, p, ul, li, article, span, + label, input, h2, + h3, + nav, + button, +} from '../../scripts/dom-builder.js'; +import { formatDateRange } from '../../scripts/scripts.js'; + +const REGIONS = [ + 'Europe', + 'North America', +]; + +const TYPES = [ + 'Conference', + 'Events', + 'Webinar', +]; + +async function fetchPostData() { + try { + const response = await fetch('/query-index.json'); + const jsonData = await response.json(); + return jsonData.data; + } catch (error) { + return []; + } +} + +let currentPageNumber = 1; +const itemsPerPage = 10; + +// Function to sort events based on their start dates +function sortEventsByDate(events) { + // Sort events based on their start dates + events.sort((dateA, dateB) => { + // Convert start dates to Date objects for comparison + const dateAnew = new Date(dateA.startdate * 1000); + const dateBnew = new Date(dateB.startdate * 1000); + + // Compare dates + if (dateAnew < dateBnew) { + return -1; // dateA comes before dateB + } if (dateAnew > dateBnew) { + return 1; // dateA comes after dateB + } + return 0; // dates are equal + }); + + return events; +} + +// Function to separate events based on current date and class parameter +function separateEventsByDate(events, currentDate, classParameter) { + const futureEvents = []; + const archivedEvents = []; + events.forEach((event) => { + const startDate = new Date(event.startdate * 1000); + const endDate = new Date(event.enddate * 1000); + + if (startDate > currentDate || endDate > currentDate) { + futureEvents.push(event); + } else if (endDate < currentDate) { + archivedEvents.push(event); + } + }); + + return classParameter === 'future' ? futureEvents : archivedEvents; +} + +async function generateEventDetails(articles) { + const articleElements = articles.map((art) => { + let date = ''; + if (art.startdate && art.enddate) { + const endDate = new Date(art.enddate * 1000).toLocaleDateString('en-Us', { month: 'short', day: '2-digit', year: 'numeric' }); + const eventDate = art.startdate === art.enddate + ? endDate : formatDateRange(art.startdate, art.enddate); + date = (art.eventtime !== '') ? `${eventDate} ${art.eventtime}` : eventDate; + } + return article( + { class: 'item' }, + div( + { class: 'image' }, + a({ + href: art.path, + title: art.title, + }, createOptimizedPicture(art.image, art.title)), + ), + div( + { class: 'content' }, + date ? p({ class: 'cite' }, date) : '', + p( + a({ + class: 'title', + title: art.title, + href: art.path, + }, capitalizeWords(art.title)), + ), + ul( + { class: 'keyword-list' }, + li({ class: 'item' }, art.type), + li({ class: 'item' }, art.address !== art.region ? art.address : art.region), + (art.address !== art.region ? li({ class: 'item' }, art.region) : ''), + ), + ), + ); + }); + return articleElements; +} + +// Function to update the events displayed based on filtering +function updateEvents(events) { + // Clear the existing event list + const itemsContainer = document.querySelector('.items'); + itemsContainer.innerHTML = ''; + + // Generate and append new event details + generateEventDetails(events).then((eventContent) => { + const pageTitle = document.title; + itemsContainer.appendChild(h2({ class: 'event-title' }, pageTitle)); + if (eventContent.length === 0) { + const noEventsMesage = h3({ class: 'no-result' }, 'No Events Found'); + itemsContainer.appendChild(noEventsMesage); + } else { + eventContent.forEach((element) => { + itemsContainer.appendChild(element); + }); + } + }); +} + +// Event listener function to handle checkbox changes +function handleCheckboxChange(event, eventData) { + const checkedCheckboxes = document.querySelectorAll('.filter-item:checked'); + const selectedOptions = Array.from(checkedCheckboxes) + .map((checkbox) => checkbox.nextSibling.textContent); + let filteredEvents; + // Filter events based on selected options + const eventTypes = []; + const regions = []; + + if (selectedOptions.length > 0) { + selectedOptions.forEach((option) => { + if (eventData.some((data) => data.type === option)) { + eventTypes.push(option); + } else if (eventData.some((data) => data.region === option)) { + regions.push(option); + } + }); + + if (eventTypes.length > 0 && regions.length === 0) { + filteredEvents = eventData.filter((data) => eventTypes.includes(data.type)); + } else if (eventTypes.length === 0 && regions.length > 0) { + filteredEvents = eventData.filter((data) => regions.includes(data.region)); + } else { + filteredEvents = eventData.filter((data) => eventTypes + .includes(data.type) && regions.includes(data.region)); + } + } else { + filteredEvents = eventData; + } + updateEvents(filteredEvents); + const paginationContainer = document.querySelector('.pagination'); + if (filteredEvents.length <= itemsPerPage) { + paginationContainer.style.display = 'none'; + } else { + paginationContainer.style.display = 'block'; + } +} + +function createEventsDropdown(eventName, options) { + const container = div({ class: 'select' }); + container.setAttribute('name', eventName); + + const btn = div({ + type: 'button', + class: 'dropdown-toggle', + value: '', + }, eventName); + // btn.addEventListener('click', toggleFilter, false); + container.append(btn); + + const dropDown = div({ class: 'dropdown-menu' }); + options.forEach((option) => { + const fieldName = toClassName(option.toString()); + dropDown.append(label( + { for: fieldName }, + input({ + type: 'checkbox', + name: fieldName, + id: fieldName, + class: 'filter-item', + }), + span(option), + )); + }); + container.append(dropDown); + + return container; +} + +function createLink(text, currentPage) { + const linkHref = currentPage === 'events' ? '/about-us/archived-events' : '/about-us/events'; + const link = p(a({ href: linkHref, title: text }, text)); + return link; +} + +async function buildSidePanel(currentPage, eventData) { + const sidePanel = div({ class: 'filter' }); + const panelTitle = p({ class: 'panel-title' }, 'Filter By:'); + + // Dropdowns + const eventTypeDropdown = createEventsDropdown('Event Type', TYPES); + const regionDropdown = createEventsDropdown('Region', REGIONS); + + // Append dropdowns to filter div + const linkText = currentPage === 'events' ? 'Archived Events' : 'Upcoming Events'; + const link = createLink(linkText, currentPage); + + // Append filter div to side panel + sidePanel.appendChild(panelTitle); + sidePanel.appendChild(eventTypeDropdown); + sidePanel.appendChild(regionDropdown); + sidePanel.appendChild(link); + + const checkboxes = sidePanel.querySelectorAll('.select .dropdown-menu .filter-item'); + checkboxes.forEach((checkbox) => { + checkbox.addEventListener('change', (event) => { + handleCheckboxChange(event, eventData); + }); + }); + + return sidePanel; +} + +function updatePaginationButtons(currentPage) { + document.querySelectorAll('.pagination .pager-item').forEach((data) => { + data.classList.remove('active'); + if (parseInt(data.textContent, 10) === currentPage) { + data.classList.add('active'); + } + }); +} + +function displayPage(page, events) { + const startIndex = (page - 1) * itemsPerPage; + const endIndex = startIndex + itemsPerPage; + const currentPageEvents = events.slice(startIndex, endIndex); + updateEvents(currentPageEvents); +} + +function handlePagination(page, events) { + currentPageNumber = page; + displayPage(currentPageNumber, events); // Update UI with new page +} + +function generatePaginationButtons(totalPages, currentPage, events) { + const paginationContainer = nav({ class: 'pagination' }); + for (let i = 1; i <= totalPages; i += 1) { + const pageButton = button({ class: 'pager-item' }, i); + if (i === currentPage) { + pageButton.classList.add('active'); + } + pageButton.addEventListener('click', () => { + handlePagination(i, events); + updatePaginationButtons(i); + }); + paginationContainer.appendChild(pageButton); + } + return paginationContainer; +} + +function getTotalPages(events) { + return Math.ceil(events.length / itemsPerPage); +} + +export default async function decorate(block) { + const outerBlock = document.querySelector('.section'); + outerBlock.classList.add('outer'); + const postData = await fetchPostData(); + const page = window.location.pathname.includes('/events'); + const currentPage = page ? 'events' : 'archived-events'; + const filteredResults = postData.filter((item) => /events\/.*$/.test(item.path.toLowerCase())); + const sortedEvents = sortEventsByDate(filteredResults); + const currentDate = new Date(); + const classParameter = document.querySelector('.events.future') ? 'future' : 'archive'; + const eventsToshow = separateEventsByDate(sortedEvents, currentDate, classParameter); + const itemsContainer = div({ class: 'items' }); + + const eventContent = await generateEventDetails(eventsToshow); + const sidePanel = await buildSidePanel(currentPage, eventsToshow); + + const wrapper = div({ class: 'list' }); + const pageTitle = document.title; + const title = h2({ class: 'event-title' }, pageTitle); + itemsContainer.append(title); + wrapper.appendChild(sidePanel); + if (eventContent && eventContent.length > 0) { + eventContent.forEach((element) => { + itemsContainer.appendChild(element); + }); + } else { + const noEventMessage = currentPage === 'events' ? 'No Upcoming Events' : 'No Archived Events'; + const noResults = h3({ class: 'no-result' }, noEventMessage); + itemsContainer.appendChild(noResults); + block.appendChild(itemsContainer); + } + + wrapper.appendChild(itemsContainer); + block.appendChild(wrapper); + if (eventsToshow.length > itemsPerPage) { + const totalPages = getTotalPages(eventsToshow); + wrapper.appendChild(generatePaginationButtons(totalPages, currentPageNumber, eventsToshow)); + + displayPage(currentPageNumber, eventsToshow); + } +} diff --git a/head.html b/head.html index 01ba501..3b7b98f 100644 --- a/head.html +++ b/head.html @@ -8,3 +8,4 @@ + diff --git a/scripts/dom-builder.js b/scripts/dom-builder.js index 4253cef..da06b26 100644 --- a/scripts/dom-builder.js +++ b/scripts/dom-builder.js @@ -86,3 +86,9 @@ export function tr(...items) { return domEl('tr', ...items); } export function td(...items) { return domEl('td', ...items); } export function th(...items) { return domEl('th', ...items); } export function time(...items) { return domEl('time', ...items); } + +export function checkbox(attributes, labelContent) { + const checkboxInput = input({ type: 'checkbox', ...attributes }); + const labelElement = label(labelContent, checkboxInput); + return div(labelElement); +} diff --git a/scripts/scripts.js b/scripts/scripts.js index b35546e..58c29f2 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -340,6 +340,22 @@ function correctUTMFlow() { } } +export function formatDateRange(startdate, enddate) { + const options = { month: 'short', day: '2-digit', year: 'numeric' }; + const formattedStartDate = new Date(Number(startdate) * 1000).toLocaleDateString('en-us', options); + const formattedEndDate = new Date(Number(enddate) * 1000).toLocaleDateString('en-us', options); + const startYear = new Date(formattedStartDate).getFullYear(); + const endYear = new Date(formattedEndDate).getFullYear(); + const differentYear = startYear !== endYear; + let dateRangeString; + if (differentYear) { + dateRangeString = `${formattedStartDate} - ${formattedEndDate}`; + } else { + dateRangeString = `${(formattedStartDate).split(',')[0]} - ${formattedEndDate}`; + } + return dateRangeString; +} + async function loadPage() { await loadEager(document); await loadLazy(document); diff --git a/styles/list.css b/styles/list.css new file mode 100644 index 0000000..4bd9744 --- /dev/null +++ b/styles/list.css @@ -0,0 +1,258 @@ +.list { + padding-top: 67px; +} + +.list > * { + font-size: 18px; + text-align: left; +} + +.list .event-title { + font-size: 36px; + padding-top: 6px; + margin-top: 0; + line-height: 0.8; + border-bottom: 2px solid #ccc; +} + +/* panel */ +.list > .filter > .panel-title { + font-size: 20px; + font-weight: bold; + margin: 0; + padding: 8px 0 22px; + border-bottom: 2px solid #ccc; +} + +/* filters */ +.list > .filter > .select { + border-bottom: 2px solid #ccc; + padding: 10px 0 16px; +} + +.list > .filter > .select > .dropdown-toggle { + cursor: pointer; + position: relative; + padding: 15px 35px 16px 1px; + text-align: left; + font-weight: bold; + height: 56px; + width: 100%; + max-width: unset; + background: none; + color: unset; + transition-duration: 0.3s; +} + +.list > .filter > .select.open > .dropdown-toggle, +.list > .filter > .select > .dropdown-toggle:hover { + color: #adb3b7; +} + + +.list > .filter > .select > .dropdown-menu > p > a { + color: unset; + text-decoration: none; +} + +.list > .filter > p > a { + font-weight: bold; +} + +.list > .filter > p > a:hover { + text-decoration: none; +} + +/* entries */ +.list > .items > .item { + height: auto; + max-width: 100%; + border-bottom: 2px solid #ccc; + padding: 37px 0 40px; +} + +.list > .items > .item:last-of-type { + padding-bottom: 22px; +} + +.list > .items > .item > .content, +.list > .items > .item > .image { + width: 100%; + height: auto; +} + +.list > .items > .item > .content { + padding-left: 30px; +} + +.list > .items > .item > .content > p > .title { + font-size: 20px; + font-weight: bold; + line-height: 1.1; + margin-top: 15px; + margin-bottom:12px; + color: unset; + text-decoration: none; +} + +ul.keyword-list { + margin: 0; + padding: 0; + list-style: none; +} + +ul li { + padding: 0 !important; +} + +ul.keyword-list li { + display: inline-block; + margin-right: 10px; + position: relative; + font-size: 18px; + vertical-align: middle; +} + +ul.keyword-list li:not(:last-of-type)::after { + content: '|'; + display: inline-block; + margin-left: 10px; +} + + +.list > .items > .item > .content > .cite { + padding: 0 !important; + font-size: 14px; + font-style: normal; + font-weight: 600; + color: #333; +} + +.list > .items > .item > .image { + padding-right: 15px; +} + +.list > .items > .item > .image img { + max-width: 100%; + height: 100%; + vertical-align: middle; + border: 0; + aspect-ratio: 16 / 16; +} + +/* Styles for pagination */ +.list > .pagination { +flex: 100%; +margin-top: 30px; +text-align: center; +} + +.list > .pagination button { +padding: 5px 10px; +margin: 0 3px; +background-color: #fff; +color: black; +border: none; +cursor: pointer; +} + +.list > .pagination button:hover { +background-color: #fff; +} + +.list > .pagination > .pager-item.active { +border: 2px solid #007bff; +color: #007bff; +background-color: #fff; + +} + +.list > .pagination > .pager-item:disabled { +opacity: 0.5; +cursor: not-allowed; +} + +@media (max-width: 991px) { + .main.section.events-container { + padding-top: 20px; + padding-bottom: 20px; + } +} + +@media only screen and (max-width: 767px) { + .list > .items > .item > .content { + margin-top: 15px; + } + + .list > .items > .item > .image { + padding-left: 15px; + } +} + +@media (min-width: 769px) { + .list { + display: flex; + flex-flow: wrap; + flex-direction: row; + } + + .list > .items > .item { + display: flex; + flex-direction: row; + padding: 37px 0 20px; + } + + .list > .items > .item > .content { + flex: 66.67%; + margin-bottom: 20px; + } + + .list > .items > .item > .content > .cite { + margin-top: 1px; + } + + .list > .items > .item > .image { + flex: 40%; + margin-bottom: 20px; + } + + .list > .filter { + width: 100%; + } + + .list > .filter > .select { + max-width: 259px; + } +} + +@media (min-width: 992px) { + .list > .filter { + width: 25%; + padding-right: 15px; + } + + .list > .items { + width: 75%; + } + + .list > .items > .item { + margin-left: 6px; + } + + .list > .items > .item:first-of-type { + padding-top: 20px; + } + + .list > .items > .item > .content { + width: 66.67%; + padding-left: 15px; + } + + .list > .items > .item > image { + width: 33.33%; + } + + .list > .filter > .panel-title { + max-width: 259px; + } +}