Skip to content

Commit

Permalink
Add handling for likes, filters, and keyboard navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
geekcrea committed Jul 31, 2024
1 parent 96bc220 commit b0a0b3c
Show file tree
Hide file tree
Showing 6 changed files with 299 additions and 58 deletions.
51 changes: 0 additions & 51 deletions data/photographerFactory.js

This file was deleted.

6 changes: 3 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
</head>
<body>
<header>
<a href="./index.html">
<a href="./photographer.html">
<img
src="assets/images/logo.png"
class="logo"
Expand All @@ -26,7 +26,7 @@ <h1>Nos photographes</h1>
<main id="main">
<div class="photographer_section"></div>
</main>
<script type="module" src="scripts/factories/photographerFactory.js"></script>
<script type="module" src="scripts/pages/index.js"></script>
<script type="module" src="./scripts/factories/photographerFactory.js"></script>
<script type="module" src="./scripts/pages/index.js"></script>
</body>
</html>
12 changes: 8 additions & 4 deletions photographer.html
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ <h3 id="photographTagline"></h3>
<header class="title-modal">
<h2 id="contact-title-wording1">Contactez-moi</h2>
<h2 id="contact-title-wording2"></h2>
<img id="closeCross" src="assets/icons/close.svg" alt="bouton de fermeture du formulaire de contact" />
<img id="closeCross" src="./assets/icons/close.svg" alt="bouton de fermeture du formulaire de contact" />
</header>
<form id="contactForm">
<div>
Expand All @@ -80,8 +80,12 @@ <h2 id="contact-title-wording2"></h2>
</form>
</div>
</div>
<script type="module" src="scripts/utils/contactForm.js"></script>
<script type="module" src="scripts/pages/photographer.js"></script>
<script type="module" src="scripts/factories/mediaFactory.js"></script>
<script type="module" src="./scripts/utils/globalState.js"></script>
<script type="module" src="./scripts/pages/photographer.js"></script>
<script type="module" src="./scripts/factories/mediaFactory.js"></script>
<script type="module" src="./scripts/utils/contactForm.js"></script>
<script type="module" src="./scripts/utils/lightBox.js"></script>
<script type="module" src="./scripts/utils/sort.js"></script>
<script type="module" src="./scripts/utils/likes.js"></script>
</body>
</html>
5 changes: 5 additions & 0 deletions scripts/utils/globalState.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// define a global state object to manage shared application state
export const globalState = {
mediaClickedIndex: -1,
media: [],
};
36 changes: 36 additions & 0 deletions scripts/utils/likes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// change the likeHeart color and increment media like count ang global like count
// unlike if likeHeart is clicked again
export function likeMedia(event) {
// get the element displaying the global like count
const globalLikeCountElement = document.getElementById('nbLike');
// get the clicked element (the like heart icon)
const targetElement = event.currentTarget;
// get the adjacent element (previous sibling) that displays the like count for the specific media
const likeCountElement = targetElement.previousElementSibling;
// extract the current like count for the specific media
let currentCount = parseInt(likeCountElement.textContent, 10);
// extract the current global like count
const currentColor = getComputedStyle(targetElement).color;

// determine the current color of the like heart icon
let globalCount = parseInt(globalLikeCountElement.textContent, 10);

// check if the media is already liked based on the color of the like heart
if (currentColor === 'rgb(144, 28, 28)') {
// if liked, set the color to black
targetElement.style.color = 'black';
// decrement the like counts
currentCount -= 1;
globalCount -= 1;
} else {
// if not liked, set the color to red
targetElement.style.color = '#901C1C';
// increment the like counts
currentCount += 1;
globalCount += 1;
}

// update the display values for both the media and global like counts
likeCountElement.textContent = currentCount;
globalLikeCountElement.textContent = globalCount;
}
247 changes: 247 additions & 0 deletions scripts/utils/sort.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
import { globalState } from '../utils/globalState.js';
import { displayMediaData } from '../pages/photographer.js';

// toggle display of sort menu
function displaySortMenu() {
const sortBlockOptions = document.querySelector('.options');
const selectedOption = document.querySelector('.selected-option');

// toggle visibility of sort options
if (sortBlockOptions.style.display === 'block') {
sortBlockOptions.style.display = 'none';
// additional visual and accessibility settings for hiding options
sortBlockOptions.style.visibility = 'hidden';
sortBlockOptions.classList.remove('open');
selectedOption.classList.remove('open');
selectedOption.setAttribute('aria-expanded', 'false');
} else {
sortBlockOptions.style.display = 'block';
sortBlockOptions.style.visibility = 'visible';
// additional visual and accessibility settings for displaying options
sortBlockOptions.classList.add('open');
selectedOption.classList.add('open');
selectedOption.setAttribute('aria-expanded', 'true');
}
// set focus to the selected option for accessibility
selectedOption.focus();
}

// handle when a sort option is clicked
function handleOptionClick(event) {
const selectedOption = document.querySelector('.selected-option');
const clickedOption = event.target;
// update the displayed selected option with the clicked one
selectedOption.textContent = clickedOption.getAttribute(
'data-original-content'
);
const sortingCriteria = clickedOption.dataset.value;
// sort media based on the selected criteria
sortAndDisplayMedia(sortingCriteria);
displaySortMenu();
}

// event listener for displaying sort menu when selected option is clicked
document
.querySelector('.selected-option')
.addEventListener('click', displaySortMenu);

// get all available sorting options
const sortOptions = document.querySelectorAll('.option');

// attach an event handler to each option to handle the click
sortOptions.forEach((sortOption) => {
sortOption.addEventListener('click', handleOptionClick);
});

// for each option, store its current content in a data attribute for later use
document.querySelectorAll('.option').forEach((option) => {
option.setAttribute('data-original-content', option.textContent);
});

// add an event listener to handle the click on the custom select
document.querySelector('.custom-select').addEventListener('click', (event) => {
if (event.target.classList.contains('option')) {
processSelectedOption(event.target);
}
});

// add an event listener to handle 'Enter' key press on the custom select
document
.querySelector('.custom-select')
.addEventListener('keydown', (event) => {
if (event.key === 'Enter' && event.target.classList.contains('option')) {
processSelectedOption(event.target);
event.preventDefault();
}
});

// process the selected sorting option
function processSelectedOption(optionElement) {
const selectedOptionElem = document.querySelector('.selected-option');
const selectedValue = optionElement.dataset.value;

// update the displayed content and attributes of the selected option based on the chosen value
switch (selectedValue) {
// set display and attributes for "Popularity" option
case 'popularity':
selectedOptionElem.textContent = 'Popularité';
selectedOptionElem.dataset.value = 'popularity';
selectedOptionElem.setAttribute('aria-label', 'Trier par popularité');
document.querySelector('.option[data-value="popularity"]').style.display =
'none';
document
.querySelector('.option[data-value="popularity"]')
.setAttribute('tabindex', '-1');
document.querySelector('.option[data-value="date"]').style.display =
'block';
document
.querySelector('.option[data-value="date"]')
.setAttribute('tabindex', '0');
document
.querySelector('.option[data-value="date"]')
.setAttribute('aria-label', 'Trier par date');
document.querySelector('.option[data-value="title"]').style.display =
'block';
document
.querySelector('.option[data-value="title"]')
.setAttribute('tabindex', '0');
document
.querySelector('.option[data-value="title"]')
.setAttribute('aria-label', 'Trier par titre');
break;

// set display and attributes for "Date" option
case 'date':
selectedOptionElem.textContent = 'Date';
selectedOptionElem.dataset.value = 'date';
selectedOptionElem.setAttribute('aria-label', 'Trier par date');
document.querySelector('.option[data-value="date"]').style.display =
'none';
document
.querySelector('.option[data-value="date"]')
.setAttribute('tabindex', '-1');
document.querySelector('.option[data-value="popularity"]').style.display =
'block';
document
.querySelector('.option[data-value="popularity"]')
.setAttribute('tabindex', '0');
document
.querySelector('.option[data-value="popularity"]')
.setAttribute('aria-label', 'Trier par popularité');
document.querySelector('.option[data-value="title"]').style.display =
'block';
document
.querySelector('.option[data-value="title"]')
.setAttribute('tabindex', '0');
document
.querySelector('.option[data-value="title"]')
.setAttribute('aria-label', 'Trier par titre');
break;

// set display and attributes for "Title" option
case 'title':
selectedOptionElem.textContent = 'Titre';
selectedOptionElem.dataset.value = 'title';
selectedOptionElem.setAttribute('aria-label', 'Trier par titre');
document.querySelector('.option[data-value="title"]').style.display =
'none';
document
.querySelector('.option[data-value="title"]')
.setAttribute('tabindex', '-1');
document.querySelector('.option[data-value="date"]').style.display =
'block';
document
.querySelector('.option[data-value="date"]')
.setAttribute('tabindex', '0');
document
.querySelector('.option[data-value="date"]')
.setAttribute('aria-label', 'Trier par date');
document.querySelector('.option[data-value="popularity"]').style.display =
'block';
document
.querySelector('.option[data-value="popularity"]')
.setAttribute('tabindex', '0');
document
.querySelector('.option[data-value="popularity"]')
.setAttribute('aria-label', 'Trier par popularité');
break;
}
// sort and display media based on the selected criteria
sortAndDisplayMedia(selectedValue);
}

// sort media based on given criteria
function sortMedia(criteria) {
// sorting the media based on the provided criteria
switch (criteria) {
case 'popularity':
return globalState.media.sort((a, b) => b.likes - a.likes);
case 'date':
return globalState.media.sort(
(a, b) => new Date(b.date) - new Date(a.date)
);
case 'title':
return globalState.media.sort((a, b) => a.title.localeCompare(b.title));
default:
console.error('Unknown sorting criteria:', criteria);
return globalState.media;
}
}

// sort media based on criteria and then display the sorted media
export function sortAndDisplayMedia(criteria) {
const sortedMedia = sortMedia(criteria);
displayMediaData(sortedMedia);
}

// keyboard accessibility: handle 'Enter' key for selected option
document
.querySelector('.selected-option')
.addEventListener('keydown', (event) => {
if (event.key === 'Enter') {
displaySortMenu();
event.preventDefault();
const firstVisibleOption = document.querySelector(
'.option:not([style*="display: none"])'
);
if (firstVisibleOption) firstVisibleOption.focus();
}
});

// keyboard accessibility: handle 'Enter' and 'Escape' keys for sort options
document.querySelectorAll('.option').forEach((optionElement) => {
optionElement.addEventListener('keydown', (event) => {
if (event.key === 'Enter') {
handleOptionClick(event);
const sortBlockOptions = document.querySelector('.options');
sortBlockOptions.style.display = 'none';
const selectedOption = document.querySelector('.selected-option');
selectedOption.focus();
event.preventDefault();
} else if (event.key === 'Escape') {
displaySortMenu();
const selectedOption = document.querySelector('.selected-option');
selectedOption.focus();
event.preventDefault();
}
});
});

// close the sort menu when clicking outside of it
document.addEventListener('click', (event) => {
if (!document.querySelector('.custom-select').contains(event.target)) {
const sortBlockOptions = document.querySelector('.options');
if (sortBlockOptions.style.display === 'block') {
displaySortMenu();
}
}
});

// keyboard accessibility: handle 'Escape' key to close sort menu
document
.querySelector('.custom-select')
.addEventListener('keydown', (event) => {
if (event.key === 'Escape') {
displaySortMenu();
}
});

0 comments on commit b0a0b3c

Please sign in to comment.