shopmate-sdk 1.1.0 → 1.1.2
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 +138 -75
- package/dist/index.cjs +360 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.min.cjs +10 -1
- package/dist/index.min.mjs +10 -1
- package/dist/index.mjs +340 -1
- package/package.json +1 -1
- package/shopmate-sdk.js +362 -1
- package/shopmate-sdk.min.js +10 -1
package/README.md
CHANGED
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
Lightweight checkout/cart SDK for browser storefronts.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Use it to:
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
---
|
|
7
|
+
- add products to a local cart from any storefront button
|
|
8
|
+
- open the ShopMate checkout in a full-screen iframe
|
|
9
|
+
- support both code-based and HTML attribute-based integrations
|
|
12
10
|
|
|
13
11
|
## Installation
|
|
14
12
|
|
|
@@ -16,25 +14,22 @@ It supports:
|
|
|
16
14
|
npm install shopmate-sdk
|
|
17
15
|
```
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
## Usage
|
|
17
|
+
## Quick start
|
|
22
18
|
|
|
23
|
-
### 1)
|
|
19
|
+
### 1) Import in a JavaScript or TypeScript app
|
|
24
20
|
|
|
25
21
|
```js
|
|
26
22
|
import ShopMate from 'shopmate-sdk';
|
|
27
23
|
|
|
28
24
|
const sdk = new ShopMate({
|
|
29
25
|
orgId: 2,
|
|
30
|
-
|
|
31
|
-
cartPosition: 'right-bottom'
|
|
26
|
+
cartPosition: 'right-bottom' // required: where to show the floating cart
|
|
32
27
|
});
|
|
33
28
|
|
|
34
29
|
sdk.extract();
|
|
35
30
|
```
|
|
36
31
|
|
|
37
|
-
### 2) CommonJS
|
|
32
|
+
### 2) Use CommonJS
|
|
38
33
|
|
|
39
34
|
```js
|
|
40
35
|
const ShopMate = require('shopmate-sdk').default;
|
|
@@ -43,38 +38,88 @@ const sdk = new ShopMate({ orgId: 2 });
|
|
|
43
38
|
sdk.extract();
|
|
44
39
|
```
|
|
45
40
|
|
|
46
|
-
### 3)
|
|
41
|
+
### 3) Use a plain script tag
|
|
42
|
+
|
|
43
|
+
```html
|
|
44
|
+
<script
|
|
45
|
+
src="https://cdn.jsdelivr.net/npm/shopmate-sdk@latest/shopmate-sdk.min.js"
|
|
46
|
+
data-org-id="2"
|
|
47
|
+
data-cart-position="right-bottom"
|
|
48
|
+
></script>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
When loaded via `<script src="...">`, the SDK auto-initializes `window.ShopMateInstance` and wires supported selectors once the page finishes loading.
|
|
52
|
+
|
|
53
|
+
## Copy-paste storefront example
|
|
54
|
+
|
|
55
|
+
Use this if you want the smallest possible integration:
|
|
47
56
|
|
|
48
57
|
```html
|
|
49
|
-
<
|
|
50
|
-
|
|
58
|
+
<button data-shopmate-product-id="101" data-shopmate-quantity="1">
|
|
59
|
+
Add to cart
|
|
60
|
+
</button>
|
|
61
|
+
|
|
62
|
+
<button data-shopmate-checkout="true">
|
|
63
|
+
Checkout
|
|
64
|
+
</button>
|
|
65
|
+
|
|
66
|
+
<script
|
|
67
|
+
src="https://cdn.jsdelivr.net/npm/shopmate-sdk@latest/shopmate-sdk.min.js"
|
|
51
68
|
data-org-id="2"
|
|
52
|
-
data-base-url="https://shopmate.hoshonto.com"
|
|
53
69
|
data-cart-position="right-bottom"
|
|
54
70
|
></script>
|
|
55
|
-
<script>
|
|
56
|
-
// Available globally
|
|
57
|
-
const sdk = new window.ShopMate({ orgId: 2 });
|
|
58
|
-
sdk.extract();
|
|
59
|
-
</script>
|
|
60
71
|
```
|
|
61
72
|
|
|
62
|
-
|
|
73
|
+
If you use the browser bundle directly, the SDK will scan the page automatically after load. If you import it in your app, call `sdk.extract()` after rendering the markup.
|
|
63
74
|
|
|
64
|
-
|
|
65
|
-
- `data-org-id`: Organization ID (required for auto-init)
|
|
66
|
-
- `data-base-url`: Custom base URL (optional)
|
|
67
|
-
- `data-cart-position`: Cart position on screen (optional)
|
|
75
|
+
## How to use
|
|
68
76
|
|
|
69
|
-
|
|
77
|
+
Create an instance, then call `extract()` on the container you want ShopMate to scan:
|
|
78
|
+
|
|
79
|
+
```js
|
|
80
|
+
const sdk = new ShopMate({ orgId: 2 });
|
|
81
|
+
sdk.extract();
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The SDK watches for these triggers:
|
|
85
|
+
|
|
86
|
+
- `[data-shopmate-product-id]` — adds the item to the cart
|
|
87
|
+
- `[data-shopmate-checkout="true"]` — opens checkout with a loading state
|
|
88
|
+
- `a[href^=".../api/v1/pub/checkout?"]` — opens checkout from a URL-based trigger
|
|
89
|
+
|
|
90
|
+
## Example markup
|
|
91
|
+
|
|
92
|
+
### Add to cart button
|
|
93
|
+
|
|
94
|
+
```html
|
|
95
|
+
<button data-shopmate-product-id="101" data-shopmate-quantity="1">
|
|
96
|
+
Add to cart
|
|
97
|
+
</button>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Checkout button
|
|
101
|
+
|
|
102
|
+
```html
|
|
103
|
+
<button data-shopmate-checkout="true">
|
|
104
|
+
Checkout
|
|
105
|
+
</button>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### URL-based checkout link
|
|
109
|
+
|
|
110
|
+
```html
|
|
111
|
+
<a href="https://shopmate.example.com/api/v1/pub/checkout?pid=101,102">
|
|
112
|
+
Checkout now
|
|
113
|
+
</a>
|
|
114
|
+
```
|
|
70
115
|
|
|
71
116
|
## Configuration
|
|
72
117
|
|
|
73
118
|
`new ShopMate(config)` accepts:
|
|
74
119
|
|
|
75
|
-
- `orgId` **(number, required)
|
|
76
|
-
- `
|
|
77
|
-
- `
|
|
120
|
+
- `orgId` **(number, required)** — organization ID
|
|
121
|
+
- `cartPosition` **(string, required)** — floating cart position on screen. **Without this, the cart button will not be visible.**
|
|
122
|
+
- `baseUrl` *(string, optional)* — backend base URL. Default: `https://shopmate.hoshonto.com`. Rarely needed unless you're using a custom backend.
|
|
78
123
|
|
|
79
124
|
Allowed `cartPosition` values:
|
|
80
125
|
|
|
@@ -82,83 +127,101 @@ Allowed `cartPosition` values:
|
|
|
82
127
|
- `left-center`, `center`, `right-center`
|
|
83
128
|
- `left-bottom`, `bottom`, `right-bottom`
|
|
84
129
|
|
|
85
|
-
---
|
|
86
|
-
|
|
87
130
|
## Public API
|
|
88
131
|
|
|
89
132
|
### `extract(rootElement?: HTMLElement)`
|
|
90
133
|
|
|
91
|
-
Scans DOM and wires click handlers
|
|
92
|
-
|
|
93
|
-
- `[data-shopmate-product-id]` → add/update cart items
|
|
94
|
-
- `[data-shopmate-checkout="true"]` → open checkout popup
|
|
134
|
+
Scans the DOM and wires click handlers for supported product and checkout elements.
|
|
95
135
|
|
|
96
136
|
### `addToCart(itemId: number, quantity: number, element?: HTMLElement)`
|
|
97
137
|
|
|
98
|
-
Adds
|
|
138
|
+
Adds or updates a cart item and refreshes the floating badge.
|
|
99
139
|
|
|
100
140
|
### `openCheckout(buttonElement?: HTMLElement)`
|
|
101
141
|
|
|
102
|
-
Opens
|
|
142
|
+
Opens the checkout iframe and shows a loading state on the clicked trigger.
|
|
103
143
|
|
|
104
144
|
### `closeCheckout()`
|
|
105
145
|
|
|
106
|
-
Closes checkout iframe and restores page scroll.
|
|
146
|
+
Closes the checkout iframe and restores page scroll.
|
|
107
147
|
|
|
108
148
|
### `createCheckoutIframe()`
|
|
109
149
|
|
|
110
|
-
Creates
|
|
111
|
-
|
|
112
|
-
---
|
|
113
|
-
|
|
114
|
-
## Data attributes supported
|
|
115
|
-
|
|
116
|
-
### Product triggers
|
|
117
|
-
|
|
118
|
-
```html
|
|
119
|
-
<button data-shopmate-product-id="101" data-shopmate-quantity="1">Add</button>
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
### Checkout trigger
|
|
123
|
-
|
|
124
|
-
```html
|
|
125
|
-
<button data-shopmate-checkout="true">Checkout</button>
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
---
|
|
150
|
+
Creates the iframe used for checkout.
|
|
129
151
|
|
|
130
152
|
## Build outputs
|
|
131
153
|
|
|
132
154
|
`npm run build` generates:
|
|
133
155
|
|
|
134
|
-
- `shopmate-sdk.js`
|
|
135
|
-
- `shopmate-sdk.min.js`
|
|
136
|
-
- `dist/index.mjs`
|
|
137
|
-
- `dist/index.min.mjs`
|
|
138
|
-
- `dist/index.cjs`
|
|
139
|
-
- `dist/index.min.cjs`
|
|
140
|
-
- `dist/index.d.ts`
|
|
141
|
-
|
|
142
|
-
---
|
|
156
|
+
- `shopmate-sdk.js` — browser-friendly global bundle
|
|
157
|
+
- `shopmate-sdk.min.js` — minified browser bundle
|
|
158
|
+
- `dist/index.mjs` — ESM build
|
|
159
|
+
- `dist/index.min.mjs` — minified ESM build
|
|
160
|
+
- `dist/index.cjs` — CommonJS build
|
|
161
|
+
- `dist/index.min.cjs` — minified CommonJS build
|
|
162
|
+
- `dist/index.d.ts` — TypeScript declarations
|
|
143
163
|
|
|
144
164
|
## NPM scripts
|
|
145
165
|
|
|
146
166
|
- `npm run build` — build all artifacts
|
|
147
167
|
- `npm run watch` — watch mode build
|
|
148
|
-
- `npm run typecheck` —
|
|
168
|
+
- `npm run typecheck` — TypeScript type-check only
|
|
149
169
|
- `npm test` — build + verify ESM/CJS/browser bundle loading
|
|
150
170
|
|
|
151
|
-
---
|
|
152
|
-
|
|
153
171
|
## Package export map
|
|
154
172
|
|
|
155
173
|
- `shopmate-sdk` → default export (`ShopMate` class)
|
|
156
174
|
- `shopmate-sdk/min` → minified module build
|
|
157
175
|
- `shopmate-sdk/browser` → browser bundle path
|
|
158
176
|
|
|
159
|
-
---
|
|
160
|
-
|
|
161
177
|
## Local demo
|
|
162
178
|
|
|
163
|
-
|
|
164
|
-
|
|
179
|
+
Open `examples/storefront-demo.html` after building to see the SDK in action.
|
|
180
|
+
|
|
181
|
+
## Publishing to npm
|
|
182
|
+
|
|
183
|
+
### Prerequisites
|
|
184
|
+
|
|
185
|
+
- Ensure you have an npm account with publish rights to the `shopmate-sdk` package
|
|
186
|
+
- Be authenticated: `npm login`
|
|
187
|
+
|
|
188
|
+
### Steps
|
|
189
|
+
|
|
190
|
+
1. **Update the version** in `package.json`:
|
|
191
|
+
```bash
|
|
192
|
+
npm version patch # for bug fixes (1.0.0 → 1.0.1)
|
|
193
|
+
npm version minor # for new features (1.0.0 → 1.1.0)
|
|
194
|
+
npm version major # for breaking changes (1.0.0 → 2.0.0)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
2. **Build the package**:
|
|
198
|
+
```bash
|
|
199
|
+
npm run build
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
3. **Verify the build** by checking the `dist/` and browser bundle files are present.
|
|
203
|
+
|
|
204
|
+
4. **Publish to npm**:
|
|
205
|
+
```bash
|
|
206
|
+
npm publish
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
5. **Verify the publication**:
|
|
210
|
+
```bash
|
|
211
|
+
npm view shopmate-sdk
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
The package will be available on jsDelivr immediately at:
|
|
215
|
+
- `https://cdn.jsdelivr.net/npm/shopmate-sdk@latest/shopmate-sdk.min.js`
|
|
216
|
+
- `https://cdn.jsdelivr.net/npm/shopmate-sdk@<version>/shopmate-sdk.min.js`
|
|
217
|
+
|
|
218
|
+
### Publishing checklist
|
|
219
|
+
|
|
220
|
+
- [ ] Version number updated and committed
|
|
221
|
+
- [ ] All tests pass (`npm test`)
|
|
222
|
+
- [ ] Build succeeds without warnings (`npm run build`)
|
|
223
|
+
- [ ] README and documentation are up to date
|
|
224
|
+
- [ ] Changelog entry added (if applicable)
|
|
225
|
+
- [ ] Authentication verified (`npm whoami`)
|
|
226
|
+
- [ ] Package published (`npm publish`)
|
|
227
|
+
- [ ] jsDelivr cache updated (automatic after ~10 minutes)
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1,360 @@
|
|
|
1
|
-
|
|
1
|
+
/* Shopmate SDK */
|
|
2
|
+
"use strict";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/index.ts
|
|
22
|
+
var index_exports = {};
|
|
23
|
+
__export(index_exports, {
|
|
24
|
+
default: () => index_default
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
var ShopMate = class {
|
|
28
|
+
constructor(config) {
|
|
29
|
+
this.baseUrl = "https://shopmate.hoshonto.com";
|
|
30
|
+
this.cartPosition = "right-bottom";
|
|
31
|
+
this.iframeShown = false;
|
|
32
|
+
this.cart = { i: [] };
|
|
33
|
+
this.orgId = config.orgId;
|
|
34
|
+
if (config.baseUrl) this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
35
|
+
if (config.cartPosition) this.cartPosition = config.cartPosition;
|
|
36
|
+
this.checkoutUrl = `${this.baseUrl}/api/v1/pub/checkout`;
|
|
37
|
+
this._injectGlobalStyles();
|
|
38
|
+
this._setupMessageListener();
|
|
39
|
+
}
|
|
40
|
+
_getMountRoot() {
|
|
41
|
+
return document.body || document.documentElement;
|
|
42
|
+
}
|
|
43
|
+
_runWhenBodyReady(fn) {
|
|
44
|
+
if (document.body) {
|
|
45
|
+
fn();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const onReady = () => {
|
|
49
|
+
document.removeEventListener("DOMContentLoaded", onReady);
|
|
50
|
+
fn();
|
|
51
|
+
};
|
|
52
|
+
document.addEventListener("DOMContentLoaded", onReady);
|
|
53
|
+
}
|
|
54
|
+
_injectGlobalStyles() {
|
|
55
|
+
if (document.getElementById("shopmate-global-styles")) return;
|
|
56
|
+
const style = document.createElement("style");
|
|
57
|
+
style.id = "shopmate-global-styles";
|
|
58
|
+
style.innerHTML = `@keyframes sm-spin{from{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes sm-soft-pulse{0%,100%{opacity:.65;transform:scale(1)}50%{opacity:1;transform:scale(1.03)}}.sm-loading-spinner{display:none;width:24px;height:24px;border:3px solid rgba(255,255,255,.28);border-radius:50%;border-top-color:#fff;animation:.8s linear infinite sm-spin}.sm-loading-shell{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:24px;color:#fff;font-weight:600;line-height:1}.sm-loading-text{font-size:13px;letter-spacing:.01em;white-space:nowrap}.sm-btn-loading{position:relative!important;overflow:hidden!important}.sm-btn-loading::before{content:'';position:absolute;inset:0;background:linear-gradient(90deg,rgba(255,255,255,.06),rgba(255,255,255,.16),rgba(255,255,255,.06));animation:1.2s ease-in-out infinite sm-soft-pulse;pointer-events:none}.sm-btn-loading .sm-loading-spinner{display:inline-block}.sm-btn-loading .sm-cart-icon{display:none}.sm-btn-loading .sm-badge{display:none!important}.sm-no-scroll{overflow:hidden!important;touch-action:none;-ms-touch-action:none}`;
|
|
59
|
+
document.head.appendChild(style);
|
|
60
|
+
}
|
|
61
|
+
_setScrollLocked(locked) {
|
|
62
|
+
const action = locked ? "add" : "remove";
|
|
63
|
+
document.body.classList[action]("sm-no-scroll");
|
|
64
|
+
document.documentElement.classList[action]("sm-no-scroll");
|
|
65
|
+
}
|
|
66
|
+
_toggleLoading(button, isLoading) {
|
|
67
|
+
if (!button) return;
|
|
68
|
+
if (isLoading) {
|
|
69
|
+
if (!button.dataset.smOriginalHtml) {
|
|
70
|
+
button.dataset.smOriginalHtml = button.innerHTML;
|
|
71
|
+
}
|
|
72
|
+
const shouldShowText = button.clientWidth >= 100 && button.clientHeight >= 44;
|
|
73
|
+
button.innerHTML = `
|
|
74
|
+
<span class="sm-loading-shell" aria-hidden="true">
|
|
75
|
+
<span class="sm-loading-spinner"></span>
|
|
76
|
+
${shouldShowText ? '<span class="sm-loading-text">Opening…</span>' : ""}
|
|
77
|
+
</span>
|
|
78
|
+
`;
|
|
79
|
+
} else if (button.dataset.smOriginalHtml !== void 0) {
|
|
80
|
+
button.innerHTML = button.dataset.smOriginalHtml;
|
|
81
|
+
delete button.dataset.smOriginalHtml;
|
|
82
|
+
}
|
|
83
|
+
button.style.pointerEvents = isLoading ? "none" : "";
|
|
84
|
+
button.setAttribute("aria-busy", isLoading ? "true" : "false");
|
|
85
|
+
if (isLoading) button.classList.add("sm-btn-loading");
|
|
86
|
+
else button.classList.remove("sm-btn-loading");
|
|
87
|
+
}
|
|
88
|
+
_pop(el, scale = 1.15, duration = 260) {
|
|
89
|
+
if (!el || typeof el.animate !== "function") return;
|
|
90
|
+
el.animate(
|
|
91
|
+
[{ transform: "scale(1)" }, { transform: `scale(${scale})` }, { transform: "scale(1)" }],
|
|
92
|
+
{ duration, easing: "ease-in-out" }
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
_getPositionStyles() {
|
|
96
|
+
const spacing = "20px";
|
|
97
|
+
const styles = {
|
|
98
|
+
position: "fixed",
|
|
99
|
+
top: "auto",
|
|
100
|
+
right: "auto",
|
|
101
|
+
bottom: "auto",
|
|
102
|
+
left: "auto",
|
|
103
|
+
transform: "none"
|
|
104
|
+
};
|
|
105
|
+
const pos = this.cartPosition;
|
|
106
|
+
if (pos.includes("top")) styles.top = spacing;
|
|
107
|
+
else if (pos.includes("bottom")) styles.bottom = spacing;
|
|
108
|
+
else {
|
|
109
|
+
styles.top = "50%";
|
|
110
|
+
styles.transform = "translateY(-50%)";
|
|
111
|
+
}
|
|
112
|
+
if (pos.includes("left")) styles.left = spacing;
|
|
113
|
+
else if (pos.includes("right")) styles.right = spacing;
|
|
114
|
+
else {
|
|
115
|
+
styles.left = "50%";
|
|
116
|
+
const currentY = styles.transform !== "none" ? "translateY(-50%)" : "";
|
|
117
|
+
styles.transform = `translateX(-50%) ${currentY}`.trim();
|
|
118
|
+
}
|
|
119
|
+
return styles;
|
|
120
|
+
}
|
|
121
|
+
_ensureFloatingCart(rootElement) {
|
|
122
|
+
if (this.floatingCartButton) return;
|
|
123
|
+
const mountRoot = rootElement || this._getMountRoot();
|
|
124
|
+
const button = document.createElement("button");
|
|
125
|
+
button.type = "button";
|
|
126
|
+
Object.assign(button.style, {
|
|
127
|
+
width: "60px",
|
|
128
|
+
height: "60px",
|
|
129
|
+
borderRadius: "50%",
|
|
130
|
+
border: "none",
|
|
131
|
+
background: "#1d4ed8",
|
|
132
|
+
color: "#ffffff",
|
|
133
|
+
fontSize: "24px",
|
|
134
|
+
display: "flex",
|
|
135
|
+
alignItems: "center",
|
|
136
|
+
justifyContent: "center",
|
|
137
|
+
boxShadow: "0 10px 25px rgba(0,0,0,0.2)",
|
|
138
|
+
cursor: "pointer",
|
|
139
|
+
zIndex: "2147483646",
|
|
140
|
+
transition: "transform 0.2s ease"
|
|
141
|
+
}, this._getPositionStyles());
|
|
142
|
+
button.innerHTML = `
|
|
143
|
+
<span class="sm-cart-icon">🛒</span>
|
|
144
|
+
<div class="sm-loading-spinner"></div>
|
|
145
|
+
`;
|
|
146
|
+
const badge = document.createElement("span");
|
|
147
|
+
badge.className = "sm-badge";
|
|
148
|
+
Object.assign(badge.style, {
|
|
149
|
+
position: "absolute",
|
|
150
|
+
top: "0",
|
|
151
|
+
right: "0",
|
|
152
|
+
minWidth: "20px",
|
|
153
|
+
height: "20px",
|
|
154
|
+
borderRadius: "10px",
|
|
155
|
+
background: "#ef4444",
|
|
156
|
+
color: "white",
|
|
157
|
+
fontSize: "11px",
|
|
158
|
+
fontWeight: "bold",
|
|
159
|
+
display: "none",
|
|
160
|
+
alignItems: "center",
|
|
161
|
+
justifyContent: "center",
|
|
162
|
+
border: "2px solid #ffffff",
|
|
163
|
+
padding: "0 4px"
|
|
164
|
+
});
|
|
165
|
+
button.appendChild(badge);
|
|
166
|
+
button.addEventListener("click", () => this.openCheckout(button));
|
|
167
|
+
mountRoot.appendChild(button);
|
|
168
|
+
this.floatingCartButton = button;
|
|
169
|
+
this.floatingCartBadge = badge;
|
|
170
|
+
}
|
|
171
|
+
_updateFloatingCartBadge() {
|
|
172
|
+
if (!this.floatingCartBadge) return;
|
|
173
|
+
const count = this.cart.i.reduce((sum, item) => sum + item.q, 0);
|
|
174
|
+
this.floatingCartBadge.style.display = count > 0 ? "flex" : "none";
|
|
175
|
+
this.floatingCartBadge.textContent = count > 99 ? "99+" : String(count);
|
|
176
|
+
}
|
|
177
|
+
_setupMessageListener() {
|
|
178
|
+
window.addEventListener("message", (event) => {
|
|
179
|
+
var _a, _b;
|
|
180
|
+
try {
|
|
181
|
+
const expectedOrigin = new URL(this.baseUrl).origin;
|
|
182
|
+
if (event.origin !== expectedOrigin) return;
|
|
183
|
+
if (((_a = event.data) == null ? void 0 : _a.type) === "closeCheckout" || ((_b = event.data) == null ? void 0 : _b.type) === "shopmate:close") {
|
|
184
|
+
this.closeCheckout();
|
|
185
|
+
}
|
|
186
|
+
} catch (e) {
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
addToCart(itemId, quantity, element) {
|
|
191
|
+
const item = this.cart.i.find((i) => i.i === itemId);
|
|
192
|
+
if (item) item.q = quantity;
|
|
193
|
+
else this.cart.i.push({ i: itemId, q: quantity });
|
|
194
|
+
if (element) {
|
|
195
|
+
this._pop(element, 1.15, 300);
|
|
196
|
+
this._animateFlyToCart(element);
|
|
197
|
+
}
|
|
198
|
+
this._updateFloatingCartBadge();
|
|
199
|
+
}
|
|
200
|
+
_animateFlyToCart(sourceElement) {
|
|
201
|
+
if (!this.floatingCartButton) return;
|
|
202
|
+
const sourceRect = sourceElement.getBoundingClientRect();
|
|
203
|
+
const cartRect = this.floatingCartButton.getBoundingClientRect();
|
|
204
|
+
const dot = document.createElement("div");
|
|
205
|
+
const sourceStyles = getComputedStyle(sourceElement);
|
|
206
|
+
const backgroundColor = sourceStyles.backgroundColor || "#1d4ed8";
|
|
207
|
+
const dotSize = Math.max(10, Math.min(sourceRect.width, sourceRect.height) / 2);
|
|
208
|
+
Object.assign(dot.style, {
|
|
209
|
+
position: "fixed",
|
|
210
|
+
left: `${sourceRect.left + sourceRect.width / 2}px`,
|
|
211
|
+
top: `${sourceRect.top + sourceRect.height / 2}px`,
|
|
212
|
+
width: `${dotSize}px`,
|
|
213
|
+
height: `${dotSize}px`,
|
|
214
|
+
borderRadius: "50%",
|
|
215
|
+
background: backgroundColor,
|
|
216
|
+
zIndex: "2147483645",
|
|
217
|
+
pointerEvents: "none"
|
|
218
|
+
});
|
|
219
|
+
this._getMountRoot().appendChild(dot);
|
|
220
|
+
const duration = 1150;
|
|
221
|
+
dot.animate([
|
|
222
|
+
{ transform: "translate(0, 0) scale(1)", opacity: "1" },
|
|
223
|
+
{ transform: `translate(${cartRect.left - sourceRect.left}px, ${cartRect.top - sourceRect.top}px) scale(0.5)`, opacity: "0" }
|
|
224
|
+
], {
|
|
225
|
+
duration,
|
|
226
|
+
easing: "ease-out",
|
|
227
|
+
fill: "forwards"
|
|
228
|
+
});
|
|
229
|
+
setTimeout(() => {
|
|
230
|
+
dot.remove();
|
|
231
|
+
this._pop(this.floatingCartButton, 1.2, 300);
|
|
232
|
+
}, duration);
|
|
233
|
+
}
|
|
234
|
+
openCheckout(buttonElement) {
|
|
235
|
+
this._toggleLoading(buttonElement, true);
|
|
236
|
+
if (!this.iframeElement) {
|
|
237
|
+
this.iframeElement = this.createCheckoutIframe();
|
|
238
|
+
this._getMountRoot().appendChild(this.iframeElement);
|
|
239
|
+
}
|
|
240
|
+
const params = new URLSearchParams({
|
|
241
|
+
orgId: this.orgId.toString(),
|
|
242
|
+
c: JSON.stringify(this.cart),
|
|
243
|
+
next: window.location.href
|
|
244
|
+
});
|
|
245
|
+
this.iframeElement.src = `${this.checkoutUrl}?${params.toString()}`;
|
|
246
|
+
this.iframeElement.onload = () => {
|
|
247
|
+
if (this.iframeElement && this.iframeElement.hasAttribute("src")) {
|
|
248
|
+
this.iframeElement.style.display = "block";
|
|
249
|
+
this.iframeShown = true;
|
|
250
|
+
this._setScrollLocked(true);
|
|
251
|
+
this._toggleLoading(buttonElement, false);
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
this.iframeElement.onerror = () => {
|
|
255
|
+
this._toggleLoading(buttonElement, false);
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
createCheckoutIframe() {
|
|
259
|
+
const iframe = document.createElement("iframe");
|
|
260
|
+
iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-popups allow-forms");
|
|
261
|
+
iframe.setAttribute("allow", "payment *; fullscreen *;");
|
|
262
|
+
Object.assign(iframe.style, {
|
|
263
|
+
width: "100vw",
|
|
264
|
+
height: "100dvh",
|
|
265
|
+
position: "fixed",
|
|
266
|
+
top: "0",
|
|
267
|
+
left: "0",
|
|
268
|
+
right: "0",
|
|
269
|
+
bottom: "0",
|
|
270
|
+
border: "0",
|
|
271
|
+
zIndex: "2147483647",
|
|
272
|
+
background: "#ffffff",
|
|
273
|
+
display: "none"
|
|
274
|
+
});
|
|
275
|
+
return iframe;
|
|
276
|
+
}
|
|
277
|
+
closeCheckout() {
|
|
278
|
+
if (this.iframeElement) {
|
|
279
|
+
this.iframeElement.style.display = "none";
|
|
280
|
+
this.iframeShown = false;
|
|
281
|
+
this.iframeElement.removeAttribute("src");
|
|
282
|
+
this._setScrollLocked(false);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
extract(rootElement = document.body) {
|
|
286
|
+
rootElement.querySelectorAll("[data-shopmate-product-id]").forEach((el) => {
|
|
287
|
+
const htmlEl = el;
|
|
288
|
+
const productId = parseInt(htmlEl.getAttribute("data-shopmate-product-id") || "", 10);
|
|
289
|
+
if (!isNaN(productId)) {
|
|
290
|
+
htmlEl.addEventListener("click", (e) => {
|
|
291
|
+
e.preventDefault();
|
|
292
|
+
this.addToCart(productId, parseInt(htmlEl.getAttribute("data-shopmate-quantity") || "1", 10), htmlEl);
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
const urlPrefix = this.checkoutUrl + "?";
|
|
297
|
+
const targetElementsWithUrlTrigger = rootElement.querySelectorAll(`a[href^="${urlPrefix}"]`);
|
|
298
|
+
targetElementsWithUrlTrigger.forEach((el) => {
|
|
299
|
+
const anchor = el;
|
|
300
|
+
const href = anchor.getAttribute("href") || "";
|
|
301
|
+
if (href.startsWith(urlPrefix)) {
|
|
302
|
+
const url = new URL(href, window.location.origin);
|
|
303
|
+
const productIdsRaw = url.searchParams.get("pid");
|
|
304
|
+
let productIds = [];
|
|
305
|
+
const cartRaw = url.searchParams.get("c");
|
|
306
|
+
let cart = null;
|
|
307
|
+
if (productIdsRaw) {
|
|
308
|
+
productIds = productIdsRaw.split(",").map((id) => parseInt(id, 10)).filter((id) => !isNaN(id));
|
|
309
|
+
} else if (cartRaw) {
|
|
310
|
+
try {
|
|
311
|
+
cart = JSON.parse(cartRaw);
|
|
312
|
+
if (cart && typeof cart === "object" && Array.isArray(cart.i)) {
|
|
313
|
+
cart.i = cart.i.filter((item) => typeof item.i === "number" && typeof item.q === "number");
|
|
314
|
+
} else {
|
|
315
|
+
cart = null;
|
|
316
|
+
}
|
|
317
|
+
} catch (e) {
|
|
318
|
+
console.warn("Invalid cart data in URL:", e);
|
|
319
|
+
cart = null;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
anchor.addEventListener("click", (e) => {
|
|
323
|
+
e.preventDefault();
|
|
324
|
+
if (productIds.length > 0) {
|
|
325
|
+
productIds.forEach((pid) => this.addToCart(pid, 1, anchor));
|
|
326
|
+
} else if (cart) {
|
|
327
|
+
cart.i.forEach((item) => this.addToCart(item.i, item.q, anchor));
|
|
328
|
+
}
|
|
329
|
+
this.openCheckout(anchor);
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
rootElement.querySelectorAll('[data-shopmate-checkout="true"]').forEach((el) => {
|
|
334
|
+
el.addEventListener("click", (e) => {
|
|
335
|
+
e.preventDefault();
|
|
336
|
+
this.openCheckout(el);
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
this._runWhenBodyReady(() => {
|
|
340
|
+
this._ensureFloatingCart(rootElement);
|
|
341
|
+
this._updateFloatingCartBadge();
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
if (typeof window !== "undefined") {
|
|
346
|
+
window.ShopMate = ShopMate;
|
|
347
|
+
const script = document.currentScript;
|
|
348
|
+
if (script && script.src) {
|
|
349
|
+
const instance = new ShopMate({
|
|
350
|
+
orgId: parseInt(script.dataset.orgId || "2", 10),
|
|
351
|
+
baseUrl: script.dataset.baseUrl || void 0,
|
|
352
|
+
cartPosition: script.dataset.cartPosition || void 0
|
|
353
|
+
});
|
|
354
|
+
window.addEventListener("load", () => {
|
|
355
|
+
window.ShopMateInstance = instance;
|
|
356
|
+
instance.extract();
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
var index_default = ShopMate;
|
package/dist/index.d.ts
CHANGED
|
@@ -24,8 +24,9 @@ declare class ShopMate {
|
|
|
24
24
|
private _getMountRoot;
|
|
25
25
|
private _runWhenBodyReady;
|
|
26
26
|
constructor(config: ShopMateConfig);
|
|
27
|
+
private _injectGlobalStyles;
|
|
27
28
|
private _setScrollLocked;
|
|
28
|
-
private
|
|
29
|
+
private _toggleLoading;
|
|
29
30
|
private _pop;
|
|
30
31
|
private _getPositionStyles;
|
|
31
32
|
private _ensureFloatingCart;
|