@returnflows/shop-overlay 1.0.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/README.md +976 -0
- package/dist/index.esm.css +3 -0
- package/dist/index.esm.js +12494 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/messages/da.json +1 -0
- package/dist/messages/de.json +1 -0
- package/dist/messages/en.json +1 -0
- package/dist/messages/es.json +1 -0
- package/dist/messages/fi.json +1 -0
- package/dist/messages/fr.json +1 -0
- package/dist/messages/it.json +1 -0
- package/dist/messages/nb.json +1 -0
- package/dist/messages/nl.json +1 -0
- package/dist/messages/pl.json +1 -0
- package/dist/messages/sv.json +1 -0
- package/dist/types/api/index.d.ts +6 -0
- package/dist/types/api/state-bridge.d.ts +37 -0
- package/dist/types/api/storefront-cart.d.ts +3 -0
- package/dist/types/api/types.d.ts +402 -0
- package/dist/types/api/widget-api.d.ts +20 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/widget/components/dialog/common/dialog-backdrop.d.ts +6 -0
- package/dist/types/widget/components/dialog/common/dialog-button.d.ts +6 -0
- package/dist/types/widget/components/dialog/common/dialog-modal.d.ts +9 -0
- package/dist/types/widget/components/dialog/common/dialog-subtitle.d.ts +2 -0
- package/dist/types/widget/components/dialog/common/dialog-title.d.ts +2 -0
- package/dist/types/widget/components/dialog/common/index.d.ts +10 -0
- package/dist/types/widget/components/dialog/return-decision-dialog.d.ts +2 -0
- package/dist/types/widget/components/drawer/adjustments/exchange-adjustments.d.ts +2 -0
- package/dist/types/widget/components/drawer/adjustments/exchange-banner.d.ts +2 -0
- package/dist/types/widget/components/drawer/adjustments/table/adjustments-table-skeleton.d.ts +2 -0
- package/dist/types/widget/components/drawer/adjustments/table/adjustments-table.d.ts +2 -0
- package/dist/types/widget/components/drawer/annoucement-bar.d.ts +2 -0
- package/dist/types/widget/components/drawer/error-screen.d.ts +6 -0
- package/dist/types/widget/components/drawer/exchange-confirmation.d.ts +2 -0
- package/dist/types/widget/components/drawer/exchange-drawer.d.ts +5 -0
- package/dist/types/widget/components/drawer/exchange-header.d.ts +2 -0
- package/dist/types/widget/components/drawer/exchange-placeholder.d.ts +2 -0
- package/dist/types/widget/components/drawer/product/product-actions.d.ts +8 -0
- package/dist/types/widget/components/drawer/product/product-attributes.d.ts +14 -0
- package/dist/types/widget/components/drawer/product/product-errors.d.ts +2 -0
- package/dist/types/widget/components/drawer/product/product-image.d.ts +7 -0
- package/dist/types/widget/components/drawer/product/product-list.d.ts +2 -0
- package/dist/types/widget/components/drawer/product/product.d.ts +9 -0
- package/dist/types/widget/components/drawer/product/quantity-picker.d.ts +20 -0
- package/dist/types/widget/components/drawer/return/return-product-actions.d.ts +10 -0
- package/dist/types/widget/components/drawer/return/return-product-badge.d.ts +7 -0
- package/dist/types/widget/components/drawer/return/return-product-list.d.ts +2 -0
- package/dist/types/widget/components/drawer/return/return-product-skeleton.d.ts +2 -0
- package/dist/types/widget/components/drawer/return/return-product.d.ts +14 -0
- package/dist/types/widget/components/drawer/scroll-indicator.d.ts +5 -0
- package/dist/types/widget/components/handlers/dialog-handler.d.ts +2 -0
- package/dist/types/widget/components/handlers/initialization-handler.d.ts +2 -0
- package/dist/types/widget/components/icons/arrow-down.d.ts +2 -0
- package/dist/types/widget/components/icons/arrow-left.d.ts +2 -0
- package/dist/types/widget/components/icons/arrow-right.d.ts +2 -0
- package/dist/types/widget/components/icons/bag.d.ts +2 -0
- package/dist/types/widget/components/icons/banner/action.d.ts +2 -0
- package/dist/types/widget/components/icons/banner/info.d.ts +2 -0
- package/dist/types/widget/components/icons/cart.d.ts +2 -0
- package/dist/types/widget/components/icons/chevron.d.ts +2 -0
- package/dist/types/widget/components/icons/giftcard.d.ts +2 -0
- package/dist/types/widget/components/icons/icon.d.ts +17 -0
- package/dist/types/widget/components/icons/spinner.d.ts +2 -0
- package/dist/types/widget/components/icons/star.d.ts +2 -0
- package/dist/types/widget/components/icons/warning.d.ts +2 -0
- package/dist/types/widget/components/icons/x.d.ts +2 -0
- package/dist/types/widget/components/is-mounted.d.ts +4 -0
- package/dist/types/widget/components/providers/error-provider.d.ts +4 -0
- package/dist/types/widget/components/providers/return-context-provider.d.ts +17 -0
- package/dist/types/widget/components/providers/root-provider.d.ts +2 -0
- package/dist/types/widget/components/providers/widget-context-provider.d.ts +6 -0
- package/dist/types/widget/components/return-avatar-stack.d.ts +2 -0
- package/dist/types/widget/components/summary/back-button.d.ts +2 -0
- package/dist/types/widget/components/summary/exchange-summary.d.ts +2 -0
- package/dist/types/widget/components/summary/exchange-total-skeleton.d.ts +2 -0
- package/dist/types/widget/components/summary/exchange-total.d.ts +2 -0
- package/dist/types/widget/components/toast/toast-container.d.ts +2 -0
- package/dist/types/widget/components/toast/toast-provider.d.ts +4 -0
- package/dist/types/widget/components/toast/toast.d.ts +16 -0
- package/dist/types/widget/components/ui/avatar/avatar-stack-skeleton.d.ts +2 -0
- package/dist/types/widget/components/ui/avatar/avatar-stack.d.ts +12 -0
- package/dist/types/widget/components/ui/avatar/avatar.d.ts +10 -0
- package/dist/types/widget/components/ui/banner/banner-skeleton.d.ts +2 -0
- package/dist/types/widget/components/ui/banner/banner.d.ts +13 -0
- package/dist/types/widget/components/ui/button.d.ts +11 -0
- package/dist/types/widget/components/ui/expander.d.ts +8 -0
- package/dist/types/widget/components/ui/image.d.ts +5 -0
- package/dist/types/widget/components/ui/link.d.ts +7 -0
- package/dist/types/widget/components/ui/price.d.ts +7 -0
- package/dist/types/widget/components/ui/skeleton/skeleton-container.d.ts +6 -0
- package/dist/types/widget/components/ui/skeleton/skeleton.d.ts +2 -0
- package/dist/types/widget/components/widget-container.d.ts +3 -0
- package/dist/types/widget/components/widget.d.ts +2 -0
- package/dist/types/widget/lib/constants.d.ts +13 -0
- package/dist/types/widget/lib/context/dialog-context.d.ts +3 -0
- package/dist/types/widget/lib/context/error-context.d.ts +10 -0
- package/dist/types/widget/lib/context/return-context.d.ts +12 -0
- package/dist/types/widget/lib/context/root-context.d.ts +2 -0
- package/dist/types/widget/lib/context/toast-context.d.ts +13 -0
- package/dist/types/widget/lib/context/widget-context.d.ts +9 -0
- package/dist/types/widget/lib/hooks/common/use-array.d.ts +14 -0
- package/dist/types/widget/lib/hooks/common/use-async-state.d.ts +40 -0
- package/dist/types/widget/lib/hooks/common/use-dialog.d.ts +11 -0
- package/dist/types/widget/lib/hooks/common/use-is-mounted.d.ts +2 -0
- package/dist/types/widget/lib/hooks/common/use-once.d.ts +3 -0
- package/dist/types/widget/lib/hooks/common/use-value-state.d.ts +7 -0
- package/dist/types/widget/lib/hooks/common/use-y-scrollable.d.ts +10 -0
- package/dist/types/widget/lib/hooks/context/use-calculation.d.ts +12 -0
- package/dist/types/widget/lib/hooks/context/use-cart-items.d.ts +9 -0
- package/dist/types/widget/lib/hooks/context/use-currency.d.ts +12 -0
- package/dist/types/widget/lib/hooks/context/use-errors.d.ts +2 -0
- package/dist/types/widget/lib/hooks/context/use-navigation.d.ts +6 -0
- package/dist/types/widget/lib/hooks/context/use-return-context.d.ts +2 -0
- package/dist/types/widget/lib/hooks/context/use-root.d.ts +2 -0
- package/dist/types/widget/lib/hooks/context/use-theme.d.ts +5 -0
- package/dist/types/widget/lib/hooks/context/use-toast.d.ts +2 -0
- package/dist/types/widget/lib/hooks/context/use-widget-context.d.ts +2 -0
- package/dist/types/widget/lib/hooks/feature/use-api-bridge.d.ts +6 -0
- package/dist/types/widget/lib/hooks/feature/use-cart-activator.d.ts +2 -0
- package/dist/types/widget/lib/hooks/feature/use-external-events.d.ts +2 -0
- package/dist/types/widget/lib/hooks/feature/use-fetch-interceptor.d.ts +4 -0
- package/dist/types/widget/lib/hooks/feature/use-override-scroll-blocking.d.ts +2 -0
- package/dist/types/widget/lib/types.d.ts +382 -0
- package/dist/types/widget/lib/utils/api.d.ts +26 -0
- package/dist/types/widget/lib/utils/cart-provider-store.d.ts +3 -0
- package/dist/types/widget/lib/utils/cart.d.ts +17 -0
- package/dist/types/widget/lib/utils/custom-events.d.ts +48 -0
- package/dist/types/widget/lib/utils/dom.d.ts +2 -0
- package/dist/types/widget/lib/utils/errors.d.ts +6 -0
- package/dist/types/widget/lib/utils/helpers.d.ts +24 -0
- package/dist/types/widget/lib/utils/i18n.d.ts +8 -0
- package/dist/types/widget/lib/utils/initialization/dataset.d.ts +5 -0
- package/dist/types/widget/lib/utils/initialization/font.d.ts +7 -0
- package/dist/types/widget/lib/utils/initialization/index.d.ts +6 -0
- package/dist/types/widget/lib/utils/initialization/styles.d.ts +4 -0
- package/dist/types/widget/lib/utils/initialization/types.d.ts +51 -0
- package/dist/types/widget/lib/utils/interceptor/fetch.d.ts +5 -0
- package/dist/types/widget/lib/utils/interceptor/index.d.ts +26 -0
- package/dist/types/widget/lib/utils/interceptor/xhr.d.ts +5 -0
- package/dist/types/widget/lib/utils/logger.d.ts +13 -0
- package/dist/types/widget/lib/utils/navigation.d.ts +7 -0
- package/dist/types/widget/lib/utils/session.d.ts +54 -0
- package/dist/types/widget/lib/utils/shopify.d.ts +6 -0
- package/dist/types/widget/lib/utils/storage.d.ts +33 -0
- package/dist/variables.css +1 -0
- package/dist/widget.css +3 -0
- package/dist/widget.js +12006 -0
- package/package.json +112 -0
package/README.md
ADDED
|
@@ -0,0 +1,976 @@
|
|
|
1
|
+
# @returnflows/shop-overlay
|
|
2
|
+
|
|
3
|
+
A modular exchange/return widget with a programmatic API for headless Shopify storefronts and custom e-commerce implementations.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Quick Start](#quick-start)
|
|
9
|
+
- [Configuration](#configuration)
|
|
10
|
+
- [API Reference](#api-reference)
|
|
11
|
+
- [Initialization](#initialization)
|
|
12
|
+
- [Drawer API](#drawer-api)
|
|
13
|
+
- [Cart API](#cart-api)
|
|
14
|
+
- [Data API](#data-api)
|
|
15
|
+
- [Toast API](#toast-api)
|
|
16
|
+
- [Events](#events)
|
|
17
|
+
- [Cart Providers](#cart-providers)
|
|
18
|
+
- [Storefront API Support](#storefront-api-support)
|
|
19
|
+
- [Examples](#examples)
|
|
20
|
+
- [Vanilla JavaScript](#vanilla-javascript)
|
|
21
|
+
- [React](#react)
|
|
22
|
+
- [TypeScript Support](#typescript-support)
|
|
23
|
+
- [Troubleshooting](#troubleshooting)
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install @returnflows/shop-overlay
|
|
29
|
+
# or
|
|
30
|
+
pnpm add @returnflows/shop-overlay
|
|
31
|
+
# or
|
|
32
|
+
yarn add @returnflows/shop-overlay
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
### Vanilla JavaScript
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
import { Returnflows } from "@returnflows/shop-overlay";
|
|
41
|
+
|
|
42
|
+
// Initialize the widget
|
|
43
|
+
await Returnflows.init({
|
|
44
|
+
onReady: () => {
|
|
45
|
+
console.log("Widget ready!");
|
|
46
|
+
},
|
|
47
|
+
onError: error => {
|
|
48
|
+
console.error("Widget error:", error);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Open the drawer
|
|
53
|
+
Returnflows.drawer.open();
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### React
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
import { useEffect } from "react";
|
|
60
|
+
import { Returnflows } from "@returnflows/shop-overlay";
|
|
61
|
+
|
|
62
|
+
function App() {
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
Returnflows.init({
|
|
65
|
+
onReady: () => console.log("Ready"),
|
|
66
|
+
onError: err => console.error(err)
|
|
67
|
+
});
|
|
68
|
+
}, []);
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<button onClick={() => Returnflows.drawer.toggle()}>Open Returns</button>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Configuration
|
|
77
|
+
|
|
78
|
+
### `ReturnflowsConfig`
|
|
79
|
+
|
|
80
|
+
Configuration options passed to `Returnflows.init()`:
|
|
81
|
+
|
|
82
|
+
| Property | Type | Default | Description |
|
|
83
|
+
| --------------------------- | ----------------------------------------- | -------------- | ----------------------------------------- |
|
|
84
|
+
| `env` | `'development' \| 'test' \| 'production'` | `'production'` | Environment for API endpoints |
|
|
85
|
+
| `debug` | `boolean` | `false` | Enable debug logging |
|
|
86
|
+
| `autoOpen` | `boolean` | `false` | Automatically open drawer on cart changes |
|
|
87
|
+
| `autoOpenTriggerMethod` | `'endpoint' \| 'change' \| 'combination'` | `'change'` | How to detect cart changes for auto-open |
|
|
88
|
+
| `autoOpenTriggerEndpoint` | `'add' \| 'update' \| 'change'` | `'change'` | Which cart endpoint triggers auto-open |
|
|
89
|
+
| `networkInterceptionMethod` | `'none' \| 'fetch' \| 'xhr' \| 'both'` | `'none'` | Network request interception method |
|
|
90
|
+
| `defaultLocale` | `string` | `'en'` | Default locale for translations |
|
|
91
|
+
| `defaultCurrency` | `string` | `'USD'` | Default currency code |
|
|
92
|
+
| `clearCartOnStart` | `boolean` | `false` | Clear cart state on initialization |
|
|
93
|
+
| `cartProvider` | `CartProvider` | `undefined` | Custom cart provider for headless setups |
|
|
94
|
+
| `onReady` | `() => void` | `undefined` | Callback when widget is ready |
|
|
95
|
+
| `onError` | `(error: Error) => void` | `undefined` | Callback on initialization error |
|
|
96
|
+
|
|
97
|
+
### Supported Locales
|
|
98
|
+
|
|
99
|
+
The widget includes translations for:
|
|
100
|
+
|
|
101
|
+
- `da` - Danish
|
|
102
|
+
- `de` - German
|
|
103
|
+
- `en` - English (default)
|
|
104
|
+
- `es` - Spanish
|
|
105
|
+
- `fi` - Finnish
|
|
106
|
+
- `fr` - French
|
|
107
|
+
- `it` - Italian
|
|
108
|
+
- `nb` - Norwegian Bokmål
|
|
109
|
+
- `nl` - Dutch
|
|
110
|
+
- `pl` - Polish
|
|
111
|
+
- `sv` - Swedish
|
|
112
|
+
|
|
113
|
+
## API Reference
|
|
114
|
+
|
|
115
|
+
### Initialization
|
|
116
|
+
|
|
117
|
+
#### `Returnflows.init(config?)`
|
|
118
|
+
|
|
119
|
+
Initialize the widget. Returns a Promise that resolves when the widget is ready.
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
await Returnflows.init({
|
|
123
|
+
defaultLocale: "en",
|
|
124
|
+
defaultCurrency: "USD",
|
|
125
|
+
onReady: () => console.log("Widget initialized"),
|
|
126
|
+
onError: error => console.error("Init failed:", error)
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
#### `Returnflows.destroy()`
|
|
131
|
+
|
|
132
|
+
Destroy the widget instance and clean up resources.
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
Returnflows.destroy();
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
#### `Returnflows.isInitialized()`
|
|
139
|
+
|
|
140
|
+
Check if the widget is currently initialized.
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
if (Returnflows.isInitialized()) {
|
|
144
|
+
Returnflows.drawer.open();
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
#### `Returnflows.version`
|
|
149
|
+
|
|
150
|
+
Get the current widget version.
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
console.log(Returnflows.version); // "1.0.0"
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Drawer API
|
|
157
|
+
|
|
158
|
+
Control the widget drawer visibility.
|
|
159
|
+
|
|
160
|
+
#### `Returnflows.drawer.open()`
|
|
161
|
+
|
|
162
|
+
Open the drawer.
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
Returnflows.drawer.open();
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
#### `Returnflows.drawer.close()`
|
|
169
|
+
|
|
170
|
+
Close the drawer.
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
Returnflows.drawer.close();
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### `Returnflows.drawer.toggle()`
|
|
177
|
+
|
|
178
|
+
Toggle the drawer open/closed state.
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
Returnflows.drawer.toggle();
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
#### `Returnflows.drawer.isOpen()`
|
|
185
|
+
|
|
186
|
+
Check if the drawer is currently open.
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
if (Returnflows.drawer.isOpen()) {
|
|
190
|
+
console.log("Drawer is open");
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Cart API
|
|
195
|
+
|
|
196
|
+
Manage the cart state programmatically.
|
|
197
|
+
|
|
198
|
+
#### `Returnflows.cart.setCart(cart)`
|
|
199
|
+
|
|
200
|
+
Set the cart data. Accepts both Shopify AJAX API format and Storefront API format.
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
// Shopify AJAX API format
|
|
204
|
+
const result = await Returnflows.cart.setCart({
|
|
205
|
+
items: [
|
|
206
|
+
{
|
|
207
|
+
variant_id: 12345,
|
|
208
|
+
quantity: 1,
|
|
209
|
+
title: "Product Name",
|
|
210
|
+
price: 2999,
|
|
211
|
+
product_id: 67890,
|
|
212
|
+
product_title: "Product Name",
|
|
213
|
+
handle: "product-handle",
|
|
214
|
+
image: "https://...",
|
|
215
|
+
grams: 500,
|
|
216
|
+
line_price: 2999,
|
|
217
|
+
final_price: 2999,
|
|
218
|
+
final_line_price: 2999
|
|
219
|
+
}
|
|
220
|
+
],
|
|
221
|
+
item_count: 1
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
console.log(result.hasChanged); // true if cart was updated
|
|
225
|
+
console.log(result.cart); // normalized cart data
|
|
226
|
+
console.log(result.calculation); // return calculation
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Returns: `Promise<{ cart: ShopifyCart | null; calculation: CalculationResponse | null; hasChanged: boolean }>`
|
|
230
|
+
|
|
231
|
+
#### `Returnflows.cart.refetch()`
|
|
232
|
+
|
|
233
|
+
Refetch cart data from the cart provider or default source.
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
await Returnflows.cart.refetch();
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
#### `Returnflows.cart.clear()`
|
|
240
|
+
|
|
241
|
+
Clear the current cart.
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
await Returnflows.cart.clear();
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Data API
|
|
248
|
+
|
|
249
|
+
Access widget state data.
|
|
250
|
+
|
|
251
|
+
#### `Returnflows.data.getCart()`
|
|
252
|
+
|
|
253
|
+
Get the current cart data.
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
const cart = Returnflows.data.getCart();
|
|
257
|
+
// Returns ShopifyCart | null
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
#### `Returnflows.data.getCalculation()`
|
|
261
|
+
|
|
262
|
+
Get the current return calculation.
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
const calculation = Returnflows.data.getCalculation();
|
|
266
|
+
// Returns CalculationResponse | null
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
#### `Returnflows.data.getOrder()`
|
|
270
|
+
|
|
271
|
+
Get the current order data.
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
const order = Returnflows.data.getOrder();
|
|
275
|
+
// Returns OrderData | null
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
#### `Returnflows.data.isLoading()`
|
|
279
|
+
|
|
280
|
+
Check if data is currently loading.
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
if (Returnflows.data.isLoading()) {
|
|
284
|
+
console.log("Loading...");
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Toast API
|
|
289
|
+
|
|
290
|
+
Display toast notifications within the widget.
|
|
291
|
+
|
|
292
|
+
#### `Returnflows.toast.show(options)`
|
|
293
|
+
|
|
294
|
+
Show a toast notification. Returns the toast ID.
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
const toastId = Returnflows.toast.show({
|
|
298
|
+
message: "Item added successfully",
|
|
299
|
+
tone: "info", // 'info' | 'tip' | 'action' | 'error' | 'giftcard'
|
|
300
|
+
duration: 5000, // auto-dismiss after 5 seconds
|
|
301
|
+
onClose: () => console.log("Toast closed")
|
|
302
|
+
});
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
#### `Returnflows.toast.dismiss(id)`
|
|
306
|
+
|
|
307
|
+
Dismiss a specific toast by ID.
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
Returnflows.toast.dismiss(toastId);
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
#### `Returnflows.toast.clear()`
|
|
314
|
+
|
|
315
|
+
Clear all toasts.
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
Returnflows.toast.clear();
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### Events
|
|
322
|
+
|
|
323
|
+
Subscribe to widget events.
|
|
324
|
+
|
|
325
|
+
#### `Returnflows.on(event, callback)`
|
|
326
|
+
|
|
327
|
+
Subscribe to an event. Returns an unsubscribe function.
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
const unsubscribe = Returnflows.on("drawer:open", () => {
|
|
331
|
+
console.log("Drawer opened");
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
// Later: unsubscribe
|
|
335
|
+
unsubscribe();
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
#### `Returnflows.off(event, callback)`
|
|
339
|
+
|
|
340
|
+
Unsubscribe from an event.
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
const handler = () => console.log("Cart updated");
|
|
344
|
+
Returnflows.on("cart:updated", handler);
|
|
345
|
+
|
|
346
|
+
// Later: unsubscribe
|
|
347
|
+
Returnflows.off("cart:updated", handler);
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
#### Available Events
|
|
351
|
+
|
|
352
|
+
| Event | Callback Type | Description |
|
|
353
|
+
| --------------------- | -------------------------------------------- | ------------------------------- |
|
|
354
|
+
| `ready` | `() => void` | Widget is initialized and ready |
|
|
355
|
+
| `drawer:open` | `() => void` | Drawer was opened |
|
|
356
|
+
| `drawer:close` | `() => void` | Drawer was closed |
|
|
357
|
+
| `cart:updated` | `(cart: ShopifyCart) => void` | Cart data was updated |
|
|
358
|
+
| `calculation:updated` | `(calculation: CalculationResponse) => void` | Return calculation was updated |
|
|
359
|
+
| `error` | `(error: Error) => void` | An error occurred |
|
|
360
|
+
|
|
361
|
+
## Cart Providers
|
|
362
|
+
|
|
363
|
+
For headless implementations, you can provide a custom cart provider to manage cart operations.
|
|
364
|
+
|
|
365
|
+
### `CartProvider` Interface
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
interface CartProvider<T extends CartInput = CartInput> {
|
|
369
|
+
getCart(): Promise<T | undefined>;
|
|
370
|
+
clearCart(): Promise<T>;
|
|
371
|
+
addItems(items: CartAddItem[]): Promise<T>;
|
|
372
|
+
updateItems(items: CartUpdateItem[]): Promise<T>;
|
|
373
|
+
removeItems(lineIds: string[]): Promise<T>;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
interface CartAddItem {
|
|
377
|
+
merchandiseId: string;
|
|
378
|
+
quantity: number;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
interface CartUpdateItem {
|
|
382
|
+
id: string;
|
|
383
|
+
merchandiseId: string;
|
|
384
|
+
quantity: number;
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Example: Custom Cart Provider
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
import {
|
|
392
|
+
Returnflows,
|
|
393
|
+
CartProvider,
|
|
394
|
+
StorefrontCart
|
|
395
|
+
} from "@returnflows/shop-overlay";
|
|
396
|
+
|
|
397
|
+
const cartProvider: CartProvider<StorefrontCart> = {
|
|
398
|
+
async getCart() {
|
|
399
|
+
const response = await fetch("/api/cart");
|
|
400
|
+
return response.json();
|
|
401
|
+
},
|
|
402
|
+
|
|
403
|
+
async clearCart() {
|
|
404
|
+
const response = await fetch("/api/cart", { method: "DELETE" });
|
|
405
|
+
return response.json();
|
|
406
|
+
},
|
|
407
|
+
|
|
408
|
+
async addItems(items) {
|
|
409
|
+
const response = await fetch("/api/cart/add", {
|
|
410
|
+
method: "POST",
|
|
411
|
+
body: JSON.stringify({ items })
|
|
412
|
+
});
|
|
413
|
+
return response.json();
|
|
414
|
+
},
|
|
415
|
+
|
|
416
|
+
async updateItems(items) {
|
|
417
|
+
const response = await fetch("/api/cart/update", {
|
|
418
|
+
method: "POST",
|
|
419
|
+
body: JSON.stringify({ items })
|
|
420
|
+
});
|
|
421
|
+
return response.json();
|
|
422
|
+
},
|
|
423
|
+
|
|
424
|
+
async removeItems(lineIds) {
|
|
425
|
+
const response = await fetch("/api/cart/remove", {
|
|
426
|
+
method: "POST",
|
|
427
|
+
body: JSON.stringify({ lineIds })
|
|
428
|
+
});
|
|
429
|
+
return response.json();
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
await Returnflows.init({ cartProvider });
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
## Storefront API Support
|
|
437
|
+
|
|
438
|
+
The widget automatically normalizes Shopify Storefront API cart format to the internal format. You can pass either format to `setCart()` or return it from your `CartProvider`.
|
|
439
|
+
|
|
440
|
+
### Storefront API Cart Format
|
|
441
|
+
|
|
442
|
+
```typescript
|
|
443
|
+
interface StorefrontCart {
|
|
444
|
+
id?: string;
|
|
445
|
+
totalQuantity?: number;
|
|
446
|
+
lines: StorefrontCartLine[] | { edges: Array<{ node: StorefrontCartLine }> };
|
|
447
|
+
checkoutUrl?: string;
|
|
448
|
+
note?: string;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
interface StorefrontCartLine {
|
|
452
|
+
id?: string;
|
|
453
|
+
quantity: number;
|
|
454
|
+
cost?: {
|
|
455
|
+
totalAmount: { amount: string; currencyCode: string };
|
|
456
|
+
amountPerQuantity?: { amount: string; currencyCode: string };
|
|
457
|
+
};
|
|
458
|
+
merchandise: {
|
|
459
|
+
id: string; // GID format: "gid://shopify/ProductVariant/12345"
|
|
460
|
+
title?: string;
|
|
461
|
+
price?: { amount: string; currencyCode: string };
|
|
462
|
+
image?: { url: string; altText?: string } | null;
|
|
463
|
+
product: {
|
|
464
|
+
id: string;
|
|
465
|
+
handle: string;
|
|
466
|
+
title: string;
|
|
467
|
+
vendor?: string;
|
|
468
|
+
};
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
### Using Storefront API Cart
|
|
474
|
+
|
|
475
|
+
```typescript
|
|
476
|
+
// Storefront API cart (GraphQL format)
|
|
477
|
+
const storefrontCart = {
|
|
478
|
+
id: "gid://shopify/Cart/abc123",
|
|
479
|
+
totalQuantity: 2,
|
|
480
|
+
lines: {
|
|
481
|
+
edges: [
|
|
482
|
+
{
|
|
483
|
+
node: {
|
|
484
|
+
id: "gid://shopify/CartLine/xyz",
|
|
485
|
+
quantity: 2,
|
|
486
|
+
cost: {
|
|
487
|
+
totalAmount: { amount: "59.98", currencyCode: "USD" },
|
|
488
|
+
amountPerQuantity: { amount: "29.99", currencyCode: "USD" }
|
|
489
|
+
},
|
|
490
|
+
merchandise: {
|
|
491
|
+
id: "gid://shopify/ProductVariant/12345",
|
|
492
|
+
title: "Medium / Blue",
|
|
493
|
+
product: {
|
|
494
|
+
id: "gid://shopify/Product/67890",
|
|
495
|
+
handle: "t-shirt",
|
|
496
|
+
title: "Classic T-Shirt"
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
]
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
// Pass directly - widget handles normalization
|
|
506
|
+
await Returnflows.cart.setCart(storefrontCart);
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
## Examples
|
|
510
|
+
|
|
511
|
+
### Vanilla JavaScript
|
|
512
|
+
|
|
513
|
+
#### Basic Integration
|
|
514
|
+
|
|
515
|
+
```html
|
|
516
|
+
<!DOCTYPE html>
|
|
517
|
+
<html>
|
|
518
|
+
<head>
|
|
519
|
+
<title>My Store</title>
|
|
520
|
+
</head>
|
|
521
|
+
<body>
|
|
522
|
+
<button id="returns-btn">Start Return</button>
|
|
523
|
+
|
|
524
|
+
<script type="module">
|
|
525
|
+
import { Returnflows } from "@returnflows/shop-overlay";
|
|
526
|
+
|
|
527
|
+
// Initialize when DOM is ready
|
|
528
|
+
document.addEventListener("DOMContentLoaded", async () => {
|
|
529
|
+
try {
|
|
530
|
+
await Returnflows.init({
|
|
531
|
+
defaultLocale: "en",
|
|
532
|
+
defaultCurrency: "USD",
|
|
533
|
+
onReady: () => {
|
|
534
|
+
document.getElementById("returns-btn").disabled = false;
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
// Listen for events
|
|
539
|
+
Returnflows.on("drawer:open", () => {
|
|
540
|
+
document.body.style.overflow = "hidden";
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
Returnflows.on("drawer:close", () => {
|
|
544
|
+
document.body.style.overflow = "";
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
Returnflows.on("calculation:updated", calc => {
|
|
548
|
+
console.log("Return value:", calc.total);
|
|
549
|
+
});
|
|
550
|
+
} catch (error) {
|
|
551
|
+
console.error("Failed to initialize:", error);
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
// Button handler
|
|
556
|
+
document.getElementById("returns-btn").addEventListener("click", () => {
|
|
557
|
+
Returnflows.drawer.toggle();
|
|
558
|
+
});
|
|
559
|
+
</script>
|
|
560
|
+
</body>
|
|
561
|
+
</html>
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
#### With Cart Provider (Headless)
|
|
565
|
+
|
|
566
|
+
```javascript
|
|
567
|
+
import { Returnflows } from "@returnflows/shop-overlay";
|
|
568
|
+
|
|
569
|
+
// Shopify Storefront API client
|
|
570
|
+
const storefrontClient = {
|
|
571
|
+
async query(query, variables) {
|
|
572
|
+
const response = await fetch("/api/storefront", {
|
|
573
|
+
method: "POST",
|
|
574
|
+
headers: { "Content-Type": "application/json" },
|
|
575
|
+
body: JSON.stringify({ query, variables })
|
|
576
|
+
});
|
|
577
|
+
return response.json();
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
// Cart provider using Storefront API
|
|
582
|
+
const cartProvider = {
|
|
583
|
+
async getCart() {
|
|
584
|
+
const { data } = await storefrontClient.query(
|
|
585
|
+
`
|
|
586
|
+
query GetCart($cartId: ID!) {
|
|
587
|
+
cart(id: $cartId) {
|
|
588
|
+
id
|
|
589
|
+
totalQuantity
|
|
590
|
+
lines(first: 100) {
|
|
591
|
+
edges {
|
|
592
|
+
node {
|
|
593
|
+
id
|
|
594
|
+
quantity
|
|
595
|
+
cost {
|
|
596
|
+
totalAmount { amount currencyCode }
|
|
597
|
+
amountPerQuantity { amount currencyCode }
|
|
598
|
+
}
|
|
599
|
+
merchandise {
|
|
600
|
+
... on ProductVariant {
|
|
601
|
+
id
|
|
602
|
+
title
|
|
603
|
+
image { url altText }
|
|
604
|
+
product {
|
|
605
|
+
id
|
|
606
|
+
handle
|
|
607
|
+
title
|
|
608
|
+
vendor
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
`,
|
|
618
|
+
{ cartId: localStorage.getItem("cartId") }
|
|
619
|
+
);
|
|
620
|
+
|
|
621
|
+
return data?.cart;
|
|
622
|
+
},
|
|
623
|
+
|
|
624
|
+
async addItems(items) {
|
|
625
|
+
const lines = items.map(item => ({
|
|
626
|
+
merchandiseId: item.merchandiseId,
|
|
627
|
+
quantity: item.quantity
|
|
628
|
+
}));
|
|
629
|
+
|
|
630
|
+
const { data } = await storefrontClient.query(
|
|
631
|
+
`
|
|
632
|
+
mutation AddToCart($cartId: ID!, $lines: [CartLineInput!]!) {
|
|
633
|
+
cartLinesAdd(cartId: $cartId, lines: $lines) {
|
|
634
|
+
cart { id totalQuantity lines(first: 100) { edges { node { id quantity } } } }
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
`,
|
|
638
|
+
{ cartId: localStorage.getItem("cartId"), lines }
|
|
639
|
+
);
|
|
640
|
+
|
|
641
|
+
return data.cartLinesAdd.cart;
|
|
642
|
+
},
|
|
643
|
+
|
|
644
|
+
async updateItems(items) {
|
|
645
|
+
const lines = items.map(item => ({
|
|
646
|
+
id: item.id,
|
|
647
|
+
merchandiseId: item.merchandiseId,
|
|
648
|
+
quantity: item.quantity
|
|
649
|
+
}));
|
|
650
|
+
|
|
651
|
+
const { data } = await storefrontClient.query(
|
|
652
|
+
`
|
|
653
|
+
mutation UpdateCart($cartId: ID!, $lines: [CartLineUpdateInput!]!) {
|
|
654
|
+
cartLinesUpdate(cartId: $cartId, lines: $lines) {
|
|
655
|
+
cart { id totalQuantity lines(first: 100) { edges { node { id quantity } } } }
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
`,
|
|
659
|
+
{ cartId: localStorage.getItem("cartId"), lines }
|
|
660
|
+
);
|
|
661
|
+
|
|
662
|
+
return data.cartLinesUpdate.cart;
|
|
663
|
+
},
|
|
664
|
+
|
|
665
|
+
async removeItems(lineIds) {
|
|
666
|
+
const { data } = await storefrontClient.query(
|
|
667
|
+
`
|
|
668
|
+
mutation RemoveFromCart($cartId: ID!, $lineIds: [ID!]!) {
|
|
669
|
+
cartLinesRemove(cartId: $cartId, lineIds: $lineIds) {
|
|
670
|
+
cart { id totalQuantity lines(first: 100) { edges { node { id quantity } } } }
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
`,
|
|
674
|
+
{ cartId: localStorage.getItem("cartId"), lineIds }
|
|
675
|
+
);
|
|
676
|
+
|
|
677
|
+
return data.cartLinesRemove.cart;
|
|
678
|
+
},
|
|
679
|
+
|
|
680
|
+
async clearCart() {
|
|
681
|
+
// Create a new cart
|
|
682
|
+
const { data } = await storefrontClient.query(`
|
|
683
|
+
mutation CreateCart {
|
|
684
|
+
cartCreate { cart { id } }
|
|
685
|
+
}
|
|
686
|
+
`);
|
|
687
|
+
localStorage.setItem("cartId", data.cartCreate.cart.id);
|
|
688
|
+
return data.cartCreate.cart;
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
|
|
692
|
+
// Initialize with cart provider
|
|
693
|
+
await Returnflows.init({
|
|
694
|
+
cartProvider,
|
|
695
|
+
onReady: () => console.log("Widget ready with headless cart")
|
|
696
|
+
});
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
### React
|
|
700
|
+
|
|
701
|
+
#### Basic Hook
|
|
702
|
+
|
|
703
|
+
```tsx
|
|
704
|
+
import { useEffect, useCallback, useState } from "react";
|
|
705
|
+
import {
|
|
706
|
+
Returnflows,
|
|
707
|
+
type ShopifyCart,
|
|
708
|
+
type CalculationResponse
|
|
709
|
+
} from "@returnflows/shop-overlay";
|
|
710
|
+
|
|
711
|
+
function useReturnflows() {
|
|
712
|
+
const [isReady, setIsReady] = useState(false);
|
|
713
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
714
|
+
const [cart, setCart] = useState<ShopifyCart | null>(null);
|
|
715
|
+
const [calculation, setCalculation] = useState<CalculationResponse | null>(
|
|
716
|
+
null
|
|
717
|
+
);
|
|
718
|
+
|
|
719
|
+
useEffect(() => {
|
|
720
|
+
let mounted = true;
|
|
721
|
+
|
|
722
|
+
const init = async () => {
|
|
723
|
+
try {
|
|
724
|
+
await Returnflows.init({
|
|
725
|
+
onReady: () => {
|
|
726
|
+
if (mounted) setIsReady(true);
|
|
727
|
+
}
|
|
728
|
+
});
|
|
729
|
+
} catch (error) {
|
|
730
|
+
console.error("Returnflows init failed:", error);
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
|
|
734
|
+
init();
|
|
735
|
+
|
|
736
|
+
// Subscribe to events
|
|
737
|
+
const unsubOpen = Returnflows.on("drawer:open", () => setIsOpen(true));
|
|
738
|
+
const unsubClose = Returnflows.on("drawer:close", () => setIsOpen(false));
|
|
739
|
+
const unsubCart = Returnflows.on("cart:updated", setCart);
|
|
740
|
+
const unsubCalc = Returnflows.on("calculation:updated", setCalculation);
|
|
741
|
+
|
|
742
|
+
return () => {
|
|
743
|
+
mounted = false;
|
|
744
|
+
unsubOpen();
|
|
745
|
+
unsubClose();
|
|
746
|
+
unsubCart();
|
|
747
|
+
unsubCalc();
|
|
748
|
+
};
|
|
749
|
+
}, []);
|
|
750
|
+
|
|
751
|
+
const openDrawer = useCallback(() => Returnflows.drawer.open(), []);
|
|
752
|
+
const closeDrawer = useCallback(() => Returnflows.drawer.close(), []);
|
|
753
|
+
const toggleDrawer = useCallback(() => Returnflows.drawer.toggle(), []);
|
|
754
|
+
|
|
755
|
+
return {
|
|
756
|
+
isReady,
|
|
757
|
+
isOpen,
|
|
758
|
+
cart,
|
|
759
|
+
calculation,
|
|
760
|
+
openDrawer,
|
|
761
|
+
closeDrawer,
|
|
762
|
+
toggleDrawer
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
export default useReturnflows;
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
#### React Component Example
|
|
770
|
+
|
|
771
|
+
```tsx
|
|
772
|
+
import { useEffect, useState } from "react";
|
|
773
|
+
import { Returnflows } from "@returnflows/shop-overlay";
|
|
774
|
+
import type {
|
|
775
|
+
ShopifyCart,
|
|
776
|
+
CalculationResponse
|
|
777
|
+
} from "@returnflows/shop-overlay";
|
|
778
|
+
|
|
779
|
+
function ReturnflowsProvider({ children }: { children: React.ReactNode }) {
|
|
780
|
+
const [initialized, setInitialized] = useState(false);
|
|
781
|
+
|
|
782
|
+
useEffect(() => {
|
|
783
|
+
Returnflows.init({
|
|
784
|
+
defaultLocale: "en",
|
|
785
|
+
defaultCurrency: "USD",
|
|
786
|
+
onReady: () => setInitialized(true),
|
|
787
|
+
onError: error => console.error("Widget error:", error)
|
|
788
|
+
});
|
|
789
|
+
}, []);
|
|
790
|
+
|
|
791
|
+
if (!initialized) {
|
|
792
|
+
return <div>Loading returns widget...</div>;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
return <>{children}</>;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
function ReturnsButton() {
|
|
799
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
800
|
+
|
|
801
|
+
useEffect(() => {
|
|
802
|
+
const unsubOpen = Returnflows.on("drawer:open", () => setIsOpen(true));
|
|
803
|
+
const unsubClose = Returnflows.on("drawer:close", () => setIsOpen(false));
|
|
804
|
+
|
|
805
|
+
return () => {
|
|
806
|
+
unsubOpen();
|
|
807
|
+
unsubClose();
|
|
808
|
+
};
|
|
809
|
+
}, []);
|
|
810
|
+
|
|
811
|
+
return (
|
|
812
|
+
<button onClick={() => Returnflows.drawer.toggle()} aria-expanded={isOpen}>
|
|
813
|
+
{isOpen ? "Close Returns" : "Start Return"}
|
|
814
|
+
</button>
|
|
815
|
+
);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
function ReturnsSummary() {
|
|
819
|
+
const [calculation, setCalculation] = useState<CalculationResponse | null>(
|
|
820
|
+
null
|
|
821
|
+
);
|
|
822
|
+
|
|
823
|
+
useEffect(() => {
|
|
824
|
+
// Get initial calculation
|
|
825
|
+
setCalculation(Returnflows.data.getCalculation());
|
|
826
|
+
|
|
827
|
+
// Subscribe to updates
|
|
828
|
+
const unsub = Returnflows.on("calculation:updated", setCalculation);
|
|
829
|
+
return unsub;
|
|
830
|
+
}, []);
|
|
831
|
+
|
|
832
|
+
if (!calculation) return null;
|
|
833
|
+
|
|
834
|
+
return (
|
|
835
|
+
<div>
|
|
836
|
+
<p>
|
|
837
|
+
Return Value: {calculation.currency.localCurrency}{" "}
|
|
838
|
+
{(calculation.total / 100).toFixed(2)}
|
|
839
|
+
</p>
|
|
840
|
+
<p>Items: {calculation.lineItems.length}</p>
|
|
841
|
+
</div>
|
|
842
|
+
);
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// Usage
|
|
846
|
+
function App() {
|
|
847
|
+
return (
|
|
848
|
+
<ReturnflowsProvider>
|
|
849
|
+
<header>
|
|
850
|
+
<ReturnsButton />
|
|
851
|
+
</header>
|
|
852
|
+
<main>
|
|
853
|
+
<ReturnsSummary />
|
|
854
|
+
</main>
|
|
855
|
+
</ReturnflowsProvider>
|
|
856
|
+
);
|
|
857
|
+
}
|
|
858
|
+
```
|
|
859
|
+
|
|
860
|
+
## TypeScript Support
|
|
861
|
+
|
|
862
|
+
The package includes full TypeScript definitions. All types are exported from the main entry point:
|
|
863
|
+
|
|
864
|
+
```typescript
|
|
865
|
+
import {
|
|
866
|
+
// API Types
|
|
867
|
+
Returnflows,
|
|
868
|
+
ReturnflowsAPI,
|
|
869
|
+
ReturnflowsConfig,
|
|
870
|
+
|
|
871
|
+
// Cart Types
|
|
872
|
+
CartInput,
|
|
873
|
+
CartProvider,
|
|
874
|
+
CartAddItem,
|
|
875
|
+
CartUpdateItem,
|
|
876
|
+
ShopifyCart,
|
|
877
|
+
ShopifyCartItem,
|
|
878
|
+
StorefrontCart,
|
|
879
|
+
StorefrontCartLine,
|
|
880
|
+
|
|
881
|
+
// Data Types
|
|
882
|
+
CalculationResponse,
|
|
883
|
+
OrderData,
|
|
884
|
+
|
|
885
|
+
// API Interfaces
|
|
886
|
+
DrawerAPI,
|
|
887
|
+
CartAPI,
|
|
888
|
+
DataAPI,
|
|
889
|
+
ToastAPI,
|
|
890
|
+
ToastOptions,
|
|
891
|
+
ToastTone,
|
|
892
|
+
|
|
893
|
+
// Events
|
|
894
|
+
EventName,
|
|
895
|
+
EventCallback,
|
|
896
|
+
|
|
897
|
+
// Other
|
|
898
|
+
Environment,
|
|
899
|
+
ThemeVariables,
|
|
900
|
+
WidgetViewState
|
|
901
|
+
} from "@returnflows/shop-overlay";
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
## Troubleshooting
|
|
905
|
+
|
|
906
|
+
### Widget not initializing
|
|
907
|
+
|
|
908
|
+
Ensure you're calling `init()` after the DOM is ready:
|
|
909
|
+
|
|
910
|
+
```javascript
|
|
911
|
+
// Vanilla JS
|
|
912
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
913
|
+
Returnflows.init();
|
|
914
|
+
});
|
|
915
|
+
|
|
916
|
+
// React - use useEffect
|
|
917
|
+
useEffect(() => {
|
|
918
|
+
Returnflows.init();
|
|
919
|
+
}, []);
|
|
920
|
+
```
|
|
921
|
+
|
|
922
|
+
### "Widget not initialized" errors
|
|
923
|
+
|
|
924
|
+
Always check `isInitialized()` before calling API methods:
|
|
925
|
+
|
|
926
|
+
```javascript
|
|
927
|
+
if (Returnflows.isInitialized()) {
|
|
928
|
+
Returnflows.drawer.open();
|
|
929
|
+
} else {
|
|
930
|
+
console.warn("Widget not ready yet");
|
|
931
|
+
}
|
|
932
|
+
```
|
|
933
|
+
|
|
934
|
+
### Cart not updating
|
|
935
|
+
|
|
936
|
+
If using a custom cart provider, ensure all methods return the updated cart:
|
|
937
|
+
|
|
938
|
+
```javascript
|
|
939
|
+
const cartProvider = {
|
|
940
|
+
async addItems(items) {
|
|
941
|
+
await fetch('/api/cart/add', { ... });
|
|
942
|
+
// Must return the updated cart!
|
|
943
|
+
return this.getCart();
|
|
944
|
+
}
|
|
945
|
+
};
|
|
946
|
+
```
|
|
947
|
+
|
|
948
|
+
### Storefront API cart not recognized
|
|
949
|
+
|
|
950
|
+
Ensure your cart has the `lines` property with the correct structure:
|
|
951
|
+
|
|
952
|
+
```javascript
|
|
953
|
+
// Correct - has lines array or edges
|
|
954
|
+
{ lines: [...] }
|
|
955
|
+
{ lines: { edges: [...] } }
|
|
956
|
+
|
|
957
|
+
// Incorrect - using items instead of lines
|
|
958
|
+
{ items: [...] } // This is AJAX API format
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
### Multiple instances
|
|
962
|
+
|
|
963
|
+
Only one widget instance can exist. Call `destroy()` before re-initializing:
|
|
964
|
+
|
|
965
|
+
```javascript
|
|
966
|
+
Returnflows.destroy();
|
|
967
|
+
await Returnflows.init({
|
|
968
|
+
/* new config */
|
|
969
|
+
});
|
|
970
|
+
```
|
|
971
|
+
|
|
972
|
+
---
|
|
973
|
+
|
|
974
|
+
## License
|
|
975
|
+
|
|
976
|
+
Proprietary - Returnflows ApS
|