varsel 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/VarselItem.svelte +825 -735
- package/dist/VarselItem.svelte.d.ts +1 -0
- package/dist/VarselItem.svelte.d.ts.map +1 -1
- package/dist/VarselManager.svelte +477 -344
- package/dist/VarselManager.svelte.d.ts +1 -1
- package/dist/VarselManager.svelte.d.ts.map +1 -1
- package/dist/VarselToaster.svelte +29 -23
- package/dist/VarselToaster.svelte.d.ts +2 -2
- package/dist/VarselToaster.svelte.d.ts.map +1 -1
- package/dist/core/accessibility.d.ts +14 -0
- package/dist/core/accessibility.d.ts.map +1 -1
- package/dist/core/accessibility.js +64 -0
- package/dist/core/swipe.d.ts +6 -0
- package/dist/core/swipe.d.ts.map +1 -1
- package/dist/core/swipe.js +6 -0
- package/dist/core/toast-factory.d.ts.map +1 -1
- package/dist/core/toast-factory.js +38 -11
- package/dist/core/toast-state.d.ts +8 -4
- package/dist/core/toast-state.d.ts.map +1 -1
- package/dist/core/toast-state.js +19 -5
- package/dist/core/toaster-instances.d.ts +1 -0
- package/dist/core/toaster-instances.d.ts.map +1 -1
- package/dist/core/toaster-instances.js +9 -2
- package/dist/core/types.d.ts +42 -9
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/variants.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/internals.d.ts +4 -4
- package/dist/internals.d.ts.map +1 -1
- package/dist/internals.js +3 -3
- package/dist/styles.css +49 -49
- package/dist/variant-icons.d.ts +25 -51
- package/dist/variant-icons.d.ts.map +1 -1
- package/dist/variant-icons.js +27 -17
- package/package.json +2 -2
package/dist/VarselItem.svelte
CHANGED
|
@@ -1,760 +1,821 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import { onDestroy, onMount } from "svelte";
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
} from "./variant-icons";
|
|
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
|
-
let
|
|
71
|
-
let
|
|
72
|
-
let
|
|
73
|
-
let
|
|
74
|
-
let
|
|
75
|
-
let
|
|
76
|
-
let
|
|
77
|
-
let
|
|
78
|
-
let
|
|
79
|
-
let
|
|
80
|
-
let
|
|
81
|
-
let
|
|
82
|
-
|
|
83
|
-
let
|
|
84
|
-
let
|
|
85
|
-
let
|
|
86
|
-
|
|
87
|
-
let
|
|
88
|
-
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
let
|
|
92
|
-
let
|
|
93
|
-
|
|
94
|
-
let enterAnimationFrame: number | null = null;
|
|
95
|
-
let focusTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
96
|
-
let pointerStart: { x: number; y: number } | null = null;
|
|
97
|
-
let dragStartTime: number | null = null;
|
|
98
|
-
let swipeAxis: SwipeAxis | null = null;
|
|
99
|
-
let lastSwipe = { x: 0, y: 0 };
|
|
100
|
-
let mounted = $state(false);
|
|
101
|
-
let prevShouldClose = false;
|
|
102
|
-
let previousDuration: number | undefined;
|
|
103
|
-
let isExiting = $state(false);
|
|
104
|
-
let exitAnimationComplete = false;
|
|
105
|
-
let hasAnimatedIn = $state(false);
|
|
106
|
-
let isPointerHeld = false;
|
|
107
|
-
type SpinnerState = "hidden" | "loading" | "finishing";
|
|
108
|
-
let spinnerState = $state<SpinnerState>("hidden");
|
|
109
|
-
let spinnerFinishTimer: ReturnType<typeof setTimeout> | null = null;
|
|
110
|
-
let hasShownSpinner = $state(false);
|
|
111
|
-
|
|
112
|
-
$effect(() => {
|
|
113
|
-
if (isLoading) {
|
|
114
|
-
hasShownSpinner = true;
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
$effect(() => {
|
|
119
|
-
if (isLoading) {
|
|
120
|
-
spinnerState = "loading";
|
|
121
|
-
} else if (spinnerState === "loading") {
|
|
122
|
-
spinnerState = "finishing";
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
$effect(() => {
|
|
127
|
-
if (spinnerState === "finishing") {
|
|
128
|
-
if (!spinnerFinishTimer) {
|
|
129
|
-
spinnerFinishTimer = setTimeout(() => {
|
|
130
|
-
spinnerState = "hidden";
|
|
131
|
-
spinnerFinishTimer = null;
|
|
132
|
-
}, 420);
|
|
133
|
-
}
|
|
134
|
-
} else if (spinnerFinishTimer) {
|
|
135
|
-
clearTimeout(spinnerFinishTimer);
|
|
136
|
-
spinnerFinishTimer = null;
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
let shouldRenderSpinner = $derived(spinnerState !== "hidden");
|
|
141
|
-
|
|
142
|
-
const handleSpinnerAnimationEnd = (event: AnimationEvent) => {
|
|
143
|
-
if (event.animationName !== "vs-spinner-finish") return;
|
|
144
|
-
spinnerState = "hidden";
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
let iconConfig = $derived(
|
|
148
|
-
hasVariantIcon(variant) ? variantIconMap[variant] : undefined,
|
|
149
|
-
);
|
|
150
|
-
let showStatusIcon = $derived(isLoading || Boolean(iconConfig));
|
|
151
|
-
|
|
152
|
-
let iconStateClass = $derived(
|
|
153
|
-
!iconConfig
|
|
154
|
-
? undefined
|
|
155
|
-
: !hasShownSpinner
|
|
156
|
-
? "vs-icon--static"
|
|
157
|
-
: isLoading
|
|
158
|
-
? "vs-icon--waiting"
|
|
159
|
-
: "vs-icon--pop",
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
const clearSwipeRefs = () => {
|
|
163
|
-
pointerStart = null;
|
|
164
|
-
dragStartTime = null;
|
|
165
|
-
swipeAxis = null;
|
|
166
|
-
lastSwipe = { x: 0, y: 0 };
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
const getFocusableElements = () => {
|
|
170
|
-
if (!toastRef) return [];
|
|
171
|
-
return Array.from(
|
|
172
|
-
toastRef.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS),
|
|
2
|
+
/**
|
|
3
|
+
* @component
|
|
4
|
+
* @description
|
|
5
|
+
* Individual toast component. Handles its own enter/exit animations,
|
|
6
|
+
* swipe-to-dismiss gestures, auto-closing timer, and rendering of content.
|
|
7
|
+
*/
|
|
8
|
+
import { onDestroy, onMount } from "svelte";
|
|
9
|
+
import {
|
|
10
|
+
ANIMATION_CONFIG,
|
|
11
|
+
FOCUSABLE_SELECTORS,
|
|
12
|
+
focusManager,
|
|
13
|
+
isFocusVisible,
|
|
14
|
+
POSITION_CONFIGS,
|
|
15
|
+
SWIPE_DISMISS_THRESHOLD,
|
|
16
|
+
SWIPE_DISMISS_VELOCITY,
|
|
17
|
+
SWIPE_EXIT_DISTANCE,
|
|
18
|
+
SWIPE_REVERSE_CANCEL_THRESHOLD,
|
|
19
|
+
cn,
|
|
20
|
+
getDefaultSwipeDirections,
|
|
21
|
+
toastContainerVariants,
|
|
22
|
+
toastContentVariants,
|
|
23
|
+
toastState,
|
|
24
|
+
type PositionedToast,
|
|
25
|
+
type ToastPosition,
|
|
26
|
+
type SwipeAxis,
|
|
27
|
+
type SwipeDirection,
|
|
28
|
+
} from "./internals";
|
|
29
|
+
import { hasVariantIcon, variantIconMap } from "./variant-icons";
|
|
30
|
+
import type { IconElement } from "./variant-icons";
|
|
31
|
+
|
|
32
|
+
let {
|
|
33
|
+
toast,
|
|
34
|
+
onRemove,
|
|
35
|
+
isGroupHovered = false,
|
|
36
|
+
expandedOffset = 0,
|
|
37
|
+
expandedGap = ANIMATION_CONFIG.EXPANDED_GAP,
|
|
38
|
+
collapsedOffset = undefined,
|
|
39
|
+
hiddenCollapsedOffset = undefined,
|
|
40
|
+
onHeightChange = undefined,
|
|
41
|
+
onGroupHoverEnter = undefined,
|
|
42
|
+
onGroupHoldChange = undefined,
|
|
43
|
+
defaultDuration = 5000,
|
|
44
|
+
defaultShowClose = true,
|
|
45
|
+
pauseOnHover = true,
|
|
46
|
+
offset = undefined,
|
|
47
|
+
expand = true,
|
|
48
|
+
visibleToasts = ANIMATION_CONFIG.MAX_VISIBLE_TOASTS,
|
|
49
|
+
isWindowFocused = true,
|
|
50
|
+
}: {
|
|
51
|
+
toast: PositionedToast;
|
|
52
|
+
onRemove: (id: string) => void;
|
|
53
|
+
isGroupHovered?: boolean;
|
|
54
|
+
expandedOffset?: number;
|
|
55
|
+
expandedGap?: number;
|
|
56
|
+
collapsedOffset?: number;
|
|
57
|
+
hiddenCollapsedOffset?: number;
|
|
58
|
+
onHeightChange?: (id: string, height: number) => void;
|
|
59
|
+
onGroupHoverEnter?: () => void;
|
|
60
|
+
onGroupHoldChange?: (holding: boolean) => void;
|
|
61
|
+
defaultDuration?: number;
|
|
62
|
+
defaultShowClose?: boolean;
|
|
63
|
+
pauseOnHover?: boolean;
|
|
64
|
+
offset?: number | string;
|
|
65
|
+
expand?: boolean;
|
|
66
|
+
visibleToasts?: number;
|
|
67
|
+
isWindowFocused?: boolean;
|
|
68
|
+
} = $props();
|
|
69
|
+
|
|
70
|
+
let id = $derived(toast.id);
|
|
71
|
+
let title = $derived(toast.title);
|
|
72
|
+
let description = $derived(toast.description);
|
|
73
|
+
let variant = $derived(toast.variant || "default");
|
|
74
|
+
let duration = $derived(toast.duration ?? defaultDuration);
|
|
75
|
+
let action = $derived(toast.action);
|
|
76
|
+
let isLoading = $derived(toast.isLoading || false);
|
|
77
|
+
let index = $derived(toast.index);
|
|
78
|
+
let renderIndex = $derived(toast.renderIndex);
|
|
79
|
+
let shouldClose = $derived(toast.shouldClose);
|
|
80
|
+
let position = $derived(toast.position || "bottom-center");
|
|
81
|
+
let className = $derived(toast.className || "");
|
|
82
|
+
let onAutoClose = $derived(toast.onAutoClose);
|
|
83
|
+
let onDismiss = $derived(toast.onDismiss);
|
|
84
|
+
let showClose = $derived(toast.showClose ?? defaultShowClose);
|
|
85
|
+
let priority = $derived(toast.priority ?? "low");
|
|
86
|
+
|
|
87
|
+
let toastRef = $state<HTMLDivElement | null>(null);
|
|
88
|
+
let isItemHovered = $state(false);
|
|
89
|
+
let isItemFocusVisible = $state(false);
|
|
90
|
+
let isSwiping = $state(false);
|
|
91
|
+
let swipeDismissDirection = $state<SwipeDirection | null>(null);
|
|
92
|
+
let animationState = $state<"entering" | "entered" | "exiting" | "stacking">(
|
|
93
|
+
"entering",
|
|
173
94
|
);
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
let
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
95
|
+
|
|
96
|
+
let timeoutRef: ReturnType<typeof setTimeout> | null = null;
|
|
97
|
+
let timerStartRef: number | null = null;
|
|
98
|
+
let remainingTime = $state<number | null>(Number.NaN);
|
|
99
|
+
let enterAnimationFrame: number | null = null;
|
|
100
|
+
let focusTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
101
|
+
let pointerStart: { x: number; y: number } | null = null;
|
|
102
|
+
let dragStartTime: number | null = null;
|
|
103
|
+
let swipeAxis: SwipeAxis | null = null;
|
|
104
|
+
let lastSwipe = { x: 0, y: 0 };
|
|
105
|
+
let maxSwipeDisplacement = 0;
|
|
106
|
+
let swipeCancelled = false;
|
|
107
|
+
let mounted = $state(false);
|
|
108
|
+
let prevShouldClose = false;
|
|
109
|
+
let previousDuration: number | undefined;
|
|
110
|
+
let isExiting = $state(false);
|
|
111
|
+
let exitAnimationComplete = false;
|
|
112
|
+
let hasAnimatedIn = $state(false);
|
|
113
|
+
let isPointerHeld = false;
|
|
114
|
+
type SpinnerState = "hidden" | "loading" | "finishing";
|
|
115
|
+
let spinnerState = $state<SpinnerState>("hidden");
|
|
116
|
+
let spinnerFinishTimer: ReturnType<typeof setTimeout> | null = null;
|
|
117
|
+
let hasShownSpinner = $state(false);
|
|
118
|
+
|
|
119
|
+
type LineElement = Extract<IconElement, { tag: "line" }>;
|
|
120
|
+
|
|
121
|
+
$effect(() => {
|
|
122
|
+
if (isLoading) {
|
|
123
|
+
hasShownSpinner = true;
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
$effect(() => {
|
|
128
|
+
if (isLoading) {
|
|
129
|
+
spinnerState = "loading";
|
|
130
|
+
} else if (spinnerState === "loading") {
|
|
131
|
+
spinnerState = "finishing";
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
$effect(() => {
|
|
136
|
+
if (spinnerState === "finishing") {
|
|
137
|
+
if (!spinnerFinishTimer) {
|
|
138
|
+
spinnerFinishTimer = setTimeout(() => {
|
|
139
|
+
spinnerState = "hidden";
|
|
140
|
+
spinnerFinishTimer = null;
|
|
141
|
+
}, 420);
|
|
142
|
+
}
|
|
143
|
+
} else if (spinnerFinishTimer) {
|
|
144
|
+
clearTimeout(spinnerFinishTimer);
|
|
145
|
+
spinnerFinishTimer = null;
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
let shouldRenderSpinner = $derived(spinnerState !== "hidden");
|
|
150
|
+
|
|
151
|
+
const handleSpinnerAnimationEnd = (event: AnimationEvent) => {
|
|
152
|
+
if (event.animationName !== "vs-spinner-finish") return;
|
|
153
|
+
spinnerState = "hidden";
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
let iconConfig = $derived(
|
|
157
|
+
hasVariantIcon(variant) ? variantIconMap[variant] : undefined,
|
|
158
|
+
);
|
|
159
|
+
let showStatusIcon = $derived(isLoading || Boolean(iconConfig));
|
|
160
|
+
|
|
161
|
+
let iconStateClass = $derived(
|
|
162
|
+
!iconConfig
|
|
163
|
+
? undefined
|
|
164
|
+
: !hasShownSpinner
|
|
165
|
+
? "vs-icon--static"
|
|
166
|
+
: isLoading
|
|
167
|
+
? "vs-icon--waiting"
|
|
168
|
+
: "vs-icon--pop",
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
const clearSwipeRefs = () => {
|
|
172
|
+
pointerStart = null;
|
|
173
|
+
dragStartTime = null;
|
|
174
|
+
swipeAxis = null;
|
|
175
|
+
lastSwipe = { x: 0, y: 0 };
|
|
176
|
+
maxSwipeDisplacement = 0;
|
|
177
|
+
swipeCancelled = false;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const getFocusableElements = () => {
|
|
181
|
+
if (!toastRef) return [];
|
|
182
|
+
return Array.from(toastRef.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS));
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
type CloseReason = "auto" | "dismiss" | null;
|
|
186
|
+
let closeReason: CloseReason = null;
|
|
187
|
+
|
|
188
|
+
const handleTransitionEnd = (event: TransitionEvent) => {
|
|
189
|
+
if (event.target !== toastRef) return;
|
|
190
|
+
if (event.propertyName !== "opacity" && event.propertyName !== "transform") return;
|
|
191
|
+
if (animationState !== "exiting") return;
|
|
192
|
+
if (exitAnimationComplete) return;
|
|
193
|
+
|
|
194
|
+
exitAnimationComplete = true;
|
|
195
|
+
if (closeReason === "auto") {
|
|
196
|
+
onAutoClose?.();
|
|
197
|
+
} else if (closeReason === "dismiss") {
|
|
198
|
+
onDismiss?.();
|
|
199
|
+
}
|
|
200
|
+
onRemove(id);
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const shiftFocusOnDismiss = () => {
|
|
204
|
+
if (!toastRef || typeof document === "undefined") return;
|
|
205
|
+
const active = document.activeElement;
|
|
206
|
+
if (!(active instanceof HTMLElement)) return;
|
|
207
|
+
if (!toastRef.contains(active)) return;
|
|
208
|
+
|
|
209
|
+
const siblings = Array.from(
|
|
210
|
+
document.querySelectorAll<HTMLElement>("[data-toast-id]"),
|
|
211
|
+
).filter(
|
|
212
|
+
(el) =>
|
|
213
|
+
el !== toastRef &&
|
|
214
|
+
el.getAttribute("data-limited") !== "true" &&
|
|
215
|
+
!el.hasAttribute("inert"),
|
|
216
|
+
);
|
|
217
|
+
const next = siblings[0];
|
|
218
|
+
if (next) {
|
|
219
|
+
const focusable = next.querySelector<HTMLElement>(FOCUSABLE_SELECTORS);
|
|
220
|
+
const target = focusable ?? next;
|
|
221
|
+
try {
|
|
222
|
+
target.focus({ preventScroll: true });
|
|
223
|
+
} catch {
|
|
224
|
+
target.focus();
|
|
225
|
+
}
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (focusManager.isClaimed()) {
|
|
230
|
+
focusManager.restoreFocusToPrevElement();
|
|
231
|
+
} else {
|
|
232
|
+
active.blur();
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const handleClose = (reason: Exclude<CloseReason, null> = "dismiss") => {
|
|
237
|
+
if (!toastRef || isExiting) return;
|
|
238
|
+
|
|
239
|
+
shiftFocusOnDismiss();
|
|
240
|
+
|
|
241
|
+
isExiting = true;
|
|
242
|
+
exitAnimationComplete = false;
|
|
243
|
+
closeReason = reason;
|
|
244
|
+
|
|
245
|
+
toastState.update(id, { shouldClose: true });
|
|
246
|
+
|
|
247
|
+
if (enterAnimationFrame) {
|
|
248
|
+
cancelAnimationFrame(enterAnimationFrame);
|
|
249
|
+
enterAnimationFrame = null;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (timeoutRef) {
|
|
253
|
+
clearTimeout(timeoutRef);
|
|
254
|
+
timeoutRef = null;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
animationState = "exiting";
|
|
258
|
+
toastState.update(id, { shouldClose: true, isLeaving: true });
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
$effect(() => {
|
|
262
|
+
const desiredClose = Boolean(shouldClose);
|
|
263
|
+
if (desiredClose && !prevShouldClose) {
|
|
264
|
+
handleClose();
|
|
265
|
+
}
|
|
266
|
+
prevShouldClose = desiredClose;
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
onMount(() => {
|
|
270
|
+
mounted = true;
|
|
271
|
+
return () => {
|
|
272
|
+
mounted = false;
|
|
273
|
+
if (enterAnimationFrame) cancelAnimationFrame(enterAnimationFrame);
|
|
274
|
+
if (timeoutRef) clearTimeout(timeoutRef);
|
|
275
|
+
if (focusTimeout) clearTimeout(focusTimeout);
|
|
276
|
+
};
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
onDestroy(() => {
|
|
230
280
|
if (enterAnimationFrame) cancelAnimationFrame(enterAnimationFrame);
|
|
231
281
|
if (timeoutRef) clearTimeout(timeoutRef);
|
|
232
282
|
if (focusTimeout) clearTimeout(focusTimeout);
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
$effect(() => {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
} else {
|
|
262
|
-
const el = toastRef;
|
|
263
|
-
const notify = () => onHeightChange?.(id, el.offsetHeight);
|
|
264
|
-
const ro = new ResizeObserver(() => notify());
|
|
265
|
-
ro.observe(el);
|
|
266
|
-
notify();
|
|
267
|
-
return () => ro.disconnect();
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
const setFocusToToast = () => {
|
|
273
|
-
if (!toastRef) return;
|
|
274
|
-
const focusableElements = getFocusableElements();
|
|
275
|
-
const firstFocusable = focusableElements[0];
|
|
276
|
-
if (firstFocusable) {
|
|
277
|
-
firstFocusable.focus();
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
toastRef.focus();
|
|
281
|
-
};
|
|
282
|
-
|
|
283
|
-
$effect(() => {
|
|
284
|
-
if (mounted && toastRef && !isExiting) {
|
|
285
|
-
if (!hasAnimatedIn && isLatest) {
|
|
286
|
-
hasAnimatedIn = true;
|
|
287
|
-
animationState = "entering";
|
|
288
|
-
if (enterAnimationFrame) {
|
|
289
|
-
cancelAnimationFrame(enterAnimationFrame);
|
|
283
|
+
if (spinnerFinishTimer) {
|
|
284
|
+
clearTimeout(spinnerFinishTimer);
|
|
285
|
+
spinnerFinishTimer = null;
|
|
286
|
+
}
|
|
287
|
+
if (isPointerHeld) {
|
|
288
|
+
isPointerHeld = false;
|
|
289
|
+
onGroupHoldChange?.(false);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
$effect(() => {
|
|
294
|
+
if (mounted && duration !== previousDuration) {
|
|
295
|
+
remainingTime = duration;
|
|
296
|
+
previousDuration = duration;
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
$effect(() => {
|
|
301
|
+
if (mounted) {
|
|
302
|
+
if (!toastRef || !onHeightChange) {
|
|
303
|
+
// do nothing
|
|
304
|
+
} else {
|
|
305
|
+
const el = toastRef;
|
|
306
|
+
const notify = () => onHeightChange?.(id, el.offsetHeight);
|
|
307
|
+
const ro = new ResizeObserver(() => notify());
|
|
308
|
+
ro.observe(el);
|
|
309
|
+
notify();
|
|
310
|
+
return () => ro.disconnect();
|
|
290
311
|
}
|
|
291
|
-
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
const setFocusToToast = () => {
|
|
316
|
+
if (!toastRef) return;
|
|
317
|
+
const focusableElements = getFocusableElements();
|
|
318
|
+
const firstFocusable = focusableElements[0];
|
|
319
|
+
if (firstFocusable) {
|
|
320
|
+
firstFocusable.focus();
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
toastRef.focus();
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
$effect(() => {
|
|
327
|
+
if (mounted && toastRef && !isExiting) {
|
|
328
|
+
if (!hasAnimatedIn && isLatest) {
|
|
329
|
+
hasAnimatedIn = true;
|
|
330
|
+
animationState = "entering";
|
|
331
|
+
if (enterAnimationFrame) {
|
|
332
|
+
cancelAnimationFrame(enterAnimationFrame);
|
|
333
|
+
}
|
|
292
334
|
enterAnimationFrame = requestAnimationFrame(() => {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
if (
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
335
|
+
enterAnimationFrame = requestAnimationFrame(() => {
|
|
336
|
+
animationState = "entered";
|
|
337
|
+
if (action) {
|
|
338
|
+
if (focusTimeout) clearTimeout(focusTimeout);
|
|
339
|
+
focusTimeout = setTimeout(
|
|
340
|
+
() => setFocusToToast(),
|
|
341
|
+
ANIMATION_CONFIG.ENTER_DURATION * 1000,
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
});
|
|
301
345
|
});
|
|
302
|
-
})
|
|
303
|
-
|
|
304
|
-
|
|
346
|
+
} else if (hasAnimatedIn) {
|
|
347
|
+
if (animationState !== "stacking" || index > 0) {
|
|
348
|
+
animationState = "stacking";
|
|
349
|
+
}
|
|
350
|
+
} else {
|
|
305
351
|
animationState = "stacking";
|
|
306
352
|
}
|
|
307
|
-
} else {
|
|
308
|
-
animationState = "stacking";
|
|
309
353
|
}
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
$effect(() => {
|
|
314
|
-
if (mounted) {
|
|
315
|
-
if (shouldClose || !hasAnimatedIn || duration <= 0) {
|
|
316
|
-
if (timeoutRef) {
|
|
317
|
-
clearTimeout(timeoutRef);
|
|
318
|
-
timeoutRef = null;
|
|
319
|
-
}
|
|
320
|
-
timerStartRef = null;
|
|
321
|
-
} else {
|
|
322
|
-
if (remainingTime == null || Number.isNaN(remainingTime)) {
|
|
323
|
-
remainingTime = duration;
|
|
324
|
-
}
|
|
354
|
+
});
|
|
325
355
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
if (isPaused) {
|
|
356
|
+
$effect(() => {
|
|
357
|
+
if (mounted) {
|
|
358
|
+
if (shouldClose || !hasAnimatedIn || duration <= 0) {
|
|
331
359
|
if (timeoutRef) {
|
|
332
360
|
clearTimeout(timeoutRef);
|
|
333
361
|
timeoutRef = null;
|
|
334
362
|
}
|
|
335
|
-
|
|
336
|
-
const elapsed = Date.now() - timerStartRef;
|
|
337
|
-
remainingTime = Math.max(0, (remainingTime ?? duration) - elapsed);
|
|
338
|
-
timerStartRef = null;
|
|
339
|
-
}
|
|
363
|
+
timerStartRef = null;
|
|
340
364
|
} else {
|
|
341
|
-
if (
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
365
|
+
if (remainingTime == null || Number.isNaN(remainingTime)) {
|
|
366
|
+
remainingTime = duration;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const isHovering = isGroupHovered || isItemHovered;
|
|
370
|
+
const isPaused =
|
|
371
|
+
(pauseOnHover && isHovering) ||
|
|
372
|
+
isItemFocusVisible ||
|
|
373
|
+
isSwiping ||
|
|
374
|
+
hiddenByStacking ||
|
|
375
|
+
!isWindowFocused;
|
|
376
|
+
|
|
377
|
+
if (isPaused) {
|
|
378
|
+
if (timeoutRef) {
|
|
379
|
+
clearTimeout(timeoutRef);
|
|
380
|
+
timeoutRef = null;
|
|
381
|
+
}
|
|
382
|
+
if (timerStartRef !== null) {
|
|
383
|
+
const elapsed = Date.now() - timerStartRef;
|
|
384
|
+
remainingTime = Math.max(0, (remainingTime ?? duration) - elapsed);
|
|
385
|
+
timerStartRef = null;
|
|
386
|
+
}
|
|
387
|
+
} else {
|
|
388
|
+
if (!timeoutRef) {
|
|
389
|
+
const ms = Math.max(0, remainingTime ?? duration);
|
|
390
|
+
if (!Number.isFinite(ms)) {
|
|
391
|
+
// Do not set timeout for infinite duration
|
|
392
|
+
} else if (ms === 0) {
|
|
350
393
|
handleClose("auto");
|
|
351
|
-
}
|
|
394
|
+
} else {
|
|
395
|
+
timerStartRef = Date.now();
|
|
396
|
+
timeoutRef = setTimeout(() => {
|
|
397
|
+
handleClose("auto");
|
|
398
|
+
}, ms);
|
|
399
|
+
}
|
|
352
400
|
}
|
|
353
401
|
}
|
|
354
402
|
}
|
|
355
403
|
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
);
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
const target = event.target as HTMLElement;
|
|
377
|
-
if (target.closest("button, a, input, textarea, select")) {
|
|
378
|
-
return;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
clearSwipeRefs();
|
|
382
|
-
pointerStart = { x: event.clientX, y: event.clientY };
|
|
383
|
-
dragStartTime = Date.now();
|
|
384
|
-
if (toastRef) {
|
|
385
|
-
toastRef.style.setProperty("--swipe-translate-x", "0px");
|
|
386
|
-
toastRef.style.setProperty("--swipe-translate-y", "0px");
|
|
387
|
-
}
|
|
388
|
-
swipeDismissDirection = null;
|
|
389
|
-
isSwiping = true;
|
|
390
|
-
if (!isPointerHeld) {
|
|
391
|
-
isPointerHeld = true;
|
|
392
|
-
onGroupHoldChange?.(true);
|
|
393
|
-
}
|
|
394
|
-
const currentTarget = event.currentTarget as HTMLElement | null;
|
|
395
|
-
currentTarget?.setPointerCapture(event.pointerId);
|
|
396
|
-
};
|
|
397
|
-
|
|
398
|
-
const handlePointerMove = (event: PointerEvent) => {
|
|
399
|
-
if (!showClose) return;
|
|
400
|
-
if (!pointerStart) return;
|
|
401
|
-
if (isExiting) return;
|
|
402
|
-
|
|
403
|
-
if (event.pointerType === "touch") {
|
|
404
|
-
event.preventDefault();
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
const xDelta = event.clientX - pointerStart.x;
|
|
408
|
-
const yDelta = event.clientY - pointerStart.y;
|
|
409
|
-
|
|
410
|
-
let axis = swipeAxis;
|
|
411
|
-
if (!axis) {
|
|
412
|
-
if (Math.abs(xDelta) > 1 || Math.abs(yDelta) > 1) {
|
|
413
|
-
axis = Math.abs(xDelta) > Math.abs(yDelta) ? "x" : "y";
|
|
414
|
-
swipeAxis = axis;
|
|
415
|
-
} else {
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
$effect(() => {
|
|
407
|
+
if (mounted && toastRef && !isSwiping && !swipeDismissDirection) {
|
|
408
|
+
toastRef.style.setProperty("--swipe-translate-x", "0px");
|
|
409
|
+
toastRef.style.setProperty("--swipe-translate-y", "0px");
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
let swipeDirections = $derived(showClose ? getDefaultSwipeDirections(position) : []);
|
|
414
|
+
|
|
415
|
+
const handlePointerDown = (event: PointerEvent) => {
|
|
416
|
+
if (!showClose) return;
|
|
417
|
+
if (event.pointerType === "mouse" && event.button !== 0) return;
|
|
418
|
+
if (event.button === 2) return;
|
|
419
|
+
if (isExiting) return;
|
|
420
|
+
|
|
421
|
+
const target = event.target as HTMLElement;
|
|
422
|
+
if (target.closest("button, a, input, textarea, select")) {
|
|
416
423
|
return;
|
|
417
424
|
}
|
|
418
|
-
}
|
|
419
425
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
426
|
+
clearSwipeRefs();
|
|
427
|
+
pointerStart = { x: event.clientX, y: event.clientY };
|
|
428
|
+
dragStartTime = Date.now();
|
|
429
|
+
if (toastRef) {
|
|
430
|
+
toastRef.style.setProperty("--swipe-translate-x", "0px");
|
|
431
|
+
toastRef.style.setProperty("--swipe-translate-y", "0px");
|
|
432
|
+
}
|
|
433
|
+
swipeDismissDirection = null;
|
|
434
|
+
isSwiping = true;
|
|
435
|
+
if (!isPointerHeld) {
|
|
436
|
+
isPointerHeld = true;
|
|
437
|
+
onGroupHoldChange?.(true);
|
|
438
|
+
}
|
|
439
|
+
const currentTarget = event.currentTarget as HTMLElement | null;
|
|
440
|
+
currentTarget?.setPointerCapture(event.pointerId);
|
|
423
441
|
};
|
|
424
442
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
swipeAxis = "y";
|
|
433
|
-
axis = "y";
|
|
434
|
-
} else if ((allowLeft && xDelta < 0) || (allowRight && xDelta > 0)) {
|
|
435
|
-
nextX = xDelta;
|
|
436
|
-
} else {
|
|
437
|
-
nextX = dampen(xDelta);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
if (axis === "y") {
|
|
442
|
-
const allowTop = swipeDirections.includes("top");
|
|
443
|
-
const allowBottom = swipeDirections.includes("bottom");
|
|
444
|
-
if (!allowTop && !allowBottom) {
|
|
445
|
-
swipeAxis = "x";
|
|
446
|
-
axis = "x";
|
|
447
|
-
} else if ((allowTop && yDelta < 0) || (allowBottom && yDelta > 0)) {
|
|
448
|
-
nextY = yDelta;
|
|
449
|
-
} else {
|
|
450
|
-
nextY = dampen(yDelta);
|
|
443
|
+
const handlePointerMove = (event: PointerEvent) => {
|
|
444
|
+
if (!showClose) return;
|
|
445
|
+
if (!pointerStart) return;
|
|
446
|
+
if (isExiting) return;
|
|
447
|
+
|
|
448
|
+
if (event.pointerType === "touch") {
|
|
449
|
+
event.preventDefault();
|
|
451
450
|
}
|
|
452
|
-
}
|
|
453
451
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
toastRef.style.setProperty("--swipe-translate-x", `${nextX}px`);
|
|
457
|
-
toastRef.style.setProperty("--swipe-translate-y", `${nextY}px`);
|
|
458
|
-
}
|
|
459
|
-
};
|
|
452
|
+
const xDelta = event.clientX - pointerStart.x;
|
|
453
|
+
const yDelta = event.clientY - pointerStart.y;
|
|
460
454
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
455
|
+
let axis = swipeAxis;
|
|
456
|
+
if (!axis) {
|
|
457
|
+
if (Math.abs(xDelta) > 1 || Math.abs(yDelta) > 1) {
|
|
458
|
+
axis = Math.abs(xDelta) > Math.abs(yDelta) ? "x" : "y";
|
|
459
|
+
swipeAxis = axis;
|
|
460
|
+
} else {
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
467
464
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
const meetsThreshold =
|
|
485
|
-
Math.abs(distance) >= SWIPE_DISMISS_THRESHOLD ||
|
|
486
|
-
velocity > SWIPE_DISMISS_VELOCITY;
|
|
487
|
-
|
|
488
|
-
if (meetsThreshold && Math.abs(distance) > 0) {
|
|
489
|
-
let direction: SwipeDirection;
|
|
490
|
-
if (axis === "x") {
|
|
491
|
-
direction = distance > 0 ? "right" : "left";
|
|
465
|
+
const dampen = (delta: number) => {
|
|
466
|
+
const factor = Math.abs(delta) / 20;
|
|
467
|
+
return delta * (1 / (1.5 + factor));
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
let nextX = 0;
|
|
471
|
+
let nextY = 0;
|
|
472
|
+
|
|
473
|
+
if (axis === "x") {
|
|
474
|
+
const allowLeft = swipeDirections.includes("left");
|
|
475
|
+
const allowRight = swipeDirections.includes("right");
|
|
476
|
+
if (!allowLeft && !allowRight) {
|
|
477
|
+
swipeAxis = "y";
|
|
478
|
+
axis = "y";
|
|
479
|
+
} else if ((allowLeft && xDelta < 0) || (allowRight && xDelta > 0)) {
|
|
480
|
+
nextX = xDelta;
|
|
492
481
|
} else {
|
|
493
|
-
|
|
482
|
+
nextX = dampen(xDelta);
|
|
494
483
|
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
if (axis === "y") {
|
|
487
|
+
const allowTop = swipeDirections.includes("top");
|
|
488
|
+
const allowBottom = swipeDirections.includes("bottom");
|
|
489
|
+
if (!allowTop && !allowBottom) {
|
|
490
|
+
swipeAxis = "x";
|
|
491
|
+
} else if ((allowTop && yDelta < 0) || (allowBottom && yDelta > 0)) {
|
|
492
|
+
nextY = yDelta;
|
|
493
|
+
} else {
|
|
494
|
+
nextY = dampen(yDelta);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
lastSwipe = { x: nextX, y: nextY };
|
|
499
|
+
|
|
500
|
+
const displacement = axis === "x" ? Math.abs(nextX) : Math.abs(nextY);
|
|
501
|
+
if (displacement > maxSwipeDisplacement) {
|
|
502
|
+
maxSwipeDisplacement = displacement;
|
|
503
|
+
}
|
|
504
|
+
if (displacement >= SWIPE_DISMISS_THRESHOLD) {
|
|
505
|
+
swipeCancelled = false;
|
|
506
|
+
} else if (maxSwipeDisplacement - displacement >= SWIPE_REVERSE_CANCEL_THRESHOLD) {
|
|
507
|
+
swipeCancelled = true;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (toastRef) {
|
|
511
|
+
toastRef.style.setProperty("--swipe-translate-x", `${nextX}px`);
|
|
512
|
+
toastRef.style.setProperty("--swipe-translate-y", `${nextY}px`);
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
const handlePointerUp = (event: PointerEvent) => {
|
|
517
|
+
if (!showClose) return;
|
|
518
|
+
const currentTarget = event.currentTarget as HTMLElement | null;
|
|
519
|
+
if (currentTarget?.hasPointerCapture(event.pointerId)) {
|
|
520
|
+
currentTarget.releasePointerCapture(event.pointerId);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (!pointerStart) {
|
|
524
|
+
swipeDismissDirection = null;
|
|
525
|
+
isSwiping = false;
|
|
526
|
+
clearSwipeRefs();
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const elapsed = dragStartTime ? Date.now() - dragStartTime : 0;
|
|
495
531
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
532
|
+
const axis = swipeAxis;
|
|
533
|
+
const { x, y } = lastSwipe;
|
|
534
|
+
let dismissed = false;
|
|
535
|
+
|
|
536
|
+
if (axis && !swipeCancelled) {
|
|
537
|
+
const distance = axis === "x" ? x : y;
|
|
538
|
+
const velocity = elapsed > 0 ? Math.abs(distance) / elapsed : 0;
|
|
539
|
+
const meetsThreshold =
|
|
540
|
+
Math.abs(distance) >= SWIPE_DISMISS_THRESHOLD ||
|
|
541
|
+
velocity > SWIPE_DISMISS_VELOCITY;
|
|
542
|
+
|
|
543
|
+
if (meetsThreshold && Math.abs(distance) > 0) {
|
|
544
|
+
let direction: SwipeDirection;
|
|
545
|
+
if (axis === "x") {
|
|
546
|
+
direction = distance > 0 ? "right" : "left";
|
|
547
|
+
} else {
|
|
548
|
+
direction = distance > 0 ? "bottom" : "top";
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
if (swipeDirections.includes(direction)) {
|
|
552
|
+
swipeDismissDirection = direction;
|
|
553
|
+
dismissed = true;
|
|
554
|
+
handleClose();
|
|
555
|
+
}
|
|
500
556
|
}
|
|
501
557
|
}
|
|
502
|
-
}
|
|
503
558
|
|
|
504
|
-
|
|
559
|
+
if (!dismissed) {
|
|
560
|
+
swipeDismissDirection = null;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
isSwiping = false;
|
|
564
|
+
clearSwipeRefs();
|
|
565
|
+
if (isPointerHeld) {
|
|
566
|
+
isPointerHeld = false;
|
|
567
|
+
onGroupHoldChange?.(false);
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
const handlePointerCancel = (event: PointerEvent) => {
|
|
572
|
+
if (!showClose) return;
|
|
573
|
+
const currentTarget = event.currentTarget as HTMLElement | null;
|
|
574
|
+
if (currentTarget?.hasPointerCapture(event.pointerId)) {
|
|
575
|
+
currentTarget.releasePointerCapture(event.pointerId);
|
|
576
|
+
}
|
|
505
577
|
swipeDismissDirection = null;
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
isPointerHeld = false;
|
|
512
|
-
onGroupHoldChange?.(false);
|
|
513
|
-
}
|
|
514
|
-
};
|
|
515
|
-
|
|
516
|
-
const handlePointerCancel = (event: PointerEvent) => {
|
|
517
|
-
if (!showClose) return;
|
|
518
|
-
const currentTarget = event.currentTarget as HTMLElement | null;
|
|
519
|
-
if (currentTarget?.hasPointerCapture(event.pointerId)) {
|
|
520
|
-
currentTarget.releasePointerCapture(event.pointerId);
|
|
521
|
-
}
|
|
522
|
-
swipeDismissDirection = null;
|
|
523
|
-
isSwiping = false;
|
|
524
|
-
clearSwipeRefs();
|
|
525
|
-
if (isPointerHeld) {
|
|
526
|
-
isPointerHeld = false;
|
|
527
|
-
onGroupHoldChange?.(false);
|
|
528
|
-
}
|
|
529
|
-
};
|
|
530
|
-
|
|
531
|
-
const zIndexBase = Number(ANIMATION_CONFIG.Z_INDEX_BASE);
|
|
532
|
-
|
|
533
|
-
let isTopPosition = $derived(position?.startsWith("top-") ?? false);
|
|
534
|
-
let maxVisibleIndex = $derived(
|
|
535
|
-
Math.max(0, visibleToasts - 1),
|
|
536
|
-
);
|
|
537
|
-
let visibleIndex = $derived(Math.min(index, maxVisibleIndex));
|
|
538
|
-
let defaultCollapsedOffset = $derived(
|
|
539
|
-
isTopPosition
|
|
540
|
-
? index * ANIMATION_CONFIG.STACK_OFFSET
|
|
541
|
-
: -(index * ANIMATION_CONFIG.STACK_OFFSET),
|
|
542
|
-
);
|
|
543
|
-
let resolvedCollapsedOffset = $derived(
|
|
544
|
-
typeof collapsedOffset === "number" && Number.isFinite(collapsedOffset)
|
|
545
|
-
? collapsedOffset
|
|
546
|
-
: defaultCollapsedOffset,
|
|
547
|
-
);
|
|
548
|
-
let resolvedHiddenCollapsedOffset = $derived(
|
|
549
|
-
typeof hiddenCollapsedOffset === "number" &&
|
|
550
|
-
Number.isFinite(hiddenCollapsedOffset)
|
|
551
|
-
? hiddenCollapsedOffset
|
|
552
|
-
: resolvedCollapsedOffset,
|
|
553
|
-
);
|
|
554
|
-
let scale = $derived(
|
|
555
|
-
Math.max(ANIMATION_CONFIG.MIN_SCALE, 1 - index * ANIMATION_CONFIG.SCALE_FACTOR),
|
|
556
|
-
);
|
|
557
|
-
let visibleScale = $derived(
|
|
558
|
-
Math.max(
|
|
559
|
-
ANIMATION_CONFIG.MIN_SCALE,
|
|
560
|
-
1 - visibleIndex * ANIMATION_CONFIG.SCALE_FACTOR,
|
|
561
|
-
),
|
|
562
|
-
);
|
|
563
|
-
let zIndex = $derived(zIndexBase - renderIndex);
|
|
564
|
-
let stackHidden = $derived(index >= visibleToasts);
|
|
565
|
-
let hiddenByStacking = $derived(stackHidden && animationState !== "exiting");
|
|
566
|
-
let isStackLeader = $derived(index === 0);
|
|
567
|
-
let isLatest = $derived(isStackLeader && !shouldClose);
|
|
568
|
-
type PositionConfig = (typeof POSITION_CONFIGS)[ToastPosition];
|
|
569
|
-
let config = $derived(
|
|
570
|
-
POSITION_CONFIGS[(position ?? "bottom-center") as ToastPosition],
|
|
571
|
-
);
|
|
572
|
-
|
|
573
|
-
let transformStyle = $derived.by(() => {
|
|
574
|
-
const baseOffsetY = stackHidden
|
|
575
|
-
? resolvedHiddenCollapsedOffset
|
|
576
|
-
: resolvedCollapsedOffset;
|
|
577
|
-
const promotionOffset =
|
|
578
|
-
typeof expandedGap === "number"
|
|
579
|
-
? expandedGap
|
|
580
|
-
: ANIMATION_CONFIG.EXPANDED_GAP;
|
|
581
|
-
const expandedTranslateY = isTopPosition ? expandedOffset : -expandedOffset;
|
|
582
|
-
const hiddenExpandedTranslateY = expandedTranslateY - promotionOffset;
|
|
583
|
-
|
|
584
|
-
let translateX = 0;
|
|
585
|
-
let translateY = baseOffsetY;
|
|
586
|
-
let scaleValue = stackHidden
|
|
587
|
-
? visibleIndex === 0
|
|
588
|
-
? 1
|
|
589
|
-
: visibleScale
|
|
590
|
-
: isStackLeader
|
|
591
|
-
? 1
|
|
592
|
-
: scale;
|
|
593
|
-
let opacityValue = stackHidden ? 0 : 1;
|
|
594
|
-
|
|
595
|
-
if (stackHidden) {
|
|
596
|
-
if (expand && isGroupHovered && animationState !== "exiting") {
|
|
597
|
-
translateX = 0;
|
|
598
|
-
translateY = hiddenExpandedTranslateY;
|
|
599
|
-
scaleValue = 1;
|
|
578
|
+
isSwiping = false;
|
|
579
|
+
clearSwipeRefs();
|
|
580
|
+
if (isPointerHeld) {
|
|
581
|
+
isPointerHeld = false;
|
|
582
|
+
onGroupHoldChange?.(false);
|
|
600
583
|
}
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
const zIndexBase = Number(ANIMATION_CONFIG.Z_INDEX_BASE);
|
|
587
|
+
|
|
588
|
+
let isTopPosition = $derived(position?.startsWith("top-") ?? false);
|
|
589
|
+
let maxVisibleIndex = $derived(Math.max(0, visibleToasts - 1));
|
|
590
|
+
let visibleIndex = $derived(Math.min(index, maxVisibleIndex));
|
|
591
|
+
let defaultCollapsedOffset = $derived(
|
|
592
|
+
isTopPosition
|
|
593
|
+
? index * ANIMATION_CONFIG.STACK_OFFSET
|
|
594
|
+
: -(index * ANIMATION_CONFIG.STACK_OFFSET),
|
|
595
|
+
);
|
|
596
|
+
let resolvedCollapsedOffset = $derived(
|
|
597
|
+
typeof collapsedOffset === "number" && Number.isFinite(collapsedOffset)
|
|
598
|
+
? collapsedOffset
|
|
599
|
+
: defaultCollapsedOffset,
|
|
600
|
+
);
|
|
601
|
+
let resolvedHiddenCollapsedOffset = $derived(
|
|
602
|
+
typeof hiddenCollapsedOffset === "number" && Number.isFinite(hiddenCollapsedOffset)
|
|
603
|
+
? hiddenCollapsedOffset
|
|
604
|
+
: resolvedCollapsedOffset,
|
|
605
|
+
);
|
|
606
|
+
let scale = $derived(
|
|
607
|
+
Math.max(ANIMATION_CONFIG.MIN_SCALE, 1 - index * ANIMATION_CONFIG.SCALE_FACTOR),
|
|
608
|
+
);
|
|
609
|
+
let visibleScale = $derived(
|
|
610
|
+
Math.max(
|
|
611
|
+
ANIMATION_CONFIG.MIN_SCALE,
|
|
612
|
+
1 - visibleIndex * ANIMATION_CONFIG.SCALE_FACTOR,
|
|
613
|
+
),
|
|
614
|
+
);
|
|
615
|
+
let zIndex = $derived(zIndexBase - renderIndex);
|
|
616
|
+
let stackHidden = $derived(index >= visibleToasts);
|
|
617
|
+
let hiddenByStacking = $derived(stackHidden && animationState !== "exiting");
|
|
618
|
+
let isStackLeader = $derived(index === 0);
|
|
619
|
+
let isLatest = $derived(isStackLeader && !shouldClose);
|
|
620
|
+
let config = $derived(
|
|
621
|
+
POSITION_CONFIGS[(position ?? "bottom-center") as ToastPosition],
|
|
622
|
+
);
|
|
623
|
+
|
|
624
|
+
let transformStyle = $derived.by(() => {
|
|
625
|
+
const baseOffsetY = stackHidden
|
|
626
|
+
? resolvedHiddenCollapsedOffset
|
|
627
|
+
: resolvedCollapsedOffset;
|
|
628
|
+
const promotionOffset =
|
|
629
|
+
typeof expandedGap === "number" ? expandedGap : ANIMATION_CONFIG.EXPANDED_GAP;
|
|
630
|
+
const expandedTranslateY = isTopPosition ? expandedOffset : -expandedOffset;
|
|
631
|
+
const hiddenExpandedTranslateY = expandedTranslateY - promotionOffset;
|
|
632
|
+
|
|
633
|
+
let translateX = 0;
|
|
634
|
+
let translateY = baseOffsetY;
|
|
635
|
+
let scaleValue = stackHidden
|
|
636
|
+
? visibleIndex === 0
|
|
637
|
+
? 1
|
|
638
|
+
: visibleScale
|
|
639
|
+
: isStackLeader
|
|
640
|
+
? 1
|
|
641
|
+
: scale;
|
|
642
|
+
let opacityValue = stackHidden ? 0 : 1;
|
|
643
|
+
|
|
644
|
+
if (stackHidden) {
|
|
645
|
+
if (expand && isGroupHovered && animationState !== "exiting") {
|
|
615
646
|
translateX = 0;
|
|
616
|
-
translateY =
|
|
647
|
+
translateY = hiddenExpandedTranslateY;
|
|
617
648
|
scaleValue = 1;
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
649
|
+
}
|
|
650
|
+
} else if (expand && isGroupHovered && animationState !== "exiting") {
|
|
651
|
+
translateX = 0;
|
|
652
|
+
translateY = expandedTranslateY;
|
|
653
|
+
scaleValue = 1;
|
|
654
|
+
opacityValue = 1;
|
|
655
|
+
} else {
|
|
656
|
+
switch (animationState) {
|
|
657
|
+
case "entering":
|
|
658
|
+
translateX = config.animateIn.x;
|
|
659
|
+
translateY = config.animateIn.y;
|
|
660
|
+
scaleValue = 1;
|
|
661
|
+
opacityValue = 0;
|
|
662
|
+
break;
|
|
663
|
+
case "entered":
|
|
664
|
+
translateX = 0;
|
|
665
|
+
translateY = baseOffsetY;
|
|
666
|
+
scaleValue = 1;
|
|
667
|
+
opacityValue = 1;
|
|
668
|
+
break;
|
|
669
|
+
case "exiting": {
|
|
670
|
+
scaleValue = 1;
|
|
671
|
+
opacityValue = 0;
|
|
672
|
+
if (swipeDismissDirection) {
|
|
673
|
+
switch (swipeDismissDirection) {
|
|
674
|
+
case "left":
|
|
675
|
+
translateX = -SWIPE_EXIT_DISTANCE;
|
|
676
|
+
translateY = 0;
|
|
677
|
+
break;
|
|
678
|
+
case "right":
|
|
679
|
+
translateX = SWIPE_EXIT_DISTANCE;
|
|
680
|
+
translateY = 0;
|
|
681
|
+
break;
|
|
682
|
+
case "top":
|
|
683
|
+
translateX = 0;
|
|
684
|
+
translateY = -SWIPE_EXIT_DISTANCE;
|
|
685
|
+
break;
|
|
686
|
+
case "bottom":
|
|
687
|
+
translateX = 0;
|
|
688
|
+
translateY = SWIPE_EXIT_DISTANCE;
|
|
689
|
+
break;
|
|
690
|
+
default:
|
|
691
|
+
translateX = config.animateOut.x;
|
|
692
|
+
translateY = config.animateOut.y;
|
|
693
|
+
break;
|
|
694
|
+
}
|
|
695
|
+
} else {
|
|
696
|
+
translateX = config.animateOut.x;
|
|
697
|
+
translateY = config.animateOut.y;
|
|
645
698
|
}
|
|
646
|
-
|
|
647
|
-
translateX = config.animateOut.x;
|
|
648
|
-
translateY = config.animateOut.y;
|
|
699
|
+
break;
|
|
649
700
|
}
|
|
650
|
-
|
|
701
|
+
default:
|
|
702
|
+
translateX = 0;
|
|
703
|
+
translateY = baseOffsetY;
|
|
704
|
+
scaleValue = isStackLeader ? 1 : scale;
|
|
705
|
+
opacityValue = stackHidden ? 0 : 1;
|
|
706
|
+
break;
|
|
651
707
|
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
const transform = `translate(calc(${translateX}px + var(--swipe-translate-x, 0px)), calc(${translateY}px + var(--swipe-translate-y, 0px))) scale(${scaleValue})`;
|
|
711
|
+
|
|
712
|
+
return {
|
|
713
|
+
transform,
|
|
714
|
+
opacity: opacityValue,
|
|
715
|
+
};
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
let transitionDuration = $derived.by(() => {
|
|
719
|
+
switch (animationState) {
|
|
720
|
+
case "entering":
|
|
721
|
+
case "entered":
|
|
722
|
+
return `${ANIMATION_CONFIG.ENTER_DURATION}s`;
|
|
723
|
+
case "exiting":
|
|
724
|
+
return `${ANIMATION_CONFIG.EXIT_DURATION}s`;
|
|
652
725
|
default:
|
|
653
|
-
|
|
654
|
-
translateY = baseOffsetY;
|
|
655
|
-
scaleValue = isStackLeader ? 1 : scale;
|
|
656
|
-
opacityValue = stackHidden ? 0 : 1;
|
|
657
|
-
break;
|
|
726
|
+
return `${ANIMATION_CONFIG.STACK_DURATION}s`;
|
|
658
727
|
}
|
|
659
|
-
}
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
let transitionTimingFunction = $derived(
|
|
731
|
+
animationState === "exiting"
|
|
732
|
+
? ANIMATION_CONFIG.EASING_EXIT
|
|
733
|
+
: ANIMATION_CONFIG.EASING_DEFAULT,
|
|
734
|
+
);
|
|
660
735
|
|
|
661
|
-
|
|
736
|
+
let canSwipe = $derived(showClose && swipeDirections.length > 0);
|
|
737
|
+
let swipeCursorClass = $derived(
|
|
738
|
+
canSwipe ? (isSwiping ? "cursor-grabbing" : "cursor-grab") : undefined,
|
|
739
|
+
);
|
|
740
|
+
|
|
741
|
+
let titleId = $derived(title ? `${id}-title` : undefined);
|
|
742
|
+
let descriptionId = $derived(description ? `${id}-desc` : undefined);
|
|
743
|
+
let dialogRole = $derived(
|
|
744
|
+
variant === "destructive" || priority === "high" ? "alertdialog" : "dialog",
|
|
745
|
+
);
|
|
746
|
+
let livePoliteness = $derived(
|
|
747
|
+
(variant === "destructive" || priority === "high" ? "assertive" : "polite") as
|
|
748
|
+
| "assertive"
|
|
749
|
+
| "polite",
|
|
750
|
+
);
|
|
751
|
+
|
|
752
|
+
let offsetStyle = $derived.by(() => {
|
|
753
|
+
if (offset === undefined) return undefined;
|
|
754
|
+
const val = typeof offset === "number" ? `${offset}px` : offset;
|
|
755
|
+
if (position.startsWith("top")) return `top: ${val};`;
|
|
756
|
+
if (position.startsWith("bottom")) return `bottom: ${val};`;
|
|
757
|
+
return undefined;
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
const handleBlurCapture = (event: FocusEvent) => {
|
|
761
|
+
const next = event.relatedTarget as Node | null;
|
|
762
|
+
if (!toastRef || !next || !toastRef.contains(next)) {
|
|
763
|
+
isItemFocusVisible = false;
|
|
764
|
+
}
|
|
765
|
+
};
|
|
662
766
|
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
767
|
+
const handleFocusCapture = (event: FocusEvent) => {
|
|
768
|
+
const target = event.target as Element | null;
|
|
769
|
+
if (target && isFocusVisible(target)) {
|
|
770
|
+
isItemFocusVisible = true;
|
|
771
|
+
}
|
|
666
772
|
};
|
|
667
|
-
});
|
|
668
|
-
|
|
669
|
-
let transitionDuration = $derived.by(() => {
|
|
670
|
-
switch (animationState) {
|
|
671
|
-
case "entering":
|
|
672
|
-
case "entered":
|
|
673
|
-
return `${ANIMATION_CONFIG.ENTER_DURATION}s`;
|
|
674
|
-
case "exiting":
|
|
675
|
-
return `${ANIMATION_CONFIG.EXIT_DURATION}s`;
|
|
676
|
-
default:
|
|
677
|
-
return `${ANIMATION_CONFIG.STACK_DURATION}s`;
|
|
678
|
-
}
|
|
679
|
-
});
|
|
680
|
-
|
|
681
|
-
let transitionTimingFunction = $derived(
|
|
682
|
-
animationState === "exiting"
|
|
683
|
-
? ANIMATION_CONFIG.EASING_EXIT
|
|
684
|
-
: ANIMATION_CONFIG.EASING_DEFAULT,
|
|
685
|
-
);
|
|
686
|
-
|
|
687
|
-
let canSwipe = $derived(showClose && swipeDirections.length > 0);
|
|
688
|
-
let swipeCursorClass = $derived(
|
|
689
|
-
canSwipe ? (isSwiping ? "cursor-grabbing" : "cursor-grab") : undefined,
|
|
690
|
-
);
|
|
691
|
-
|
|
692
|
-
let titleId = $derived(title ? `${id}-title` : undefined);
|
|
693
|
-
let descriptionId = $derived(description ? `${id}-desc` : undefined);
|
|
694
|
-
let liveRole = $derived(variant === "destructive" ? "alert" : "status");
|
|
695
|
-
let livePoliteness = $derived(
|
|
696
|
-
(variant === "destructive" ? "assertive" : "polite") as "assertive" | "polite",
|
|
697
|
-
);
|
|
698
|
-
|
|
699
|
-
let offsetStyle = $derived.by(() => {
|
|
700
|
-
if (offset === undefined) return undefined;
|
|
701
|
-
const val = typeof offset === "number" ? `${offset}px` : offset;
|
|
702
|
-
if (position.startsWith("top")) return `top: ${val};`;
|
|
703
|
-
if (position.startsWith("bottom")) return `bottom: ${val};`;
|
|
704
|
-
return undefined;
|
|
705
|
-
});
|
|
706
|
-
|
|
707
|
-
const handleBlurCapture = (event: FocusEvent) => {
|
|
708
|
-
const next = event.relatedTarget as Node | null;
|
|
709
|
-
if (!toastRef || !next || !toastRef.contains(next)) {
|
|
710
|
-
isItemHovered = false;
|
|
711
|
-
}
|
|
712
|
-
};
|
|
713
773
|
</script>
|
|
714
774
|
|
|
715
775
|
<div
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
776
|
+
bind:this={toastRef}
|
|
777
|
+
class={cn(
|
|
778
|
+
toastContainerVariants({ position, variant }),
|
|
779
|
+
className,
|
|
780
|
+
swipeCursorClass,
|
|
781
|
+
stackHidden && "pointer-events-none",
|
|
782
|
+
)}
|
|
783
|
+
style:transform-origin={position?.startsWith("top-") ? "center top" : "center bottom"}
|
|
784
|
+
style:z-index={zIndex}
|
|
785
|
+
style:transition={isSwiping
|
|
786
|
+
? `transform 0s linear, opacity ${transitionDuration} ${transitionTimingFunction}`
|
|
787
|
+
: `transform ${transitionDuration} ${transitionTimingFunction}, opacity ${transitionDuration} ${transitionTimingFunction}`}
|
|
788
|
+
style:transform={transformStyle.transform}
|
|
789
|
+
style:opacity={transformStyle.opacity}
|
|
790
|
+
style={offsetStyle}
|
|
791
|
+
role={stackHidden ? undefined : dialogRole}
|
|
792
|
+
aria-live={stackHidden ? undefined : livePoliteness}
|
|
793
|
+
aria-atomic={stackHidden ? undefined : "true"}
|
|
794
|
+
aria-modal={stackHidden ? undefined : "false"}
|
|
795
|
+
aria-labelledby={stackHidden ? undefined : titleId}
|
|
796
|
+
aria-describedby={stackHidden ? undefined : descriptionId}
|
|
797
|
+
aria-hidden={stackHidden ? true : undefined}
|
|
798
|
+
tabindex="-1"
|
|
799
|
+
ontransitionend={handleTransitionEnd}
|
|
800
|
+
data-toast-id={id}
|
|
801
|
+
data-priority={priority}
|
|
741
802
|
>
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
803
|
+
<div
|
|
804
|
+
role="presentation"
|
|
805
|
+
class={cn(swipeCursorClass, "touch-none")}
|
|
806
|
+
aria-busy={isLoading ? "true" : undefined}
|
|
807
|
+
onpointerdown={handlePointerDown}
|
|
808
|
+
onpointermove={handlePointerMove}
|
|
809
|
+
onpointerup={handlePointerUp}
|
|
810
|
+
onpointercancel={handlePointerCancel}
|
|
811
|
+
onmouseenter={() => {
|
|
812
|
+
isItemHovered = true;
|
|
813
|
+
onGroupHoverEnter?.();
|
|
814
|
+
}}
|
|
815
|
+
onmouseleave={() => (isItemHovered = false)}
|
|
816
|
+
onfocuscapture={handleFocusCapture}
|
|
817
|
+
onblurcapture={handleBlurCapture}
|
|
818
|
+
>
|
|
758
819
|
{#if toast.component}
|
|
759
820
|
{@const Component = toast.component}
|
|
760
821
|
<Component {id} {toast} {...toast.componentProps} />
|
|
@@ -765,22 +826,22 @@ const handleBlurCapture = (event: FocusEvent) => {
|
|
|
765
826
|
type="button"
|
|
766
827
|
onclick={() => handleClose()}
|
|
767
828
|
class={cn(
|
|
768
|
-
"
|
|
829
|
+
"rounded-vs-sm text-vs-foreground/45 hover:bg-vs-popover-muted hover:text-vs-foreground/70 ease-vs-button focus-visible:ring-vs-ring/50 absolute end-2 top-2 cursor-pointer p-1 transition-[background-color,color,box-shadow] duration-100 focus-visible:ring-1 focus-visible:outline-none",
|
|
769
830
|
)}
|
|
770
831
|
aria-label="Close toast"
|
|
771
832
|
>
|
|
772
833
|
<svg
|
|
773
834
|
aria-hidden="true"
|
|
774
835
|
class="h-4 w-4"
|
|
775
|
-
viewBox="0 0
|
|
836
|
+
viewBox="0 0 18 18"
|
|
776
837
|
fill="none"
|
|
777
838
|
stroke="currentColor"
|
|
778
|
-
stroke-width="
|
|
839
|
+
stroke-width="1.5"
|
|
779
840
|
stroke-linecap="round"
|
|
780
841
|
stroke-linejoin="round"
|
|
781
842
|
>
|
|
782
|
-
<line x1="
|
|
783
|
-
<line x1="
|
|
843
|
+
<line x1="14" y1="4" x2="4" y2="14" />
|
|
844
|
+
<line x1="4" y1="4" x2="14" y2="14" />
|
|
784
845
|
</svg>
|
|
785
846
|
</button>
|
|
786
847
|
{/if}
|
|
@@ -788,7 +849,9 @@ const handleBlurCapture = (event: FocusEvent) => {
|
|
|
788
849
|
<div class="p-4 pe-8">
|
|
789
850
|
<div class="flex gap-3">
|
|
790
851
|
{#if showStatusIcon}
|
|
791
|
-
<span
|
|
852
|
+
<span
|
|
853
|
+
class="relative inline-flex h-4 w-4 shrink-0 items-center justify-center"
|
|
854
|
+
>
|
|
792
855
|
{#if shouldRenderSpinner}
|
|
793
856
|
<span
|
|
794
857
|
class={cn(
|
|
@@ -803,21 +866,45 @@ const handleBlurCapture = (event: FocusEvent) => {
|
|
|
803
866
|
onanimationend={handleSpinnerAnimationEnd}
|
|
804
867
|
>
|
|
805
868
|
<svg
|
|
806
|
-
viewBox="0 0
|
|
869
|
+
viewBox="0 0 18 18"
|
|
807
870
|
fill="none"
|
|
808
871
|
stroke="currentColor"
|
|
809
|
-
stroke-width="
|
|
872
|
+
stroke-width="1.5"
|
|
810
873
|
stroke-linecap="round"
|
|
811
874
|
stroke-linejoin="round"
|
|
812
875
|
>
|
|
813
|
-
<line x1="
|
|
814
|
-
<line
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
876
|
+
<line x1="9" y1="1.75" x2="9" y2="4.25" />
|
|
877
|
+
<line
|
|
878
|
+
x1="14.127"
|
|
879
|
+
y1="3.873"
|
|
880
|
+
x2="12.359"
|
|
881
|
+
y2="5.641"
|
|
882
|
+
opacity="0.88"
|
|
883
|
+
/>
|
|
884
|
+
<line x1="16.25" y1="9" x2="13.75" y2="9" opacity="0.75" />
|
|
885
|
+
<line
|
|
886
|
+
x1="14.127"
|
|
887
|
+
y1="14.127"
|
|
888
|
+
x2="12.359"
|
|
889
|
+
y2="12.359"
|
|
890
|
+
opacity="0.63"
|
|
891
|
+
/>
|
|
892
|
+
<line x1="9" y1="16.25" x2="9" y2="13.75" opacity="0.5" />
|
|
893
|
+
<line
|
|
894
|
+
x1="3.873"
|
|
895
|
+
y1="14.127"
|
|
896
|
+
x2="5.641"
|
|
897
|
+
y2="12.359"
|
|
898
|
+
opacity="0.38"
|
|
899
|
+
/>
|
|
900
|
+
<line x1="1.75" y1="9" x2="4.25" y2="9" opacity="0.25" />
|
|
901
|
+
<line
|
|
902
|
+
x1="3.873"
|
|
903
|
+
y1="3.873"
|
|
904
|
+
x2="5.641"
|
|
905
|
+
y2="5.641"
|
|
906
|
+
opacity="0.13"
|
|
907
|
+
/>
|
|
821
908
|
</svg>
|
|
822
909
|
</span>
|
|
823
910
|
{/if}
|
|
@@ -833,26 +920,29 @@ const handleBlurCapture = (event: FocusEvent) => {
|
|
|
833
920
|
viewBox={iconConfig.viewBox}
|
|
834
921
|
fill="none"
|
|
835
922
|
stroke="currentColor"
|
|
836
|
-
stroke-width="
|
|
923
|
+
stroke-width="1.5"
|
|
837
924
|
stroke-linecap="round"
|
|
838
925
|
stroke-linejoin="round"
|
|
839
926
|
>
|
|
840
927
|
{#each iconConfig.elements as element, elementIndex (elementIndex)}
|
|
841
928
|
{#if element.tag === "path"}
|
|
842
|
-
|
|
929
|
+
{#if element.fill}
|
|
930
|
+
<path d={element.d} fill="currentColor" stroke="none" />
|
|
931
|
+
{:else}
|
|
932
|
+
<path d={element.d} />
|
|
933
|
+
{/if}
|
|
843
934
|
{:else if element.tag === "line"}
|
|
935
|
+
{@const lineEl = element as LineElement}
|
|
936
|
+
|
|
844
937
|
<line
|
|
845
|
-
x1={
|
|
846
|
-
y1={
|
|
847
|
-
x2={
|
|
848
|
-
y2={
|
|
849
|
-
|
|
850
|
-
{:else if element.tag === "circle"}
|
|
851
|
-
<circle
|
|
852
|
-
cx={element.cx}
|
|
853
|
-
cy={element.cy}
|
|
854
|
-
r={element.r}
|
|
938
|
+
x1={lineEl.x1}
|
|
939
|
+
y1={lineEl.y1}
|
|
940
|
+
x2={lineEl.x2}
|
|
941
|
+
y2={lineEl.y2}
|
|
942
|
+
opacity={lineEl.opacity ?? undefined}
|
|
855
943
|
/>
|
|
944
|
+
{:else if element.tag === "polyline"}
|
|
945
|
+
<polyline points={element.points} />
|
|
856
946
|
{/if}
|
|
857
947
|
{/each}
|
|
858
948
|
</svg>
|
|
@@ -872,7 +962,7 @@ const handleBlurCapture = (event: FocusEvent) => {
|
|
|
872
962
|
{#if description}
|
|
873
963
|
<div
|
|
874
964
|
id={descriptionId}
|
|
875
|
-
class="text-sm leading-snug text-
|
|
965
|
+
class="text-vs-foreground/70 text-sm leading-snug text-balance select-none"
|
|
876
966
|
>
|
|
877
967
|
{description}
|
|
878
968
|
</div>
|
|
@@ -885,7 +975,7 @@ const handleBlurCapture = (event: FocusEvent) => {
|
|
|
885
975
|
action.onClick();
|
|
886
976
|
handleClose();
|
|
887
977
|
}}
|
|
888
|
-
class="relative inline-flex cursor-pointer items-center justify-center
|
|
978
|
+
class="rounded-vs-md bg-vs-foreground hover:bg-vs-foreground/90 text-vs-popover shadow-vs-button ease-vs-button focus-visible:ring-offset-vs-ring-offset/50 focus-visible:ring-vs-ring/50 border-vs-foreground relative inline-flex cursor-pointer items-center justify-center border px-3 py-1.5 text-sm font-medium transition-[background-color,color,box-shadow] duration-100 focus-visible:ring-1 focus-visible:ring-offset-1 focus-visible:outline-none"
|
|
889
979
|
>
|
|
890
980
|
{action.label}
|
|
891
981
|
</button>
|
|
@@ -896,5 +986,5 @@ const handleBlurCapture = (event: FocusEvent) => {
|
|
|
896
986
|
</div>
|
|
897
987
|
</div>
|
|
898
988
|
{/if}
|
|
899
|
-
|
|
989
|
+
</div>
|
|
900
990
|
</div>
|