Skip to content

Latest commit

 

History

History
891 lines (652 loc) · 38.5 KB

notes.md

File metadata and controls

891 lines (652 loc) · 38.5 KB

Developing using Vue

vue serve App.vue

As usual, Derrick taught me this. Vue is a UI framework, which provides foundation for software developers to build web programs. While this is the general definition of a framework, I'm not too sure how a UI framework is distinct from this definition, but I guess I'll learn as I go.

how is this diff from gcc vue serve app.vue preprocesses ???

gcc is compiler run the outputed binary filename.o

we use vue (UI framework)

Starting a new Node Project

  1. Generate a package.json to handle my 3rd party packages, where node and yarn understand package.json, where the list of dependencies and build scripts are documented.

To ask Vue to generate and installs the list of dependencies for us, in the root project directory, and provides a sample project

vue create boba-bill

Since we are using Vue to build our project, we want a couple of the default files generated by the vue create command, including:

  • package.json
  • main.js, that imports the Vue library and uses the Vue object to open and run our project, placed inside the "src" folder
  • config files like babel.config.js and
  • vue.config.js which sets the base url for production builds

We copied these files over and deleted everything else.

Things I should prob use

Props

"Child components are like functions, and props are like arguments. Can use props to pass stuff"

an example

Parent-Child Components

In the early stages of my project, I was confused about everything that was going on and I couldn't describe my questions without really circumstantial questions regarding my code with screenshots. Derrick articulated my question best:

The checkbox is in Friend component but you want to have a list of what's checked in the FriendList parent component?

The reason why my code didn't work was because props are for passing data only from parent to child, so if you want to pass it the other way, in Vue, you emit events.

Derrick explains colloquially that

"something" can emit events and another part of your code can listen for those events and run stuff based on those events. So parent waits for x to happen, then when you, for example, click a child, it yells "I got clicked!" and parent hears it and runs some code

Explaining my code

Documentation of the instance of emitting in my project (:

I'm super proud of when I finally learned how to allow the parent component to listen for a response from the child component!

In this super clear example of how to emit child events, one thing I was a little confused about was which code snippets belonged in the parent and child components. So first, I'd like to clarify that code snippets 1, 2, and 4 are in the parent components. Code snippets 3 and 5, the ones relating to the <button> Enlarge text </button> and $emit are in Child.

explaining the example

To summarize what the example in the Vue docs illustrates, you need to do 4 things to enable the parent component to listen for child events:

  1. a data property in the Vue component declaration to store the information you're listening for from the Child component (this is postFontSize from code snippet 1 from the Vue docs example)
    • located in parent component, written in [java]<script>
  2. specify using the data property you declared, where this information will be presented in the template (code snippet 2)
  3. Use the v-on directive while calling the child to listen for an event in child (in this case, when the button is pressed), and run some Javascript (which is to increment postFontSize) when the child is triggered. (an example of this is in code snippet 4)
    • both this and #2 are in the parent component, in the <template> section
  4. Include v-on:...="$emit('...')" in the HTML element that will communicate a user's response back to the parent (code snippet 5)
    • located in the child component

Emitting (obsolete but simple ex. nonetheless)

Long after this project is over, I might forget how exactly I emitted information from the Child component back to the parent, so I'll show code snippets of my code as examples for each of the numbered parts I explained in the section above.

  1. Variable declaration selectedFriends in the Vue component that will store the results of what we're listening for in the Child component. link to repo
export default {
  components: {
    InputText,
    Friend
  },
  data() {
    return {
      ... ,
      selectedFriends: []
    };
  },
  methods: {
    ...
  }
};
  1. Where the results of the response from the Child component will be displayed. link
<p>{{ selectedFriends }}</p>
  1. The FriendList component listens for select-friend events from its children Friend and runs the provided event handler "selectedFriends.push(friend.text)" when it receives a select-friend event. link
<Friend
  v-for="friend in friends"
  :key="friend.id"
  :name="friend"
  v-on:select-friend="checkboxHandler(friend)"
/>
  1. Inside the Child component, the v-on listener will notify the parent in the event of a select-friend-- which occurs when the checkbox is clicked. link to repo
<input type="checkbox" id="checkbox" v-on:click="$emit('select-friend')" />
  1. Then, in a class method (which is called by v-on modifier in parent), I saved the names of the friends who were checked in this array called selectedFriends using this method:
/* @param: instance of friend; {id: someNum, text: "name"}
     * returns: name of friend added/removed from selectedFriends list
     */
    checkboxHandler(friend) {
      // if name is in list, remove name
      if (this.selectedFriends.find(element => element === friend.text)) {
        /* why is it so complicated to remove an item from an array?
          splice requires 1) index of thing you're removing
          2) num of things you're removing*/
        this.selectedFriends.splice(
          this.selectedFriends.indexOf(friend.text),
          1
        );
      } else {
        this.selectedFriends.push(friend.text);
      }
    }

2 way binding by emitting

Problem: I had difficulty allowing for the exchange of data between parent and child components using emitting. As demonstrated above, I had to write a function in the parent TransactionList to both save and remove the name from the array selectedFriends, and I felt like Vue should have an easier way to do all of this.

I could achieve this behavior by using the sync modifier and a getter and setter in the computed part of the child component. From StackOverFlow

TL;DR of what's happening in the code:

.sync is used in the parent component while calling the child, and cats is the temporary array that stores the emitted responses from the child. Inside the child's method, the child does (update:cats, [info that child component is passing back to the parent]).

< SplitBetw :cats.sync="selectedFriends">

// inside child component, method section:
this.$emit('update:cats, newValue);

The long explanation, with examples of this phenomenon in my code to follow.

  1. While passing the array to the child component, an additional .sync modifier is used on cats, a temporary array that stores the emitted responses from the child. The responses are then safely stored in selectedFriends.
<SplitBetw
  v-for="friend in friends"
  :key="friend"
  :name="friend"
  :cats.sync="selectedFriends"
/>
  • :key is used so that Vue can understand how to loop and go through your array. Typically, key is the index or a unique name.
  • I used :name to change friend into name on the child-level, so that the component uses name to refer to a single friend. The variable friend does not exist on the child-level, even though it does on the parent.
  • selectedFriends is an array in the parent that will store the emitted responses (checked checkboxes in this case) from child. It is found within data() of TransactionList.
  1. Moving onto the child component, the input tag for the checkbox requires the v-model to be set to a function defined in the computed section of the component. I named this function updateCheckbox, and it is under this function where the getter and setter is defined.
<label for="{name}">
  <input
    v-model="updateCheckbox"
    type="checkbox"
    id="{name}"
    :value="{ name }"
  />
  {{ name }}
</label>
  1. The getter and setter. Again, cats is a temporary place to store the emitted value the child will send back to parent. Remember to include cats in prop!
export default {
  props: {
    // return selected checkboxes to selectedFriends
    cats: Array,
    ...
    }
  },
  computed: {
    updateCheckbox: {
      get() {
        return this.cats;
      },
      set(newValue) {
        this.$emit("update:cats", newValue);
      }
    }
  }
};
  1. Going back to the input tag declaration, I had a bug where clicking on one checkbox checked them all, and it was fixed by including the :value = {name} modifier! So don't forget it.

TODO: give example and explantion from own code

data as a function?

In the parent(?) definition of the Vue component, data must be returned as a function so that both the parent and child instances can retain an independent copy of the returned data object.

The importance of gitignore

node_modules are build packages generated by Vue for my project and can grow to an astronomical size, hence this joke shared by Derrick: 1

Therefore, it's important to include node_modules in your .gitignore file, but if you already committed the node_modules file before adding the .gitignore, you need to untrack the directory and remove it from git, while keeping the file git rm -r --cached node_modules

Checkboxes

God were they unexpectedly difficult to get right ^^;

I used many components to organize my data, so the parent component always passed a string (the name of the friend) to the checkbox components, so that the checkbox components would generate the checkboxes themselves. (see the relationship between TransactionList and SplitBetw)

However, this meant the the child component, SplitBetw, had to emit the checked checkboxes back to the parent. This emitting behavior is basically the child component telling the parent which checkbox has been clicked.

Migrating a Form to a New Component

By this stage of development, my project has started to have quite a few components, and with many components requires more knowledge of how to pass data between components.

Potential Errors when using Repeated Components

When re-using components, for example in my project when I re-used the SplitBetw component to display the list of checkboxes selected from both the "Split Between" and "Payers" section. For example, I have the following friends to choose from each section:

{"Split Between": [Derrick, Bunbun]}
{"Payers": [Derrick, Bunbun]}

Since I had set the id of the checkbox input in SplitBetw to simply be the name (ie. "Derrick"), clicking on the checkbox for "Split Between"'s Derrick also checked the box for "Payer"'s Derrick. Therefore, the id needed to not only contain the name but the section ("Split Between" or "Payers") as well.

Problematic:

<input type="checkbox" :id="name" ... />
<label :for="name">
  {{ name }}
</label>

Fixed bugs (for now): Out in the parent component, the name and type are passed in:

<h2>Payers</h2>
<SplitBetw
  v-for="friend in friends"
  :name="friend"
  type="payers"
  :key="friend"
  :returnedCheckboxes.sync="selectedPayers"
/>

To ensure that the child component SplitBetw knows what those two values name and type are, make sure to include them in the props section of the Vue instance!

export default {
  props: {
    // sync-ed with parent, parent receives an arr selectedFriends
    returnedCheckboxes: Array,

    // local re-name of "friend", so the ids can be unique across diff iterations of SplitBetw component
    name: String,
    type: String
  },
  ...
};

In the child component, the html portion:

<!-- now, ids are unique across diff iterations of component-->
<input
  :id="name + type"
  v-model="updateCheckbox"
  type="checkbox"
  :value="name"
/>
<label :for="name + type">
  {{ name }}
</label>

Explanation of what v-model and :value do

v-model syncs the checkbox with the function updateCheckbox. Thus, every time a certain checkbox is clicked or unclicked, this function updates the returnedCheckboxes array that returns the list of friends selected from this component.

This is why the :value of the checkbox must be set to the name of the friend, as the getter and setter use value to add/remove the name from the returnedCheckboxes array that is emitted back up to the parent component, InputForm

Unexpected usage of v-model

Up until this point, I had used v-model to save an input value to an initialized variable in the Vue instance, such as these code snippets from InputForm

<input id="expense" v-model="yourExpense" ... />
export default {
  ...
  data() {
    return {
      ...
      yourExpense: "",
    }
  },
  methods: {
    checkForErrors:{
      ...
      // then do something with the input u got
      this.yourExpense
    }
  }
};

However, it was the first time I saw v-model used to trigger a function like here. please look above in the previous section for the example! it's really interesting.

what is value used for?

By default, v-model uses value as a prop! This can be modified, however

Nested Vue Components

I found this blog while working with nested Vue components that was sooo useful!

WHAT IS THE COMPUTED THING

Apparently, it's a computed setter

"which allows us to both receive a dynamic value as well as call a function when we attempt to change that value." - zaengle blog

?? still have many questions. get to this later LOL

HOWEVER the example noted in the docs doesn't work for our use case.

NO!! :

computed: {
  localForm: {
      get() { return this.value },
      set(localForm) {this.$emit('input', localForm)}
  }

"v-html should only used when you want to render HTML content from a variable"

Using computed for Dynamic Components

When switching between the "Friends", "Transaction", and "Calculate Owed" sections of the web app, I implemented tabs. Each of the components I just listed occupied a page on the tab. The key to easily implementing toggle-able tabs using Vue is using dynamic Components:

  1. binding the is attribute that's set equal to
  2. a computed method (maybe named currentTabComponent()?) that returns the active page/component that we'll be viewing
  3. v-bind to assign any props the component takes in that's set equal to
  4. a computed method to return the properties
  5. :class binding to assign a component as active at the response to a button
    1. Therefore, the active component would have the following class binding:
    <div class="tab-button active"></div>
    1. Thus, it's possible to style the active component in the css file using the tag name
    .active {
      color: red;
    }
  6. v-on:click to assign a tab as clicked on click
  7. data variable (perhaps named activeTab ?) to store the name of the tab that's open
<button
  v-for="tab in tabs"
  :key="tab"
  class="tab"
  :class="['tab-button', { active: tab === activeTab }]"
  @click="activeTab = tab"
>
  <span class="tab-copy">{{ tab }}</span>
  <!-- TODO: handle this later
            <span class="tab-background">
              <span class="tab-rounding left"></span>
              <span class="tab-rounding right"></span>
            </span> -->
</button>
<component
  v-bind:is="currentTabComponent"
  v-bind="currentProperties"
  class="tab"
></component>

Here is a StackOverflow that was particularly useful for learning when to use the computed property for dynamic components.

We can use v-bind:class to dynamically toggle between classes. I used the :class binding to set a tab as the active one.

Vue docs examples

That I referenced!

Use v-on to send data + call parent's method from child

I learned this from this StackOverFlow post that when emitting a value from the child component, in the parent component, you can:

  1. Bind the emitted variable to a parent-level property like this:
<InputForm :friends="friends" :emit-form.sync="newTransaction" />

so that the emitted localForm object (from the child using this call: this.$emit('update:emit-form', this.localForm);) is saved to the parent-level property newTransaction. You can then take this newTransaction property and add it to the list of transactions you have in the component, OR

  1. Directly use v-on to listen for the child-level emit event update:emit-form to be called in the child InputForm and call the parent-level method addTransaction.
<InputForm :friends="friends" @update:emit-form="addTransaction" />

Conveniently, the second field of the emit statement used in the child (so this.localForm) is the parameter of the method you will declare in parent!

methods: {
    // @param: returned form data from InputForm
    addTransaction(newTransaction) {
      console.log("Adding a transaction");
      this.transactions.push(newTransaction);
    }
  }

So in this method, you can directly add the emitted object from child into the list transactions in parent! This is a better solution for this situation compared to the first because the 1st only handles passing the form object from the child, whereas the second handles both passing the object and triggering a method to add the object to the list transactions.

Components that Return Numbers

I've been getting this intriguing type error recently when I duplicated the InputText component and made one to take a numerical input for cost. In InputNum, whenever I submitted the form, an error message indicating that "Expected Number, got String" shows up, referencing the parent component InputForm in this line:

<InputNum v-model="localForm.expense" />

In the child component InputNum, I was confused because the expected input type is indeed a number

<template>
  <label>
    Cost
    <br />
    <input
      type="number"
      :value="value"
      v-on="listeners"
    />
    ...
</template>

and what is emitted is the input

export default {
  props: {
    value: {
      type: Number
    }
  },
  ...

  computed: {
    listeners() {
      return {
        // Pass all component listeners directly to input
        ...this.$listeners,

        // Override input listener of the same name from v-on
        input: event => this.$emit("input", event.target.value)
      };
    }
  }

So, I originally fixed the error by converting the input to a number, assuming that somehow the input is a string for some reason.

// change:
input: event => this.$emit("input.Number()", event.target.value);

This removed the error message "Expected Number, got String" but a new issue arose. Now, whenever the form was submitted in InputForm, the field for expense would just reset to 0. input.Number() was causing an issue.

In the end, StackOverflow came to the rescue again. Suffixing with v-model.number in the parent ensures that the emitted result is parsed as number. This proved to be less buggy that trying to change the input into a number in the child, as done previously.

<InputNum v-model.number="localForm.expense" />

Hovering with Vue

I had been following this blog post on how to toggle hovering over components on Vue to toggle visibility of an edit name button next to the name itself. The how-to: In the parent component FriendList, I included v-on (the @) to listen to the child component for a new edited friend name, if any.

<ol>
  <Friend
    v-for="friend in friends"
    :key="friend"
    :name="friend"
    @update:emit-name="editName"
  />
</ol>

Inside the child component Friend, each for loop passes in a friend name that needs to be created into a list item. I used v-on to listen for when the mouse hovers over the specific list item, like "1. Derrick", and this toggles the visibility of the edit button. ``html

  • {{ name }} edit
  • ```

    hover and edit are both booleans I initialized in this component to help me keep track of which elements I want to show.

    v-if vs v-show

    So far, I've only used v-if, but the difference (described in the docs) is that event listeners and components are actually created and destroyed on toggle with v-if. On the other hand, elements tagged with v-show are always rendered, with css-based toggling.

    "Generally speaking, v-if has higher toggle costs while v-show has higher initial render costs. So prefer v-show if you need to toggle something very often, and prefer v-if if the condition is unlikely to change at runtime."

    Therefore, I used v-show to toggle the edit button in Friend.vue that becomes visible on hover, and v-if for editing the name, as the components for that operation would only be generated when the user clicks on the "edit" button, and not on render.

    Design Thoughts

    Fonts

    So far, my not finalized font choices are

    • Biorhyme for headings
      • I found these font recommendations from this blog post
    • Varela Round for the body
    • Cute Font, my most questionable choice, is for displaying restaurant names in the boba balls
    
    

    Refactoring

    Using js functions from another file

    Following this StackOverFlow post, I was able to import functions from a local js file in order to move the error checking functions out to an external file so I don't have to look at it while debugging component stuff!

    To do this, I must export the error checking functions as an object that I named formTests

    let formTests = {
      formIsFilled() {
        // error checking to see if all form fields are filled
      }
    };
    
    export default formTests;

    This all was written in a file I named "errorChecks.js"

    Then in the Vue component I wish to use these functions in, I must include the relational file path and the name of the file that contains the js tests.

    import formTests from "../assets/errorChecks.js";

    where the file organization is as follows

    - assets
      -errorChecks
    - components
      - current Vue component
    

    Then when I want to use the function formIsFilled from another js file in the Vue component, I simply call

    formTests.formIsFilled();

    Flexbox tips for self

    1. Use a container to wrap elements in a row.
      • containers are the bread and butter of flexbox. My naming scheme for these wrappers are ___Container
      • Without containers, you can't align
    2. align-items to vertically align html elements. See ex
    3. Use justify-content to determine space between each other. See examples
      • Most notable example is like when u write a diary entry, maybe u have the title aligned left, and the date aligned right on the same line. You'd use justify-content: space-between on the container wrapping those two <span> elements to display them on opposite ends
    4. Use column-gap within a container to put space between elements
    • ex:
    .buttonsContainer {
      margin: 0 1rem; /*space between elements*/
      display: flex;
      column-gap: 0.5rem;
    }

    My Animation Struggles

    animating the text box

    When collecting text inputs, I wanted to animate the bottom of the input box expanding when the user focuses on the text box. Visualizing the behavior, this is an unselected text box vs a text box that's being typed in

    Name        Name
    ____        Feli
                --------
    

    note about focus that I initially made a mistake with: "focus" is a state only applied to inputs, so I couldn't focus on a <div>, only a button, text box, and etc.

    Coding the Animation

    Inside InputText.vue, the html for the input text box, I need a container (in this case, it is the label) that encapsulates everything.

    <label :for="question">
      Name
      <br />
      <input type="text" id="question" />
      <!-- styling for the line is found in Stylesheet.css -->
      <span class="line"></span>
    </label>

    Thus, when the input is focused, the label and line both need to respond by:

    1. changing color to pink and
    2. line needs to be animated traveling down.

    To achieve this behavior, Derrick suggested that I use the adjacent sibling combinator + to indicate that I'd like to style the second html element when the first element is focused.

    /* adjacent sibling combinator in use
      animation for focus enter  */
    #name:focus + .line {
      transition: transform 125ms;
      transform: translateY(10px);
      border-color: #f09381;
    }
    
    /* animation for when focus leaves */
    .line {
      will-change: transform;
      transition: transform 450ms;
    }

    StackOverflow post that helped me figure out which combinator to use.

    To animate the label, I took advantage of focus-within.

    I initially ran into an obstacle while animating this because the focus element, the text input, is sandwiched between the label and line, which both required css animations. However, adjacent combinators like + and ~ discussed only serve to style elements that come after the focused element. Therefore the label can't be animated with simply those combinators.

    To solve this, I moved the input and line elements into the <label> element (since i remembered that u could treat the label as a wrapper), so the label is now the container holding the <input>. This works, because focus-within styles the container when the input element inside is focused.

    Therefore, the css animating the label (when the label is the container) is

    label:focus-within {
      color: #f09381;
    }

    Conditional Styling

    What happens when I use a component for two different instances, once on white background, and another on black background, and the black text cannot be seen on black background?

    Check this issue for an image showing this problem!

    The font color depended on a conditional, and how would you implement this in Vue?

    Directly applying styles in Javascript

    In my two instances of InputForm, the empty form was on white background, and modify-able, filled out form on black background. Thus, to determine whether the font should be black or white, I needed to know whether the form was empty or not.

    Fortunately, upon creation of a form, my initEdits() method executes only when the form is filled in advance, so I added to this method, directly changing the font color to white.

    initEdits(){
      // if form is filled
      if(this.autoFillFormData){
      ...
    
      /* directly manipulate InputText to change font color
          of input to white so it can be seen on black background */
      this.$refs.busnInput.changeColor();
      }
    }

    How to directly modify styles for a child component

    In the parent, I marked the child component I'm going to be directly modifying with a ref

    <InputText v-model="localForm.name" question="Business name" ref="busnInput" />

    Inside the child, since I wanted to change the font color of the <input> element only and nothing else, I also marked the input element with a ref

    <label>
      {{ question }}
      <br />
      <input type="text" ... ref="input" />
    </label>

    note: Notice that the ref names are different!

    Then, I defined in the child component InputText a method that the parent will call to change the color of the <input> element to white.

    methods: {
      changeColor: function() {
        this.$refs.input.style.color = "white";
      }
    }

    Then, back in the parent InputForm, we can use

    this.$refs.busnInput.changeColor();

    to call the child component's method from the parent and change the font color to white!

    Color

    Up until now, I've been making mock-ups in Adobe Photoshop and I've only used hex notation (ie. #ff246) to indicate color. While following a tutorial, however, I realized that there are 2 other ways to describe the same color. I can use

    1. RGB -> red, green, blue values rgb(240, 147, 129)
    2. HSL -> hue, saturation, lightness hsl(340deg 100% 32%) To switch between these, I can use the various color converters and color pickers available on the internet! I just wanted to save the different names of how to describe all these colors here in case I need them in the future.

    Conditional Style Bindings using :style

    The method described above is what I originally used, and it works well, but I'm not sure if it's best practice, since I didn't really see refs used in the beginner's docs of Vue. Style bindings seem like the more elegant and less hacky (methods reminiscient of older forms of web development without frameworks where devs were forced to modify html and css directly through js), and most of all, more modern.

    context My app is divided into 3 tabs: Friends, Transaction, and Calculate, and I wanted to underline the tab with pink if the tab is open. View the full code

    explanation of code

    Each of the tabs are named so, and have the normal styling class tab-button of grey text and grey underline applied. When the button is clicked, the clicked button becomes the active tab, and the @click toggles this behavior for us. So far, the code is within what a beginner html and css developer can expect :)

    <button
      v-for="(tab, index) in tabs"
      :key="index"
      class="tab-button"
      @click="activeTab = tab"
    ></button>

    Now moving on to the new info, the underline that we're going to conditionally style pink depending on whether the tab is active or not is as follows:

    <span class="tab-front" :style="tab === activeTab && styleActive"
      >{{ tab }}</span
    >

    Using :style, the first expression tab === activeTab evaluates whether the tab will apply the special style of the second, styleActive. This blog post describes this as a guard expression using a logical AND to apply styleActive if the boolean tab === activeTab evaluates to true.

    styleActive is a computed method that returns the css as an object that we will be conditionally applying. Make sure to include the Vue syntax of separating each item with a comma (,) instead of a semi-colon like in css, and attributes with dashes need to be wrapped in quotes!

    export default {
      computed: {
        styleActive() {
          return {
            "border-bottom": "5px solid #f09381",
            color: "black"
          };
        }
      }

    References

    • I followed this helpful blog post on the best practices of using class bindings and applied the similar concepts for style.
    • an example of the usage of style bindings from StackOverflow was helpful in figuring out the exact syntax. In particular, when modifying css, I learned from this post that css styles need to be wrapped by quotations (ie. 'background-color' : 'black'), otherwise Vue wouldn't know what it is
    • this StackOverFlow post helped me realize that the syntax {active: ____} expects a boolean in the blank. Therefore, it can be a variable that is saved as a boolean, like "isActive", or an expression that evaluates to a boolean, like my activeTab === tab ( I didn't really know how this worked bc I copied this from some expert's code and didn't get it for the longest time)
    • this was the most helpful StackOverflow post! It showed me that these style bindings could be saved as objects and toggled as a computed method. Before, I didn't know what the difference between computed methods and normal methods were ( I think i still don't really know), but I saw how they could be used in this way, and it was really eye-opening

    Dynamic Transitions

    I had no idea how to program sliding transitions dependent on the direction of how the tabs are switched until I stumbled upon this StackOverflow post. Unfortunately, though, I had a hard time understanding the official Vue documentation for Dynamic transitions, so perhaps I can improve on this explanation someday.

    implementation

    Since the direction the tabs will slide will depend on whether the tab indices will be changing from an ascending or descending value, I wrote a helper function to determine the direction of the slide transition

    data() {
        return {
          /* starts at 0 bc the first tab's index is 0*/
          previousTab: 0,
          transitionLeft: true
        }
    },
    methods: {
        findTabDirection(newIndex) {
          console.log(`${this.previousTab} -> ${newIndex}`);
    
          if (this.previousTab < newIndex) {
            console.log("shift left");
            this.transitionLeft = true;
          } else {
            console.log("shift right");
            this.transitionLeft = false;
          }
          this.previousTab = newIndex;
        }
      }

    I needed previousTab to keep track of the last index to determine the direction, and transitionLeft is the boolean value that is absolutely crucial for the dynamic transition because it determines which direction, so therefore, which transition I'll be using.

    TODO: continue explaining the transitions in Animations.css

    Evidence that Coding is Creative Writing

    I just want to put this somewhere to demonstrate how I can write a function that does the same thing differently.

    Here, I'm writing a function that checks if a newly entered name already exists in the list of friends. To ensure that "Derrick" and "derrick" are not considered different names because of case, I used the string member function String.toLowerCase() to change the name to lowercase before evaluating.

    This is what I wrote a couple months ago using .some()

    isDuplicate(name) {
      return this.friends.some(
        friendName => friendName.toLowerCase() === name.toLowerCase()
      ); // true if name is in friends
    }

    Today, on May 1, 2021, I wrote this:

    isDuplicateF(friends, newName) {
        return friends
          .map(friend => friend.toLowerCase())
          .includes(newName.toLowerCase()); // true if name is in friends
      }

    The new one I wrote is kinda verbose and less intuitive. Not readable. I think i'll use the old one!