11<script setup lang="ts">
22// This is so bad lol pls fix
3- import { type Message , type ToastControllerProps , type ToastLayout , type ToastPostion , type ToastToDismiss } from ' @/types/pinesTypes' ;
3+ import type { Message , ToastControllerProps , ToastLayout , ToastPostion , ToastToDismiss } from ' @/types/pinesTypes' ;
44
5+ import { DEFAULT_GAP , MOBILE_VIEWPORT_OFFSET , SCALE_STEP , TOAST_WIDTH , VIEWPORT_OFFSET , VISIBLE_TOASTS_AMOUNT , Y_OFFSET_STEP , Z_STEP } from ' @/service/toaster/constants' ;
56import { nextTick , onMounted , ref , watch , watchEffect } from ' vue' ;
67import { ToastState } from ' @/service/toaster/toastService' ;
78
89import ToastNotification from ' @/components/pinesUI/ToastNotification.vue' ;
910
10- // Visible toasts amount
11- const VISIBLE_TOASTS_AMOUNT = 3 ;
12-
13- // Viewport padding
14- const VIEWPORT_OFFSET = ' 24px' ;
15- const MOBILE_VIEWPORT_OFFSET = ' 16px' ;
16-
17- // Default toast width
18- const TOAST_WIDTH = 0 ;
19-
20- // Default gap between toasts
21- const DEFAULT_GAP = 16 ;
22-
2311const props = withDefaults (defineProps <ToastControllerProps >(), {
2412 layout: ' default' ,
2513 position: ' bottom-right' ,
@@ -33,8 +21,8 @@ const props = withDefaults(defineProps<ToastControllerProps>(), {
3321const container = ref <HTMLElement >();
3422
3523const messages = ref <Message []>([]);
36- const layout = ref <ToastLayout >(props .layout );
3724const position = ref <ToastPostion >(props .position );
25+ const layout = ref <ToastLayout >(props .layout );
3826
3927const toastsHovered = ref (false );
4028const expanded = ref (props .layout === ' expanded' );
@@ -45,35 +33,53 @@ const burnTimeout = ref<null | number>(null);
4533function deleteToastWithId(id : string ) {
4634 messages .value = messages .value .filter ((msg ) => msg .id !== id );
4735 ToastState .dismiss (id );
48- stackToasts ();
36+ stackToasts (' DELETE ' );
4937}
5038
51- function stackToasts() {
52- positionToasts ();
39+ function stackToasts(_ ? : any ) {
40+ positionToasts (_ );
5341 calculateHeightOfToastsContainer ();
5442
5543 if (heightRecalculateTimeout .value ) clearTimeout (heightRecalculateTimeout .value );
5644
45+ // This calculates container height after the toasts are positioned
46+ if (! expanded .value ) return ;
47+
5748 heightRecalculateTimeout .value = window .setTimeout (() => {
49+ // positionToasts(_);
5850 calculateHeightOfToastsContainer ();
59- }, 300 );
51+ }, 100 );
6052}
6153
62- function positionToasts() {
63- if (messages .value .length == 0 ) return ;
54+ function positionToasts(_ ? : any ) {
55+ if (messages .value .length == 0 ) {
56+ return ;
57+ }
6458
6559 try {
66- let scaleBuffer = 1 ;
67- let yBuffer = 0 ;
68- let zBuffer = 200 ;
6960 const bottomFlag = position .value .includes (' bottom' );
70- let totalHeight = 0 ;
7161 const toastElements = [];
62+
63+ let totalHeight = 0 ;
64+
65+ let scaleBuffer = 1 + SCALE_STEP ;
66+ let yBuffer = - Y_OFFSET_STEP ;
67+ let zBuffer = 200 + Z_STEP ;
68+
7269 for (let i = 0 ; i < messages .value .length ; i ++ ) {
70+ const msg = messages .value [i ];
7371 const toast = document .getElementById (` ${messages .value [i ].id } ` );
7472
7573 if (! toast ) return ;
7674
75+ zBuffer -= Z_STEP ;
76+ scaleBuffer -= SCALE_STEP ;
77+ yBuffer += Y_OFFSET_STEP ;
78+
79+ msg .scale = scaleBuffer ;
80+ msg .offsetY = yBuffer ;
81+ msg .zIndex = zBuffer ;
82+
7783 toast .style .zIndex = ` ${zBuffer } ` ;
7884 toastElements .push (toast );
7985
@@ -87,22 +93,27 @@ function positionToasts() {
8793
8894 totalHeight += toast .offsetHeight ;
8995
90- toast .style .scale = ` 1 ` ;
91- toast .style .transform = ` translateY(0px) ` ;
92- } else if (i > 0 ) {
93- toast .style .scale = ` ${scaleBuffer } ` ;
94-
95- if (bottomFlag && i < props .maxVisibleToasts ) {
96- toast .style .transform = ` translateY(-${yBuffer }px) ` ;
97- } else {
98- alignBottom (toastElements [0 ], toast );
99- toast .style .transform = ` translateY(${yBuffer }px) ` ;
100- }
96+ // toast.style.scale = `1`;
97+ msg .scale = 1 ;
98+
99+ msg .offsetY = 0 ;
100+
101+ // toast.style.setProperty('--offset-y', '0px');
102+ continue ;
101103 }
102104
103- zBuffer -= 10 ;
104- scaleBuffer -= 0.06 ;
105- yBuffer += 16 ;
105+ msg .scale = scaleBuffer ;
106+ // toast.style.scale = `${scaleBuffer}`;
107+
108+ if (bottomFlag ) {
109+ msg .offsetY *= - 1 ;
110+ // toast.style.setProperty('--offset-y', `-${yBuffer}px`);
111+ } else {
112+ alignBottom (toastElements [0 ], toast );
113+ // toast.style.setProperty('--offset-y', `${yBuffer}px`);
114+ }
115+
116+ Object .assign (messages .value [i ], msg );
106117 }
107118
108119 handleToastsOverflow (toastElements );
@@ -123,12 +134,12 @@ function handleToastsOverflow(toastElements: HTMLElement[]) {
123134 return ;
124135 }
125136
126- burnToast .firstElementChild ?.classList .remove (' opacity-100' );
137+ // burnToast.firstElementChild?.classList.remove('opacity-100');
127138 burnToast .firstElementChild ?.classList .add (' opacity-0' );
128139
129- if (burnTimeout .value ) {
130- clearTimeout (burnTimeout .value );
131- }
140+ // if (burnTimeout.value) {
141+ // clearTimeout(burnTimeout.value);
142+ // }
132143
133144 // Burn 🔥 (remove) last toast
134145 burnTimeout .value = window .setTimeout (function () {
@@ -159,15 +170,17 @@ function calculateHeightOfToastsContainer() {
159170
160171 if (! firstToastRectangle || ! lastToastRectangle ) return ;
161172
162- if (toastsHovered .value ) {
163- if (position .value .includes (' bottom' )) {
164- container .value .style .height = firstToastRectangle .top + firstToastRectangle .height - lastToastRectangle .top + ' px' ;
165- } else {
166- container .value .style .height = lastToastRectangle .top + lastToastRectangle .height - firstToastRectangle .top + ' px' ;
167- }
168- } else {
173+ if (! toastsHovered .value ) {
169174 container .value .style .height = firstToastRectangle .height + ' px' ;
175+ return ;
170176 }
177+
178+ if (position .value .includes (' bottom' )) {
179+ container .value .style .height = firstToastRectangle .top + firstToastRectangle .height - lastToastRectangle .top + ' px' ;
180+ return ;
181+ }
182+
183+ container .value .style .height = lastToastRectangle .top + lastToastRectangle .height - firstToastRectangle .top + ' px' ;
171184}
172185
173186function alignBottom(element1 : HTMLElement , element2 : HTMLElement ) {
@@ -204,36 +217,44 @@ function resetTop() {
204217}
205218
206219onMounted (() => {
207- stackToasts ();
220+ stackToasts (' MOUNT ' );
208221});
209222
210223watch (
211224 () => toastsHovered .value ,
212225 (value ) => {
213- if (layout .value == ' default' ) {
214- if (position .value .includes (' bottom' )) {
215- resetBottom ();
216- } else {
217- resetTop ();
218- }
226+ if (layout .value !== ' default' ) return ;
219227
220- if (value ) {
221- // calculate the new positions
222- expanded .value = true ;
223- if (layout .value == ' default' ) {
224- stackToasts ();
225- }
226- } else {
227- if (layout .value == ' default' ) {
228- expanded .value = false ;
229- // setTimeout(function(){
230- stackToasts ();
231- // }, 10);
232- window .setTimeout (function () {
233- stackToasts ();
234- }, 10 );
235- }
236- }
228+ if (position .value .includes (' bottom' )) {
229+ resetBottom ();
230+ } else {
231+ resetTop ();
232+ }
233+
234+ if (value ) {
235+ // calculate the new positions
236+ expanded .value = true ;
237+ stackToasts (' HOVER' );
238+ return ;
239+ }
240+
241+ expanded .value = false ;
242+ // setTimeout(function () {
243+ // This can get called a bunch of times but since the timer is so short, it will probably be called before the event happens again
244+ stackToasts (' HOVER CLOSE' );
245+ // }, 10);
246+ // window.setTimeout(function () {
247+ // stackToasts('POST HOVER CLOSE');
248+ // }, 10);
249+ },
250+ );
251+
252+ watch (
253+ () => messages .value ,
254+ () => {
255+ // Ensure expanded is always false when no toasts are present / only one left
256+ if (messages .value .length <= 1 ) {
257+ expanded .value = false ;
237258 }
238259 },
239260);
@@ -256,22 +277,21 @@ watchEffect((onInvalidate) => {
256277 { ... messages .value [indexOfExistingToast ], ... newToast },
257278 ... messages .value .slice (indexOfExistingToast + 1 ),
258279 ];
259- } else {
260- const toast = newToast as Message ;
261-
262- if (toast .position ) position .value = toast .position ;
263-
264- messages .value .unshift ({
265- ... toast ,
266- id: toast .id ,
267- type: toast .type ?? ' default' ,
268- position: toast .position ?? position .value ,
269- life: toast .life ?? props .defaultLife ,
270- title: toast .title ,
271- });
272280
273- // messages.value = [toast as Message, ...messages.value] ;
281+ return ;
274282 }
283+ const toast = newToast as Message ;
284+
285+ if (toast .position ) position .value = toast .position ;
286+
287+ messages .value .unshift ({
288+ ... toast ,
289+ id: toast .id ,
290+ type: toast .type ?? ' default' ,
291+ position: toast .position ?? position .value ,
292+ life: toast .life ?? props .defaultLife ,
293+ title: toast .title ,
294+ });
275295 });
276296 });
277297
@@ -281,31 +301,37 @@ watchEffect((onInvalidate) => {
281301
282302<template >
283303 <teleport to =" body" >
284- <ul
304+ <ol
285305 v-cloak
286306 ref =" container"
287- :tabindex = " -1 "
288- :class = "
289- `fixed w-full ${TOAST_WIDTH ? `sm:w-[${TOAST_WIDTH}px]` : 'sm:max-w-sm'} group z-[500] [ & >*]:px-4 [ & >*]:px-[${mobileViewportOffset ?? viewportOffset}] [ & >*]:sm:px-6 [ & >*]:sm:px-[${viewportOffset}] my-4 sm:my-6 my-[${mobileViewportOffset ?? viewportOffset}] sm:my-[${viewportOffset}] ` +
290- `${position == 'top-right' ? 'right-0 top-0' : ''} ` +
291- `${position == 'top-left' ? 'left-0 top-0' : ''} ` +
292- `${position == 'top-center' ? 'left-1/2 -translate-x-1/2 top-0' : ''} ` +
293- `${position == 'bottom-right' ? 'right-0 bottom-0' : ''} ` +
294- `${position == 'bottom-left' ? 'left-0 bottom-0' : ''} ` +
295- `${position == 'bottom-center' ? 'left-1/2 -translate-x-1/2 bottom-0' : ''} `
296- "
307+ :class = " [
308+ `fixed w-full group z-[500] [ & >*]:px-4 [ & >*]:px-[${mobileViewportOffset ?? viewportOffset}] [ & >*]:sm:px-6 [ & >*]:sm:px-[${viewportOffset}] my-4 sm:my-6 my-[${mobileViewportOffset ?? viewportOffset}] sm:my-[${viewportOffset}]`,
309+ `${TOAST_WIDTH ? `sm:w-[${TOAST_WIDTH}px]` : 'sm:max-w-sm'}`,
310+ `${position == 'top-right' ? 'right-0 top-0' : ''}`,
311+ `${position == 'top-left' ? 'left-0 top-0' : ''}`,
312+ `${position == 'top-center' ? 'left-1/2 -translate-x-1/2 top-0' : ''}`,
313+ `${position == 'bottom-right' ? 'right-0 bottom-0' : ''}`,
314+ `${position == 'bottom-left' ? 'left-0 bottom-0' : ''}`,
315+ `${position == 'bottom-center' ? 'left-1/2 -translate-x-1/2 bottom-0' : ''}`,
316+ ] "
297317 @mouseenter =" toastsHovered = true"
298318 @mouseleave =" toastsHovered = false"
299319 >
300320 <ToastNotification
301- v-for =" toast in messages "
321+ v-for =" ( toast , index ) in messages "
302322 v-bind =" toast "
303323 :key =" toast .id "
304- :stack =" stackToasts "
324+ :stack ="
325+ () => {
326+ stackToasts (' TOAST' );
327+ }
328+ "
305329 :toastCount =" messages .length "
306330 :position =" position "
331+ :expanded =" expanded "
332+ :index =" index "
307333 @close =" deleteToastWithId "
308334 />
309- </ul >
335+ </ol >
310336 </teleport >
311337</template >
0 commit comments