Vueのモーダルコンポーネントを作成する

デザイン

BULMAのスタイルを使用したモーダルコンポーネントを作成します。
タイトルとボタンを配置するエリアも表示します。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
<template>
  <div>
    <transition name="unfold">
      <div
          v-show="isShow"
          class="modal is-active"
      >
        <div class="modal-background"></div>
        <div class="modal-card">
          <header class="modal-card-head">
            <p class="modal-card-title">
              <slot name="header">title</slot>
            </p>
            <button class="delete" aria-label="close"></button>
          </header>
          <section class="modal-card-body">
            <!-- Content ... -->
            <slot>
            </slot>
          </section>
          <footer class="modal-card-foot">
            <slot name="footer">
              <button class="button is-success">Save changes</button>
              <button class="button">Cancel</button>
            </slot>
          </footer>
        </div>
      </div>
    </transition>
  </div>
</template>

<script>
export default {
  name: "BaseModal",
  props: {
    visibility: { type: Boolean, default: false },
  },
  emits:['changeModalState'],
  computed: {
    isShow() {
      return !!this.visibility;
    }
  },
  methods: {
    closeModal() {
      this.$emit('changeModalState', { "id": this.$el.id, "visibility": false })
    }
  },
  mounted() {
    (this.$el.querySelectorAll('.modal-background, .modal-close, .modal-card-head .delete, .modal-card-foot .button') || []).forEach(($close) => {
      const $target = $close.closest('.modal')
      $close.addEventListener('click', () => {
        this.closeModal($target)
      })
    })
  },
  beforeUnmount() {
    (this.$el.querySelectorAll('.modal-background, .modal-close, .modal-card-head .delete, .modal-card-foot .button') || []).forEach(($close) => {
      const $target = $close.closest('.modal')
      $close.removeEventListener('click', () => {
        this.closeModal($target)
      })
    })
  }
}
</script>

<style scoped lang="scss">
.unfold-enter-active {
  transform:scaleY(.01) scaleX(0);
  animation: unfoldIn .8s ease;
  .modal-card {
    transform:scale(0);
    animation: zoomIn .5s .3s ease-out;
  }
}

.unfold-leave-active {
  animation: unfoldOut .5s .3s ease-in;
  .modal-card {
    animation: zoomOut .8s ease-in;
  }
}

.unfold-enter-from {
  line-height: 0;
  opacity: 0;
}

@keyframes unfoldIn {
  0% {
    transform:scaleY(.005) scaleX(0);
  }
  50% {
    transform:scaleY(.005) scaleX(1);
  }
  100% {
    transform:scaleY(1) scaleX(1);
  }
}

@keyframes unfoldOut {
  0% {
    transform:scaleY(1) scaleX(1);
  }
  50% {
    transform:scaleY(.005) scaleX(1);
  }
  100% {
    transform:scaleY(.005) scaleX(0);
  }
}

@keyframes zoomIn {
  0% {
    transform:scale(0);
  }
  100% {
    transform:scale(1);
  }
}

@keyframes zoomOut {
  0% {
    transform:scale(1);
  }
  100% {
    transform:scale(0);
  }
}

</style>
</template>

感想

BULMAのスタイルに合わせて、is-activeで表示と非表示の切替を行いたかったのですが、できませんでした。 is-activeで非表示にしてしまうと、animation開始時に非表示になってしまうためです。
classにはis-activeを設定し続け、transitionのanimationが終了後にv-showで非表示にすることにしました。

リンク

モーダルコンポーネントの追加

Last Mod: May 21, 2022