JSFiddle - React, Tailwind, and code Playground

by kurotanshi

HTML

<script src="https://unpkg.com/vue@next"></script>
<div id="app">
  <menu-component :title="menuData.name" :child="menuData.childNodes"></menu-component>
</div>

SCSS

#app {
  display: block;
  padding: 1rem;
  font-size: 1rem;
}

#app > ul {
  > li {
    margin-left: 0;
  }
}

ul > li {
  display: block;
  margin-left: 1rem;  
  margin-bottom: 0.5rem;
  padding: 5px;
  background-color: #eee;
}

h2.has-child {
  cursor: pointer;
  
  &::before {  
    content: '+ ';
  }
}

h2.has-child.is-open::before {
  content: '- ';
}

JavaScript

const menuData = {
  name: '好書推薦',
  childNodes: [{
      name: 'Git',
      childNodes: [{
        name: '為你自己學 Git'
      }]
    },
    {
      name: '前端開發',
      childNodes: [{
          name: '金魚都能懂的 CSS 選取器'
        },
        {
          name: '0 陷阱!0 誤解!8 天重新認識 JavaScript!'
        },
        {
          name: '讓 TypeScript 成為你全端開發的 ACE!'
        },
      ]
    },
    {
      name: 'IoT',
      childNodes: [{
        name: 'IoT沒那麼難!新手用 JavaScript 入門做自己的玩具!'
      }]
    },
  ]
};

const app = Vue.createApp({
  data() {
    return {
      menuData
    }
  }
});

app.component('menu-component', {
  name: `menu-component`,
  props: {
  	title: String,
  	child: {
    	type: Array,
      default: []
    }
  },
  data () {
  	return {
    	isOpen: false
    }
  },
  template: `
  	<ul>
    	<li>
      	<template v-if="child.length > 0">
          <h2 class="has-child"
            :class="{ 'is-open': isOpen }"
          	@click="isOpen = !isOpen">{{ title }}</h2>
          <menu-component 
            v-show="isOpen"
            v-for="c in child"
            :title="c.name"
            :child="c.childNodes"
          />
        </template>
        <a v-else>{{ title }}</a>
      </li>
    </ul>		
  `
});

app.mount('#app');