Vue3のcomposable( hook )を使ったシンプルな確認ダイアログ

● 使い方

src/views/AboutView.vue

import { useConfirmDialog } from "@/composables/useConfirmDialog";
import ConfirmDialog from "@/components/ConfirmDialog.vue";

const { confirm } = useConfirmDialog({
  title: "HOME画面に移動してよろしいですか?",
  text: "OKボタンを押すと画面遷移します",
});

const handleDialog = async () => {
  if (await confirm()) {
    console.log("okがクリックされました");
  } else {
    console.log("cancelがクリックされました");
  }
};
    <button @click="handleDialog">Aboutダイアログ表示</button>
    <ConfirmDialog></ConfirmDialog>

以上です。シンプルに記述できますね。

必要なファイルは2ファイル 「src/composables/useConfirmDialog.ts」 「src/components/ConfirmDialog.vue」 だけです。

src/composables/useConfirmDialog.ts

import { ref } from "vue";

type DialogText = {
  title?: string;
  text?: string;
  ok?: string;
  cancel?: string;
};

const isOpen = ref<boolean>(false);

const dialogText = ref<DialogText>({
  title: "○○します。よろしいですか?",
  text: "確認して以下のボタンをクリックしてください。",
  ok: "OK",
  cancel: "キャンセル",
});

let _resolve: (value: boolean | PromiseLike<boolean>) => void;

export function useConfirmDialog(props: DialogText = {}) {
  if (props.title) dialogText.value.title = props.title;
  if (props.text) dialogText.value.text = props.text;
  if (props.ok) dialogText.value.ok = props.ok;
  if (props.cancel) dialogText.value.cancel = props.cancel;

  const confirm = () => {
    isOpen.value = true;
    return new Promise<boolean>((resolve) => {
      _resolve = resolve;
    });
  };

  const ok = () => {
    isOpen.value = false;
    _resolve(true);
  };

  const cancel = () => {
    isOpen.value = false;
    _resolve(false);
  };

  const close = () => {
    isOpen.value = false;
  };

  return {
    confirm,
    ok,
    cancel,
    close,
    isOpen,
    dialogText,
  };
}

src/components/ConfirmDialog.vue

<template>
  <div
    v-if="isOpen"
    class="confirm-dialog confirm-dialog-show"
    id="confirm-dialog149993"
    style="margin-top: -86px"
  >
    <div class="confirm-dialog-title">
      {{ dialogText.title }}
    </div>
    <div class="confirm-dialog-content">
      <p class="confirm-dialog-message">
        {{ dialogText.text }}
      </p>
    </div>
    <div class="confirm-dialog-action">
      <button @click="ok" class="btn-ok">{{ dialogText.ok }}</button>
      <button @click="cancel" class="btn-cancel">
        {{ dialogText.cancel }}
      </button>
    </div>
  </div>
  <div v-if="isOpen" @click="close" class="confirm-dialog-overlay"></div>
</template>

<script lang="ts" setup>
import { useConfirmDialog } from "@/composables/useConfirmDialog";

const { isOpen, ok, cancel, close, dialogText } = useConfirmDialog();
</script>

<style scoped>
.confirm-dialog {
  color: #1b1919;
  position: fixed;
  z-index: 1061;
  border-radius: 2px;
  width: 400px;
  margin-left: -200px;
  background-color: #fff;
  -webkit-box-shadow: 0 11px 15px -7px rgba(0, 0, 0, 0.2),
    0 24px 38px 3px rgba(0, 0, 0, 0.14), 0 9px 46px 8px rgba(0, 0, 0, 0.12);
  box-shadow: 0 11px 15px -7px rgba(0, 0, 0, 0.2),
    0 24px 38px 3px rgba(0, 0, 0, 0.14), 0 9px 46px 8px rgba(0, 0, 0, 0.12);
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  left: 50%;
  top: 50%;
  font-size: 16px;
}

.confirm-dialog.confirm-dialog-show {
  -webkit-animation: bounceIn 0.35s ease;
  -o-animation: bounceIn 0.35s ease;
  animation: bounceIn 0.35s ease;
}

.confirm-dialog .confirm-dialog-title {
  text-align: center;
  padding: 24px 30px 20px 30px;
  font-size: 18px;
  line-height: 1.4;
  font-weight: bold;
  color: #1b1919;
}

.confirm-dialog .confirm-dialog-title + .confirm-dialog-content {
  padding-top: 0;
}

.confirm-dialog .confirm-dialog-content {
  text-align: justify;
  padding: 30px 30px 5px 30px;
}

.confirm-dialog .confirm-dialog-content .confirm-dialog-message {
  font-size: 14px;
  line-height: 1.4;
  text-align: center;
  margin: 0;
  padding: 0;
  color: #635a56;
}

.confirm-dialog .confirm-dialog-content .confirm-dialog-prompt input {
  width: 100%;
  height: 36px;
  display: inline-block;
  padding: 6px 0;
  -webkit-box-shadow: none;
  box-shadow: none;
  border: none;
  outline: none;
  font-size: 16px;
  color: #1b1919;
  border-bottom: 1px solid #d9d6d4;
}

.confirm-dialog .confirm-dialog-action {
  display: flex;
  flex-direction: column;
  margin: 10px 30px;
}

.confirm-dialog .confirm-dialog-action [class*="btn-"] {
  font-size: 14px;
  margin: 7px 0;
  cursor: pointer;
  color: #1b1919;
  height: 36px;
  min-width: 88px;
  text-align: center;
  display: inline-block;
  border: 1px solid #dedede;
  border-radius: 12px;
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0.12);
  -webkit-transition: all 0.45s cubic-bezier(0.23, 1, 0.32, 1);
  -o-transition: all 0.45s cubic-bezier(0.23, 1, 0.32, 1);
  transition: all 0.45s cubic-bezier(0.23, 1, 0.32, 1);
}

.confirm-dialog .confirm-dialog-action .btn-ok {
  background-color: black;
  border-color: black;
  color: #fff;
}

.confirm-dialog .confirm-dialog-action .btn-ok:active {
  background-color: #444;
}

.confirm-dialog .confirm-dialog-action .btn-cancel {
  background-color: #ececec;
  color: #635a56;
}

.confirm-dialog .confirm-dialog-action .btn-cancel:active {
  background-color: #dcdcdc;
}

@media all and (max-width: 540px) {
  .confirm-dialog {
    width: auto;
    margin-left: 0;
    margin-right: 0;
    left: 15px;
    right: 15px;
  }
}

.confirm-dialog-overlay {
  position: fixed;
  background-color: #000;
  z-index: 1060;
  height: 100%;
  width: 100%;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  opacity: 0.4;
  -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
  -webkit-transition: opacity 0.45s cubic-bezier(0.23, 1, 0.32, 1);
  -o-transition: opacity 0.45s cubic-bezier(0.23, 1, 0.32, 1);
  transition: opacity 0.45s cubic-bezier(0.23, 1, 0.32, 1);
}
</style>
添付ファイル1
No.2280
02/03 11:27

edit

添付ファイル