Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Vue Button Symphony</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> | |
| <script src="https://unpkg.com/feather-icons"></script> | |
| <style> | |
| .btn-pulse:hover { | |
| animation: pulse 1.5s infinite; | |
| } | |
| @keyframes pulse { | |
| 0% { transform: scale(1); } | |
| 50% { transform: scale(1.05); } | |
| 100% { transform: scale(1); } | |
| } | |
| .v-enter-active, | |
| .v-leave-active { | |
| transition: all 0.5s ease; | |
| } | |
| .v-enter-from, | |
| .v-leave-to { | |
| opacity: 0; | |
| transform: translateY(20px); | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gradient-to-br from-indigo-50 to-purple-50 min-h-screen"> | |
| <div id="app" class="container mx-auto px-4 py-12"> | |
| <div class="text-center mb-16"> | |
| <h1 class="text-5xl font-bold text-indigo-800 mb-4">Vue Button Symphony</h1> | |
| <p class="text-xl text-indigo-600 max-w-2xl mx-auto"> | |
| A playful demonstration of Vue 3's reactivity with a hierarchical button system | |
| </p> | |
| </div> | |
| <div class="flex justify-center mb-12"> | |
| <button @click="resetAll" class="px-6 py-3 bg-gradient-to-r from-indigo-500 to-purple-600 text-white rounded-full shadow-lg hover:shadow-xl transition-all duration-300 flex items-center"> | |
| <i data-feather="refresh-cw" class="mr-2"></i> | |
| Reset Symphony | |
| </button> | |
| </div> | |
| <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8"> | |
| <div v-for="(buttonGroup, index) in buttonGroups" :key="index" | |
| class="bg-white p-6 rounded-2xl shadow-md hover:shadow-lg transition-all duration-300"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <h2 class="text-2xl font-semibold text-indigo-700">{{ buttonGroup.name }}</h2> | |
| <button @click="buttonGroup.showChildren = !buttonGroup.showChildren" | |
| class="p-2 rounded-full hover:bg-indigo-50 transition-colors"> | |
| <i :data-feather="buttonGroup.showChildren ? 'chevron-up' : 'chevron-down'"></i> | |
| </button> | |
| </div> | |
| <button @click="toggleButton(buttonGroup)" | |
| :class="['w-full py-3 px-4 rounded-lg mb-4 transition-all duration-300 btn-pulse', | |
| buttonGroup.active ? 'bg-indigo-600 text-white' : 'bg-indigo-100 text-indigo-800']"> | |
| {{ buttonGroup.active ? 'Active' : 'Activate' }} {{ buttonGroup.name }} | |
| </button> | |
| <transition-group name="v"> | |
| <div v-if="buttonGroup.showChildren" class="space-y-3 mt-3 pl-4 border-l-2 border-indigo-200"> | |
| <div v-for="(child, childIndex) in buttonGroup.children" :key="childIndex" | |
| class="bg-indigo-50 p-3 rounded-lg"> | |
| <button @click="toggleNestedButton(buttonGroup, child)" | |
| :class="['w-full py-2 px-3 rounded-md transition-all', | |
| child.active ? 'bg-purple-600 text-white' : 'bg-purple-100 text-purple-800']"> | |
| {{ child.active ? '✓ ' : '' }}{{ child.name }} | |
| </button> | |
| <transition-group name="v" v-if="child.showGrandchildren"> | |
| <div v-for="(grandchild, gIndex) in child.grandchildren" :key="gIndex" | |
| class="mt-2 ml-4 pl-2 border-l-2 border-purple-200"> | |
| <button @click="toggleGrandchildButton(buttonGroup, child, grandchild)" | |
| :class="['w-full py-1 px-2 rounded text-sm transition-all', | |
| grandchild.active ? 'bg-pink-600 text-white' : 'bg-pink-100 text-pink-800']"> | |
| {{ grandchild.active ? '✓ ' : '' }}{{ grandchild.name }} | |
| </button> | |
| </div> | |
| </transition-group> | |
| <button v-if="child.grandchildren.length > 0" | |
| @click="child.showGrandchildren = !child.showGrandchildren" | |
| class="mt-2 text-xs text-purple-500 hover:text-purple-700 flex items-center"> | |
| <i :data-feather="child.showGrandchildren ? 'minus' : 'plus'" class="w-3 h-3 mr-1"></i> | |
| {{ child.showGrandchildren ? 'Hide' : 'Show' }} details | |
| </button> | |
| </div> | |
| </div> | |
| </transition-group> | |
| </div> | |
| </div> | |
| <div class="mt-16 bg-white p-6 rounded-2xl shadow-md max-w-2xl mx-auto"> | |
| <h2 class="text-2xl font-semibold text-indigo-700 mb-4">Symphony Log</h2> | |
| <div class="bg-indigo-50 p-4 rounded-lg max-h-64 overflow-y-auto"> | |
| <div v-for="(log, index) in logs" :key="index" | |
| class="py-2 border-b border-indigo-100 last:border-0"> | |
| <p class="text-indigo-800">{{ log }}</p> | |
| </div> | |
| <p v-if="logs.length === 0" class="text-indigo-400 italic">No activity yet...</p> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| const { createApp, ref } = Vue; | |
| createApp({ | |
| setup() { | |
| const buttonGroups = ref([ | |
| { | |
| name: 'Melody', | |
| active: false, | |
| showChildren: true, | |
| children: [ | |
| { | |
| name: 'Piano', | |
| active: false, | |
| showGrandchildren: false, | |
| grandchildren: [ | |
| { name: 'C Major', active: false }, | |
| { name: 'A Minor', active: false }, | |
| { name: 'G Dominant', active: false } | |
| ] | |
| }, | |
| { | |
| name: 'Violin', | |
| active: false, | |
| showGrandchildren: false, | |
| grandchildren: [ | |
| { name: 'Legato', active: false }, | |
| { name: 'Staccato', active: false } | |
| ] | |
| } | |
| ] | |
| }, | |
| { | |
| name: 'Harmony', | |
| active: false, | |
| showChildren: true, | |
| children: [ | |
| { | |
| name: 'Chords', | |
| active: false, | |
| showGrandchildren: false, | |
| grandchildren: [ | |
| { name: 'Triad', active: false }, | |
| { name: 'Seventh', active: false }, | |
| { name: 'Ninth', active: false } | |
| ] | |
| }, | |
| { | |
| name: 'Progressions', | |
| active: false, | |
| showGrandchildren: false, | |
| grandchildren: [ | |
| { name: 'I-IV-V', active: false }, | |
| { name: 'ii-V-I', active: false } | |
| ] | |
| } | |
| ] | |
| }, | |
| { | |
| name: 'Rhythm', | |
| active: false, | |
| showChildren: true, | |
| children: [ | |
| { | |
| name: 'Drums', | |
| active: false, | |
| showGrandchildren: false, | |
| grandchildren: [ | |
| { name: '4/4 Time', active: false }, | |
| { name: '3/4 Time', active: false } | |
| ] | |
| }, | |
| { | |
| name: 'Percussion', | |
| active: false, | |
| showGrandchildren: false, | |
| grandchildren: [ | |
| { name: 'Tambourine', active: false }, | |
| { name: 'Maracas', active: false } | |
| ] | |
| } | |
| ] | |
| } | |
| ]); | |
| const logs = ref([]); | |
| const toggleButton = (group) => { | |
| group.active = !group.active; | |
| addLog(`${group.name} ${group.active ? 'activated' : 'deactivated'}`); | |
| }; | |
| const toggleNestedButton = (group, child) => { | |
| child.active = !child.active; | |
| addLog(`${child.name} in ${group.name} ${child.active ? 'activated' : 'deactivated'}`); | |
| }; | |
| const toggleGrandchildButton = (group, child, grandchild) => { | |
| grandchild.active = !grandchild.active; | |
| addLog(`${grandchild.name} in ${child.name} (${group.name}) ${grandchild.active ? 'activated' : 'deactivated'}`); | |
| }; | |
| const resetAll = () => { | |
| buttonGroups.value.forEach(group => { | |
| group.active = false; | |
| group.children.forEach(child => { | |
| child.active = false; | |
| child.grandchildren.forEach(grandchild => { | |
| grandchild.active = false; | |
| }); | |
| }); | |
| }); | |
| addLog('Reset all buttons'); | |
| }; | |
| const addLog = (message) => { | |
| const now = new Date(); | |
| const timestamp = now.toLocaleTimeString(); | |
| logs.value.unshift(`[${timestamp}] ${message}`); | |
| if (logs.value.length > 20) { | |
| logs.value.pop(); | |
| } | |
| }; | |
| return { | |
| buttonGroups, | |
| logs, | |
| toggleButton, | |
| toggleNestedButton, | |
| toggleGrandchildButton, | |
| resetAll | |
| }; | |
| }, | |
| mounted() { | |
| feather.replace(); | |
| }, | |
| updated() { | |
| feather.replace(); | |
| } | |
| }).mount('#app'); | |
| </script> | |
| </body> | |
| </html> | |