87. Closing the Modal
npm create vue@latest
이 강의에서는 모달을 닫는 방법을 배울 것입니다.
이것은 간단하고 빠른 수정이 될 것입니다.
이전과는 달리, 상태를 매핑하는 방법을 배웠기 때문에 스토어를 매핑하지 않을 것입니다.
시연 목적으로 상태를 매핑하는 것만으로 충분할 것 같습니다.
같은 문제를 해결하는 다양한 방법을 보여드리고 싶습니다.
결국 선택은 개발자의 몫입니다.
인증 컴포넌트 파일을 열어서 스크립트 블록으로 이동합시다.
Pinia 패키지로부터 mapWritableState
함수를 가져옵니다.
계산된 객체(computed object
) 안에서 이 함수를 확산(스프레드) 연산자와 함께 호출합시다.
useModalStore
객체를 전달하고, 마지막으로 배열에 isOpen
상태 속성을 추가합니다.
이제 상태 속성이 준비되었습니다.
목표는 이 속성을 false
로 설정하는 것입니다.
이렇게 하면 모달이 닫힐 것입니다.
템플릿으로 스크롤하여 Modal Close Button
이라는 주석을 찾습니다.
이 주석 아래에는 modal-close
클래스를 가진 div
태그가 있을 것입니다.
이 요소는 모달의 닫기 버튼을 포함합니다.
클릭 이벤트에 대해 리스너를 추가할 것입니다.
이 이벤트 내에서 isOpen
속성을 false
로 설정하겠습니다.
변경 사항을 저장하고 페이지를 새로고침하세요.
모달을 열고 닫는 것을 테스트해봅시다.
이전에는 속성을 토글하는 메소드를 정의했지만, 이벤트에서 값을 직접 토글하는 것도 괜찮습니다.
또한 우리는 스토어 대신 상태를 매핑했습니다.
Pinia는 매우 유연합니다.
상태와 상호 작용하기 위한 다양한 옵션을 제공합니다.
<script setup lang="ts">
import AppHeader from '@/components/AppHeader.vue'
import AppAuth from '@/components/AppAuth.vue'
</script>
<template>
<AppHeader />
<!-- Introduction -->
<section class="mb-8 py-20 text-white text-center relative">
<div
class="absolute inset-0 w-full h-full bg-contain introduction-bg"
style="background-image: url('assets/img/header.png')"
></div>
<div class="container mx-auto">
<div class="text-white main-header-content">
<h1 class="font-bold text-5xl mb-5">Listen to Great Music!</h1>
<p class="w-full md:w-8/12 mx-auto">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus et dolor mollis, congue
augue non, venenatis elit. Nunc justo eros, suscipit ac aliquet imperdiet, venenatis et
sapien. Duis sed magna pulvinar, fringilla lorem eget, ullamcorper urna.
</p>
</div>
</div>
<img
class="relative block mx-auto mt-5 -mb-20 w-auto max-w-full"
src="/assets/img/introduction-music.png"
/>
</section>
<!-- Main Content -->
<section class="container mx-auto">
<div class="bg-white rounded border border-gray-200 relative flex flex-col">
<div class="px-6 pt-6 pb-5 font-bold border-b border-gray-200">
<span class="card-title">Songs</span>
<!-- Icon -->
<i class="fa fa-headphones-alt float-right text-green-400 text-xl"></i>
</div>
<!-- Playlist -->
<ol id="playlist">
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
</ol>
<!-- .. end Playlist -->
</div>
</section>
<!-- Player -->
<div class="fixed bottom-0 left-0 bg-white px-4 py-2 w-full">
<!-- Track Info -->
<div class="text-center">
<span class="song-title font-bold">Song Title</span> by
<span class="song-artist">Artist</span>
</div>
<div class="flex flex-nowrap gap-4 items-center">
<!-- Play/Pause Button -->
<button type="button">
<i class="fa fa-play text-gray-500 text-xl"></i>
</button>
<!-- Current Position -->
<div class="player-currenttime">00:00</div>
<!-- Scrub Container -->
<div class="w-full h-2 rounded bg-gray-200 relative cursor-pointer">
<!-- Player Ball -->
<span class="absolute -top-2.5 -ml-2.5 text-gray-800 text-lg" style="left: 50%">
<i class="fas fa-circle"></i>
</span>
<!-- Player Progress Bar-->
<span
class="block h-2 rounded bg-gradient-to-r from-green-500 to-green-400"
style="width: 50%"
></span>
</div>
<!-- Duration -->
<div class="player-duration">03:06</div>
</div>
</div>
<AppAuth />
</template>
<script setup lang="ts">
import useModal from '@/stores/modal'
import { storeToRefs } from 'pinia'
const modalStore = useModal()
const { isOpen } = storeToRefs(modalStore)
const toggleAuthModal = () => {
isOpen.value = !isOpen.value
console.log(isOpen.value)
}
</script>
<template>
<!-- Header -->
<header id="header" class="bg-gray-700">
<nav class="container mx-auto flex justify-start items-center py-5 px-4">
<!-- App Name -->
<a class="text-white font-bold uppercase text-2xl mr-4" href="#">Music</a>
<div class="flex flex-grow items-center">
<!-- Primary Navigation -->
<ul class="flex flex-row mt-1">
<!-- Navigation Links -->
<li>
<a class="px-2 text-white" href="#" @click.prevent="toggleAuthModal"
>Login / Register</a
>
</li>
<li>
<a class="px-2 text-white" href="#">Manage</a>
</li>
</ul>
</div>
</nav>
</header>
</template>
<style scoped></style>
<script setup lang="ts">
import useModal from '@/stores/modal'
import { storeToRefs } from 'pinia'
const modalStore = useModal()
const { hiddenClass, isOpen } = storeToRefs(modalStore)
</script>
<template>
<!-- Auth Modal -->
<div class="fixed z-10 inset-0 overflow-y-auto" id="modal" :class="hiddenClass">
<div
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"
>
<div class="fixed inset-0 transition-opacity">
<div class="absolute inset-0 bg-gray-800 opacity-75"></div>
</div>
<!-- This element is to trick the browser into centering the modal contents. -->
<span class="hidden sm:inline-block sm:align-middle sm:h-screen">​</span>
<div
class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"
>
<!-- Add margin if you want to see some of the overlay behind the modal-->
<div class="py-4 text-left px-6">
<!--Title-->
<div class="flex justify-between items-center pb-4">
<p class="text-2xl font-bold">Your Account</p>
<!-- Modal Close Button -->
<div class="modal-close cursor-pointer z-50" @click="isOpen = false">
<i class="fas fa-times"></i>
</div>
</div>
<!-- Tabs -->
<ul class="flex flex-wrap mb-4">
<li class="flex-auto text-center">
<a
class="block rounded py-3 px-4 transition hover:text-white text-white bg-blue-600"
href="#"
>Login</a
>
</li>
<li class="flex-auto text-center">
<a class="block rounded py-3 px-4 transition" href="#">Register</a>
</li>
</ul>
<!-- Login Form -->
<form>
<!-- Email -->
<div class="mb-3">
<label class="inline-block mb-2">Email</label>
<input
type="email"
class="block w-full py-1.5 px-3 text-gray-800 border border-gray-300 transition duration-500 focus:outline-none focus:border-black rounded"
placeholder="Enter Email"
/>
</div>
<!-- Password -->
<div class="mb-3">
<label class="inline-block mb-2">Password</label>
<input
type="password"
class="block w-full py-1.5 px-3 text-gray-800 border border-gray-300 transition duration-500 focus:outline-none focus:border-black rounded"
placeholder="Password"
/>
</div>
<button
type="submit"
class="block w-full bg-purple-600 text-white py-1.5 px-3 rounded transition hover:bg-purple-700"
>
Submit
</button>
</form>
<!-- Registration Form -->
<form>
<!-- Name -->
<div class="mb-3">
<label class="inline-block mb-2">Name</label>
<input
type="text"
class="block w-full py-1.5 px-3 text-gray-800 border border-gray-300 transition duration-500 focus:outline-none focus:border-black rounded"
placeholder="Enter Name"
/>
</div>
<!-- Email -->
<div class="mb-3">
<label class="inline-block mb-2">Email</label>
<input
type="email"
class="block w-full py-1.5 px-3 text-gray-800 border border-gray-300 transition duration-500 focus:outline-none focus:border-black rounded"
placeholder="Enter Email"
/>
</div>
<!-- Age -->
<div class="mb-3">
<label class="inline-block mb-2">Age</label>
<input
type="number"
class="block w-full py-1.5 px-3 text-gray-800 border border-gray-300 transition duration-500 focus:outline-none focus:border-black rounded"
/>
</div>
<!-- Password -->
<div class="mb-3">
<label class="inline-block mb-2">Password</label>
<input
type="password"
class="block w-full py-1.5 px-3 text-gray-800 border border-gray-300 transition duration-500 focus:outline-none focus:border-black rounded"
placeholder="Password"
/>
</div>
<!-- Confirm Password -->
<div class="mb-3">
<label class="inline-block mb-2">Confirm Password</label>
<input
type="password"
class="block w-full py-1.5 px-3 text-gray-800 border border-gray-300 transition duration-500 focus:outline-none focus:border-black rounded"
placeholder="Confirm Password"
/>
</div>
<!-- Country -->
<div class="mb-3">
<label class="inline-block mb-2">Country</label>
<select
class="block w-full py-1.5 px-3 text-gray-800 border border-gray-300 transition duration-500 focus:outline-none focus:border-black rounded"
>
<option value="USA">USA</option>
<option value="Mexico">Mexico</option>
<option value="Germany">Germany</option>
</select>
</div>
<!-- TOS -->
<div class="mb-3 pl-6">
<input type="checkbox" class="w-4 h-4 float-left -ml-6 mt-1 rounded" />
<label class="inline-block">Accept terms of service</label>
</div>
<button
type="submit"
class="block w-full bg-purple-600 text-white py-1.5 px-3 rounded transition hover:bg-purple-700"
>
Submit
</button>
</form>
</div>
</div>
</div>
</div>
</template>
<style scoped></style>
import { defineStore } from 'pinia'
import { computed, ref } from 'vue'
const useModal = defineStore('modal', () => {
const isOpen = ref(false)
const hiddenClass = computed(() => (!isOpen.value ? 'hidden' : ''))
return {
isOpen,
hiddenClass
}
})
export default useModal