0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Vue3 + DaisyUIで通知を作成

Last updated at Posted at 2024-07-21

はじめに

ユーザにエラーなどを通知する際のトーストみたいなものです。
DaisyUI に Alertコンポーネント があるのですが、それをただ表示するのでは味気ないので横からスライドして入ってくるものを作りたかった感じです。

調べるとVueには TransitionGroup という組み込みコンポーネントが用意されており、
これを利用すると v-for でのリスト表示時、リストに入ったとき出たときにクラスが付与されてアニメーションが設定できるようです。

作ったもの

画面収録-2024-07-21-19.10.33.gif
なんかgifにしたらクソ遅いですけど、実際はもっと早いです

実装

Alertコンポーネント

components/alert/Alert.vue
<template>
  <transition-group name="alert" tag="ul" class="alert-wrapper md:w-1/2 w-full">
    <li v-for="alert in alerts" :key="alert.key" class="my-2 w-full">
      <div role="alert" class="alert flex justify-between" :class="getAlertColorClass(alert.color)" >
        <div class="flex gap-1.5">
          <svg
            xmlns="http://www.w3.org/2000/svg"
            fill="none"
            viewBox="0 0 24 24"
            class="stroke-current h-6 w-6 shrink-0">
            <path
              stroke-linecap="round"
              stroke-linejoin="round"
              stroke-width="2"
              d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
          </svg>
          <span>
            {{ alert.message }}
          </span>
        </div>

        <button class="btn btn-sm btn-ghost" v-if="alert.closeable" @click="closeAlert(alert)">
          <span class="icon-[ic--baseline-close]" style="width: 20px; height: 20px;"></span>
        </button>
      </div>
    </li>
  </transition-group>
</template>

<script setup lang="ts">
import { alerts, closeAlert } from '@/composables/alert';

function getAlertColorClass(color: string = 'default') {
  let className: string | null | undefined = null;

  switch (color) {
    case 'default':
      className = '';
      break;
    case 'info':
      className = 'alert-info';
      break;
    case 'success':
      className = 'alert-success';
      break;
    case 'warning':
      className = 'alert-warning';
      break;
    case 'error':
      className = 'alert-error';
      break;
  }

  return className;
}
</script>

<style>
.alert-wrapper {
  position: absolute;
  right: 0;
  bottom: 0;
  z-index: 1000000;
  padding: 20px;
  overflow: hidden;
}

.alert-move, /* 移動する要素にトランジションを適用 */
.alert-enter-active,
.alert-leave-active {
  transition: all 0.8s ease;
}

.alert-enter-from,
.alert-leave-to {
  opacity: 0;
  transform: translateX(50%);
}

/* leave する項目をレイアウトフローから外すことで
   アニメーションが正しく計算されるようになる */
.alert-leave-active {
  position: absolute;
}
</style>

template部分は DaisyUI のコンポーネントを利用する形で、Alertのタイプによってクラスを変更している形です。
スタイルはVueのドキュメントほぼそのままです。
.alert-wrapper は通知形式に表示する用の設定です。(ルートの要素に position: relative; も必要)
閉じるアイコンなどは外部のライブラリを利用しています

通知の表示ロジック

composables/alert.ts
import { ref, type Ref } from 'vue';

interface Alert {
  key?: string,
  /**
   * アラートのタイプ
   */
  color?: string | 'default' | 'info' | 'success' | 'warning' | 'error',
  /**
   * 表示するメッセージ
   */
  message?: string,
  /**
   * 閉じるボタンを表示するかどうか
   */
  closeable?: boolean,
  /**
   * 閉じるまでの秒数
   * 0以下で無効 その場合closeableが必要
   * clseableも無効の場合は5秒後に削除
   */
  close_at?: number,
};

const alerts: Ref<Alert[]> = ref<Alert[]>([]);

const pushAlert = ({ message, color = 'default', closeable = false, close_at = 0}: Alert) => {
  close_at = close_at > 0 ? close_at : 0;
  const alert: Alert = {
    key: crypto.randomUUID(),
    color,
    message,
    closeable,
    close_at,
  };

  alerts.value.push(alert);

  // 閉じるボタン非表示かつ、閉じる秒数も設定されていない場合は、強制的に5秒後に削除
  if (!closeable && !close_at) {
    close_at = 5;
  }

  if (close_at) {
    /**
     * close_at秒後に消す
     */
    setTimeout(() => {
      closeAlert(alert);
    }, close_at * 1000);
  }

};

const closeAlert = (alert: Alert) => {
  alerts.value = alerts.value.filter((item) => item.key !== alert.key);
}

export {
  type Alert,
  pushAlert,
  closeAlert,
  alerts,
};

ここでは通知のリストを配列として保持し、配列に出し入れする処理を関数としてエクスポートしています。
通知の要素としては

  • key: 配列で管理する際の key
  • color: 通知するアラートのタイプ
  • message: 通知するメッセージ
  • closeable: 閉じるボタンを表示するかどうか
  • close_at: x秒後に非表示

を用意して、通知を表示する際に指定する形にしています。(keyは自動で設定)
通知の表示非表示は、リスト(配列)の要素があるかないかで表現される感じです。

利用

  • ルートのコンポーネントで Alert コンポーネントを呼び出し (ここは teleport などが使えるかもですが、未検証)
  • 通知の表示は以下のように†コンポーザブルをインポートして†()、pushAlertを呼び出す形です
test.ts
import { pushAlert } from '@/composables/alert'

pushAlert({/** 省略 */});

締め

外部のライブラリなどを使わずにいい感じに通知アニメーションが作れて良いですね。
TypeScript初心者なので色々使い方など間違ってるかもしれませんが...

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?