@cartridge/controller 0.11.2 → 0.11.3
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/.turbo/turbo-build$colon$deps.log +18 -18
- package/.turbo/turbo-build.log +16 -16
- package/dist/iframe/base.d.ts +0 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1854 -811
- package/dist/index.js.map +1 -1
- package/dist/node/index.cjs +1 -1
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.d.cts +1 -1
- package/dist/node/index.d.ts +1 -1
- package/dist/node/index.js +1 -1
- package/dist/node/index.js.map +1 -1
- package/dist/{provider-s-80NdXp.js → provider-DCZ6z1Gs.js} +38 -24
- package/dist/provider-DCZ6z1Gs.js.map +1 -0
- package/dist/session.js +2 -2
- package/dist/stats.html +1 -1
- package/dist/toast/components/close-button.d.ts +1 -0
- package/dist/toast/components/progress-bar.d.ts +6 -0
- package/dist/toast/index.d.ts +57 -0
- package/dist/toast/styles.d.ts +1 -0
- package/dist/toast/types.d.ts +39 -0
- package/dist/toast/utils/progress-bar.d.ts +9 -0
- package/dist/toast/utils.d.ts +8 -0
- package/dist/toast/variants/achievement.d.ts +3 -0
- package/dist/toast/variants/error.d.ts +3 -0
- package/dist/toast/variants/index.d.ts +6 -0
- package/dist/toast/variants/marketplace.d.ts +3 -0
- package/dist/toast/variants/network-switch.d.ts +3 -0
- package/dist/toast/variants/quest.d.ts +3 -0
- package/dist/toast/variants/transaction.d.ts +3 -0
- package/dist/types.d.ts +3 -3
- package/dist/utils.d.ts +1 -0
- package/dist/wallets/index.d.ts +1 -0
- package/dist/wallets/phantom-evm/index.d.ts +7 -0
- package/dist/wallets/types.d.ts +2 -2
- package/package.json +2 -3
- package/src/iframe/base.ts +2 -20
- package/src/index.ts +1 -0
- package/src/toast/components/close-button.ts +82 -0
- package/src/toast/components/progress-bar.ts +60 -0
- package/src/toast/index.ts +263 -0
- package/src/toast/styles.ts +142 -0
- package/src/toast/types.ts +67 -0
- package/src/toast/utils/progress-bar.ts +23 -0
- package/src/toast/utils.ts +65 -0
- package/src/toast/variants/achievement.ts +244 -0
- package/src/toast/variants/error.ts +107 -0
- package/src/toast/variants/index.ts +6 -0
- package/src/toast/variants/marketplace.ts +145 -0
- package/src/toast/variants/network-switch.ts +72 -0
- package/src/toast/variants/quest.ts +164 -0
- package/src/toast/variants/transaction.ts +256 -0
- package/src/types.ts +1 -0
- package/src/utils.ts +15 -0
- package/src/wallets/bridge.ts +4 -0
- package/src/wallets/index.ts +1 -0
- package/src/wallets/phantom-evm/index.ts +8 -0
- package/src/wallets/types.ts +5 -1
- package/dist/provider-s-80NdXp.js.map +0 -1
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { ToastOptions } from "./types";
|
|
2
|
+
import {
|
|
3
|
+
isInIframe,
|
|
4
|
+
getTargetDocument,
|
|
5
|
+
getToastContainer,
|
|
6
|
+
DEFAULT_DURATION,
|
|
7
|
+
DEFAULT_POSITION,
|
|
8
|
+
TOAST_MESSAGE_TYPE,
|
|
9
|
+
removeToast,
|
|
10
|
+
} from "./utils";
|
|
11
|
+
import { injectStyles } from "./styles";
|
|
12
|
+
import {
|
|
13
|
+
injectErrorStyles,
|
|
14
|
+
createErrorToast,
|
|
15
|
+
injectTransactionStyles,
|
|
16
|
+
createTransactionToast,
|
|
17
|
+
injectNetworkSwitchStyles,
|
|
18
|
+
createNetworkSwitchToast,
|
|
19
|
+
injectAchievementStyles,
|
|
20
|
+
createAchievementToast,
|
|
21
|
+
injectQuestStyles,
|
|
22
|
+
createQuestToast,
|
|
23
|
+
injectMarketplaceStyles,
|
|
24
|
+
createMarketplaceToast,
|
|
25
|
+
} from "./variants";
|
|
26
|
+
import { addProgressBarToToast } from "./utils/progress-bar";
|
|
27
|
+
|
|
28
|
+
// Create toast element based on variant
|
|
29
|
+
function createToastElement(options: ToastOptions): HTMLElement {
|
|
30
|
+
switch (options.variant) {
|
|
31
|
+
case "error":
|
|
32
|
+
return createErrorToast(options);
|
|
33
|
+
case "transaction":
|
|
34
|
+
return createTransactionToast(options);
|
|
35
|
+
case "network-switch":
|
|
36
|
+
return createNetworkSwitchToast(options);
|
|
37
|
+
case "achievement":
|
|
38
|
+
return createAchievementToast(options);
|
|
39
|
+
case "quest":
|
|
40
|
+
return createQuestToast(options);
|
|
41
|
+
case "marketplace":
|
|
42
|
+
return createMarketplaceToast(options);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Inject variant-specific styles
|
|
47
|
+
function injectVariantStyles(
|
|
48
|
+
targetDoc: Document,
|
|
49
|
+
variant: ToastOptions["variant"],
|
|
50
|
+
): void {
|
|
51
|
+
switch (variant) {
|
|
52
|
+
case "error":
|
|
53
|
+
injectErrorStyles(targetDoc);
|
|
54
|
+
break;
|
|
55
|
+
case "transaction":
|
|
56
|
+
injectTransactionStyles(targetDoc);
|
|
57
|
+
break;
|
|
58
|
+
case "network-switch":
|
|
59
|
+
injectNetworkSwitchStyles(targetDoc);
|
|
60
|
+
break;
|
|
61
|
+
case "achievement":
|
|
62
|
+
injectAchievementStyles(targetDoc);
|
|
63
|
+
break;
|
|
64
|
+
case "quest":
|
|
65
|
+
injectQuestStyles(targetDoc);
|
|
66
|
+
break;
|
|
67
|
+
case "marketplace":
|
|
68
|
+
injectMarketplaceStyles(targetDoc);
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Show toast on target document (parent or current)
|
|
74
|
+
function showToastOnDocument(
|
|
75
|
+
targetDoc: Document,
|
|
76
|
+
options: ToastOptions,
|
|
77
|
+
): () => void {
|
|
78
|
+
// Inject common styles if needed
|
|
79
|
+
injectStyles(targetDoc);
|
|
80
|
+
|
|
81
|
+
// Inject variant-specific styles
|
|
82
|
+
injectVariantStyles(targetDoc, options.variant);
|
|
83
|
+
|
|
84
|
+
// Get container
|
|
85
|
+
const position = options.position || DEFAULT_POSITION;
|
|
86
|
+
const container = getToastContainer(targetDoc, position);
|
|
87
|
+
|
|
88
|
+
// Create toast element
|
|
89
|
+
const toastElement = createToastElement(options);
|
|
90
|
+
|
|
91
|
+
// Set up dismiss function
|
|
92
|
+
const dismiss = () => removeToast(toastElement);
|
|
93
|
+
|
|
94
|
+
// Add to container
|
|
95
|
+
container.appendChild(toastElement);
|
|
96
|
+
|
|
97
|
+
// Setup close button
|
|
98
|
+
const closeButton = toastElement.querySelector("#close-button");
|
|
99
|
+
if (closeButton) {
|
|
100
|
+
closeButton.addEventListener("click", dismiss);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Handle duration and progress bar
|
|
104
|
+
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
105
|
+
const duration = options.duration ?? DEFAULT_DURATION;
|
|
106
|
+
const isInfiniteDuration = !isFinite(duration) || duration <= 0;
|
|
107
|
+
|
|
108
|
+
// Add progress bar to all variants except network-switch
|
|
109
|
+
if (options.variant !== "network-switch") {
|
|
110
|
+
// Determine border radius based on variant
|
|
111
|
+
const borderRadius =
|
|
112
|
+
options.variant === "error" || options.variant === "transaction" ? 8 : 4;
|
|
113
|
+
|
|
114
|
+
if (isInfiniteDuration) {
|
|
115
|
+
// Show static progress bar for infinite duration (no animation, no auto-dismiss)
|
|
116
|
+
addProgressBarToToast(toastElement, Infinity, () => {}, borderRadius);
|
|
117
|
+
} else {
|
|
118
|
+
// Animated progress bar with auto-dismiss
|
|
119
|
+
addProgressBarToToast(toastElement, duration, dismiss, borderRadius);
|
|
120
|
+
}
|
|
121
|
+
} else if (!isInfiniteDuration) {
|
|
122
|
+
// Network-switch variant uses setTimeout instead of progress bar
|
|
123
|
+
timeoutId = setTimeout(dismiss, duration);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Return dismiss function
|
|
127
|
+
return () => {
|
|
128
|
+
if (timeoutId) {
|
|
129
|
+
clearTimeout(timeoutId);
|
|
130
|
+
}
|
|
131
|
+
dismiss();
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Set up message listener on parent window to handle toast requests from iframes
|
|
136
|
+
let messageListenerSetup = false;
|
|
137
|
+
function setupMessageListener(): void {
|
|
138
|
+
if (messageListenerSetup || typeof window === "undefined") {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Only set up listener on parent window (not in iframe)
|
|
143
|
+
if (isInIframe()) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
window.addEventListener("message", (event) => {
|
|
148
|
+
// Basic origin check - in production, you might want stricter checks
|
|
149
|
+
if (event.data?.type === TOAST_MESSAGE_TYPE && event.data?.options) {
|
|
150
|
+
const targetDoc = document;
|
|
151
|
+
if (targetDoc) {
|
|
152
|
+
showToastOnDocument(targetDoc, event.data.options);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
messageListenerSetup = true;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Show a toast notification
|
|
162
|
+
*
|
|
163
|
+
* The toast will always appear on the parent page, even if called from within an iframe.
|
|
164
|
+
* This ensures toasts are visible above all content, including the keychain iframe.
|
|
165
|
+
*
|
|
166
|
+
* @param options - Toast options with variant-specific properties
|
|
167
|
+
* @returns A function to manually dismiss the toast
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```ts
|
|
171
|
+
* import { toast } from "@cartridge/controller";
|
|
172
|
+
*
|
|
173
|
+
* // Error toast
|
|
174
|
+
* toast({
|
|
175
|
+
* variant: "error",
|
|
176
|
+
* message: "Transaction failed",
|
|
177
|
+
* });
|
|
178
|
+
*
|
|
179
|
+
* // Transaction toast
|
|
180
|
+
* toast({
|
|
181
|
+
* variant: "transaction",
|
|
182
|
+
* hash: "0x1234...",
|
|
183
|
+
* status: "success",
|
|
184
|
+
* amount: "100",
|
|
185
|
+
* token: "ETH"
|
|
186
|
+
* });
|
|
187
|
+
*
|
|
188
|
+
* // Network switch toast
|
|
189
|
+
* toast({
|
|
190
|
+
* variant: "network-switch",
|
|
191
|
+
* networkName: "Mainnet",
|
|
192
|
+
* networkIcon: <url to icon image>
|
|
193
|
+
* });
|
|
194
|
+
*
|
|
195
|
+
* // Achievement toast
|
|
196
|
+
* toast({
|
|
197
|
+
* variant: "achievement",
|
|
198
|
+
* itemName: "First Achievement",
|
|
199
|
+
* itemImage: "https://example.com/trophy.png"
|
|
200
|
+
* action: "purchased" | "sold",
|
|
201
|
+
* });
|
|
202
|
+
*
|
|
203
|
+
* // Marketplace toast
|
|
204
|
+
* toast({
|
|
205
|
+
* variant: "marketplace",
|
|
206
|
+
* itemName: "Cool NFT #123",
|
|
207
|
+
* action: "purchased",
|
|
208
|
+
* price: "0.5",
|
|
209
|
+
* currency: "ETH",
|
|
210
|
+
* image: "https://example.com/nft.png"
|
|
211
|
+
* });
|
|
212
|
+
* ```
|
|
213
|
+
*/
|
|
214
|
+
export function toast(options: ToastOptions): () => void {
|
|
215
|
+
// Ensure we're in a browser environment
|
|
216
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
217
|
+
console.warn("Toast can only be used in a browser environment");
|
|
218
|
+
return () => {};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Set up message listener on parent window if not already set up
|
|
222
|
+
setupMessageListener();
|
|
223
|
+
|
|
224
|
+
// Check if we're in an iframe
|
|
225
|
+
if (isInIframe()) {
|
|
226
|
+
// Try to get parent document
|
|
227
|
+
const targetDoc = getTargetDocument();
|
|
228
|
+
|
|
229
|
+
if (targetDoc) {
|
|
230
|
+
// Same-origin iframe, can access parent document directly
|
|
231
|
+
return showToastOnDocument(targetDoc, options);
|
|
232
|
+
} else {
|
|
233
|
+
// Cross-origin iframe, use postMessage
|
|
234
|
+
try {
|
|
235
|
+
if (window.parent) {
|
|
236
|
+
window.parent.postMessage(
|
|
237
|
+
{
|
|
238
|
+
type: TOAST_MESSAGE_TYPE,
|
|
239
|
+
options: options,
|
|
240
|
+
},
|
|
241
|
+
"*", // In production, specify target origin
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
} catch (e) {
|
|
245
|
+
console.warn("Failed to send toast message to parent:", e);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Return a no-op dismiss function for cross-origin case
|
|
249
|
+
return () => {};
|
|
250
|
+
}
|
|
251
|
+
} else {
|
|
252
|
+
// Not in iframe, show toast directly on current document
|
|
253
|
+
const targetDoc = document;
|
|
254
|
+
return showToastOnDocument(targetDoc, options);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export * from "./types";
|
|
259
|
+
|
|
260
|
+
// Initialize message listener when module loads (if in browser environment)
|
|
261
|
+
if (typeof window !== "undefined") {
|
|
262
|
+
setupMessageListener();
|
|
263
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { TOAST_CONTAINER_ID } from "./utils";
|
|
2
|
+
|
|
3
|
+
// Inject CSS styles if not already present
|
|
4
|
+
export function injectStyles(targetDoc: Document): void {
|
|
5
|
+
if (targetDoc.getElementById("cartridge-toast-styles")) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const style = targetDoc.createElement("style");
|
|
10
|
+
style.id = "cartridge-toast-styles";
|
|
11
|
+
style.textContent = getCommonStyles();
|
|
12
|
+
targetDoc.head.appendChild(style);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getCommonStyles(): string {
|
|
16
|
+
return `
|
|
17
|
+
#${TOAST_CONTAINER_ID} {
|
|
18
|
+
position: fixed;
|
|
19
|
+
z-index: 999999;
|
|
20
|
+
pointer-events: none;
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
align-items: flex-end;
|
|
24
|
+
gap: 12px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
#${TOAST_CONTAINER_ID}.top-left {
|
|
28
|
+
top: 20px;
|
|
29
|
+
left: 20px;
|
|
30
|
+
align-items: flex-start;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
#${TOAST_CONTAINER_ID}.top-right {
|
|
34
|
+
top: 20px;
|
|
35
|
+
right: 20px;
|
|
36
|
+
align-items: flex-end;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
#${TOAST_CONTAINER_ID}.top-center {
|
|
40
|
+
top: 20px;
|
|
41
|
+
left: 50%;
|
|
42
|
+
transform: translateX(-50%);
|
|
43
|
+
align-items: center;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
#${TOAST_CONTAINER_ID}.bottom-left {
|
|
47
|
+
bottom: 20px;
|
|
48
|
+
left: 20px;
|
|
49
|
+
align-items: flex-start;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
#${TOAST_CONTAINER_ID}.bottom-right {
|
|
53
|
+
bottom: 20px;
|
|
54
|
+
right: 20px;
|
|
55
|
+
align-items: flex-end;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
#${TOAST_CONTAINER_ID}.bottom-center {
|
|
59
|
+
bottom: 20px;
|
|
60
|
+
left: 50%;
|
|
61
|
+
transform: translateX(-50%);
|
|
62
|
+
align-items: center;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.cartridge-toast {
|
|
66
|
+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
|
|
67
|
+
display: flex;
|
|
68
|
+
align-items: center;
|
|
69
|
+
animation: cartridge-toast-slide-in 0.3s ease-out;
|
|
70
|
+
overflow: hidden;
|
|
71
|
+
pointer-events: auto;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
#${TOAST_CONTAINER_ID}.top-right .cartridge-toast,
|
|
75
|
+
#${TOAST_CONTAINER_ID}.bottom-right .cartridge-toast {
|
|
76
|
+
align-self: flex-end;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
#${TOAST_CONTAINER_ID}.top-left .cartridge-toast,
|
|
80
|
+
#${TOAST_CONTAINER_ID}.bottom-left .cartridge-toast {
|
|
81
|
+
align-self: flex-start;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
#${TOAST_CONTAINER_ID}.top-center .cartridge-toast,
|
|
85
|
+
#${TOAST_CONTAINER_ID}.bottom-center .cartridge-toast {
|
|
86
|
+
align-self: center;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@keyframes cartridge-toast-slide-in {
|
|
90
|
+
from {
|
|
91
|
+
opacity: 0;
|
|
92
|
+
transform: translateY(10px);
|
|
93
|
+
}
|
|
94
|
+
to {
|
|
95
|
+
opacity: 1;
|
|
96
|
+
transform: translateY(0);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.cartridge-toast.closing {
|
|
101
|
+
animation: cartridge-toast-slide-out 0.2s ease-in forwards;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@keyframes cartridge-toast-slide-out {
|
|
105
|
+
from {
|
|
106
|
+
opacity: 1;
|
|
107
|
+
transform: translateY(0);
|
|
108
|
+
}
|
|
109
|
+
to {
|
|
110
|
+
opacity: 0;
|
|
111
|
+
transform: translateY(10px);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@media (max-width: 640px) {
|
|
116
|
+
.cartridge-toast {
|
|
117
|
+
min-width: calc(100vw - 40px);
|
|
118
|
+
max-width: calc(100vw - 40px);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
#${TOAST_CONTAINER_ID}.top-left,
|
|
122
|
+
#${TOAST_CONTAINER_ID}.top-right,
|
|
123
|
+
#${TOAST_CONTAINER_ID}.top-center {
|
|
124
|
+
top: 10px;
|
|
125
|
+
left: 20px;
|
|
126
|
+
right: 20px;
|
|
127
|
+
transform: none;
|
|
128
|
+
align-items: stretch;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
#${TOAST_CONTAINER_ID}.bottom-left,
|
|
132
|
+
#${TOAST_CONTAINER_ID}.bottom-right,
|
|
133
|
+
#${TOAST_CONTAINER_ID}.bottom-center {
|
|
134
|
+
bottom: 10px;
|
|
135
|
+
left: 20px;
|
|
136
|
+
right: 20px;
|
|
137
|
+
transform: none;
|
|
138
|
+
align-items: stretch;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
`;
|
|
142
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
export type ToastPosition =
|
|
2
|
+
| "top-left"
|
|
3
|
+
| "top-right"
|
|
4
|
+
| "top-center"
|
|
5
|
+
| "bottom-left"
|
|
6
|
+
| "bottom-right"
|
|
7
|
+
| "bottom-center";
|
|
8
|
+
|
|
9
|
+
// Base toast options shared by all variants
|
|
10
|
+
export interface BaseToastOptions {
|
|
11
|
+
duration?: number; // in milliseconds, 0 means persistent
|
|
12
|
+
position?: ToastPosition;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Error Toast
|
|
16
|
+
export interface ErrorToastOptions extends BaseToastOptions {
|
|
17
|
+
variant: "error";
|
|
18
|
+
message: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Transaction Toast
|
|
22
|
+
export interface TransactionToastOptions extends BaseToastOptions {
|
|
23
|
+
variant: "transaction";
|
|
24
|
+
status: "confirming" | "confirmed";
|
|
25
|
+
isExpanded?: boolean;
|
|
26
|
+
label?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Network Switch Toast
|
|
30
|
+
export interface NetworkSwitchToastOptions extends BaseToastOptions {
|
|
31
|
+
variant: "network-switch";
|
|
32
|
+
networkName: string;
|
|
33
|
+
networkIcon?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Achievement Toast
|
|
37
|
+
export interface AchievementToastOptions extends BaseToastOptions {
|
|
38
|
+
variant: "achievement";
|
|
39
|
+
title: string;
|
|
40
|
+
subtitle?: string;
|
|
41
|
+
xpAmount: number;
|
|
42
|
+
isDraft?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Quest Toast
|
|
46
|
+
export interface QuestToastOptions extends BaseToastOptions {
|
|
47
|
+
variant: "quest";
|
|
48
|
+
title: string;
|
|
49
|
+
subtitle: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Marketplace Toast
|
|
53
|
+
export interface MarketplaceToastOptions extends BaseToastOptions {
|
|
54
|
+
variant: "marketplace";
|
|
55
|
+
itemName: string;
|
|
56
|
+
itemImage: string;
|
|
57
|
+
action: "purchased" | "sold";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Union type for all toast variants
|
|
61
|
+
export type ToastOptions =
|
|
62
|
+
| ErrorToastOptions
|
|
63
|
+
| TransactionToastOptions
|
|
64
|
+
| NetworkSwitchToastOptions
|
|
65
|
+
| AchievementToastOptions
|
|
66
|
+
| QuestToastOptions
|
|
67
|
+
| MarketplaceToastOptions;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ProgressBar } from "../components/progress-bar";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Add a progress bar to a toast element
|
|
5
|
+
*
|
|
6
|
+
* @param toast - The toast element to add the progress bar to
|
|
7
|
+
* @param duration - Duration in milliseconds
|
|
8
|
+
* @param onComplete - Callback when progress completes
|
|
9
|
+
* @param borderRadius - Optional border radius in pixels (default: 8px)
|
|
10
|
+
*/
|
|
11
|
+
export function addProgressBarToToast(
|
|
12
|
+
toast: HTMLElement,
|
|
13
|
+
duration: number,
|
|
14
|
+
onComplete: () => void,
|
|
15
|
+
borderRadius?: number,
|
|
16
|
+
): void {
|
|
17
|
+
const progressBar = ProgressBar({
|
|
18
|
+
duration,
|
|
19
|
+
onComplete,
|
|
20
|
+
borderRadius,
|
|
21
|
+
});
|
|
22
|
+
toast.appendChild(progressBar);
|
|
23
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export const TOAST_CONTAINER_ID = "cartridge-toast-container";
|
|
2
|
+
export const DEFAULT_DURATION = 3000;
|
|
3
|
+
export const DEFAULT_POSITION = "bottom-right";
|
|
4
|
+
export const TOAST_MESSAGE_TYPE = "cartridge-toast-show";
|
|
5
|
+
|
|
6
|
+
// Check if we're in an iframe
|
|
7
|
+
export function isInIframe(): boolean {
|
|
8
|
+
try {
|
|
9
|
+
return typeof window !== "undefined" && window.self !== window.top;
|
|
10
|
+
} catch {
|
|
11
|
+
return true; // If we can't access window.top, we're likely in an iframe
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Get the target document (parent if in iframe, current if not)
|
|
16
|
+
export function getTargetDocument(): Document | null {
|
|
17
|
+
if (typeof document === "undefined") {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (isInIframe()) {
|
|
22
|
+
try {
|
|
23
|
+
// Try to access parent document
|
|
24
|
+
if (window.parent && window.parent.document) {
|
|
25
|
+
return window.parent.document;
|
|
26
|
+
}
|
|
27
|
+
} catch (e) {
|
|
28
|
+
console.warn("Failed to access parent document:", e);
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return document;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Get or create toast container
|
|
37
|
+
export function getToastContainer(
|
|
38
|
+
targetDoc: Document,
|
|
39
|
+
position: string,
|
|
40
|
+
): HTMLElement {
|
|
41
|
+
let container = targetDoc.getElementById(TOAST_CONTAINER_ID);
|
|
42
|
+
|
|
43
|
+
if (!container) {
|
|
44
|
+
container = targetDoc.createElement("div");
|
|
45
|
+
container.id = TOAST_CONTAINER_ID;
|
|
46
|
+
if (targetDoc.body) {
|
|
47
|
+
targetDoc.body.appendChild(container);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Update position class
|
|
52
|
+
container.className = position;
|
|
53
|
+
|
|
54
|
+
return container;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Remove toast with animation
|
|
58
|
+
export function removeToast(toast: HTMLElement): void {
|
|
59
|
+
toast.classList.add("closing");
|
|
60
|
+
setTimeout(() => {
|
|
61
|
+
if (toast.parentNode) {
|
|
62
|
+
toast.parentNode.removeChild(toast);
|
|
63
|
+
}
|
|
64
|
+
}, 200);
|
|
65
|
+
}
|