Frontend/vue3

[Vue] modal창 띄우기

dddzr 2023. 4. 4. 10:11

Modal

모달창은 기존의 브라우저 페이지 위에 새로운 윈도우 창이 아닌 레이어를 까는 것을 말합니다.
기존의 페이지와 부모-자식 관계를 갖는 창이고 사용자가 모달 창을 닫기 전에는

부모창에서 다른 작업을 수행하지 못하도록 합니다.

 

메인 구조

일단 메인 구조는 left, center, right 영역으로 나누어져 있습니다.

여기서 right영역의 menu1Contents에서 모달을 띄우는 코드입니다.

<!-- MainContainer -->
<template>
  <!-- LEFT -->
  <div id="left">
    <!-- left Menu -->
  </div>
  <!-- CENTER -->
  <div id="center">
    <HTMLEditor></HTMLEditor>
  </div>
  <!-- RIGHT -->
  <div id="right">
    <div id="tabbar">
      <div class="label">
        <ul>
          <li class="active">{{ $t('MenuTab1') }}</li>
          <li>{{ $t('MenuTab2') }}</li>
        </ul>
      </div>
      <div class="description">
        <!-- MenuTab1 -->
        <Menu1Contents></Menu1Contents>
        <!-- MenuTab2 -->
        <Menu2Contents></Menu2Contents>
      </div>
    </div>
  </div>
  <!-- //RIGHT -->
</template>

 

부모창

1. 모달 open

v-if를 을 사용하여 조건이 참일 때 모달을 오픈합니다.

예를들어,  아래는 v-if="isModalOpen"로 버튼을 클릭했을 때 isModalOpen이 true가 되도록한 코드입니다.

2. 모달 close 이벤트

@close-modal 이벤트를 수신하여 isModalOpen를 false로 바꿔 모달을 닫습니다.

이벤트명은 모달창에서 전달한 이벤트와 동일해야 합니다.

<!-- Menu1Contents.vue -->
<template>
  <div class="active">
    <ul class="design">
      <li>
        <ModalVue v-if="isModalOpen" @close-modal="isModalOpen = false" @ok-modal="modalSave"></ModalVue>
        <div class="Info">
          <ul>
            <li class="box1"></li>
            <li>
              <label for="">{{ $t('Info1') }}</label>
              <input class="infoInputBox" type="text" :value="selectedElem.Info1" />
            </li>
            <li>
              <label for="">{{ $t('Info2') }}</label>
              <input class="infoInputBox" type="text" :value="selectedElem.Info2" />
            </li>
            <li class="box2"></li>
          </ul>
        </div>
      </li>
      <li>
        <button @click="modalOpen()" style="width: 100px; height: 30px; background: white">{{ $t('open') }}</button>
      </li>
    </ul>
  </div>
</template>

<script>
import { storeToRefs } from 'pinia';
import ModalVue from '../modal/ModalVue.vue';

export default {
  name: 'Menu1Contents',
  components: {
    ModalVue
  },
  setup() {
    const { selectedElem } = storeToRefs(properties);
    return { selectedElem };
  },
  data() {
    return {
      isModalOpen: false
    };
  },
  watch: {},

  mounted() {},

  methods: {
    modalOpen() {
      this.isModalOpen = true;
    },
    modalSave() {
    	//code...
    }
  }
};
</script>
<style scoped></style>

 

모달

1. 모달 위치 설정

부모영역이 중앙이 아니기 때문에 모달을 띄울 때 위치조정이 필요합니다.

모달영역의 position은 fiexd로 설정하고 mounted함수에서 화면 중앙에 위치하도록 설정해줍니다.

2. 모달 close 이벤트

@click="$emit('close-modal')" 를 사용하여 부모창에 close 이벤트를 전달합니다.

<!-- Content1Modal.vue -->
<template>
  <div>
    <div class="modal">
      <div class="overlay" @click="$emit('close-modal')"></div>
      <div class="modal-card">
      	<div class="modal-title">
        	Title
        </div>
        <div class="modal-contents">
          <!-- contents --> 
        </div>
        <div class="modalbtn-container">
          <button class="modalbtn" @click="$emit('ok-modal')">OK</button>
          <button class="modalbtn" @click="$emit('close-modal')">Close</button>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  name: 'Content1Modal',
  mounted() {
    const modal = this.$el.querySelector('.modal');
    const modalWidth = modal.offsetWidth;
    const modalHeight = modal.offsetHeight;
    const parentLeft = modal.parentNode.offsetLeft;
    const parentTop = modal.parentNode.offsetTop;

    modal.style.top = `calc(50% + ${parentTop}px)`;
    modal.style.left = `calc(50% + ${parentLeft}px)`;
    modal.style.marginTop = `-${modalHeight / 2}px`;
    modal.style.marginLeft = `-${modalWidth / 2}px`;

    const overlay = this.$el.querySelector('.overlay');
    overlay.style.height = `calc(100% + ${modalHeight}px)`;
    overlay.style.top = `${modal.style.marginTop.replace('px', '')}px`;
  }
};
</script>

<style>
.modal {
  position: fixed;
  z-index: 9999;
  height: fit-content;
}
.overlay {
  width: 100%;
  height: 100%;
  position: fixed;
  left: 0;
  right: 0;
  opacity: 0.5;
  background: black;
}
.modal-card {
  position: relative;
  background: white;
  padding-top: 20px;
  padding-bottom: 20px;
}
.modal-contents {
  height: 700px;
  overflow: auto;
  padding-inline: 20px;
}
.modal-title {
  font-size: 16pt;
  font-weight: 600;
  margin-bottom: 15px;
  padding-left: 20px;
}
.modalbtn-container {
  display: flex;
  justify-content: center;
}
.modalbtn {
  width: 100px;
  height: 30px;
  border: 1px solid;
  margin-inline: 10px;
}

</style>