๊ด€๋ฆฌ ๋ฉ”๋‰ด

๐Ÿ–ฅ dev-ruby

[VanillaJS] ํ”„๋กœ๊ทธ๋ž˜๋จธ์Šค ๊ณผ์ œ ํ…Œ์ŠคํŠธ๋ฅผ ์—ฐ์Šตํ•˜๋ฉด์„œ ๋ฐฐ์šด ์›น ํŽ˜์ด์ง€ ๊ตฌ์กฐ ๋ณธ๋ฌธ

javascript

[VanillaJS] ํ”„๋กœ๊ทธ๋ž˜๋จธ์Šค ๊ณผ์ œ ํ…Œ์ŠคํŠธ๋ฅผ ์—ฐ์Šตํ•˜๋ฉด์„œ ๋ฐฐ์šด ์›น ํŽ˜์ด์ง€ ๊ตฌ์กฐ

ruby_s 2022. 5. 11. 13:07
728x90
๋ฐ˜์‘ํ˜•
SMALL

์ด๋ฒˆ์— ์šฐํ…Œ์บ  5๊ธฐ 2์ฐจ ๊ณผ์ œํ…Œ์ŠคํŠธ๋ฅผ ์ค€๋น„ํ•˜๊ธฐ ์œ„ํ•ด ํ”„๋กœ๊ทธ๋ž˜๋จธ์Šค์— ๊ณ ์–‘์ด ์‚ฌ์ง„์ฒฉ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ด๋ผ๋Š” ๊ณผ์ œํ…Œ์ŠคํŠธ๋ฅผ ํ’€์–ด๋ณด์•˜๋‹ค. ์ง€๋‚œ๋ฒˆ์— ๊ณ ์–‘์ด ์‚ฌ์ง„ ๊ฒ€์ƒ‰ํ•˜๊ธฐ๋ฅผ ํ–ˆ์—ˆ๋Š”๋ฐ ๊ทธ๋•Œ ๋ฐ”๋‹๋ผJS๊ฐ€ ์ฒ˜์Œ์ด๋ผ ์ง„์งœ ์•„๋ฌด๊ฒƒ๋„ ๋ชจ๋ฅด๊ณ  ์ด๊ฒƒ์ €๊ฒƒ ๋’ค์ง€๋ฉด์„œ ๊ตฌํ˜„ํ–ˆ๋”๋‹ˆ ๋จธ๋ฆฌ์— ๋‚จ์•„์žˆ๋Š”๊ฒŒ ์—†๋‹ค.. ๋ฐ”๋‹๋ผ JS์ž์ฒด๊ฐ€ ๋„ˆ๋ฌด ์žฌ๋ฏธ์—†์–ด์„œ ๊พธ์—ญ๊พธ์—ญ ํ•ด์„œ ๊ทธ๋Ÿฐ๊ฒƒ๋„ ์žˆ๋Š” ๊ฒƒ ๊ฐ™๋‹ค ใ…Ž
๋ญ ์จ‹๋“  ์„œ๋ก ์€ ์ด๋งŒํ•˜๊ณ ,

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ๊ณผ์ œํ…Œ์ŠคํŠธ๋ฅผ ๋”ฑ ์—ด์—ˆ์„ ๋•Œ ์•„๋ฌด๊ฒƒ๋„ ์—†๋Š” ์ƒ ํŒŒ์ผ์— ์–ด๋–ป๊ฒŒ ๊ตฌ์กฐ๋ฅผ ๊ตฌ์„ฑํ•ด๋‚˜๊ฐ€๋ฉด ๋  ์ง€ ์•Œ์•„๋ณผ ๊ฒƒ์ด๋‹ค.

๋จผ์ € ์ฒ˜์Œ์—” index.html์ด ์žˆ์„ ๊ฒƒ์ด๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด์„œ ํ•ด๋‹น ํŒŒ์ผ์•ˆ์— ์š”์†Œ๊ฐ€ ๊ณ„์† ์ถ”๊ฐ€ ๋  ๊ฒƒ์ด๋‹ค.
htmlํŒŒ์ผ์—์„œ ํ•  ์ผ์€ jsํŒŒ์ผ์„ ์—ฐ๊ฒฐํ•ด์ฃผ๋ฉด ๋œ๋‹ค. 

<html>
  <head>
    <title>๊ณ ์–‘์ด ์‚ฌ์ง„์ฒฉ!</title>
    <link rel="stylesheet" href="./src/styles/style.css" />
  </head>
  <body>
    <h1>๊ณ ์–‘์ด ์‚ฌ์ง„์ฒฉ</h1>
    <main class="App"></main>
    <script src="src/js/index.js" type="module"></script>
  </body>
</html>

scriptํƒœ๊ทธ๋Š” <head>์•ˆ์— ๋„ฃ์–ด๋„, <body>์•ˆ์— ๋„ฃ์–ด๋„, <html>์•ˆ์— ๋„ฃ์–ด๋„ ์ƒ๊ด€ ์—†๋‹ค. ๋‹ค๋งŒ, html์˜ bodyํƒœ๊ทธ๋ณด๋‹ค jsํŒŒ์ผ์ผ ๋จผ์ € ์ปดํŒŒ์ผ๋  ์‹œ jsํŒŒ์ผ์€ body์•ˆ์˜ ํƒœ๊ทธ๋“ค์„ ์ฝ์ง€ ๋ชปํ•  ๊ฒƒ์ด๋‹ค. ์šฐ๋ฆฌ๊ฐ€ DOM์„ ์กฐ์ ˆํ•˜๋ ค๋ฉด ์กฐ์ ˆํ•  ์š”์†Œ๋“ค๋ณด๋‹ค ์•„๋ž˜ ์ชฝ์— script๋ฅผ ์—ฐ๊ฒฐํ•ด์ค˜์•ผ ํ•œ๋‹ค.
์—ฌ๊ธฐ์„œ ๋˜ ์ค‘์š”ํ•œ ๊ฒŒ ์žˆ๋‹ค. scriptํƒœ๊ทธ์˜ ์†์„ฑ์— ๋ณด๋ฉด type="module"์ด ๋ณด์ธ๋‹ค. export/import๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ด ์†์„ฑ์„ ๊ผญ ์ถ”๊ฐ€ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

์ด์ œ jsํŒŒ์ผ์„ ํ•˜๋‚˜์”ฉ ์ƒ์„ฑํ•ด๋ณด์ž. ๋‚˜๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ ํŒŒ์ผ๊ตฌ์กฐ๋ฅผ ๊ตฌ์„ฑํ–ˆ๋‹ค.

์ปดํฌ๋„ŒํŠธ ๋ถ„๋ฆฌ๋Š” ์„ ํƒ์ด ์•„๋‹Œ ํ•„์ˆ˜๋‹ค !

1. index.js

import App from "./App.js";
new App(document.querySelector(".App"));

๊ฐ€์žฅ ๋จผ์ € ํ•ด์•ผ ํ• ์ผ์€ ๋ฉ”์ธ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ(App,js)์— DOM์š”์†Œ๋ฅผ ์—ฐ๊ฒฐํ•ด์ฃผ๋Š” ๊ฒƒ์ด๋‹ค. import๋กœ App์„ ๊ฐ€์ ธ์˜จ ํ›„, new ์—ฐ์‚ฐ์ž๋กœ App๊ฐ์ฒด์•ˆ ์— ์ตœ์ƒ๋‹จ์˜ App DOM์š”์†Œ๋ฅผ ๋„ฃ์–ด์ฃผ๋ฉด ๋œ๋‹ค.

2. api.js

const API_END_POINT =
  "https://zl3m4qq0l9.execute-api.ap-northeast-2.amazonaws.com/dev";

export const request = async (nodeId) => {
  try {
    const response = await fetch(`${API_END_POINT}/${nodeId || ""}`, {
      method: "GET",
    });
    if (response.ok) return response.json();
    else throw new Error("error");
  } catch (e) {
    throw new Error("error");
  }
};

ํ•œ๋ฒˆ ๊ตฌํ˜„ํ•ด๋ณด๋‹ˆ๊นŒ api๋ฅผ ์š”์ฒญํ•˜๋Š” ๋ถ€๋ถ„์„ ์ฒ˜์Œ์— ๊ตฌํ˜„ํ•ด๋†“๋Š” ๊ฒƒ์ด ํŽธํ•œ ๊ฒƒ ๊ฐ™๋‹ค๊ณ  ๋Š๊ผˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋‹ค์Œ์€ api๋ฅผ ์š”์ฒญํ•˜๋Š” ํŒŒ์ผ์ด๋‹ค.

๊ณตํ†ต url์€ ์ „์—ญ๋ณ€์ˆ˜๋กœ ๋‘์—ˆ๋‹ค. ๋„ค์ด๋ฐ(API_END_POINT)์€ ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค ์ฝ”๋“œ๋ฅผ ์ฐธ๊ณ ํ–ˆ๋‹ค.
๊ธฐ๋ณธ์ ์œผ๋กœ api์š”์ฒญ ํ•จ์ˆ˜๋Š” async awaitํ•จ์ˆ˜๋กœ ๊ตฌํ˜„ํ•˜์ž. try catch๋ฌธ์œผ๋กœ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋„ ํ•ด์ฃผ์ž.
Promise๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ „๋‹ฌ ๋ฐ›์•„์กŒ๋‹ค๋ฉด response๋ฅผ json์œผ๋กœ ๋ฆฌํ„ดํ•ด์ฃผ์ž. 
(์—๋Ÿฌ๋ฅผ ๋ฑ‰์„ ๋•Œ๋Š” throw๋กœ ๋ฑ‰์–ด์ค„ ๊ฒƒ)

3. App.js

import { request } from "../api/api.js";
import Breadcrumb from "./Breadcrumb.js";
import Nodes from "./Nodes.js";
import ImageView from "./ImageView.js";
import Loading from "./Loading.js";

const cache = {};

export default function App($app) {
    this.state = {
        isRoot: true,
        nodes: [],
        depthStack: [],
        currentPath: null,
        selectedFilePath: null,
        isLoading: true,
    };
  
    this.setState = (nextState) => {
        this.state = nextState;
    };
  
    this.init = async () => {
        try {
            const rootNodes = await request(null);
            this.setState({
            ...this.state,
            nodes: rootNodes,
            });
        } catch (e) {
            throw new Error(e);
        }
    };
    this.init();
}

์ดˆ๊ธฐ App์ด๋‹ค.

this.state: `this.${๋ณ€์ˆ˜๋ช…}`์ด๋Ÿฐ ๋ฐฉ์‹์œผ๋กœ ์ง€์—ญ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•ด์ฃผ๋ฉด ๋œ๋‹ค. state์•ˆ์— ํ•„์š”ํ•œ ์š”์†Œ๋“ค์„ ํ•˜๋‚˜์”ฉ ์ถ”๊ฐ€ํ•ด์ฃผ๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•œ๋‹ค.

this.setState: ์ง€์—ญ๋ณ€์ˆ˜๋“ค์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ๊ฐ ์ปดํฌ๋„ŒํŠธ์— ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๋ฐ˜์˜ํ•ด์ฃผ๊ธฐ ์œ„ํ•ด ์ƒ์„ฑํ•œ ํ•จ์ˆ˜์ด๋‹ค.

this.init: ์ฒซ ์ดˆ๊ธฐ ํ™”๋ฉด์„ ๊ตฌ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ๋งŒ๋“  ํ•จ์ˆ˜์ด๋‹ค.
1. await request()๋กœ api์š”์ฒญ์„ ๋ฐ›์•„์˜จ๋‹ค. 
2. this.setState()๋กœ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๋ฐ˜์˜ํ•ด์ค€๋‹ค. -> ...this.state: ๊ธฐ์กด state๋Š” ๊ทธ๋Œ€๋กœ ๋ฐ˜์˜ํ•˜๊ณ  nodes๋Š” rootNodes๋กœ ๋ณ€๊ฒฝํ•˜๋„๋ก.

4. Nodes.js

export default function Nodes({ $app, state, onClick, onBackClick }) {
  this.state = state;
  this.onClick = onClick;
  this.onBackClick = onBackClick;

  this.setState = (nextState) => {
    this.state = nextState;
    this.render();
  };

  const nodes = document.createElement("div");
  nodes.className = "Nodes";

  this.render = () => {
    const template = this.state.nodes
      .map((node) => {
        return `
        <div class="Node" data-node-id="${node.id}">
          <img src="./assets/${node.type.toLowerCase()}.png"
          data-node-id="${node.id}"/>
          <div>${node.name}</div>
        </div>
      `;
      })
      .join("");

    const backTemplate = `
      <div class="Node">
        <img src="./assets/prev.png" />
      </div>`;

    nodes.innerHTML = this.state.isRoot
      ? template
      : `
      ${backTemplate}
      ${template}
    `;

    $app.appendChild(nodes);
  };

  nodes.addEventListener("click", (e) => {
    const { nodeId } = e.target.closest(".Node").dataset;
    if (nodeId) {
      const selectedNode = this.state.nodes.find((node) => node.id === nodeId);
      if (selectedNode) {
        this.onClick(selectedNode);
      }
    } else {
      this.onBackClick();
    }
  });

  this.render();
}

๊ทธ๋ƒฅ ์ตœ์ข… ์ฝ”๋“œ๋ฅผ ๋ถ™์ด๊ฒ ๋‹ค ์ด ์ปดํฌ๋„ŒํŠธ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ƒ๋‹จ์— this.${๋ณ€์ˆ˜๋ช…}์œผ๋กœ ํ•„์š”ํ•œ ๋ณ€์ˆ˜๋“ค์„ ์ •์˜ํ•œ๋‹ค.

์—ฌ๊ธฐ์„œ ์ปดํฌ๋„ŒํŠธ ์˜์กด๋„๋ฅผ ์ค„์ด๊ธฐ ์œ„ํ•ด ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›์•„์˜จ $app, state ๊ฐ™์€ ๋ณ€์ˆ˜๋“ค์„ this.state = state ์ฒ˜๋Ÿผ ์ง€์—ญ๋ณ€์ˆ˜๋กœ ๋งŒ๋“ค์–ด ์ฃผ๋„๋ก ํ•œ๋‹ค.

์—ฌ๊ธฐ๋„ this.setState๊ฐ€ ๋ณด์ด๋Š”๋ฐ, Nodes์˜ setState๋Š” App.js์—์„œ ํ˜ธ์ถœ๋œ๋‹ค. App.js์˜ setState์•ˆ์—์„œ ํ˜ธ์ถœ๋œ๋‹ค. App์—์„œ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด setState๊ฐ€ ํ˜ธ์ถœ๋˜๊ณ  ๊ทธ ์•ˆ์—์„œ nodes.setState({๋ณ€๊ฒฝ๋œ ๋ณ€์ˆ˜}) ์ด๋ ‡๊ฒŒ ํ˜ธ์ถœ๋œ๋‹ค. ๊ทธ๋ŸผNodes์˜ ์ง€์—ญ๋ณ€์ˆ˜์ธ this.state์— App์—์„œ ๋ณ€๊ฒฝ๋˜์—ˆ๋˜ nextState๊ฐ€ ๋ฐ˜์˜๋œ๋‹ค.

์•„๋ž˜ ์ฐธ๊ณ .

  this.setState = (nextState) => {
    this.state = nextState;
    nodes.setState({
      isRoot: this.state.isRoot,
      nodes: this.state.nodes,
      currentPath: this.state.currentPath,
    });
  };

 

๊ทธ๋ฆฌ๊ณ  ๊ผญ ์ปดํฌ๋„ŒํŠธ ์•ˆ์˜ ์ง€์—ญ๋ณ€์ˆ˜๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ๋‹ค๋ฉด this.render()๋ฅผ ๋‹ค์‹œ ํ˜ธ์ถœํ•ด์ค˜์•ผ html์— ๋ฐ˜์˜์ด ๋œ๋‹ค. ๋”ฐ๋ผ์„œ this.setState์—์„œ ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ๋‹ค ๋ฐ˜์˜๋œ ํ›„ render()๋ฅผ ํ˜ธ์ถœํ•ด์ฃผ๋„๋ก ํ•œ๋‹ค. ์•„ ๊ทธ๋ฆฌ๊ณ  ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๋•Œ๋งŒ ํ˜ธ์ถœ ๋˜๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ ์ดˆ๊ธฐ์—๋„ ํ˜ธ์ถœ์ด ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ตœํ•˜๋‹จ์— this.render(); ๋„ ์ถ”๊ฐ€ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

  this.setState = (nextState) => {
    this.state = nextState;
    this.render();
  };

 

render์—์„œ๋Š” ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ์— ๊ด€๋ จ๋œ ์ถ”๊ฐ€ํ•ด์ค˜์•ผ ํ•  ์š”์†Œ๋“ค์„ ์กฐ์ž‘ํ•œ๋‹ค. ์กฐ์ž‘๋งŒ ํ•˜๋ฉด ์•ˆ๋œ๋‹ค. ์šฐ๋ฆฌ๋Š” new App์•ˆ์— $app์œผ๋กœ ์ตœ์ƒ๋‹จ DOM์š”์†Œ๋ฅผ ์ „๋‹ฌ ๋ฐ›์•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ ์ปดํฌ๋„ŒํŠธ๋งˆ๋‹ค $app์„ ์ „๋‹ฌํ•ด์ค„ ํ•„์š”๊ฐ€ ์žˆ๋‹ค. $app์— ์ถ”๊ฐ€ํ•ด์ค„ ์š”์†Œ๋“ค์„ appendChild๋กœ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.

$app.appendChild(nodes);

 

์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ์˜ ๊ฒฝ์šฐ ์›ฌ๋งŒํ•˜๋ฉด renderํ•จ์ˆ˜ ์•ˆ์— ์ •์˜ํ•˜์ง€ ๋ง๊ณ  ๋ฐ–์— ์ •์˜ํ•˜๋„๋ก ํ•˜์ž. -> ์ด๋ฒคํŠธ ์ตœ์ ํ™”

nodes.addEventListener("click", (e) => {
    const { nodeId } = e.target.closest(".Node").dataset;
    if (nodeId) {
      const selectedNode = this.state.nodes.find((node) => node.id === nodeId);
      if (selectedNode) {
        this.onClick(selectedNode);
      }
    } else {
      this.onBackClick();
    }
  });

์ด๋Ÿฐ์‹์œผ๋กœ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๋“ค๋„ ๊ตฌ์„ฑํ•˜๋ฉด ๋œ๋‹ค ! ์•„๋ž˜์— ์ „์ฒด ์ฝ”๋“œ๋ฅผ ์ฒจ๋ถ€ํ•˜๊ฒ ๋‹ค.


index.js

import App from "./App.js";
new App(document.querySelector(".App"));

 

api.js

const API_END_POINT =
  "https://zl3m4qq0l9.execute-api.ap-northeast-2.amazonaws.com/dev";

export const request = async (nodeId) => {
  try {
    const response = await fetch(`${API_END_POINT}/${nodeId || ""}`, {
      method: "GET",
    });
    if (response.ok) return response.json();
    else throw new Error("error");
  } catch (e) {
    throw new Error("error");
  }
};

 

App.js

import { request } from "../api/api.js";
import Breadcrumb from "./Breadcrumb.js";
import Nodes from "./Nodes.js";
import ImageView from "./ImageView.js";
import Loading from "./Loading.js";

const cache = {};

export default function App($app) {
  this.state = {
    isRoot: true,
    nodes: [],
    depthStack: [],
    currentPath: null,
    selectedFilePath: null,
    isLoading: true,
  };

  const loading = new Loading({ $app, initialState: this.state.isLoading });

  const breadcrumb = new Breadcrumb({
    $app,
    state: this.state.depthStack,
    onClick: (index) => {
      // ๋ฃจํŠธ ๋…ธ๋“œ๋ฅผ ์„ ํƒํ•œ ๊ฒฝ์šฐ
      if (index === null) {
        this.setState({
          ...this.state,
          isRoot: true,
          depthStack: [],
          nodes: cache["root"],
          selectedFilePath: null,
        });
        return;
      }

      // ํ˜„์žฌ path๋ฅผ ์„ ํƒํ•œ ๊ฒฝ์šฐ
      if (index === this.state.depthStack.length) {
        return;
      }

      // ๊ทธ ์™ธ
      const depthStack = this.state.depthStack.slice(0, index + 1);

      this.setState({
        ...this.state,
        depthStack: depthStack,
        nodes: cache[depthStack[depthStack.length - 1]].id,
        selectedFilePath: null,
      });
    },
  });

  const nodes = new Nodes({
    $app,
    state: this.state,
    onClick: async (node) => {
      try {
        this.setState({
          ...this.state,
          isLoading: true,
        });
        if (node.type === "DIRECTORY") {
          if (cache[node.id]) {
            this.setState({
              ...this.state,
              isRoot: false,
              depthStack: [...this.state.depthStack, node],
              nodes: cache[node.id],
            });
          } else {
            const nextNodes = await request(node.id);
            this.setState({
              ...this.state,
              isRoot: false,
              depthStack: [...this.state.depthStack, node],
              nodes: nextNodes,
            });
            cache[node.id] = nextNodes;
          }
        } else if (node.type === "FILE") {
          this.setState({
            ...this.state,
            selectedFilePath: node.filePath,
          });
        }
      } catch (e) {
        throw new Error(e.message);
      } finally {
        this.setState({
          ...this.state,
          isLoading: false,
        });
      }
    },
    onBackClick: async () => {
      try {
        this.setState({
          ...this.state,
          isLoading: true,
        });
        this.state.depthStack.pop();
        const prevId = this.state.depthStack.length
          ? this.state.depthStack[this.state.depthStack.length].id
          : null;

        if (prevId === null) {
          this.setState({
            isRoot: true,
            nodes: cache["root"],
            depthStack: this.state.depthStack,
          });
        } else {
          this.setState({
            ...this.state,
            isRoot: false,
            nodes: cache[prevId],
            selectedFilePath: null,
            depthStack: this.state.depthStack,
          });
        }
      } catch (e) {
        throw new Error(e.message);
      } finally {
        this.setState({
          ...this.state,
          isLoading: false,
        });
      }
    },
  });

  const imageView = new ImageView({ $app, state: this.state.selectedFilePath });

  this.setState = (nextState) => {
    this.state = nextState;
    nodes.setState({
      isRoot: this.state.isRoot,
      nodes: this.state.nodes,
      currentPath: this.state.currentPath,
    });
    breadcrumb.setState(this.state.depthStack);
    imageView.setState(this.state.selectedFilePath);
    loading.setState(this.state.isLoading);
  };

  this.init = async () => {
    try {
      const rootNodes = await request(null);

      this.setState({
        ...this.state,
        nodes: rootNodes,
      });
      cache["root"] = rootNodes;
    } catch (e) {
      throw new Error(e);
    } finally {
      this.setState({
        ...this.state,
        isLoading: false,
      });
    }
  };
  this.init();
}

 

Nodes.js

export default function Nodes({ $app, state, onClick, onBackClick }) {
  this.state = state;
  this.onClick = onClick;
  this.onBackClick = onBackClick;

  this.setState = (nextState) => {
    this.state = nextState;
    this.render();
  };

  const nodes = document.createElement("div");
  nodes.className = "Nodes";

  this.render = () => {
    const template = this.state.nodes
      .map((node) => {
        return `
        <div class="Node" data-node-id="${node.id}">
          <img src="./assets/${node.type.toLowerCase()}.png"
          data-node-id="${node.id}"/>
          <div>${node.name}</div>
        </div>
      `;
      })
      .join("");

    const backTemplate = `
      <div class="Node">
        <img src="./assets/prev.png" />
      </div>`;

    nodes.innerHTML = this.state.isRoot
      ? template
      : `
      ${backTemplate}
      ${template}
    `;

    $app.appendChild(nodes);
  };

  nodes.addEventListener("click", (e) => {
    const { nodeId } = e.target.closest(".Node").dataset;
    if (nodeId) {
      const selectedNode = this.state.nodes.find((node) => node.id === nodeId);
      if (selectedNode) {
        this.onClick(selectedNode);
      }
    } else {
      this.onBackClick();
    }
  });

  this.render();
}

 

Breadcrumb.js

export default function Breadcrumb({ $app, state, onClick }) {
  this.state = state;
  this.onClick = onClick;
  this.setState = (nextState) => {
    this.state = nextState;
    this.render();
  };

  this.$target = document.createElement("nav");
  this.$target.className = "Breadcrumb";
  $app.appendChild(this.$target);

  this.render = () => {
    if (this.state.length) {
      this.$target.innerHTML = `<div class="nav-item">root</div>
    ${this.state
      .map(
        (node, index) =>
          `<div class="nav-item" data-index="${index + 1}">${node.name}</div>`
      )
      .join("")}`;
    } else {
      this.$target.innerHTML = `<div>root</div>`;
    }
  };

  this.$target.addEventListener("click", (e) => {
    const { index } = e.target.closest(".nav-item").dataset;
    this.onClick(parseInt(index, 10) || null);
  });

  this.render();
}

 

ImageView.js

const IMAGE_PATH_PREFIX = `https://fe-dev-matching-2021-03-serverlessdeploymentbuck-t3kpj3way537.s3.ap-northeast-2.amazonaws.com/public`;

export default function ImageView({ $app, initialState }) {
  this.state = initialState;
  this.setState = (nextState) => {
    this.state = nextState;
    this.render();
  };

  this.$target = document.createElement("div");
  this.$target.className = "Modal ImageViewer";

  this.render = () => {
    this.$target.innerHTML = `
      <div class="content">
      ${this.state ? `<img src="${IMAGE_PATH_PREFIX}${this.state}" />` : ""}
      </div>
    `;
    this.$target.style.display = this.state ? "block" : "none";
  };

  this.$target.addEventListener("click", (e) => {
    e.target === this.$target["Modal"]
      ? this.$target.classList.remove("ImageViewer")
      : false;
    this.$target.innerHTML = "";
    this.$target.style.display = "none";
  });

  this.render();
  $app.appendChild(this.$target);
}

 

Loading.js

export default function Loading({ $app, initialState }) {
  this.state = initialState;

  this.$target = document.createElement("div");
  this.$target.className = "Loading Modal";
  $app.appendChild(this.$target);

  this.setState = (nextState) => {
    this.state = nextState;
    this.render();
  };

  this.render = () => {
    this.$target.innerHTML = `
        <div class="content">
            <img src="./assets/nyan-cat.gif"/>
        </div>
    `;

    if (this.state) this.$target.style.display = "block";
    else this.$target.style.display = "none";
  };
  this.render();
}

 

728x90
๋ฐ˜์‘ํ˜•
LIST