<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: kimrodrig</title>
    <description>The latest articles on DEV Community by kimrodrig (@kimrodrig).</description>
    <link>https://dev.to/kimrodrig</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F926906%2F75176bee-edb5-47eb-b2a1-b7381b961eeb.png</url>
      <title>DEV Community: kimrodrig</title>
      <link>https://dev.to/kimrodrig</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kimrodrig"/>
    <language>en</language>
    <item>
      <title>Reproducing the View-Master Effect in Javascript</title>
      <dc:creator>kimrodrig</dc:creator>
      <pubDate>Fri, 07 Oct 2022 20:50:57 +0000</pubDate>
      <link>https://dev.to/kimrodrig/reproducing-the-view-master-effect-in-javascript-18ko</link>
      <guid>https://dev.to/kimrodrig/reproducing-the-view-master-effect-in-javascript-18ko</guid>
      <description>&lt;p&gt;I was working on a React project this week—a platform for sharing and finding sublets—based on the standard thumbnail view. As opposed to, for instance, list view (useless for apartments!) or map view. The apartment search is like dating: you want to see it before you bother to go and see it. &lt;/p&gt;

&lt;p&gt;Each listing was rendered as an individual component. It was easy enough to integrate a thumbnail photo here; all that was needed was to call the first image in the the image array within the listing object, as saved in the db.json file I had drafted. The object looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
      "id": 3,
      "title": "Sun-dappled 1 bedroom",
      "description": "Clean and open 1 bed 1 bath apartment right in the middle of downtown LA. Come take a look today and make it your own!",
      "location": "Los Angeles, California",
      "price": 2170,
      "bedrooms": 1,
      "baths": 1,
      "availability": "10/11",
      "email": "donaldotreply@gmail.com",
      "image": [
        "./images/image3.jpg",
        "./images/image9.jpg",
        "./images/image16.jpg"
      ]
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the html component would be as simple as &lt;code&gt;&amp;lt;img src={listing.image[0]} alt={listing.title} /&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;But how to get that effect where you can preview the images by flipping through them while remaining in thumbnail view? As if looking through a View-Master reel?&lt;/p&gt;

&lt;p&gt;I decided to create two buttons, right and left, that would mount the image, using .png icons I sniffed out out online. Manually positioning them, I proceeded to create a hover effect in the .css file, so they would by default be hidden from view, and only emerge to politely greet the visiting cursor:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Syu9fRfQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h4hlylkdanbei0muy6e0.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Syu9fRfQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h4hlylkdanbei0muy6e0.gif" alt="Image description" width="592" height="760"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code was remarkably simple: &lt;/p&gt;

&lt;p&gt;css:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.button-container {
  visibility: hidden;
}

.image-container:hover .button-container {
  visibility: visible;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div className="image-container"&amp;gt;
  &amp;lt;img src={listing.image[0]} alt={listing.title} /&amp;gt;
  &amp;lt;div className="button-container"&amp;gt;
    &amp;lt;button className="right-btn"&amp;gt;&amp;lt;span className='right-icon'&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;
    &amp;lt;button className="left-btn"&amp;gt;&amp;lt;span className='left-icon'&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what about the actual functionality? I've always been in favor of a two-state solution. So up top I established a state for image, naturally defaulted to the first, and one for the index of the image to call, naturally defaulted to zero.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const [imageToDisplay, setImageToDisplay] = useState(listing.image[0])
const [imageToDisplayIndex, setImageToDisplayIndex] = useState(0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I updated the returned html component to incorporate the state variable and requisite onClick functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div className="image-container"&amp;gt;
  &amp;lt;img src={imageToDisplay} alt={listing.title} /&amp;gt;
  &amp;lt;div className="button-container"&amp;gt;
    &amp;lt;button onClick={handleRightClick} className="right-btn"&amp;gt;&amp;lt;span className='right-icon'&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;
    &amp;lt;button onClick={handleLeftClick} className="left-btn"&amp;gt;&amp;lt;span className='left-icon'&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There remained only the aforementioned handle functions, which would also need to update our state variables. I composed a simple algorithm for the right click. If the length of the array is still greater than 1 + the index (or so-called "imageToDisplayIndex"), increase the index by one and update the image. Because we aren't yet at the end of our array. (We need to add 1 due to the fact that indexing begins at 0.) Otherwise, which is to say we've reached our final image, reset the counter and make our image the first one in the array. &lt;br&gt;
(We also need to call stopPropagation(), since the underlying component is clickable, otherwise a click on the right-button will simply function as a click on the whole listing card.)&lt;/p&gt;

&lt;p&gt;For the left click, the logic is the same, just reversed. If we are anywhere ahead of our first image, just decrease the index by one. However, if we're at our first image, set the index and the image to the last one in the array (in other words, the array's length - 1).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function handleRightClick(e){
  e.stopPropagation()
  if (listing.image.length &amp;gt; (imageToDisplayIndex+1)){
    setImageToDisplay(listing.image[imageToDisplayIndex+1])
    setImageToDisplayIndex(imageToDisplayIndex+1)
  }
  else{
    setImageToDisplayIndex(0)
    setImageToDisplay(listing.image[0])
  }
}

function handleLeftClick(e){
  e.stopPropagation()
  if (imageToDisplayIndex &amp;gt; 0){
    setImageToDisplay(listing.image[imageToDisplayIndex-1])
    setImageToDisplayIndex(imageToDisplayIndex-1)
  }
  else{
    setImageToDisplay(listing.image[listing.image.length-1])
    setImageToDisplayIndex(listing.image.length-1)
  }
} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And our result looks like this! Of course these are shamelessly stock photos; clearly they do not belong to the same property. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Uic_OkS1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/26kuakygjev7rxx285hn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Uic_OkS1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/26kuakygjev7rxx285hn.gif" alt="Image description" width="562" height="768"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm sure there is a more efficient way to write this by availing oneself of some library somewhere. But if you're going to get into the real estate game, shouldn't you get some practice Doing It Yourself? &lt;/p&gt;

</description>
    </item>
    <item>
      <title>Clean Out Yer Fridge</title>
      <dc:creator>kimrodrig</dc:creator>
      <pubDate>Fri, 16 Sep 2022 23:19:25 +0000</pubDate>
      <link>https://dev.to/kimrodrig/clean-out-yer-fridge-2p90</link>
      <guid>https://dev.to/kimrodrig/clean-out-yer-fridge-2p90</guid>
      <description>&lt;p&gt;Lunch, for many people, such as myself, is breakfast. So by the time lunchtime arrives, the last thing I want is to plan an elaborate meal in addition to making it, eating it, and cleaning up its wake. I’m hungry, and I know I’ll end up preparing, under the obstinate force of my habit, something I’ve made before. That’s fine, I like my repertoire, but I also like to explore and adventure and learn new things, so another uninspired panino feels hackneyed, a wasted opportunity. What to do? If only I could outsource the decision (and the preparation, but I’m not quite there yet) to someone else! If only I could be offered some suggestions for dishes I could make with what I already have?&lt;/p&gt;

&lt;p&gt;This is the sort of thought process which absolutely did not lead me to create the Recipe Idea Generator web app, though it is a nice retrospective explication. For an app should have real utility—that’s the most important thing—in the end it’s a tool, not a jewel. But as a first project, the culmination of our twee first phase and therefore an embodiment of its curriculum, scope was the priority. What can we do with what we have and what we know? In truth, we looked through those free APIs whose functionality we could understand and fashioned our idea around one that seemed manageable. &lt;/p&gt;

&lt;p&gt;Nonetheless, it had all the characteristics of a good idea: it seemed feasible, useful, scalable, reasonably challenging, possibly original, fun. The little thing will give you recipe ideas, random if you like, or ones incorporating a couple of ingredients you already have. So we mustered the sort of enthusiasm which you find most concentrated at the beginning of an undertaking and tackled the main part right away.&lt;/p&gt;

&lt;p&gt;The first step was figuring out how to obtain the information from the API–—basically, figuring out how the API worked. It seems they offered a few methods for our convenience. There was a simple way to fetch a random meal (and its corollary metadata: picture of the finished dish, ingredients, instructions, et cetera); there was a meal lookup by a meal “ID”; filter by ingredients; list of some ten latest additions. Once we realized the data we wanted was all available on the other side of these links, we needed only to call the fetch() method on each of these URLs to have the API yield its treasures to us.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fetch(`https://www.themealdb.com/api/json/v2/9973533/lookup.php?i=${id}`)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We began with a loadRandom() function to fetch a random meal and populate the webpage with the relevant properties of that meal: namely, picture, name, instructions, and ingredients. This was relatively straightforward, save for a complication with the way the ingredients were stored in the object. There were 20 placeholder ingredient key-value pairs, and if the recipe required fewer than that then each numbered ingredient property after the final one was stored as an empty string (for example, strIngredient9, strIngredient10…in an eight-ingredient meal object). So we inserted a docile twentyfold for-loop to fill out the ingredients list while excluding from it any empty strings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function loadRecipe(meal){
    recipeImage.src = meal.strMealThumb
    recipeInstructions.textContent = meal.strInstructions
    recipeName.textContent = meal.strMeal
    recipeIngredients.innerHTML = ""
    for (let i = 1; i &amp;lt; 21; i++){
        let ingredientName = meal[`strIngredient${i}`]
        let ingredientMeasure = meal[`strMeasure${i}`]
        let ingredientEntry = ingredientMeasure + ' ' + ingredientName
        const li = document.createElement("li")
        if (ingredientName != "" &amp;amp;&amp;amp; ingredientName != null){
            li.textContent = ingredientEntry
            recipeIngredients.append(li)
        }   
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we wrote a loadBanner() function to iterate through ten “latest” meals (whatever that means, but again here the priority was practicing communicating with and accommodating an API) and represent their thumbnails in a top-of-page banner. Herein we called a click-obsessed event listener to make sure whenever one of these was selected by the cursor, its underlying recipe would load into the main display (name, directions, ingredients), with the help of a certain loadMeal() (functionally identical to loadRandom()). &lt;/p&gt;

&lt;p&gt;Searching by ingredients proved equal to clearing and reloading the banner (and we already had this function) except this time with the results of the API’s filter method. Once the submit form was in place and prepared to record items in the hungry user’s pantry, we needed only to reuse the functions we already wrote. Two complications arose. &lt;em&gt;Primo&lt;/em&gt;, the API is reached by URL, so inputs are case-sensitive and desire underscores in lieu of spaces. Chicken Breast is no good there, even if that’s what the user types, since what we need is chicken_breast. So we patched that up with a handy textify() function that archaically iterated through the characters of the string and groomed them to the exacting standards of the URL. (I also used this opportunity to hardcode a conversion from ‘eggplant’ to ‘aubergine’ so that American users wouldn’t miss out on Baingan Bharta). &lt;em&gt;Secondo&lt;/em&gt;, the filter results are pruned down meal object, without most of the metadata required, albeit with a meal ID. A getMealById() function utilizing the API’s lookup method solved this problem neatly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function textify(string){
    let newString = "";
    for (letter in string){
        if (string[letter] == " "){
            newString = newString + "_"
        } 
        else{
            newString = newString + string[letter].toLowerCase()
        }
    }
    if (newString == "eggplant"){
        newString = "aubergine"
    }
    return newString
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Feeling rather omnipotent by this point, we decided to haughtily add another form kindly requesting an email address to which a shopping list would be sent. The technical process here was adding an event listener fastened to a submit form which would launch the email (window.open(mailto and interpolations)). We availed ourselves of an encodeURIComponent method which preened the ingredient list for the email format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function sendEmail() {
    emailForm.addEventListener("submit", (e) =&amp;gt;{
        let body = encodeURIComponent(recipeIngredients.innerText)
        e.preventDefault()
        const address = e.target["address"].value;
        const subject = encodeURIComponent("Here Is Your Recipe Shopping List!")
        window.open(`mailto:${address}?subject=${subject}&amp;amp;body=${body}`)
    })
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally we had the CSS to wrangle with. Suffice it to say a lot of time was spent wrestling with buttery divs losing their shapes, slipping and sliding in every direction, if not going so far as to clamber on top of one another like box turtles.  We eventually agreed that flex-boxes would be the most convenient solution. But some things are better tackled after lunch. I wonder what I can make with a fridgeful of courgettes?&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
