Skip to content

Kakamotobi/fe-max--shopping

 
 

Repository files navigation

fe-max--shopping

Table of Contents

Getting Started

  1. Clone the repository
    git clone https://github.com/Kakamotobi/fe-max--shopping.git
  2. Install dependencies
    npm install
  3. Run the development server
    npm run dev

Project Requirements

About

  • Amazon mainpage clone.

Key Features

  • Top Nav Bar
    • Login Section
      • Login "tooltip"
        • Appears after 1 second after page load.
      • Login Section Hover
        • Login "tooltip" disappears.
        • Login modal appears.
        • Close modal when no longer hovering over the section or modal.
        • Dim main section.
    • Shipping Section Hover
      • Shipping address modal appears.
      • Close modal when no longer hovering over the section or modal.
      • Dim main section.
    • Navbar expands the whole width of the viewport.
      • Search bar grows when vw >= 1120px.
  • Hero Infinite Carousel
    • Automatically move to next slide after 10s of no interaction.
  • Search Form
    • Fetch autocomplete data
    • Autocomplete Panel
      • Use up/down arrows to navigate autocomplete options.
  • Side Bar
    • Open when burger button on sub nav is clicked.
    • Fetch contents from server.
    • Main Menu
      • Map main menu contents to corresponding sub menus.
      • Compressed portion of contents
    • Sub Menus
  • Back Drop (dimmed layer)
  • Server
    • Hero images endpoint
    • Card images endpoint
    • Search autocomplete endpoint
    • Side bar contents endpoint

Dev Log

Web Components and SCSS

  • SCSS cannot be naturally hooked into Web Components.

Approach 1

  • Compile SCSS files into CSS (using dart-sass compiler) and fetch it as text in JS. Then, insert that CSS string into the Web Component.

  • Example

    sass index.scss:build/css/index.css
    const cssString = await (await fetch("build/styles/index.css")).text();
    
    const template = document.createElement("template");
    const style = document.createElement("style");
    
    template.innerHTML = `
      <div>
        <p>Hello!</p>
      </div>
    `;
    style.textContent = cssString;
    
    export default class TestComponent extends HTMLElement {
      constructor() {
        super();
        const shadowRoot = this.attachShadow({ mode: "open" });
        shadowRoot.append(template.content.cloneNode(true), style);
      }
    }
    
    customElements.define("test-component", TestComponent);
  • Although the overhead is minimal since it is fetched from my own server (relatively cheap), it is still a request/response cycle.

    • However, it is acceptable since the request is only sent once for each corresponding component (i.e. no need to fetch every time the component is generated).

Approach 2

  • Use SASS' JavaScript API.
  • Example 1 - compile
    const sass = require("sass");
    const cssString = sass.compile("./index.scss").css;
  • Example 2 - compileString
    const sass = require("sass");
    const sassString = `
      div {
        p {
          color: blue;
        }
      }
    `;
    const cssString = sass.compileString(sassString).css;
    • No formatter --> Prone to runtime errors.
  • Problem: the "sass" library only supports CommonJS.
    • Need a way to use ESModules instead of Commonjs.
      • Transpile the whole "sass" library to ESModules?
        • Check "sass-loader" source code?
      • Simpler and better to use a module bundler.

SCSS Modules - @use vs @import

  • @import makes variables, mixins, etc. globally accessible.

    • @import is discouraged as it will eventually be removed.
    // index.scss
    
    @import "./_variables.scss";
    @import "./otherfile.scss";
    // _variables.scss
    
    $color-blue: "blue";
    // otherfile.scss
    
    p {
      color: $color-blue;
    }
  • @use makes variables, mixins, etc. only available within the scope of the current file (i.e. not globally accessible).

    // index.scss
    
    @use "./otherfile.scss";
    // _variables.scss
    
    $color-blue: "blue";
    // otherfile.scss
    
    @use "./variables.scss" as *;
    
    p {
      color: $color-blue;
    }

Elements that are not Suitable for Attaching the Shadow DOM

  • Elements related to headings, tables, form, img, inline elements (Ex: a, span).
  • The Shadow DOM can be attached to any HTML tag. However, attaching it to one of these elements may not make sense and lead to displacements.

Dimmed Layer/Backdrop

Ver. 1

  • Need to dim the main portion upon hovering over certain tooltips, and upon showing autocomplete panel.
  • Trigger the dimmed layer by creating and dispatching a custom event from the components to the top-header (parent) component.

Ver. 2

  • Declare a single back-drop component in the body.
  • Any component that needs a backdrop will have a reference to that back-drop component.
  • Set the desired position and height of the backdrop when in need.
  • Approach
    • Make back-drop reactive instead of passive.
      • i.e. back-drop activates/deactivates based on external events in components that use back-drop but those components are unaware of back-drop.
  • Steps
    • Register custom events ("showSelf", "hideSelf").
    • In BackDrop, loop through its listenables and for each listenable, add event listeners to those custom events (and while doing so, include its backdrop logic - activate/deactivate).
    • A ComponentWithBackDrop component dispatches the custom event to itself when needed --> BackDrop activates/deactivates based on the custom event that occured in itself.
      • ComponentWithBackDrop components are unaware of BackDrop.
  • Save all instances of ComponentWithBackDrop in a static property.
    • Use this static property to close all instances that are not currently being activated.
    • Hence, at any point in time, there can only be one ComponentWithBackDrop that is showing.
    • No need to handle z-index anymore.

Keyboard Event

  • When typing in Korean, the last character stays in "composing" state.
  • The keyboard event fires twice when isComposing is true.
  • To prevent double firing, we can ignore the first event that was fired, which is when isComposing is true.
    • i.e. when the input is in "composing" state and the arrow key is pressed, two of the same events are fired. The first should be ignored and only the second should fall through.

Side Bar

  • Possible approaches to map the options in the main menu to their corresponding sub menu.
    1. Assign ids, to begin with, in the original data.
    2. Use indices to assign ids in the fetched data.
  • Side Bar data does not change often and the number of items is relatively small. Also, the ids are exclusively used for the UI. Therefore, there is really no other reason to assign ids in the data itself. Simply assign and use the indices once the data is fetched.

SoC

  • To what extent and by what standard should components and/or logic be separated?
  • Is it ideal to separate things that, when alteration is needed, alter together at the same time?
  • Am I separating things that only really make sense to be used together?
  • Think about the use cases of said components and logic rather than trying to separate everything just for the sake of separating or trying to blindly follow an architecture/pattern.

SearchForm, AutocompletePanel

  • AutocompletePanel requires some sort of input/form from the user that it will base its contents on.
    • This is provided by SearchForm and hence, AutocompletePanel if needed, will be solely used for SearchForm.
  • Therefore, consider AutocompletePanel an inherent part of SearchForm and focus on making SearchForm as reusable as possible.
Implementation
  • SearchFormService receives an endpoint and a default search term to handle the business logic (fetching, serialize/deserialize, current/previous search term comparison).
  • AutocompletePanel State
    • Search results - handled through attributeChangedCallback lifecycle.

Reactive Programming

  • Objective: build self-responsible modules that are focused on their own functionality rather than changing external state. —> Separation of Concerns
  • Ex: whenever a ComponentWithBackDrop shows itself, activate the BackDrop component.
    • The position of the arrow’s tail represents the place of invocation.
    • Traditionally, the arrow’s tail is directly at the source itself. However, in reactive programming, the arrow's tail is not directly at the source. The source is unaware that it is triggering the arrow, and hence, also unaware of the arrow's target.
    • BackDrop.

Passive vs Reactive BackDrop

Passive BackDrop

Reactive BackDrop

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 81.1%
  • SCSS 18.2%
  • HTML 0.7%