diff --git a/README.md b/README.md index 21685896a..6656d2592 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ -# Hang In There - -A boilerplate repo. +# Hang in There -## Set Up +### ๐ŸŽจ Abstract: +[//]: <> +This "Motivational Posters" project generates a random motivational poster to inspire the user. Other functionality includes creating a unique poster with user input for a title, quote and photo. All of these posters can be saved and viewed in a group. For an added bonus, viewers can go to an "unmotivational poster" page for some dark humor. What better way to motivate people than through comedy! If these unmotivational posters are not to the users taste, they can double-click them to make them disappear from the page. +### ๐Ÿ–ฅ๏ธ Installation Instructions: +[//]: <> 1. Fork this repository 2. Clone down your new, forked repo 3. `cd` into the repository @@ -15,32 +17,29 @@ Project spec & rubric can be found [here](https://curriculum.turing.edu/module2/ To view your project: 1. In your terminal, navigate to your project repo -2. Run the command `open index.html` - -______________________________________________________ -# README Template -Before turning this project in, erase this line and everything above it and fill in the info below. -______________________________________________________ - -# Hang in There - -### Abstract: -[//]: <> (Briefly describe what you built and its features. What problem is the app solving? How does this application solve that problem?) +2. Run the command `open index.html` -### Installation Instructions: -[//]: <> (What steps does a person have to take to get your app cloned down and running?) - -### Preview of App: +### ๐Ÿ”Ž Preview of App: [//]: <> (Provide ONE gif or screenshot of your application - choose the "coolest" piece of functionality to show off. gifs preferred!) -### Context: -[//]: <> (Give some context for the project here. How long did you have to work on it? How far into the Turing program are you?) - -### Contributors: -[//]: <> (Who worked on this application? Link to your GitHub. Consider also providing LinkedIn link) - -### Learning Goals: -[//]: <> (What were the learning goals of this project? What tech did you work with?) - -### Wins + Challenges: -[//]: <> (What are 2-3 wins you have from this project? What were some challenges you faced - and how did you get over them?) +### ๐Ÿ—’๏ธ Context: +[//]: <> +This solo GitHub project is from Turing School of Software and Design's start of Mod 2, which is the seventh week of the program. Within an eight day deadline, this assignment challenged us to integrate our skills learned thus far to design, develop and deploy a fully functional GitHub project. + +### ๐Ÿ‘๐Ÿฝ Credits: +[//]: <> +Terra D. Manning
+https://www.linkedin.com/in/terra-manning/
+https://github.com/TDManning
+ +### ๐ŸŒฑ Learning Goals: +[//]: <> +1. Practice reading, understanding, and using existing code +2. Write clean, DRY JavaScript + * Build out funcitonality using functions that6 show trends toward SRP + * Manipulate the page after it has loaded by adding, removing, and updating elements on the DOM +3. Use CSS and HTML to match styling and layout of provided comps + +### ๐Ÿ† Wins + Challenges: +[//]: <> +Overall, this project was a lot of fun and each creating the javascript code in each iteration was rewarding because of its immediate functionality on the DOM. It was challenging to get the CSS alignment correct using flexbox and javascript, however the use of the Dev Console Tools make the trial and error process much more manageable. diff --git a/index.html b/index.html index ca0707621..be26bcc37 100644 --- a/index.html +++ b/index.html @@ -15,9 +15,11 @@

Title

Quote

+ + + \ No newline at end of file diff --git a/src/main.js b/src/main.js index d818a0ab6..8552d96f6 100644 --- a/src/main.js +++ b/src/main.js @@ -1,8 +1,6 @@ -// query selector variables go here ๐Ÿ‘‡ - // we've provided you with some data to work with ๐Ÿ‘‡ // tip: you can tuck this data out of view with the dropdown found near the line number where the variable is declared -var images = [ +const images = [ "./assets/bees.jpg", "./assets/bridge.jpg", "./assets/butterfly.jpg", @@ -22,7 +20,7 @@ var images = [ "./assets/tiger.jpg", "./assets/turtle.jpg" ]; -var titles = [ +const titles = [ "determination", "success", "inspiration", @@ -59,7 +57,7 @@ var titles = [ "understanding", "wisdom" ]; -var quotes = [ +const quotes = [ "Donโ€™t downgrade your dream just to fit your reality, upgrade your conviction to match your destiny.", "You are braver than you believe, stronger than you seem and smarter than you think.", "You are confined only by the walls you build yourself.", @@ -99,13 +97,177 @@ var quotes = [ "Each person must live their life as a model for others.", "A champion is defined not by their wins but by how they can recover when they fall." ]; +let unmotivationalPosters = [ + { + name: "FAILURE", + description: "Why bother trying? It's probably not worth it.", + price: 68.00, + year: 2019, + vintage: true, + img_url: "./assets/failure.jpg", + }, + { + name: "MEDIOCRITY", + description: "Dreams are just thatโ€”dreams.", + price: 127.00, + year: 2021, + vintage: false, + img_url: "./assets/mediocrity.jpg", + }, + { + name: "REGRET", + description: "Hard work rarely pays off.", + price: 89.00, + year: 2018, + vintage: true, + img_url: "./assets/regret.jpg", + }, + { + name: "FUTILITY", + description: "You're not good enough.", + price: 150.00, + year: 2016, + vintage: false, + img_url: "./assets/futility.jpg", + }, + { + name: "DEFEAT", + description: "It's too late to start now.", + price: 35.00, + year: 2023, + vintage: false, + img_url: "./assets/defeat.jpg", + }, + { + name: "HOPELESSNESS", + description: "Stay in your comfort zone; it's safer.", + price: 112.00, + year: 2020, + vintage: true, + img_url: "./assets/hopelessness.jpg", + }, + { + name: "LAZINESS", + description: "You can't change anything.", + price: 25.00, + year: 2022, + vintage: false, + img_url: "./assets/laziness.jpg", + }, + { + name: "PROCRASTINATION", + description: "Better to avoid failure by not trying at all.", + price: 48.00, + year: 2017, + vintage: true, + img_url: "./assets/procrastination.jpg", + }, + { + name: "DESPAIR", + description: "Let someone else do it; youโ€™ll just mess it up.", + price: 73.00, + year: 2015, + vintage: false, + img_url: "./assets/despair.jpg", + }, + { + name: "NEGLECT", + description: "Happiness is overrated.", + price: 160.00, + year: 2019, + vintage: true, + img_url: "./assets/neglect.jpg", + }, + { + name: "FEAR", + description: "Giving up is always an option.", + price: 91.00, + year: 2014, + vintage: false, + img_url: "./assets/fear.jpg", + }, + { + name: "APATHY", + description: "No one cares about your effort.", + price: 110.00, + year: 2016, + vintage: true, + img_url: "./assets/apathy.jpg", + }, + { + name: "MISERY", + description: "Why take risks when you can stay stagnant?", + price: 55.00, + year: 2021, + vintage: false, + img_url: "./assets/misery.jpg", + }, + { + name: "BLAME", + description: "Expect disappointment and you'll never be disappointed.", + price: 39.00, + year: 2017, + vintage: true, + img_url: "./assets/blame.jpg", + }, + { + name: "DOUBT", + description: "Success is for other people, not you.", + price: 140.00, + year: 2020, + vintage: false, + img_url: "./assets/doubt.jpg", + } +]; + var savedPosters = []; var currentPoster; +// query selector variables go here ๐Ÿ‘‡ +var posterImage = document.querySelector(".poster-img") +var posterTitle = document.querySelector(".poster-title") +var posterQuote = document.querySelector(".poster-quote") +var userPosterImage = document.querySelector("#poster-image-url") +var userPosterTitle = document.querySelector("#poster-title") +var userPosterQuote = document.querySelector("#poster-quote") +var mainPosterViewSection = document.querySelector(".main-poster") +var OwnPosterFormSection = document.querySelector(".poster-form") +var savedPostersSection = document.querySelector(".saved-posters") +var savedPostersGrid = document.querySelector(".saved-posters-grid") +var makeOwnPosterButton = document.querySelector(".show-form") +var showSavedPostersButton = document.querySelector(".show-saved") +var showMainPageButton = document.querySelector(".show-main") +var backToMainButton = document.querySelector(".back-to-main") +var backToMainFromUnmotivationalButton = document.querySelector(".back-to-main-from-unmotivational") +var showRandomPosterButton = document.querySelector(".show-random") +var savePosterButton = document.querySelector('.save-poster') +var showUserPosterButton = document.querySelector(".make-poster") +var showUnmotivationalPostersButton = document.querySelector(".show-unmotivational") +var unmotivationalPostersSection = document.querySelector(".unmotivational-section") +var unmotivationalGrid = document.querySelector("#unmotivational-grid") +var saveMessage = document.querySelector("#save-message") + // event listeners go here ๐Ÿ‘‡ +window.addEventListener("load", showRandomHomepagePoster) +makeOwnPosterButton.addEventListener("click", function() {handleView('form')}) +showSavedPostersButton.addEventListener("click", function() {handleView('saved'); displaySavedPosters()}) +showMainPageButton.addEventListener("click", function() {handleView('main')}) +backToMainButton.addEventListener("click", function() {handleView('main'); showRandomHomepagePoster()}) +showUserPosterButton.addEventListener("click", function() {handleView('main'); showUserCreatedPoster(event)}) +backToMainFromUnmotivationalButton.addEventListener("click", function() {handleView('main')})//possible refactor. all back to main buttons linked +showUnmotivationalPostersButton.addEventListener("click", function() {handleView('unmotivational'); displayCleanedPosters (cleanData(unmotivationalPosters))}); +//When this button is clicked the following happens: +//1. the unmotivational page view is rendered (from the handle view function) +//2. the cleanData function processes each poster (from the unmotivationalPosters array) with a cleaned version. +//3. That cleaned data is passed as an argument to the displayCleanedPosters function +showRandomPosterButton.addEventListener("click", showRandomHomepagePoster) +savePosterButton.addEventListener("click", saveCurrentPoster) +unmotivationalGrid.addEventListener("dblclick", deleteUnmotivationalPoster) //the dblclick is attacvhed to the parent container (#unmotivational-grid) +unmotivationalPostersSection.addEventListener("dblclick", deleteUnmotivationalPoster) + +//function(): when the button is clicked, the anonymous callback function() is executed. the anonymous callback function keeps the other functions from being run until the button is acutually clicked. // functions and event handlers go here ๐Ÿ‘‡ -// (we've provided two to get you started)! function getRandomIndex(array) { return Math.floor(Math.random() * array.length); } @@ -116,4 +278,146 @@ function createPoster(imageURL, title, quote) { imageURL: imageURL, title: title, quote: quote} -} \ No newline at end of file +} + +function handleView(view) { + const views = { + main: mainPosterViewSection, + form: OwnPosterFormSection, + saved: savedPostersSection, + unmotivational: unmotivationalPostersSection + } + //this object above links the keys to the dom elements (via the query selectors) + + Object.values(views).forEach((section) => { + section.classList.add("hidden") + // console.log(Object.values(views)) + }) + //above we start by removing all of the view sections + //Object.values() takes my views object above it and returns an array + //containing all of the values of that objectโ€™s properties, ignoring the keys. + + views[view].classList.remove("hidden") + // console.log(views[view]) +} + +function showPoster(imageURL, title, quote) { + posterImage.src = imageURL + posterTitle.innerText = title + posterQuote.innerText = quote + + currentPoster = { + imageURL: imageURL, + title: title, + quote: quote + } +} + +function showRandomHomepagePoster() { + + saveMessage.classList.add("hidden") + + let randomImage = images[getRandomIndex(images)]; + let randomTitle = titles[getRandomIndex(titles)]; + let randomQuote = quotes[getRandomIndex(quotes)]; + + showPoster(randomImage, randomTitle, randomQuote) + }; + +function showUserCreatedPoster(event) { + event.preventDefault(); + + saveMessage.classList.add("hidden") + + let userImage = userPosterImage.value + let userTitle = userPosterTitle.value + let userQuote = userPosterQuote.value + + showPoster(userImage, userTitle, userQuote) + addUserPosterInput(userImage, userTitle, userQuote) +} + +function addUserPosterInput(image, title, quote) { + images.push(image) + titles.push(title) + quotes.push(quote) +} + +function saveCurrentPoster() { //.some checks if there is already a poster in the array with the same image, title, and quote as the currentPoster +if (!savedPosters.some((poster) => { //If it find a match/evaluates to TRUE, the duplicate poster is not saved. + return poster.imageURL === currentPoster.imageURL && + poster.title === currentPoster.title && + poster.quote === currentPoster.quote + })) { + savedPosters.push(currentPoster) + saveMessage.classList.add("hidden") + // console.log("Poster saved:", currentPoster); + // console.log("Saved Posters Array:", savedPosters); + } else { + saveMessage.innerText = "Duplicate poster! This poster cannot be saved." + saveMessage.classList.remove("hidden") + // console.log("Poster is already saved"); + } +} + +function displaySavedPosters() { + savedPostersGrid.innerHTML = savedPosters.map((poster) => { + return ` +
+ +

${poster.title}

+

${poster.quote}

+
`; + }).join('') //need to use .join b/c map returns an array of strings. An array cant be put into innerHTML. Join turns the array into a string. +} + +function cleanData(posters) { + let cleanedData = posters.map((poster) => { + return { + imageURL:poster.img_url, + title: poster.name, + quote: poster.description, + } + }) + // console.log("Cleaned Data:", cleanedData) + return cleanedData +} +// console.log(cleanData(unmotivationalPosters)) + +function displayCleanedPosters(posters) { + console.log("Rendering the following posters:", posters) + unmotivationalGrid.innerHTML = posters.map((poster, index) => { //added the index here. the id is added so that posters can also be identified and deleted. + return ` +
+ +

${poster.title}

+

${poster.quote}

+
` + }).join('') //ABOVE: the index is identfying each unique poster (by name) so that it can specifically be deleted. +} +// console.log(displayCleanedPosters(unmotivationalPosters)) + +function deleteUnmotivationalPoster(event) { + const posterElement = event.target.closest(".mini-poster"); + + if (posterElement) { + const posterId = parseInt(posterElement.getAttribute("data-id")); + // Get the data-id from the element + //parseInt to convert a string to an integer + console.log("Deleting poster with ID:", posterId); + + unmotivationalPosters.splice(posterId, 1); + + // Re-render the cleaned posters after deleting the selected one + // Clean the updated array + let cleanedData = cleanData(unmotivationalPosters); + displayCleanedPosters(cleanedData); +// Re-display the posters + console.log("Unmotivational posters after deletion:", unmotivationalPosters); + } +} + +//when a .mini-poster is double clicked the event handler checks if the target is inside a .mini-poster div +//event.target.closest is a method used to find the nearest parent .mini-poster of the clicked area +//when clicked the poster is removed from the array (unmotivational posters) using splice +//call the displayCleanedPosters function again to refresh the display and auto-update diff --git a/styles.css b/styles.css index 0402eaee9..0e104064f 100644 --- a/styles.css +++ b/styles.css @@ -26,7 +26,10 @@ button { } button:hover { - background: darkslateblue; + background: rgb(150, 137, 236); + transform: scale(1.05); + color: black; + } .hidden { @@ -84,6 +87,10 @@ button:hover { margin-bottom: 30px; } +h2 { + margin-bottom: 20px; +} + .poster-form form { align-content: center; display: flex; @@ -110,10 +117,14 @@ button:hover { } .saved-posters-grid { - display: grid; - grid-template-columns: repeat(3, 1fr); - grid-gap: 20px; - padding: 20px; + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 8px; +} + +.back-to-main, .back-to-main-from-unmotivational { + margin-top: 20px; } .mini-poster { @@ -144,3 +155,42 @@ button:hover { margin-bottom: 10px; text-transform: uppercase; } + +#save-message { + color: rgb(189, 48, 48); + font-size: 25px; + font-weight: bold; + padding: 10px; +} + +/* ~~~~~ UNMOTIVATIONAL POSTER SECTION ~~~~~ */ +.unmotivational-section .poster-title-unmotivational { + color: black; +} + +.mini-poster-unmotivational:hover .poster-title-unmotivational { + color: rgb(105, 27, 27); +} + +.mini-poster-unmotivational:hover { + background: rgb(104, 102, 102); + box-shadow: 0 0 40px rgb(189, 48, 48); + transition: box-shadow 0.3s ease; +} + +.mini-poster-unmotivational { + background: rgb(139, 137, 137); + border-radius: 10px; + color: white; + height: 340px; + padding: 25px; + width: 425px; +} + +.mini-poster-unmotivational .poster-title { + margin-top: 20px; +} + +.mini-poster-unmotivational .poster-quote { + margin-top: 10px; +} \ No newline at end of file