From 48ed69b35eaed28ec12428e86e927e81db637b39 Mon Sep 17 00:00:00 2001
From: Rick <rick@belniakmedia.com>
Date: Sat, 23 Nov 2024 02:21:14 -0600
Subject: [PATCH] Implements "pause on hover" feature & exposes onMouseEnter
 and onMouseLeave callback props

---
 packages/primevue/src/toast/BaseToast.vue    |  8 +++++
 packages/primevue/src/toast/Toast.d.ts       |  8 +++++
 packages/primevue/src/toast/ToastMessage.vue | 35 +++++++++++++++++---
 3 files changed, 47 insertions(+), 4 deletions(-)

diff --git a/packages/primevue/src/toast/BaseToast.vue b/packages/primevue/src/toast/BaseToast.vue
index 3cd4dbb4e..70f612ce2 100644
--- a/packages/primevue/src/toast/BaseToast.vue
+++ b/packages/primevue/src/toast/BaseToast.vue
@@ -49,6 +49,14 @@ export default {
         closeButtonProps: {
             type: null,
             default: null
+        },
+        onMouseEnter: {
+            type: Function,
+            default: undefined
+        },
+        onMouseLeave: {
+            type: Function,
+            default: undefined
         }
     },
     style: ToastStyle,
diff --git a/packages/primevue/src/toast/Toast.d.ts b/packages/primevue/src/toast/Toast.d.ts
index 6e5dece06..b0dea3f64 100755
--- a/packages/primevue/src/toast/Toast.d.ts
+++ b/packages/primevue/src/toast/Toast.d.ts
@@ -250,6 +250,14 @@ export interface ToastProps {
      * @defaultValue false
      */
     unstyled?: boolean;
+    /**
+     * Used to specify a callback function to be run when the @mouseenter event is fired on the message component.
+     */
+    onMouseEnter?: Function | undefined;
+    /**
+     * Used to specify a callback function to be run when the @mouseleave event is fired on the message component.
+     */
+    onMouseLeave?: Function | undefined;
 }
 
 /**
diff --git a/packages/primevue/src/toast/ToastMessage.vue b/packages/primevue/src/toast/ToastMessage.vue
index 8e1a3988c..1e200b761 100755
--- a/packages/primevue/src/toast/ToastMessage.vue
+++ b/packages/primevue/src/toast/ToastMessage.vue
@@ -1,5 +1,5 @@
 <template>
-    <div :class="[cx('message'), message.styleClass]" role="alert" aria-live="assertive" aria-atomic="true" v-bind="ptm('message')">
+    <div :class="[cx('message'), message.styleClass]" role="alert" aria-live="assertive" aria-atomic="true" v-bind="ptm('message')" @mouseenter="onMouseEnter" @mouseleave="onMouseLeave">
         <component v-if="templates.container" :is="templates.container" :message="message" :closeCallback="onCloseClick" />
         <div v-else :class="[cx('messageContent'), message.contentStyleClass]" v-bind="ptm('messageContent')">
             <template v-if="!templates.message">
@@ -34,6 +34,8 @@ export default {
     extends: BaseComponent,
     emits: ['close'],
     closeTimeout: null,
+    createdAt: null,
+    lifeRemaining: null,
     props: {
         message: {
             type: null,
@@ -70,15 +72,20 @@ export default {
     },
     mounted() {
         if (this.message.life) {
-            this.closeTimeout = setTimeout(() => {
-                this.close({ message: this.message, type: 'life-end' });
-            }, this.message.life);
+            this.lifeRemaining = this.message.life;
+            this.startTimeout();
         }
     },
     beforeUnmount() {
         this.clearCloseTimeout();
     },
     methods: {
+        startTimeout() {
+            this.createdAt = new Date().valueOf();
+            this.closeTimeout = setTimeout(() => {
+                this.close({ message: this.message, type: 'life-end' });
+            }, this.lifeRemaining);
+        },
         close(params) {
             this.$emit('close', params);
         },
@@ -91,6 +98,26 @@ export default {
                 clearTimeout(this.closeTimeout);
                 this.closeTimeout = null;
             }
+        },
+        onMouseEnter(event) {
+            this.props.onMouseEnter && this.props.onMouseEnter(event);
+            if (event.defaultPrevented) {
+                return;
+            }
+            if (this.message.life) {
+                this.lifeRemaining = this.createdAt + this.lifeRemaining - Date().valueOf();
+                this.createdAt = null;
+                this.clearCloseTimeout();
+            }
+        },
+        onMouseLeave(event) {
+            this.props.onMouseLeave && this.props.onMouseLeave(event);
+            if (event.defaultPrevented) {
+                return;
+            }
+            if (this.message.life) {
+                this.startTimeout();
+            }
         }
     },
     computed: {