bitsnap-react 1.8.0 → 1.10.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 +162 -0
- package/dist/CartProvider-HXLGQ4EX.mjs +2 -0
- package/dist/CartProvider-OHHYOMSG.js +2 -0
- package/dist/CartProvider-OHHYOMSG.js.map +1 -0
- package/dist/analytics.d.mts +45 -0
- package/dist/analytics.d.ts +45 -0
- package/dist/analytics.js +2 -0
- package/dist/analytics.js.map +1 -0
- package/dist/analytics.mjs +2 -0
- package/dist/catalog.d.mts +14 -1
- package/dist/catalog.d.ts +14 -1
- package/dist/catalog.js +1 -1
- package/dist/catalog.js.map +1 -1
- package/dist/catalog.mjs +1 -1
- package/dist/chunk-3Z6VNXBP.js +2 -0
- package/dist/chunk-3Z6VNXBP.js.map +1 -0
- package/dist/chunk-43IIYJ6J.mjs +2 -0
- package/dist/chunk-43IIYJ6J.mjs.map +1 -0
- package/dist/chunk-6D3OORMK.mjs +2 -0
- package/dist/chunk-6D3OORMK.mjs.map +1 -0
- package/dist/chunk-ADYVTMGH.mjs +2 -0
- package/dist/chunk-ADYVTMGH.mjs.map +1 -0
- package/dist/{chunk-WRCXZMKK.js → chunk-AJNULBC3.js} +2 -2
- package/dist/chunk-AJNULBC3.js.map +1 -0
- package/dist/chunk-B32ACCW2.js +2 -0
- package/dist/chunk-B32ACCW2.js.map +1 -0
- package/dist/chunk-DDY7IBMG.mjs +2 -0
- package/dist/chunk-DDY7IBMG.mjs.map +1 -0
- package/dist/chunk-DPKT6ITB.js +4 -0
- package/dist/chunk-DPKT6ITB.js.map +1 -0
- package/dist/{chunk-T2POA6RW.js → chunk-F46RJVWC.js} +2 -2
- package/dist/chunk-F46RJVWC.js.map +1 -0
- package/dist/chunk-F6GQLKA6.js +2 -0
- package/dist/chunk-F6GQLKA6.js.map +1 -0
- package/dist/chunk-JSDGZ4NY.js.map +1 -1
- package/dist/chunk-JXCHRSPW.mjs +4 -0
- package/dist/chunk-JXCHRSPW.mjs.map +1 -0
- package/dist/chunk-KOYBZORL.js.map +1 -1
- package/dist/chunk-RLEC25JI.mjs +2 -0
- package/dist/chunk-RLEC25JI.mjs.map +1 -0
- package/dist/chunk-RVILRUW3.js +2 -0
- package/dist/chunk-RVILRUW3.js.map +1 -0
- package/dist/chunk-T2OUOO4X.mjs +2 -0
- package/dist/chunk-T2OUOO4X.mjs.map +1 -0
- package/dist/chunk-VIII45AC.js +2 -0
- package/dist/chunk-VIII45AC.js.map +1 -0
- package/dist/chunk-XIHGWNTS.js +2 -0
- package/dist/chunk-XIHGWNTS.js.map +1 -0
- package/dist/chunk-YXFJG4MF.mjs +2 -0
- package/dist/chunk-YXFJG4MF.mjs.map +1 -0
- package/dist/chunk-ZPTHK4OX.mjs +2 -0
- package/dist/{chunk-OJT3YO2R.mjs.map → chunk-ZPTHK4OX.mjs.map} +1 -1
- package/dist/consent.d.mts +165 -0
- package/dist/consent.d.ts +165 -0
- package/dist/consent.js +75 -0
- package/dist/consent.js.map +1 -0
- package/dist/consent.mjs +75 -0
- package/dist/consent.mjs.map +1 -0
- package/dist/course.d.mts +13 -3
- package/dist/course.d.ts +13 -3
- package/dist/course.js +1 -1
- package/dist/course.js.map +1 -1
- package/dist/course.mjs +1 -1
- package/dist/course.mjs.map +1 -1
- package/dist/embed/analytics.js +1 -0
- package/dist/embed/consent.js +74 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +54 -7
- package/dist/index.d.ts +54 -7
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/instrument-CmxK7S4T.d.mts +929 -0
- package/dist/instrument-CmxK7S4T.d.ts +929 -0
- package/dist/internal.js.map +1 -1
- package/dist/methods-A5KGZBAM.js +2 -0
- package/dist/methods-A5KGZBAM.js.map +1 -0
- package/dist/methods-AOHKV7IN.mjs +2 -0
- package/dist/methods-AOHKV7IN.mjs.map +1 -0
- package/dist/models.js.map +1 -1
- package/dist/returns.d.mts +9 -1
- package/dist/returns.d.ts +9 -1
- package/dist/returns.js +1 -1
- package/dist/returns.js.map +1 -1
- package/dist/returns.mjs +1 -1
- package/dist/{types-PSBBDdQm.d.mts → types-BnVWLcoB.d.mts} +13 -0
- package/dist/{types-BTm_ZolC.d.ts → types-i-IuN4Im.d.ts} +13 -0
- package/package.json +46 -45
- package/dist/CartProvider-5KAG5QGA.mjs +0 -2
- package/dist/CartProvider-7GJXW2IP.js +0 -2
- package/dist/CartProvider-7GJXW2IP.js.map +0 -1
- package/dist/chunk-45AP7UZE.mjs +0 -2
- package/dist/chunk-45AP7UZE.mjs.map +0 -1
- package/dist/chunk-7XTDFTQU.js +0 -4
- package/dist/chunk-7XTDFTQU.js.map +0 -1
- package/dist/chunk-C6Z7LOHB.mjs +0 -2
- package/dist/chunk-C6Z7LOHB.mjs.map +0 -1
- package/dist/chunk-HB6KQT26.mjs +0 -4
- package/dist/chunk-HB6KQT26.mjs.map +0 -1
- package/dist/chunk-LWK4E5G5.js +0 -2
- package/dist/chunk-LWK4E5G5.js.map +0 -1
- package/dist/chunk-OJT3YO2R.mjs +0 -2
- package/dist/chunk-OUYRKNZJ.mjs +0 -2
- package/dist/chunk-OUYRKNZJ.mjs.map +0 -1
- package/dist/chunk-P2YB25KZ.js +0 -2
- package/dist/chunk-P2YB25KZ.js.map +0 -1
- package/dist/chunk-T2POA6RW.js.map +0 -1
- package/dist/chunk-VPKE2ZOR.mjs +0 -2
- package/dist/chunk-VPKE2ZOR.mjs.map +0 -1
- package/dist/chunk-WRCXZMKK.js.map +0 -1
- package/dist/chunk-WSM4OM72.mjs +0 -2
- package/dist/chunk-WSM4OM72.mjs.map +0 -1
- package/dist/chunk-XRNTUSBH.js +0 -2
- package/dist/chunk-XRNTUSBH.js.map +0 -1
- package/dist/chunk-Z2BKIX6F.js +0 -2
- package/dist/chunk-Z2BKIX6F.js.map +0 -1
- package/dist/methods-ITD6WFIY.mjs +0 -2
- package/dist/methods-JENIGNF7.js +0 -2
- package/dist/methods-JENIGNF7.js.map +0 -1
- /package/dist/{CartProvider-5KAG5QGA.mjs.map → CartProvider-HXLGQ4EX.mjs.map} +0 -0
- /package/dist/{methods-ITD6WFIY.mjs.map → analytics.mjs.map} +0 -0
package/README.md
CHANGED
|
@@ -1099,3 +1099,165 @@ The panel provides the following screens:
|
|
|
1099
1099
|
|
|
1100
1100
|
- Toggle all notifications
|
|
1101
1101
|
- Notification preferences
|
|
1102
|
+
|
|
1103
|
+
## Merchant analytics (`bitsnap-react/analytics`)
|
|
1104
|
+
|
|
1105
|
+
A small tracker that sends buyer-behaviour events (page views, product views, cart and checkout steps) to Bitsnap, so your dashboard can show visits, sources and funnels. By default it is cookieless: it never uses `localStorage`, `sessionStorage` or IndexedDB, events live in memory and the visitor is recognised on the server by a daily-rotating hash that is never stored. The one exception is the consented `bs_vid_<projectKey>` cookie, described in [Returning visitors](#returning-visitors-the-bs_vid_projectkey-cookie).
|
|
1106
|
+
|
|
1107
|
+
What an event holds: the event name, a timestamp, the page host and path (no query string, no fragment), the referrer's host, `utm_*` and `ref` from the landing URL, ids of the product/variant/order involved, and a few numbers (price in minor units, quantity). It never holds names, emails, phone numbers, addresses, IPs, coupon codes, form values or free text.
|
|
1108
|
+
|
|
1109
|
+
Tracking stays off when `enabled` is `false`, when the browser sends Global Privacy Control (`navigator.globalPrivacyControl`), or when the browser is automated (`navigator.webdriver`). When [Bitsnap Consent](#cookie-consent-bitsnap-consent) is on the page and the visitor rejected analytics, the tracker stops and drops what it had queued. Without a stored choice it keeps going, because it stores nothing on the device (the `bs_vid_<projectKey>` cookie needs a granted choice).
|
|
1110
|
+
|
|
1111
|
+
### Script tag
|
|
1112
|
+
|
|
1113
|
+
```html
|
|
1114
|
+
<script src="https://bitsnap.pl/embed/v1/analytics.js" data-project="pk_…" defer></script>
|
|
1115
|
+
```
|
|
1116
|
+
|
|
1117
|
+
The standalone file is `dist/embed/analytics.js` (about 7.8 KB gzipped, no React). It sends a `page_view` on load and when the path changes through `history.pushState`/`replaceState`/back-forward (a change of the query string or hash alone is not a new page). Attributes: `data-project` (required), `data-env="live|test"`, `data-endpoint` (default `https://collect.bitsnap.pl`), `data-auto="on|off"` (default `on`), `data-page-type="shop|catalog|product|cart|checkout|…"`.
|
|
1118
|
+
|
|
1119
|
+
Bitsnap's own pages set three more: `data-source="script|storefront|dashboard"` (default `script`), `data-channel="embed|hosted|custom_domain"` (default `embed`) and `data-visitor-id`, a 22-character id that is only honoured with `data-source="dashboard"` (on any other source the collector computes the visitor id itself). With `data-source="dashboard"` the script sends `app_page_view` (with the route) instead of `page_view`. Unknown values fall back to the defaults.
|
|
1120
|
+
|
|
1121
|
+
```js
|
|
1122
|
+
window.bitsnap.track("product_view", { price_minor: 4900, currency: "PLN", availability: "in_stock" }, { product_id: "…" });
|
|
1123
|
+
window.bitsnap.page("product");
|
|
1124
|
+
|
|
1125
|
+
// Before the script has loaded, queue calls instead:
|
|
1126
|
+
(window.bitsnap = window.bitsnap || { q: [] }).q.push(["track", "variant_selected", {}, { product_id: "…" }]);
|
|
1127
|
+
```
|
|
1128
|
+
|
|
1129
|
+
### React
|
|
1130
|
+
|
|
1131
|
+
```tsx
|
|
1132
|
+
import { BitsnapAnalyticsProvider, useAnalytics } from "bitsnap-react/analytics";
|
|
1133
|
+
|
|
1134
|
+
<BitsnapAnalyticsProvider projectKey="pk_…" onEvent={(event) => myTool.track(event.event, event.props)}>
|
|
1135
|
+
<App />
|
|
1136
|
+
</BitsnapAnalyticsProvider>;
|
|
1137
|
+
|
|
1138
|
+
function Product({ id }: { id: string }) {
|
|
1139
|
+
const { track } = useAnalytics();
|
|
1140
|
+
// track("product_view", { price_minor, currency, availability }, { product_id: id })
|
|
1141
|
+
}
|
|
1142
|
+
```
|
|
1143
|
+
|
|
1144
|
+
The provider sends a `page_view` on mount and on every path change (`pushState`, `replaceState`, Back/Forward), like the script tag. Pass `autoPageViews={false}` to turn that off and call `page()` yourself; otherwise drop your own `page()` calls, or each page is counted twice. The same goes for instrumented components used without a provider: they start the automatic page views too, so a manual `page()` next to them double-counts unless you pass `analytics={false}` to them. An automatic page view carries no page type of its own (a component without a provider supplies its own), so it does not inherit one set by an earlier manual `page(type)`. The page views are a per-page singleton too: the page gets exactly one `page_view` per path change however many providers, instrumented components or `analytics.js` embeds it has.
|
|
1145
|
+
|
|
1146
|
+
`useAnalytics()` returns no-ops outside the provider and on the server. `onEvent` receives every event before it is sent, so you can forward it to your own tools after your own consent step; it is not called while tracking is off. The tracker is a per-page singleton: the first provider's `projectKey`, `env` and `endpoint` win.
|
|
1147
|
+
|
|
1148
|
+
Without React, use the core directly (`startAutoPageViews(() => tracker.page(null))` gives you the same automatic page views): `initTracker({ projectKey })` returns the page's tracker (`track`, `page`, `flush`, `setEnabled`, `onEvent`). Events are batched: sent every 5 seconds, at 20 events, and when the page is hidden (`fetch` with `keepalive`, with `navigator.sendBeacon` as the fallback). Failed sends are not retried.
|
|
1149
|
+
|
|
1150
|
+
### Automatic analytics in components
|
|
1151
|
+
|
|
1152
|
+
The Bitsnap components report their own events, so a project key is all you need. Each component takes an `analytics` prop:
|
|
1153
|
+
|
|
1154
|
+
```tsx
|
|
1155
|
+
<ProductList projectID="…" /> // tracks (default)
|
|
1156
|
+
<ProductList projectID="…" analytics={false} /> // opt out
|
|
1157
|
+
<ProductList projectID="…" analytics={{ onEvent: (e) => myTool.track(e.event, e.props), env: "test" }} />
|
|
1158
|
+
```
|
|
1159
|
+
|
|
1160
|
+
`onEvent` receives only the events of that component. `env: "test"` events are stored but never reach the merchant's stats. Parts inside a component inherit its setting; a surrounding `<BitsnapAnalyticsProvider>` owns the tracker and its `onEvent` still sees everything (and its `autoPageViews` decides on the page views). Nothing is written to Storage, cookies only as described under [Returning visitors](#returning-visitors-the-bs_vid_projectkey-cookie), and events carry no personal data (no emails, names, order contents or free text).
|
|
1161
|
+
|
|
1162
|
+
| Component | Events |
|
|
1163
|
+
| --- | --- |
|
|
1164
|
+
| `ProductList` | `product_list_viewed`, `product_impression` (card ≥50% visible), `product_clicked`, `add_to_cart` |
|
|
1165
|
+
| `BitsnapCheckout` / `Bitsnap.addProductToCart` | `cart_opened`, `checkout_started`, `add_to_cart`, `remove_from_cart` |
|
|
1166
|
+
| `ApplePay` | `apple_pay_clicked`, `payment_method_selected`, `delivery_method_selected`, `payment_submitted`, `apple_pay_result` |
|
|
1167
|
+
| `GooglePay` | `payment_method_selected`, `payment_submitted` |
|
|
1168
|
+
| Notifications | `availability_signup` |
|
|
1169
|
+
| Panel | `panel_login`, `panel_order_viewed`, `panel_download` |
|
|
1170
|
+
| Returns | `return_started`, `return_submitted` |
|
|
1171
|
+
| Course | `course_opened`, `lesson_started`, `lesson_completed`, `video_progress` (25/50/75/100% of naturally played time), `test_submitted`, `lesson_rated` |
|
|
1172
|
+
|
|
1173
|
+
Without a `<BitsnapAnalyticsProvider>`, an instrumented component that starts the tracker also sends the automatic `page_view`s (it does not when `analytics={false}`). The components do not emit `product_view`, `checkout_step_completed`, `checkout_validation_error`, `coupon_applied` or `coupon_rejected` (the drawer hands over to the hosted checkout). Use `useAnalytics()` or the embed script for those. Because the tracker is a per-page singleton, the first project key wins: use one project per page. The course sends nothing in creator preview.
|
|
1174
|
+
|
|
1175
|
+
For your own components, `useInstrument(projectKey, analytics)` returns the same `{ track }` helper and `<InstrumentProvider value={instrument}>` shares it with the parts below.
|
|
1176
|
+
|
|
1177
|
+
### Returning visitors: the `bs_vid_<projectKey>` cookie
|
|
1178
|
+
|
|
1179
|
+
Without a cookie the server can only tell visitors apart for one day. When [Bitsnap Consent](#cookie-consent-bitsnap-consent) is on the page and the visitor grants **analytics**, the tracker sets one first-party cookie:
|
|
1180
|
+
|
|
1181
|
+
| | |
|
|
1182
|
+
| --- | --- |
|
|
1183
|
+
| Name | `bs_vid_<projectKey>`, with your bare public project key (like `bitsnap_consent_<projectKey>`). It is per project because consent is: on bitsnap.pl many merchants share one site, and one merchant's consent or refusal must never grant or delete another's id |
|
|
1184
|
+
| Value | 22 random base64url characters (16 bytes from `crypto.getRandomValues`); not derived from anything about the visitor |
|
|
1185
|
+
| Lifetime | 390 days (under the 13 months the CNIL allows), set once and not extended on later visits |
|
|
1186
|
+
| Domain | The widest registrable domain the browser accepts (like Google Analytics), host-only on `localhost` and IP hosts |
|
|
1187
|
+
| Attributes | `Path=/; SameSite=Lax`, plus `Secure` on https |
|
|
1188
|
+
| Set when | Bitsnap Consent reports `analytics` granted, on load or on the `bitsnap:consent` event |
|
|
1189
|
+
| Deleted when | `analytics` is withdrawn or the choice is reset: on the host and every parent domain |
|
|
1190
|
+
|
|
1191
|
+
While analytics is granted and the cookie exists, every event carries it as `client_id`, and the requests that create a checkout or payment link (the cart's `/buy` and `/api/payment/link/auto` calls) send it as the `X-Bitsnap-Visitor` header. The server derives a stable visitor id from it, which lets your stats count returning visitors and credit a purchase to the visit that led to it. Without a granted choice (no banner, a refusal, a choice still loading, Global Privacy Control) there is no cookie, no `client_id` and no header, and tracking stays cookieless as before.
|
|
1192
|
+
|
|
1193
|
+
The cookie only follows a banner that speaks for the project: a project-scope banner (`scope: "project"`) with the same project key, or, on your own site, a site-scope banner. A banner for another project never sets, reads or deletes it, and on bitsnap.pl (and its subdomains) the site-wide choice is bitsnap's own, so only a project-scope choice for your key counts.
|
|
1194
|
+
|
|
1195
|
+
Declare the cookie in your banner's cookie list (category `analytics`, purpose "shop statistics: recognising returning visitors", lifetime 13 months): add `{ name: "Bitsnap statistics", category: "analytics", cookies: ["bs_vid_<projectKey>"] }` to `services` in the banner config, with your exact bare project key, which also lets the banner delete it on withdrawal from pages that run no tracker. Do not use a `bs_vid_*` prefix: the banner deletes every cookie that matches it, which on a site shared by several merchants (bitsnap.pl) would wipe other projects' ids too.
|
|
1196
|
+
|
|
1197
|
+
### Release notes (1.10.0)
|
|
1198
|
+
|
|
1199
|
+
- **Requires bitsnap.pl with `X-Bitsnap-Visitor` support.** The cart's `/buy` and `/api/payment/link/auto` requests now send that header while analytics is granted, and a server whose CORS preflight does not allow it makes those checkouts fail in the browser. Deploy bitsnap.pl first, then publish and install this version.
|
|
1200
|
+
- **Page views are now automatic by default.** `<BitsnapAnalyticsProvider>` and instrumented components without a provider send a `page_view` on mount and on every path change. If you call `page()` yourself, pass `autoPageViews={false}` to the provider (and `analytics={false}` to components used without one), or each page is counted twice.
|
|
1201
|
+
- **Update the consent script too.** The tracker only sets `bs_vid_<projectKey>` for a banner that reports its `scope` and `projectKey` (1.10.0 and later); an older consent script is treated as not speaking for the project, so the tracker stays cookieless.
|
|
1202
|
+
- **Update `analytics.js` and the npm package together.** Both share one page-wide registry to send a single `page_view` per path change; an older copy of either does not know about it, and the page is counted twice while both run.
|
|
1203
|
+
- New consented first-party cookie `bs_vid_<projectKey>` and `client_id` on events (see above); declare it in your banner's cookie list by its exact name.
|
|
1204
|
+
|
|
1205
|
+
## Cookie consent (Bitsnap Consent)
|
|
1206
|
+
|
|
1207
|
+
An embeddable cookie banner with equal "Accept all" / "Reject all" buttons, per-category settings, light and dark themes, Polish and English texts, and Google Consent Mode v2. It renders in a Shadow DOM, stores only one first-party cookie (`bitsnap_consent`) and never uses `localStorage`.
|
|
1208
|
+
|
|
1209
|
+
### Script tag
|
|
1210
|
+
|
|
1211
|
+
```html
|
|
1212
|
+
<script src="https://bitsnap.pl/embed/v1/consent.js" data-project="pk_…" defer></script>
|
|
1213
|
+
```
|
|
1214
|
+
|
|
1215
|
+
The standalone file is `dist/embed/consent.js` (about 9 KB gzipped, no React). Attributes: `data-project`, `data-theme="auto|light|dark"`, `data-locale="auto|pl|en"`, `data-position="bottom|bottom-left|bottom-right"`, `data-privacy-url`, `data-scope="project"`, `data-api-host`, `data-collect-host`, `data-gcm="on|off"`, `data-categories="analytics,marketing"` (used when there is no remote config), `data-reload-on-withdraw="true"`.
|
|
1216
|
+
|
|
1217
|
+
For a live preview in a settings page, add `data-preview="on"`: the banner is shown on every load, nothing is read from or written to the cookie, and nothing is sent to the collect host. `data-title` and `data-description` replace the banner heading and text (the same options exist as `preview`, `title` and `description` in the core `ConsentOptions`).
|
|
1218
|
+
|
|
1219
|
+
With `data-project` the banner loads its config from `/api/consent/config` (3 s timeout, then defaults) and sends each choice to `<collect host>/consent`. Without it nothing leaves the page.
|
|
1220
|
+
|
|
1221
|
+
### React
|
|
1222
|
+
|
|
1223
|
+
```tsx
|
|
1224
|
+
import { BitsnapConsent, useConsent } from "bitsnap-react/consent";
|
|
1225
|
+
|
|
1226
|
+
<BitsnapConsent projectKey="pk_…" locale="pl" privacyPolicyUrl="/polityka-prywatnosci" />;
|
|
1227
|
+
|
|
1228
|
+
function CookieSettings() {
|
|
1229
|
+
const { consent, open } = useConsent();
|
|
1230
|
+
return <button onClick={open}>Cookie settings ({consent?.c.marketing ? "marketing on" : "marketing off"})</button>;
|
|
1231
|
+
}
|
|
1232
|
+
```
|
|
1233
|
+
|
|
1234
|
+
`<BitsnapConsent />` renders nothing on the server. A `config` prop (`ConsentConfig`, partial) overrides the project config.
|
|
1235
|
+
|
|
1236
|
+
### Loading scripts after consent
|
|
1237
|
+
|
|
1238
|
+
```html
|
|
1239
|
+
<script type="text/plain" data-bitsnap-consent="marketing" data-src="https://example.com/pixel.js"></script>
|
|
1240
|
+
<script type="text/plain" data-bitsnap-consent="analytics">/* inline code */</script>
|
|
1241
|
+
<iframe data-bitsnap-consent="marketing" data-src="https://www.youtube.com/embed/…"></iframe>
|
|
1242
|
+
```
|
|
1243
|
+
|
|
1244
|
+
After consent for the category the placeholder is replaced by a real script (attributes copied, `type` dropped, `data-src` becomes `src`), and an iframe gets its `src`. Categories are `analytics`, `marketing` and `preferences`.
|
|
1245
|
+
|
|
1246
|
+
### JavaScript API
|
|
1247
|
+
|
|
1248
|
+
```js
|
|
1249
|
+
BitsnapConsent.get(); // null or { id, ts, bv, c: { analytics, marketing, preferences } }
|
|
1250
|
+
BitsnapConsent.on("change", (d) => {}); // returns an unsubscribe function; d.action is accept_all | reject_all | custom | withdraw | reset
|
|
1251
|
+
BitsnapConsent.open(); // settings layer; any [data-bitsnap-consent-open] element does the same
|
|
1252
|
+
BitsnapConsent.acceptAll(); BitsnapConsent.rejectAll(); BitsnapConsent.accept({ analytics: true });
|
|
1253
|
+
BitsnapConsent.reset(); // clears the choice and asks again
|
|
1254
|
+
BitsnapConsent.ready; // Promise, resolves once config and the stored choice are applied
|
|
1255
|
+
BitsnapConsent.scope; // "global" (bitsnap_consent) or "project" (bitsnap_consent_<projectKey>)
|
|
1256
|
+
BitsnapConsent.projectKey; // the bare public project key the banner was started with, or null
|
|
1257
|
+
```
|
|
1258
|
+
|
|
1259
|
+
`window` also receives `CustomEvent("bitsnap:consent", { detail })` on every choice. Categories missing from `accept()` are denied. When a visitor withdraws a category, the cookies listed in `services[].cookies` are deleted (best effort; a trailing `*` matches a prefix), and the page reloads if `reloadOnWithdraw` is set.
|
|
1260
|
+
|
|
1261
|
+
### Google Consent Mode v2
|
|
1262
|
+
|
|
1263
|
+
If `gtag` or `dataLayer` exists (or `data-gcm="on"`), the script sets `consent default` (everything denied except `security_storage`, `wait_for_update: 500`) before anything else, and `consent update` after each choice: analytics → `analytics_storage`, marketing → `ad_storage`, `ad_user_data`, `ad_personalization`, preferences → `functionality_storage`, `personalization_storage`.
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{B as c,C as d,D as e,E as f,F as g,G as h,H as i}from"./chunk-JXCHRSPW.mjs";import"./chunk-DDY7IBMG.mjs";import"./chunk-KOVKUJAW.mjs";import"./chunk-YXFJG4MF.mjs";import"./chunk-T2OUOO4X.mjs";import{l as a,m as b}from"./chunk-43IIYJ6J.mjs";import"./chunk-RLEC25JI.mjs";import"./chunk-ADYVTMGH.mjs";import"./chunk-U6FLIRIV.mjs";export{c as MARKETING_AGREEMENT_ID,d as bitsnapProjectID,h as default,i as getCheckoutMethods,b as getLocale,f as getProjectID,a as setLocale,e as setProjectID,g as useCartProvider};
|
|
2
|
+
//# sourceMappingURL=CartProvider-HXLGQ4EX.mjs.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});var _chunkDPKT6ITBjs = require('./chunk-DPKT6ITB.js');require('./chunk-RVILRUW3.js');require('./chunk-KOYBZORL.js');require('./chunk-F6GQLKA6.js');require('./chunk-AJNULBC3.js');var _chunkVIII45ACjs = require('./chunk-VIII45AC.js');require('./chunk-B32ACCW2.js');require('./chunk-XIHGWNTS.js');require('./chunk-JSDGZ4NY.js');exports.MARKETING_AGREEMENT_ID = _chunkDPKT6ITBjs.B; exports.bitsnapProjectID = _chunkDPKT6ITBjs.C; exports.default = _chunkDPKT6ITBjs.G; exports.getCheckoutMethods = _chunkDPKT6ITBjs.H; exports.getLocale = _chunkVIII45ACjs.m; exports.getProjectID = _chunkDPKT6ITBjs.E; exports.setLocale = _chunkVIII45ACjs.l; exports.setProjectID = _chunkDPKT6ITBjs.D; exports.useCartProvider = _chunkDPKT6ITBjs.F;
|
|
2
|
+
//# sourceMappingURL=CartProvider-OHHYOMSG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/emems/.t3/worktrees/bitsnap-react/visitor-cookie/dist/CartProvider-OHHYOMSG.js"],"names":[],"mappings":"AAAA,+HAAiF,+BAA4B,+BAA4B,+BAA4B,+BAA4B,sDAA+C,+BAA4B,+BAA4B,+BAA4B,8YAAsL","file":"/home/emems/.t3/worktrees/bitsnap-react/visitor-cookie/dist/CartProvider-OHHYOMSG.js"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { T as Tracker, a as TrackerOptions, C as ClientEvent } from './instrument-CmxK7S4T.mjs';
|
|
2
|
+
export { A as AnalyticsOption, d as AppEventName, B as BrowserEventName, e as Channel, D as DEFAULT_ENDPOINT, E as Env, f as EventName, h as EventProps, l as Instrument, I as InstrumentProvider, P as PageType, S as Source, j as StorefrontEventName, b as TrackExtra, c as createTracker, g as getTracker, i as initTracker, n as noopInstrument, r as resetTracker, s as scoreBucket, u as useInstrument, k as useInstrumentContext } from './instrument-CmxK7S4T.mjs';
|
|
3
|
+
import * as react from 'react';
|
|
4
|
+
import { ReactNode } from 'react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Calls `send` for the current path and again on every path change, unless
|
|
8
|
+
* another starter on the page already reported that path: the page gets one
|
|
9
|
+
* `page_view` per path change however many providers, components and embeds
|
|
10
|
+
* start it. Returns the function that stops this starter.
|
|
11
|
+
*/
|
|
12
|
+
declare function startAutoPageViews(send: () => void, win?: Window): () => void;
|
|
13
|
+
|
|
14
|
+
type AnalyticsApi = Pick<Tracker, "track" | "page">;
|
|
15
|
+
type BitsnapAnalyticsProviderProps = {
|
|
16
|
+
/** Public project key (the `projectID` the rest of the SDK uses). */
|
|
17
|
+
projectKey: string;
|
|
18
|
+
env?: TrackerOptions["env"];
|
|
19
|
+
/** `false` turns tracking off. */
|
|
20
|
+
enabled?: boolean;
|
|
21
|
+
endpoint?: string;
|
|
22
|
+
/** Called with every event before it is sent, e.g. to forward it to the merchant's own tools. */
|
|
23
|
+
onEvent?: (event: ClientEvent) => void;
|
|
24
|
+
/**
|
|
25
|
+
* Sends `page_view` on mount and on every path change (`pushState`, `replaceState`, Back/Forward).
|
|
26
|
+
* Default `true`; set `false` to call `page()` yourself.
|
|
27
|
+
*/
|
|
28
|
+
autoPageViews?: boolean;
|
|
29
|
+
children?: ReactNode;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Starts the page's tracker and hands `track` and `page` to `useAnalytics()`.
|
|
33
|
+
* The tracker is a per-page singleton: the first valid `projectKey` wins, but
|
|
34
|
+
* `env`, `endpoint` and `enabled` of any provider update the running tracker.
|
|
35
|
+
* It sends a `page_view` for the current page and every path change, once per
|
|
36
|
+
* path however many providers, instrumented components or `analytics.js`
|
|
37
|
+
* embeds the page has.
|
|
38
|
+
* Nothing is tracked on the server. The only thing stored on the visitor's
|
|
39
|
+
* device is the `bs_vid_<projectKey>` cookie, and only while Bitsnap Consent grants analytics.
|
|
40
|
+
*/
|
|
41
|
+
declare function BitsnapAnalyticsProvider({ projectKey, env, enabled, endpoint, onEvent, autoPageViews, children, }: BitsnapAnalyticsProviderProps): react.FunctionComponentElement<react.ProviderProps<AnalyticsApi>>;
|
|
42
|
+
/** `track` and `page`; no-ops outside a `<BitsnapAnalyticsProvider>`. */
|
|
43
|
+
declare function useAnalytics(): AnalyticsApi;
|
|
44
|
+
|
|
45
|
+
export { type AnalyticsApi, BitsnapAnalyticsProvider, type BitsnapAnalyticsProviderProps, ClientEvent, Tracker, TrackerOptions, startAutoPageViews, useAnalytics };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { T as Tracker, a as TrackerOptions, C as ClientEvent } from './instrument-CmxK7S4T.js';
|
|
2
|
+
export { A as AnalyticsOption, d as AppEventName, B as BrowserEventName, e as Channel, D as DEFAULT_ENDPOINT, E as Env, f as EventName, h as EventProps, l as Instrument, I as InstrumentProvider, P as PageType, S as Source, j as StorefrontEventName, b as TrackExtra, c as createTracker, g as getTracker, i as initTracker, n as noopInstrument, r as resetTracker, s as scoreBucket, u as useInstrument, k as useInstrumentContext } from './instrument-CmxK7S4T.js';
|
|
3
|
+
import * as react from 'react';
|
|
4
|
+
import { ReactNode } from 'react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Calls `send` for the current path and again on every path change, unless
|
|
8
|
+
* another starter on the page already reported that path: the page gets one
|
|
9
|
+
* `page_view` per path change however many providers, components and embeds
|
|
10
|
+
* start it. Returns the function that stops this starter.
|
|
11
|
+
*/
|
|
12
|
+
declare function startAutoPageViews(send: () => void, win?: Window): () => void;
|
|
13
|
+
|
|
14
|
+
type AnalyticsApi = Pick<Tracker, "track" | "page">;
|
|
15
|
+
type BitsnapAnalyticsProviderProps = {
|
|
16
|
+
/** Public project key (the `projectID` the rest of the SDK uses). */
|
|
17
|
+
projectKey: string;
|
|
18
|
+
env?: TrackerOptions["env"];
|
|
19
|
+
/** `false` turns tracking off. */
|
|
20
|
+
enabled?: boolean;
|
|
21
|
+
endpoint?: string;
|
|
22
|
+
/** Called with every event before it is sent, e.g. to forward it to the merchant's own tools. */
|
|
23
|
+
onEvent?: (event: ClientEvent) => void;
|
|
24
|
+
/**
|
|
25
|
+
* Sends `page_view` on mount and on every path change (`pushState`, `replaceState`, Back/Forward).
|
|
26
|
+
* Default `true`; set `false` to call `page()` yourself.
|
|
27
|
+
*/
|
|
28
|
+
autoPageViews?: boolean;
|
|
29
|
+
children?: ReactNode;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Starts the page's tracker and hands `track` and `page` to `useAnalytics()`.
|
|
33
|
+
* The tracker is a per-page singleton: the first valid `projectKey` wins, but
|
|
34
|
+
* `env`, `endpoint` and `enabled` of any provider update the running tracker.
|
|
35
|
+
* It sends a `page_view` for the current page and every path change, once per
|
|
36
|
+
* path however many providers, instrumented components or `analytics.js`
|
|
37
|
+
* embeds the page has.
|
|
38
|
+
* Nothing is tracked on the server. The only thing stored on the visitor's
|
|
39
|
+
* device is the `bs_vid_<projectKey>` cookie, and only while Bitsnap Consent grants analytics.
|
|
40
|
+
*/
|
|
41
|
+
declare function BitsnapAnalyticsProvider({ projectKey, env, enabled, endpoint, onEvent, autoPageViews, children, }: BitsnapAnalyticsProviderProps): react.FunctionComponentElement<react.ProviderProps<AnalyticsApi>>;
|
|
42
|
+
/** `track` and `page`; no-ops outside a `<BitsnapAnalyticsProvider>`. */
|
|
43
|
+
declare function useAnalytics(): AnalyticsApi;
|
|
44
|
+
|
|
45
|
+
export { type AnalyticsApi, BitsnapAnalyticsProvider, type BitsnapAnalyticsProviderProps, ClientEvent, Tracker, TrackerOptions, startAutoPageViews, useAnalytics };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});var _chunkB32ACCW2js = require('./chunk-B32ACCW2.js');require('./chunk-XIHGWNTS.js');require('./chunk-JSDGZ4NY.js');exports.BitsnapAnalyticsProvider = _chunkB32ACCW2js.i; exports.DEFAULT_ENDPOINT = _chunkB32ACCW2js.c; exports.InstrumentProvider = _chunkB32ACCW2js.l; exports.createTracker = _chunkB32ACCW2js.d; exports.getTracker = _chunkB32ACCW2js.f; exports.initTracker = _chunkB32ACCW2js.e; exports.noopInstrument = _chunkB32ACCW2js.k; exports.resetTracker = _chunkB32ACCW2js.g; exports.scoreBucket = _chunkB32ACCW2js.r; exports.startAutoPageViews = _chunkB32ACCW2js.b; exports.useAnalytics = _chunkB32ACCW2js.j; exports.useInstrument = _chunkB32ACCW2js.n; exports.useInstrumentContext = _chunkB32ACCW2js.m;
|
|
2
|
+
//# sourceMappingURL=analytics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/emems/.t3/worktrees/bitsnap-react/visitor-cookie/dist/analytics.js"],"names":[],"mappings":"AAAA,+HAAsH,+BAA4B,+BAA4B,klBAAsR","file":"/home/emems/.t3/worktrees/bitsnap-react/visitor-cookie/dist/analytics.js"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{b as e,c as t,d as r,e as n,f as o,g as a,i as s,j as p,k as c,l as i,m,n as u,r as v}from"./chunk-RLEC25JI.mjs";import"./chunk-ADYVTMGH.mjs";import"./chunk-U6FLIRIV.mjs";export{s as BitsnapAnalyticsProvider,t as DEFAULT_ENDPOINT,i as InstrumentProvider,r as createTracker,o as getTracker,n as initTracker,c as noopInstrument,a as resetTracker,v as scoreBucket,e as startAutoPageViews,p as useAnalytics,u as useInstrument,m as useInstrumentContext};
|
|
2
|
+
//# sourceMappingURL=analytics.mjs.map
|
package/dist/catalog.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
+
import { A as AnalyticsOption } from './instrument-CmxK7S4T.mjs';
|
|
3
4
|
import { S as SlotAppearance, A as AppearanceElements } from './appearance-core-Dt2grZdl.mjs';
|
|
4
5
|
import { BitsnapModels } from './models.mjs';
|
|
5
6
|
import 'zod';
|
|
@@ -168,6 +169,11 @@ type ProductListItemContext = {
|
|
|
168
169
|
locale: string;
|
|
169
170
|
/** Adds the product to the cart the way the built-in button does. */
|
|
170
171
|
addToCart: () => Promise<void>;
|
|
172
|
+
/**
|
|
173
|
+
* Reports a click on the product (`product_clicked`). Call it from the link
|
|
174
|
+
* a custom card renders; the built-in card does it itself.
|
|
175
|
+
*/
|
|
176
|
+
trackClick: () => void;
|
|
171
177
|
};
|
|
172
178
|
type ProductListProps = UseProductsOptions & {
|
|
173
179
|
/**
|
|
@@ -189,6 +195,13 @@ type ProductListProps = UseProductsOptions & {
|
|
|
189
195
|
/** Open the cart drawer after the default add to cart. Defaults to `true`. */
|
|
190
196
|
showCartOnAdd?: boolean;
|
|
191
197
|
className?: string;
|
|
198
|
+
/**
|
|
199
|
+
* Usage statistics for your Bitsnap dashboard (list views, impressions,
|
|
200
|
+
* clicks, add to cart). On by default; `false` turns it off. An object
|
|
201
|
+
* lets you forward every event: `{ onEvent }`. Nothing is stored on the
|
|
202
|
+
* visitor's device.
|
|
203
|
+
*/
|
|
204
|
+
analytics?: AnalyticsOption;
|
|
192
205
|
};
|
|
193
206
|
/**
|
|
194
207
|
* Responsive grid of the shop's products with pagination, loading, empty and
|
|
@@ -202,7 +215,7 @@ type ProductListProps = UseProductsOptions & {
|
|
|
202
215
|
* server) to have the products in the HTML; without it the server renders the
|
|
203
216
|
* loading skeleton and the browser fetches the first page.
|
|
204
217
|
*/
|
|
205
|
-
declare function ProductList({ productHref, appearance, texts, renderItem, onAddToCart, showCartOnAdd, className, ...options }: ProductListProps): react_jsx_runtime.JSX.Element;
|
|
218
|
+
declare function ProductList({ productHref, appearance, texts, renderItem, onAddToCart, showCartOnAdd, className, analytics, ...options }: ProductListProps): react_jsx_runtime.JSX.Element;
|
|
206
219
|
|
|
207
220
|
/**
|
|
208
221
|
* Formats a price in minor units (grosze, cents) for the locale, the same way
|
package/dist/catalog.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
+
import { A as AnalyticsOption } from './instrument-CmxK7S4T.js';
|
|
3
4
|
import { S as SlotAppearance, A as AppearanceElements } from './appearance-core-Dt2grZdl.js';
|
|
4
5
|
import { BitsnapModels } from './models.js';
|
|
5
6
|
import 'zod';
|
|
@@ -168,6 +169,11 @@ type ProductListItemContext = {
|
|
|
168
169
|
locale: string;
|
|
169
170
|
/** Adds the product to the cart the way the built-in button does. */
|
|
170
171
|
addToCart: () => Promise<void>;
|
|
172
|
+
/**
|
|
173
|
+
* Reports a click on the product (`product_clicked`). Call it from the link
|
|
174
|
+
* a custom card renders; the built-in card does it itself.
|
|
175
|
+
*/
|
|
176
|
+
trackClick: () => void;
|
|
171
177
|
};
|
|
172
178
|
type ProductListProps = UseProductsOptions & {
|
|
173
179
|
/**
|
|
@@ -189,6 +195,13 @@ type ProductListProps = UseProductsOptions & {
|
|
|
189
195
|
/** Open the cart drawer after the default add to cart. Defaults to `true`. */
|
|
190
196
|
showCartOnAdd?: boolean;
|
|
191
197
|
className?: string;
|
|
198
|
+
/**
|
|
199
|
+
* Usage statistics for your Bitsnap dashboard (list views, impressions,
|
|
200
|
+
* clicks, add to cart). On by default; `false` turns it off. An object
|
|
201
|
+
* lets you forward every event: `{ onEvent }`. Nothing is stored on the
|
|
202
|
+
* visitor's device.
|
|
203
|
+
*/
|
|
204
|
+
analytics?: AnalyticsOption;
|
|
192
205
|
};
|
|
193
206
|
/**
|
|
194
207
|
* Responsive grid of the shop's products with pagination, loading, empty and
|
|
@@ -202,7 +215,7 @@ type ProductListProps = UseProductsOptions & {
|
|
|
202
215
|
* server) to have the products in the HTML; without it the server renders the
|
|
203
216
|
* loading skeleton and the browser fetches the first page.
|
|
204
217
|
*/
|
|
205
|
-
declare function ProductList({ productHref, appearance, texts, renderItem, onAddToCart, showCartOnAdd, className, ...options }: ProductListProps): react_jsx_runtime.JSX.Element;
|
|
218
|
+
declare function ProductList({ productHref, appearance, texts, renderItem, onAddToCart, showCartOnAdd, className, analytics, ...options }: ProductListProps): react_jsx_runtime.JSX.Element;
|
|
206
219
|
|
|
207
220
|
/**
|
|
208
221
|
* Formats a price in minor units (grosze, cents) for the locale, the same way
|
package/dist/catalog.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true});var
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});var _chunk3Z6VNXBPjs = require('./chunk-3Z6VNXBP.js');require('./chunk-KOYBZORL.js');require('./chunk-VIII45AC.js');require('./chunk-B32ACCW2.js');require('./chunk-XIHGWNTS.js');require('./chunk-JSDGZ4NY.js');exports.PRODUCT_LIST_SLOT_NAMES = _chunk3Z6VNXBPjs.a; exports.ProductList = _chunk3Z6VNXBPjs.d; exports.formatProductPrice = _chunk3Z6VNXBPjs.b; exports.useProducts = _chunk3Z6VNXBPjs.c;
|
|
2
2
|
//# sourceMappingURL=catalog.js.map
|
package/dist/catalog.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/emems/
|
|
1
|
+
{"version":3,"sources":["/home/emems/.t3/worktrees/bitsnap-react/visitor-cookie/dist/catalog.js"],"names":[],"mappings":"AAAA,+HAA4D,+BAA4B,+BAA4B,+BAA4B,+BAA4B,+BAA4B,0LAA+F","file":"/home/emems/.t3/worktrees/bitsnap-react/visitor-cookie/dist/catalog.js"}
|
package/dist/catalog.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{a as o,b as r,c as e,d as f}from"./chunk-
|
|
1
|
+
import{a as o,b as r,c as e,d as f}from"./chunk-6D3OORMK.mjs";import"./chunk-KOVKUJAW.mjs";import"./chunk-43IIYJ6J.mjs";import"./chunk-RLEC25JI.mjs";import"./chunk-ADYVTMGH.mjs";import"./chunk-U6FLIRIV.mjs";export{o as PRODUCT_LIST_SLOT_NAMES,f as ProductList,r as formatProductPrice,e as useProducts};
|
|
2
2
|
//# sourceMappingURL=catalog.mjs.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var _chunkKOYBZORLjs = require('./chunk-KOYBZORL.js');var _chunkVIII45ACjs = require('./chunk-VIII45AC.js');var _chunkB32ACCW2js = require('./chunk-B32ACCW2.js');var It=["root","grid","card","cardLink","image","imagePlaceholder","body","name","description","price","comparePrice","badge","actions","addToCartButton","selectButton","message","pagination","paginationButton","pageIndicator","emptyState","error","retryButton","skeleton"],wt={fontFamily:"--bitsnap-products-font-family",fontSize:"--bitsnap-products-font-size",background:"--bitsnap-products-background",surface:"--bitsnap-products-surface",foreground:"--bitsnap-products-foreground",mutedForeground:"--bitsnap-products-muted-foreground",border:"--bitsnap-products-border",buttonBackground:"--bitsnap-products-button-background",buttonForeground:"--bitsnap-products-button-foreground",buttonHoverBackground:"--bitsnap-products-button-hover-background",hoverSurface:"--bitsnap-products-hover-surface",focusRing:"--bitsnap-products-focus-ring",error:"--bitsnap-products-error",badgeBackground:"--bitsnap-products-badge-background",badgeForeground:"--bitsnap-products-badge-foreground",radius:"--bitsnap-products-radius"},Et={light:{background:"#ffffff",surface:"#f9fafb",foreground:"#18181b",mutedForeground:"#71717a",border:"#e4e4e7",buttonBackground:"#18181b",buttonForeground:"#fafafa",buttonHoverBackground:"#000000",hoverSurface:"#f4f4f5",focusRing:"#71717a",error:"#dc2626",badgeBackground:"#f4f4f5",badgeForeground:"#3f3f46",radius:"8px"},dark:{background:"#18181b",surface:"#27272a",foreground:"#fafafa",mutedForeground:"#a1a1aa",border:"#3f3f46",buttonBackground:"#fafafa",buttonForeground:"#18181b",buttonHoverBackground:"#e4e4e7",hoverSurface:"#3f3f46",focusRing:"#a1a1aa",error:"#f87171",badgeBackground:"#3f3f46",badgeForeground:"#e4e4e7",radius:"8px"}},_t=[];function lt(t){let e=_chunkKOYBZORLjs.a.call(void 0, t,{defaultTheme:"light",presets:Et,variables:wt,shadcnVariables:_t});return{theme:e.theme,tokens:e.tokens,elements:e.elements,boundaryClassName:e.theme,boundaryStyle:e.boundaryStyle}}function q(t,e){return _chunkKOYBZORLjs.b.call(void 0, t.elements,e)}function pt(t,e){try{return new Intl.NumberFormat(t,{style:"currency",currency:e,currencyDisplay:"symbol"})}catch (e2){return}}function H(t,e,r=_chunkVIII45ACjs.k){let o=t/100,n=_nullishCoalesce(pt(r,e), () => (pt(_chunkVIII45ACjs.k,e)));return n!=null?n.format(o):`${o.toFixed(2)} ${e}`}var _react = require('react');var Dt=24,Ot=48,M=class extends Error{constructor(e,r){super(e),this.name="ProductListError",this.status=r}};function O(t){return t==null||!Number.isFinite(t)?Dt:Math.min(Ot,Math.max(1,Math.floor(t)))}function mt(t,e){return Math.max(1,Math.ceil(t/e))}function N(t){return t.replace(/\/+$/,"")}function Nt(t){let e=new URLSearchParams;e.set("limit",String(t.limit)),e.set("offset",String(t.offset)),t.locale&&e.set("locale",t.locale);let r=_optionalChain([t, 'access', _2 => _2.search, 'optionalAccess', _3 => _3.trim, 'call', _4 => _4()]);return r&&e.set("search",r),`${N(t.host)}/api/integrations/${encodeURIComponent(t.projectID)}/public-commerce/product-list?${e.toString()}`}function Rt(t){return typeof t=="object"&&t!=null&&Array.isArray(t.items)&&typeof t.totalCount=="number"}async function ft(t,e){let r=await fetch(_chunkVIII45ACjs.c.call(void 0, Nt(t)),{method:"GET",credentials:"omit",headers:{Accept:"application/json"},signal:e});if(!r.ok)throw new M(`Could not load products (${r.status})`,r.status);let o=await r.json();if(!Rt(o))throw new M("Unexpected product list response");return o}function G(t){let{projectID:e,locale:r,search:o,initialData:n}=t,[a]=_react.useState.call(void 0, ()=>_chunkVIII45ACjs.f),i=_nullishCoalesce(t.host, () => (a)),d=O(t.pageSize),c=JSON.stringify([e,i,_nullishCoalesce(r, () => (null)),_nullishCoalesce(_optionalChain([o, 'optionalAccess', _5 => _5.trim, 'call', _6 => _6()]), () => (null)),d]),[m,u]=_react.useState.call(void 0, {queryKey:c,page:1});m.queryKey!==c&&u({queryKey:c,page:1});let[b,v]=_react.useState.call(void 0, 0),[f,l]=_react.useState.call(void 0, ()=>({requestKey:n!=null?`${c}|1|0`:"",data:n,error:void 0})),g=_nullishCoalesce(_optionalChain([f, 'access', _7 => _7.data, 'optionalAccess', _8 => _8.totalCount]), () => (0)),P=mt(g,d),h=m.queryKey===c?m.page:1,k=g>0?Math.min(h,P):h,C=`${c}|${k}|${b}`,T=f.requestKey===C;_react.useEffect.call(void 0, ()=>{if(T)return;let y=new AbortController;return ft({projectID:e,host:i,locale:r,search:o,limit:d,offset:(k-1)*d},y.signal).then(x=>{y.signal.aborted||l({requestKey:C,data:x,error:void 0})},x=>{y.signal.aborted||l(w=>({requestKey:C,data:w.data,error:x instanceof Error?x:new Error(String(x))}))}),()=>y.abort()},[C,T]);let I=_react.useCallback.call(void 0, y=>{let x=Math.max(1,Math.floor(y));u({queryKey:c,page:g>0?Math.min(x,P):x})},[c,g,P]),A=_react.useCallback.call(void 0, ()=>v(y=>y+1),[]);return _react.useMemo.call(void 0, ()=>({products:_nullishCoalesce(_optionalChain([f, 'access', _9 => _9.data, 'optionalAccess', _10 => _10.items]), () => ([])),totalCount:g,page:k,pageCount:P,setPage:I,isLoading:!T,error:T?f.error:void 0,refetch:A}),[f,g,k,P,I,T,A])}function Pt(t,e){if(t.projectID!=null&&t.projectID!==e.projectID)return{ok:!1,reason:`the page cart already belongs to project "${t.projectID}"`};let r=null,o=e.host?N(e.host):"",n=N(t.host);if(o&&o!==n){if(n!==N(t.defaultHost))return{ok:!1,reason:`the page cart already uses the host "${t.host}"`};r=o}return{ok:!0,setProjectID:t.projectID==null?e.projectID:null,setHost:r}}function Q(t,e){let r=e.cartItemId!=null&&e.cartItemId!==e.id?e.cartItemId:null;t.track("add_to_cart",{qty:1,value_minor:e.price,currency:e.currency},{product_id:e.id,variant_id:r})}async function bt(t,e){let[{Bitsnap:r},{getProjectID:o,setProjectID:n}]=await Promise.all([Promise.resolve().then(() => _interopRequireWildcard(require("./methods-A5KGZBAM.js"))),Promise.resolve().then(() => _interopRequireWildcard(require("./CartProvider-OHHYOMSG.js")))]),a=Pt({projectID:o(),host:_chunkVIII45ACjs.f,defaultHost:_chunkVIII45ACjs.e},e);if(!a.ok)return Ft(a.reason);if(a.setProjectID!=null&&n(a.setProjectID),a.setHost!=null&&_chunkVIII45ACjs.g.call(void 0, a.setHost),await r.addProductToCart(_nullishCoalesce(t.cartItemId, () => (t.id)),1,void 0,{name:t.name,price:t.price,currency:t.currency,analytics:_chunkB32ACCW2js.k})!=null)throw new Error("Could not add the product to the cart");e.analytics!=null&&Q(e.analytics,t),e.showCart&&r.showCart()}function Ft(t){throw console.warn(`bitsnap-react: ProductList did not add to the cart because ${t}. Pass onAddToCart to use another cart.`),new Error("The page cart belongs to another project or host")}var $=50,Ut=1e3;function yt(t,e=Ut){let r=new Set,o=new Map,n=[],a=[],i,d,c=!1;function m(){for(d!==void 0&&clearTimeout(d),d=void 0;n.length>0;){let l=n.slice(0,$),g=a.slice(0,$);n=n.slice($),a=a.slice($);try{t({productIds:l,positions:g})}catch (e3){}}}function u(){m();try{_optionalChain([_chunkB32ACCW2js.f.call(void 0, ), 'optionalAccess', _11 => _11.flush, 'call', _12 => _12()])}catch (e4){}}let b=()=>{typeof document<"u"&&document.visibilityState==="hidden"&&u()};function v(){c||typeof window>"u"||(c=!0,window.addEventListener("pagehide",u,!0),document.addEventListener("visibilitychange",b,!0))}function f(){if(i)return i;if(!(typeof IntersectionObserver>"u"))return i=new IntersectionObserver(l=>{for(let g of l){let P=o.get(g.target);!P||!g.isIntersecting||g.intersectionRatio<.5||(o.delete(g.target),_optionalChain([i, 'optionalAccess', _13 => _13.unobserve, 'call', _14 => _14(g.target)]),!r.has(P.id)&&(r.add(P.id),n.push(P.id),a.push(P.position)))}n.length>0&&d===void 0&&(d=setTimeout(m,e),_optionalChain([d, 'access', _15 => _15.unref, 'optionalCall', _16 => _16()]))},{threshold:[.5]}),v(),i}return{observe(l,g,P){try{if(r.has(g))return()=>{};let h=f();return h?(o.set(l,{id:g,position:P}),h.observe(l),()=>{o.delete(l),h.unobserve(l)}):()=>{}}catch (e5){return()=>{}}},flush:m,destroy(){try{m(),_optionalChain([i, 'optionalAccess', _17 => _17.disconnect, 'call', _18 => _18()]),c&&(window.removeEventListener("pagehide",u,!0),document.removeEventListener("visibilitychange",b,!0),c=!1)}catch (e6){}i=void 0,o.clear()}}}var Ht={en:{addToCart:"Add to cart",addingToCart:"Adding\u2026",addedToCart:"Added to cart",addToCartError:"Could not add to the cart. Try again.",chooseOptions:"Choose",viewProduct:"View",unavailable:"Unavailable",subscription:"Subscription",priceFrom:"from {price}",originalPrice:"Original price",loading:"Loading products",empty:"There are no products to show.",error:"Could not load the products.",retry:"Try again",pagination:"Pagination",previousPage:"Previous",nextPage:"Next",pageOf:"Page {page} of {pageCount}"},pl:{addToCart:"Dodaj do koszyka",addingToCart:"Dodawanie\u2026",addedToCart:"Dodano do koszyka",addToCartError:"Nie uda\u0142o si\u0119 doda\u0107 do koszyka. Spr\xF3buj ponownie.",chooseOptions:"Wybierz",viewProduct:"Zobacz",unavailable:"Niedost\u0119pny",subscription:"Subskrypcja",priceFrom:"od {price}",originalPrice:"Cena przed obni\u017Ck\u0105",loading:"\u0141adowanie produkt\xF3w",empty:"Brak produkt\xF3w do wy\u015Bwietlenia.",error:"Nie uda\u0142o si\u0119 za\u0142adowa\u0107 produkt\xF3w.",retry:"Spr\xF3buj ponownie",pagination:"Paginacja",previousPage:"Poprzednia",nextPage:"Nast\u0119pna",pageOf:"Strona {page} z {pageCount}"}};function Lt(t,e){return _chunkVIII45ACjs.i.call(void 0, Ht,t,e)}function X(t,e){return t.replace(/\{(\w+)\}/g,(r,o)=>o in e?String(e[o]):r)}var _jsxruntime = require('react/jsx-runtime');var Tt=_react.createContext.call(void 0, void 0),zt={observe:()=>()=>{},clicked:()=>{}},vt=_react.createContext.call(void 0, zt);function j(){let t=_react.useContext.call(void 0, Tt);if(t==null)throw new Error("ProductList parts must be rendered inside ProductList");return t}function p(t,e){let{appearance:r}=j(),o=q(r,t);return{"data-bitsnap-products-slot":t,className:_chunkVIII45ACjs.d.call(void 0, e,_optionalChain([o, 'optionalAccess', _19 => _19.className])),style:_optionalChain([o, 'optionalAccess', _20 => _20.style])}}var Z="inline-flex cursor-pointer items-center justify-center px-4 py-2 text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50",Ct=`${Z} w-full`,$t=`${Ct} no-underline`;function Kt({product:t,onAdd:e}){let{texts:r}=j(),[o,n]=_react.useState.call(void 0, "idle"),a=p("addToCartButton",Ct),i=p("message","mt-2 text-xs");_react.useEffect.call(void 0, ()=>{if(o!=="added")return;let m=setTimeout(()=>n("idle"),2e3);return()=>clearTimeout(m)},[o]);let d=_react.useCallback.call(void 0, async()=>{n("adding");try{await e(),n("added")}catch(m){console.error("bitsnap-react: could not add to the cart",m),n("error")}},[e]),c=o==="adding"?r.addingToCart:o==="added"?r.addedToCart:r.addToCart;return _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment,{children:[_jsxruntime.jsx.call(void 0, "button",{type:"button",...a,disabled:o==="adding","aria-label":`${c}: ${t.name}`,onClick:d,children:c}),o==="error"?_jsxruntime.jsx.call(void 0, "p",{...i,role:"alert",children:r.addToCartError}):null]})}function Vt({product:t,position:e,href:r,onAdd:o,renderItem:n}){let{texts:a,locale:i}=j(),d=_react.useContext.call(void 0, vt),c=_react.useRef.call(void 0, null);_react.useEffect.call(void 0, ()=>{if(c.current!=null)return d.observe(c.current,t.id,e)},[d,t.id,e]);let m=_react.useCallback.call(void 0, ()=>d.clicked(t,e),[d,t,e]),u=p("card","relative flex flex-col overflow-hidden border transition-colors"),b=p("cardLink","flex flex-1 flex-col no-underline"),v=p("image","aspect-square w-full overflow-hidden"),f=p("imagePlaceholder","aspect-square w-full"),l=p("body","flex flex-1 flex-col gap-1 p-4"),g=p("name","text-base font-medium leading-snug"),P=p("description","line-clamp-2 text-sm"),h=p("price","mt-auto pt-2 text-base font-semibold"),k=p("comparePrice","ml-2 text-sm font-normal"),C=p("badge","absolute left-3 top-3 px-2 py-0.5 text-xs font-medium"),T=p("actions","p-4 pt-0"),I=p("selectButton",$t),A=H(t.price,t.currency,i),y=t.compareAtPrice!=null&&t.compareAtPrice>t.price?H(t.compareAtPrice,t.currency,i):null;if(n!=null)return _jsxruntime.jsx.call(void 0, "li",{ref:c,...u,"data-unavailable":t.available?void 0:"",children:n(t,{href:r,price:A,compareAtPrice:y,texts:a,locale:i,addToCart:o,trackClick:m})});let x=_nullishCoalesce(_optionalChain([t, 'access', _21 => _21.image, 'optionalAccess', _22 => _22.thumbnailUrl]), () => (_optionalChain([t, 'access', _23 => _23.image, 'optionalAccess', _24 => _24.url]))),w=t.available?t.isRecurring?a.subscription:null:a.unavailable;return _jsxruntime.jsxs.call(void 0, "li",{ref:c,...u,"data-unavailable":t.available?void 0:"",children:[_jsxruntime.jsxs.call(void 0, "a",{...b,href:r,onClick:m,children:[x!=null?_jsxruntime.jsx.call(void 0, "div",{...v,children:_jsxruntime.jsx.call(void 0, "img",{src:x,alt:"",loading:"lazy",decoding:"async",className:"h-full w-full object-cover"})}):_jsxruntime.jsx.call(void 0, "div",{...f,"aria-hidden":"true"}),_jsxruntime.jsxs.call(void 0, "div",{...l,children:[_jsxruntime.jsx.call(void 0, "h3",{...g,children:t.name}),t.shortDescription?_jsxruntime.jsx.call(void 0, "p",{...P,children:t.shortDescription}):null,_jsxruntime.jsxs.call(void 0, "p",{...h,children:[t.priceFrom?X(a.priceFrom,{price:A}):A,y!=null?_jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment,{children:[_jsxruntime.jsxs.call(void 0, "span",{className:"sr-only",children:[a.originalPrice,": "]}),_jsxruntime.jsx.call(void 0, "del",{...k,children:y})]}):null]})]})]}),w!=null?_jsxruntime.jsx.call(void 0, "span",{...C,"data-kind":t.available?"subscription":"unavailable",children:w}):null,_jsxruntime.jsx.call(void 0, "div",{...T,children:t.available?t.addToCart?_jsxruntime.jsx.call(void 0, Kt,{product:t,onAdd:o}):_jsxruntime.jsx.call(void 0, "a",{...I,href:r,onClick:m,children:t.variantCount>1?a.chooseOptions:a.viewProduct}):_jsxruntime.jsx.call(void 0, "button",{type:"button",...I,disabled:!0,children:a.unavailable})})]})}var St="m-0 grid list-none grid-cols-[repeat(auto-fill,minmax(min(100%,15rem),1fr))] gap-5 p-0";function qt({count:t,label:e}){let r=p("grid",St),o=p("skeleton","aspect-[3/4] animate-pulse");return _jsxruntime.jsx.call(void 0, "div",{...r,role:"status","aria-busy":"true","aria-label":e,children:Array.from({length:t},(n,a)=>_jsxruntime.jsx.call(void 0, "div",{...o,"aria-hidden":"true"},a))})}function Gt({page:t,pageCount:e,isLoading:r,onPage:o}){let{texts:n}=j(),a=p("pagination","mt-8 flex items-center justify-center gap-4"),i=p("paginationButton",Z),d=p("pageIndicator","text-sm");return _jsxruntime.jsxs.call(void 0, "nav",{...a,"aria-label":n.pagination,children:[_jsxruntime.jsx.call(void 0, "button",{type:"button",...i,"aria-disabled":r||t<=1,onClick:()=>{!r&&t>1&&o(t-1)},children:n.previousPage}),_jsxruntime.jsx.call(void 0, "span",{...d,"aria-live":"polite",children:X(n.pageOf,{page:t,pageCount:e})}),_jsxruntime.jsx.call(void 0, "button",{type:"button",...i,"aria-disabled":r||t>=e,onClick:()=>{!r&&t<e&&o(t+1)},children:n.nextPage})]})}function Qt({options:t,productHref:e,renderItem:r,onAddToCart:o,showCartOnAdd:n}){let{texts:a}=j(),{products:i,page:d,pageCount:c,setPage:m,isLoading:u,error:b,refetch:v}=G(t),f=_chunkB32ACCW2js.m.call(void 0, ),l=_optionalChain([t, 'access', _25 => _25.search, 'optionalAccess', _26 => _26.trim, 'call', _27 => _27()])?"search":"catalog",g=O(t.pageSize),P=_react.useRef.call(void 0, !1);_react.useEffect.call(void 0, ()=>{P.current||u||b!=null||(P.current=!0,f.track("product_list_viewed",{list_id:l,item_count:i.length}))},[f,u,b,l,i.length]);let h=_react.useMemo.call(void 0, ()=>yt(({productIds:L,positions:_})=>f.track("product_impression",{list_id:l,product_ids:L,positions:_})),[f,l]);_react.useEffect.call(void 0, ()=>()=>h.destroy(),[h]);let k=_react.useMemo.call(void 0, ()=>({observe:h.observe,clicked:(L,_)=>f.track("product_clicked",{list_id:l,position:_},{product_id:L.id})}),[h,f,l]),C=_react.useRef.call(void 0, null),T=_react.useRef.call(void 0, !1),I=_react.useCallback.call(void 0, L=>{T.current=!0,m(L)},[m]);_react.useEffect.call(void 0, ()=>{u||!T.current||(T.current=!1,_optionalChain([C, 'access', _28 => _28.current, 'optionalAccess', _29 => _29.focus, 'call', _30 => _30({preventScroll:!0})]),_optionalChain([C, 'access', _31 => _31.current, 'optionalAccess', _32 => _32.scrollIntoView, 'call', _33 => _33({block:"start"})]))},[u]);let A=p("grid",St),y=p("emptyState","py-12 text-center text-sm"),x=p("error","flex flex-col items-center gap-3 py-12 text-center text-sm"),w=p("retryButton",Z),{projectID:J,host:W}=t,kt=_react.useCallback.call(void 0, L=>async()=>{if(o!=null){await _chunkB32ACCW2js.p.call(void 0, ()=>o(L)),Q(f,L);return}await bt(L,{projectID:J,host:W,showCart:n,analytics:f})},[o,J,W,n,f]),E;return u&&i.length===0?E=_jsxruntime.jsx.call(void 0, qt,{count:Math.min(O(t.pageSize),8),label:a.loading}):b!=null?E=_jsxruntime.jsxs.call(void 0, "div",{...x,role:"alert",children:[_jsxruntime.jsx.call(void 0, "p",{children:a.error}),_jsxruntime.jsx.call(void 0, "button",{type:"button",...w,onClick:v,children:a.retry})]}):i.length===0?E=_jsxruntime.jsx.call(void 0, "p",{...y,children:a.empty}):E=_jsxruntime.jsx.call(void 0, "ul",{...A,"aria-busy":u||void 0,className:_chunkVIII45ACjs.d.call(void 0, A.className,u&&"opacity-60"),children:i.map((L,_)=>_jsxruntime.jsx.call(void 0, Vt,{product:L,position:(d-1)*g+_,href:e(L),onAdd:kt(L),renderItem:r},L.id))}),_jsxruntime.jsx.call(void 0, vt.Provider,{value:k,children:_jsxruntime.jsxs.call(void 0, "div",{ref:C,tabIndex:-1,className:"p-1 outline-none scroll-mt-4",children:[E,c>1&&b==null?_jsxruntime.jsx.call(void 0, Gt,{page:d,pageCount:c,isLoading:u,onPage:I}):null]})})}var Xt=t=>`/p/${encodeURIComponent(t.uniqueSlug)}`;function Zt({productHref:t=Xt,appearance:e,texts:r,renderItem:o,onAddToCart:n,showCartOnAdd:a=!0,className:i,analytics:d,...c}){let m=_chunkB32ACCW2js.n.call(void 0, c.projectID,d,"catalog"),u=_nullishCoalesce(c.locale, () => (_chunkVIII45ACjs.k)),b=_react.useMemo.call(void 0, ()=>lt(e),[e]),v=_react.useMemo.call(void 0, ()=>Lt(u,r),[u,r]),f=_react.useMemo.call(void 0, ()=>({appearance:b,texts:v,locale:u}),[b,v,u]),l=q(b,"root");return _jsxruntime.jsx.call(void 0, _chunkB32ACCW2js.l,{value:m,children:_jsxruntime.jsx.call(void 0, Tt.Provider,{value:f,children:_jsxruntime.jsx.call(void 0, "div",{"data-bitsnap-products":"root","data-bitsnap-products-slot":"root",className:_chunkVIII45ACjs.d.call(void 0, "bitsnap-react",b.boundaryClassName,i,_optionalChain([l, 'optionalAccess', _34 => _34.className])),style:{...b.boundaryStyle,..._optionalChain([l, 'optionalAccess', _35 => _35.style])},children:_jsxruntime.jsx.call(void 0, Qt,{options:c,productHref:t,renderItem:o,onAddToCart:n,showCartOnAdd:a})})})})}exports.a = It; exports.b = H; exports.c = G; exports.d = Zt;
|
|
2
|
+
//# sourceMappingURL=chunk-3Z6VNXBP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/emems/.t3/worktrees/bitsnap-react/visitor-cookie/dist/chunk-3Z6VNXBP.js","../src/components/products/appearance.ts","../src/components/products/format.ts"],"names":["PRODUCT_LIST_SLOT_NAMES","PRODUCT_LIST_TOKEN_VARIABLES","PRODUCT_LIST_THEME_PRESETS","PRODUCT_LIST_SHADCN_VARIABLES","resolveProductListAppearance","appearance","resolved","resolveAppearanceCore","getProductListSlotAppearance","slot","getSlotAppearance","createFormatter","locale","currency","formatProductPrice","amount","DEFAULT_LOCALE","value","formatter"],"mappings":"AAAA,y/BAAgD,sDAAqF,sDAAiF,IC2DzMA,EAAAA,CAA0B,CACrC,MAAA,CACA,MAAA,CACA,MAAA,CACA,UAAA,CACA,OAAA,CACA,kBAAA,CACA,MAAA,CACA,MAAA,CACA,aAAA,CACA,OAAA,CACA,cAAA,CACA,OAAA,CACA,SAAA,CACA,iBAAA,CACA,cAAA,CACA,SAAA,CACA,YAAA,CACA,kBAAA,CACA,eAAA,CACA,YAAA,CACA,OAAA,CACA,aAAA,CACA,UACF,CAAA,CA8BaC,EAAAA,CAA+B,CAC1C,UAAA,CAAY,gCAAA,CACZ,QAAA,CAAU,8BAAA,CACV,UAAA,CAAY,+BAAA,CACZ,OAAA,CAAS,4BAAA,CACT,UAAA,CAAY,+BAAA,CACZ,eAAA,CAAiB,qCAAA,CACjB,MAAA,CAAQ,2BAAA,CACR,gBAAA,CAAkB,sCAAA,CAClB,gBAAA,CAAkB,sCAAA,CAClB,qBAAA,CAAuB,4CAAA,CACvB,YAAA,CAAc,kCAAA,CACd,SAAA,CAAW,+BAAA,CACX,KAAA,CAAO,0BAAA,CACP,eAAA,CAAiB,qCAAA,CACjB,eAAA,CAAiB,qCAAA,CACjB,MAAA,CAAQ,2BACV,CAAA,CASaC,EAAAA,CAGT,CACF,KAAA,CAAO,CACL,UAAA,CAAY,SAAA,CACZ,OAAA,CAAS,SAAA,CACT,UAAA,CAAY,SAAA,CACZ,eAAA,CAAiB,SAAA,CACjB,MAAA,CAAQ,SAAA,CACR,gBAAA,CAAkB,SAAA,CAClB,gBAAA,CAAkB,SAAA,CAClB,qBAAA,CAAuB,SAAA,CACvB,YAAA,CAAc,SAAA,CACd,SAAA,CAAW,SAAA,CACX,KAAA,CAAO,SAAA,CACP,eAAA,CAAiB,SAAA,CACjB,eAAA,CAAiB,SAAA,CACjB,MAAA,CAAQ,KACV,CAAA,CACA,IAAA,CAAM,CACJ,UAAA,CAAY,SAAA,CACZ,OAAA,CAAS,SAAA,CACT,UAAA,CAAY,SAAA,CACZ,eAAA,CAAiB,SAAA,CACjB,MAAA,CAAQ,SAAA,CACR,gBAAA,CAAkB,SAAA,CAClB,gBAAA,CAAkB,SAAA,CAClB,qBAAA,CAAuB,SAAA,CACvB,YAAA,CAAc,SAAA,CACd,SAAA,CAAW,SAAA,CACX,KAAA,CAAO,SAAA,CACP,eAAA,CAAiB,SAAA,CACjB,eAAA,CAAiB,SAAA,CACjB,MAAA,CAAQ,KACV,CACF,CAAA,CAGMC,EAAAA,CACJ,CAAC,CAAA,CAEI,SAASC,EAAAA,CACdC,CAAAA,CAC+B,CAC/B,IAAMC,CAAAA,CAAWC,gCAAAA,CAIfF,CAAY,CACZ,YAAA,CAAc,OAAA,CACd,OAAA,CAASH,EAAAA,CACT,SAAA,CAAWD,EAAAA,CACX,eAAA,CAAiBE,EACnB,CAAC,CAAA,CAED,MAAO,CACL,KAAA,CAAOG,CAAAA,CAAS,KAAA,CAChB,MAAA,CAAQA,CAAAA,CAAS,MAAA,CACjB,QAAA,CAAUA,CAAAA,CAAS,QAAA,CACnB,iBAAA,CAAmBA,CAAAA,CAAS,KAAA,CAC5B,aAAA,CAAeA,CAAAA,CAAS,aAC1B,CACF,CAGO,SAASE,CAAAA,CACdF,CAAAA,CACAG,CAAAA,CACuC,CACvC,OAAOC,gCAAAA,CAAkBJ,CAAS,QAAA,CAAUG,CAAI,CAClD,CChNA,SAASE,EAAAA,CACPC,CAAAA,CACAC,CAAAA,CAC+B,CAC/B,GAAI,CACF,OAAO,IAAI,IAAA,CAAK,YAAA,CAAaD,CAAAA,CAAQ,CACnC,KAAA,CAAO,UAAA,CACP,QAAA,CAAAC,CAAAA,CACA,eAAA,CAAiB,QACnB,CAAC,CACH,CAAA,UAAQ,CAEN,MACF,CACF,CAOO,SAASC,CAAAA,CACdC,CAAAA,CACAF,CAAAA,CACAD,CAAAA,CAAiBI,kBAAAA,CACT,CACR,IAAMC,CAAAA,CAAQF,CAAAA,CAAS,GAAA,CACjBG,CAAAA,kBACJP,EAAAA,CAAgBC,CAAAA,CAAQC,CAAQ,CAAA,SAChCF,EAAAA,CAAgBK,kBAAAA,CAAgBH,CAAQ,GAAA,CAE1C,OAAOK,CAAAA,EAAa,IAAA,CAChBA,CAAAA,CAAU,MAAA,CAAOD,CAAK,CAAA,CACtB,CAAA,EAAA","file":"/home/emems/.t3/worktrees/bitsnap-react/visitor-cookie/dist/chunk-3Z6VNXBP.js","sourcesContent":[null,"import type { CSSProperties } from \"react\";\nimport {\n getSlotAppearance,\n resolveAppearanceCore,\n type AppearanceElements,\n type ShadcnVariableMapping,\n type SlotAppearance,\n} from \"@/src/lib/appearance-core\";\n\n/**\n * Product list appearance API.\n *\n * It mirrors `CheckoutAppearance` and `ReturnsAppearance` (theme, tokens,\n * elements) so a shop can reuse one brand object for all of them. The resolver\n * is pure: it turns the caller input into a theme name, the tokens in effect,\n * the slot overrides and the inline style for the theme boundary.\n */\n\nexport type ProductListTheme = \"light\" | \"dark\";\n\nexport type ProductListAppearanceTokens = {\n /** Font family of the list. The library never loads fonts. */\n fontFamily?: string;\n /** Base font size. */\n fontSize?: string;\n /** List background. */\n background?: string;\n /** Raised surfaces: cards, image placeholders, skeletons. */\n surface?: string;\n /** Main text color. */\n foreground?: string;\n /** Secondary text: descriptions, the struck-through price. */\n mutedForeground?: string;\n /** Borders of cards and buttons. */\n border?: string;\n /** \"Add to cart\" button background. */\n buttonBackground?: string;\n /** \"Add to cart\" button text. */\n buttonForeground?: string;\n /** \"Add to cart\" button background on hover. */\n buttonHoverBackground?: string;\n /** Hover surface of cards and secondary buttons. */\n hoverSurface?: string;\n /** Focus ring color. */\n focusRing?: string;\n /** Error and unavailable state color. */\n error?: string;\n /** Badge background (subscription, unavailable). */\n badgeBackground?: string;\n /** Badge text. */\n badgeForeground?: string;\n /** Corner radius of cards, images and buttons. */\n radius?: string;\n};\n\n/**\n * Stable slot names. They are exposed as `data-bitsnap-products-slot`\n * attributes so they can also be targeted from plain CSS.\n */\nexport const PRODUCT_LIST_SLOT_NAMES = [\n \"root\",\n \"grid\",\n \"card\",\n \"cardLink\",\n \"image\",\n \"imagePlaceholder\",\n \"body\",\n \"name\",\n \"description\",\n \"price\",\n \"comparePrice\",\n \"badge\",\n \"actions\",\n \"addToCartButton\",\n \"selectButton\",\n \"message\",\n \"pagination\",\n \"paginationButton\",\n \"pageIndicator\",\n \"emptyState\",\n \"error\",\n \"retryButton\",\n \"skeleton\",\n] as const;\n\nexport type ProductListSlotName = (typeof PRODUCT_LIST_SLOT_NAMES)[number];\n\nexport type ProductListSlotAppearance = SlotAppearance;\n\nexport type ProductListAppearanceElements =\n AppearanceElements<ProductListSlotName>;\n\nexport type ProductListAppearance = {\n /** `\"light\"` or `\"dark\"`. Omitted means `\"light\"`. */\n theme?: ProductListTheme;\n /** Partial overrides applied over the theme preset. */\n tokens?: ProductListAppearanceTokens;\n /** Per element `className`/`style` overrides. */\n elements?: ProductListAppearanceElements;\n};\n\nexport type ResolvedProductListAppearance = {\n theme: ProductListTheme;\n /** Tokens that are in effect (the theme preset plus caller overrides). */\n tokens: ProductListAppearanceTokens;\n elements: ProductListAppearanceElements;\n /** Theme marker class for the theme boundary. */\n boundaryClassName: string;\n /** Inline custom properties applied to the list root. */\n boundaryStyle: CSSProperties;\n};\n\n/** CSS custom property used for each token. */\nexport const PRODUCT_LIST_TOKEN_VARIABLES = {\n fontFamily: \"--bitsnap-products-font-family\",\n fontSize: \"--bitsnap-products-font-size\",\n background: \"--bitsnap-products-background\",\n surface: \"--bitsnap-products-surface\",\n foreground: \"--bitsnap-products-foreground\",\n mutedForeground: \"--bitsnap-products-muted-foreground\",\n border: \"--bitsnap-products-border\",\n buttonBackground: \"--bitsnap-products-button-background\",\n buttonForeground: \"--bitsnap-products-button-foreground\",\n buttonHoverBackground: \"--bitsnap-products-button-hover-background\",\n hoverSurface: \"--bitsnap-products-hover-surface\",\n focusRing: \"--bitsnap-products-focus-ring\",\n error: \"--bitsnap-products-error\",\n badgeBackground: \"--bitsnap-products-badge-background\",\n badgeForeground: \"--bitsnap-products-badge-foreground\",\n radius: \"--bitsnap-products-radius\",\n} as const;\n\nexport type ProductListTokenName = keyof typeof PRODUCT_LIST_TOKEN_VARIABLES;\n\n/**\n * Complete palettes. They carry colors and radius only, so typography is never\n * reset implicitly. `products.css` falls back to the light values, which keeps\n * the list usable even when the boundary style is missing.\n */\nexport const PRODUCT_LIST_THEME_PRESETS: Record<\n ProductListTheme,\n ProductListAppearanceTokens\n> = {\n light: {\n background: \"#ffffff\",\n surface: \"#f9fafb\",\n foreground: \"#18181b\",\n mutedForeground: \"#71717a\",\n border: \"#e4e4e7\",\n buttonBackground: \"#18181b\",\n buttonForeground: \"#fafafa\",\n buttonHoverBackground: \"#000000\",\n hoverSurface: \"#f4f4f5\",\n focusRing: \"#71717a\",\n error: \"#dc2626\",\n badgeBackground: \"#f4f4f5\",\n badgeForeground: \"#3f3f46\",\n radius: \"8px\",\n },\n dark: {\n background: \"#18181b\",\n surface: \"#27272a\",\n foreground: \"#fafafa\",\n mutedForeground: \"#a1a1aa\",\n border: \"#3f3f46\",\n buttonBackground: \"#fafafa\",\n buttonForeground: \"#18181b\",\n buttonHoverBackground: \"#e4e4e7\",\n hoverSurface: \"#3f3f46\",\n focusRing: \"#a1a1aa\",\n error: \"#f87171\",\n badgeBackground: \"#3f3f46\",\n badgeForeground: \"#e4e4e7\",\n radius: \"8px\",\n },\n};\n\n/** The list renders its own elements, so no shared primitive needs remapping. */\nconst PRODUCT_LIST_SHADCN_VARIABLES: ShadcnVariableMapping<ProductListTokenName> =\n [];\n\nexport function resolveProductListAppearance(\n appearance?: ProductListAppearance,\n): ResolvedProductListAppearance {\n const resolved = resolveAppearanceCore<\n ProductListTokenName,\n ProductListTheme,\n ProductListSlotName\n >(appearance, {\n defaultTheme: \"light\",\n presets: PRODUCT_LIST_THEME_PRESETS,\n variables: PRODUCT_LIST_TOKEN_VARIABLES,\n shadcnVariables: PRODUCT_LIST_SHADCN_VARIABLES,\n });\n\n return {\n theme: resolved.theme,\n tokens: resolved.tokens,\n elements: resolved.elements,\n boundaryClassName: resolved.theme,\n boundaryStyle: resolved.boundaryStyle,\n };\n}\n\n/** Caller overrides for a single slot, if any were provided. */\nexport function getProductListSlotAppearance(\n resolved: ResolvedProductListAppearance,\n slot: ProductListSlotName,\n): ProductListSlotAppearance | undefined {\n return getSlotAppearance(resolved.elements, slot);\n}\n","import { DEFAULT_LOCALE } from \"../checkout/locale\";\n\nfunction createFormatter(\n locale: string,\n currency: string,\n): Intl.NumberFormat | undefined {\n try {\n return new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency,\n currencyDisplay: \"symbol\",\n });\n } catch {\n // RangeError: invalid locale or currency code.\n return undefined;\n }\n}\n\n/**\n * Formats a price in minor units (grosze, cents) for the locale, the same way\n * the cart does (`amount / 100`). An unknown locale falls back to Polish and an\n * invalid currency code to a plain number followed by the code.\n */\nexport function formatProductPrice(\n amount: number,\n currency: string,\n locale: string = DEFAULT_LOCALE,\n): string {\n const value = amount / 100;\n const formatter =\n createFormatter(locale, currency) ??\n createFormatter(DEFAULT_LOCALE, currency);\n\n return formatter != null\n ? formatter.format(value)\n : `${value.toFixed(2)} ${currency}`;\n}\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{a as c}from"./chunk-RLEC25JI.mjs";var p="X-Bitsnap-Client",a=`bitsnap-react@${c}`,T="bs_client",L={[p]:a},E=e=>t=>(t.header.set(p,a),e(t));function h(e){let t=e.indexOf("#"),o=t===-1?e:e.slice(0,t),s=t===-1?"":e.slice(t),n=o.includes("?")?/[?&]$/.test(o)?"":"&":"?";return`${o}${n}${T}=${encodeURIComponent(a)}${s}`}var y="https://bitsnap.pl",g=y;function v(e){console.log("setCustomHost",e),g=e}function d(e,t){return t.toLowerCase().startsWith("en")?e.en:e.pl}function f(e,t){let o={...e};if(t!=null)for(let[s,n]of Object.entries(t))n!=null&&(o[s]=n);return o}function u(e,t,o){return f(d(e,t),o)}function x(e,t){return e[t]??t}var C={en:{cart:"Cart",emptyCart:"Your cart is empty.",total:"Total:",delivery:"+ delivery",chooseCountry:"Choose a country",nextStep:"Next step",unavailableProducts:"Not all products are available."},pl:{cart:"Koszyk",emptyCart:"Brak produkt\xF3w w koszyku.",total:"Suma:",delivery:"+ dostawa",chooseCountry:"Wybierz kraj",nextStep:"Nast\u0119pny krok",unavailableProducts:"Nie wszystkie produkty s\u0105 dost\u0119pne w magazynie."}};function m(e,t){return u(C,e,t)}function A(e,t,o){return x(m(t,o),e)}var i="pl-PL",l="bitsnap-locale",r;function w(e){let t=e?.trim();r=t?t.toLowerCase()==="en"?"en-US":t.toLowerCase()==="pl"?i:t:void 0,typeof localStorage<"u"&&(r==null?localStorage.removeItem(l):localStorage.setItem(l,r))}function H(){return r??(typeof localStorage<"u"?localStorage.getItem(l)??i:i)}import{clsx as b}from"clsx";import{twMerge as K}from"tailwind-merge";function $(...e){return K(b(e))}export{L as a,E as b,h as c,$ as d,y as e,g as f,v as g,d as h,u as i,A as j,i as k,w as l,H as m};
|
|
2
|
+
//# sourceMappingURL=chunk-43IIYJ6J.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/client-header.ts","../src/components/checkout/constants.ts","../src/lib/texts.ts","../src/components/checkout/texts.ts","../src/components/checkout/locale.ts","../src/lib/utils.ts"],"sourcesContent":["import type { Interceptor } from \"@connectrpc/connect\";\nimport { SDK_VERSION } from \"../version\";\n\nexport const CLIENT_HEADER_NAME = \"X-Bitsnap-Client\";\nexport const CLIENT_HEADER_VALUE = `bitsnap-react@${SDK_VERSION}`;\n\nexport const CLIENT_QUERY_PARAM = \"bs_client\";\n\n/**\n * Spread into the `headers` of a fetch that already triggers a CORS preflight\n * (JSON POST, Connect RPC). Never use it on a simple cross-origin GET: the\n * custom header would force a preflight that older servers and merchant\n * proxies do not allow. Use `withClientParam` there.\n */\nexport const clientHeaders: Record<string, string> = {\n [CLIENT_HEADER_NAME]: CLIENT_HEADER_VALUE,\n};\n\n/** Connect interceptor adding the client header to every RPC. */\nexport const clientHeaderInterceptor: Interceptor = (next) => (req) => {\n req.header.set(CLIENT_HEADER_NAME, CLIENT_HEADER_VALUE);\n return next(req);\n};\n\n/** Adds `bs_client=<pkg>@<ver>` to a URL, for simple GETs that must stay preflight-free. */\nexport function withClientParam(url: string): string {\n const hash = url.indexOf(\"#\");\n const base = hash === -1 ? url : url.slice(0, hash);\n const fragment = hash === -1 ? \"\" : url.slice(hash);\n const separator = base.includes(\"?\") ? (/[?&]$/.test(base) ? \"\" : \"&\") : \"?\";\n return `${base}${separator}${CLIENT_QUERY_PARAM}=${encodeURIComponent(CLIENT_HEADER_VALUE)}${fragment}`;\n}\n","export const DEFAULT_HOST = \"https://bitsnap.pl\";\nexport let HOST = DEFAULT_HOST;\n\nexport function setCustomHost(host: string) {\n console.log(\"setCustomHost\", host);\n HOST = host;\n}\n","/**\n * Shared text machinery: typed string tables per locale, the locale fallback\n * and a merge with caller overrides. Each module (checkout, later returns)\n * supplies its own key type and default tables.\n */\n\n/** Strings of one locale, keyed by text key. */\nexport type TextTable<Key extends string> = Record<Key, string>;\n\n/** Default tables for the supported locales. */\nexport type LocaleTables<Key extends string> = {\n en: TextTable<Key>;\n pl: TextTable<Key>;\n};\n\n/**\n * Picks the table for a locale. Any locale starting with `en` (case\n * insensitive, so `en`, `en-US`, `EN-gb`) gets English; everything else,\n * including unknown locales, gets the Polish default.\n */\nexport function pickLocaleTable<Key extends string>(\n tables: LocaleTables<Key>,\n locale: string,\n): TextTable<Key> {\n return locale.toLowerCase().startsWith(\"en\") ? tables.en : tables.pl;\n}\n\n/**\n * Merges caller overrides over a table. Overrides apply regardless of the\n * locale; `undefined`/`null` entries are ignored so the default stays.\n */\nexport function mergeTexts<Key extends string>(\n table: TextTable<Key>,\n overrides?: Partial<TextTable<Key>>,\n): TextTable<Key> {\n const result: Record<string, string> = { ...table };\n\n if (overrides != null) {\n for (const [key, value] of Object.entries<string | undefined>(overrides)) {\n if (value != null) {\n result[key] = value;\n }\n }\n }\n\n return result as TextTable<Key>;\n}\n\n/** Locale table merged with the caller overrides. */\nexport function resolveTexts<Key extends string>(\n tables: LocaleTables<Key>,\n locale: string,\n overrides?: Partial<TextTable<Key>>,\n): TextTable<Key> {\n return mergeTexts(pickLocaleTable(tables, locale), overrides);\n}\n\n/** Looks up a single text. Unknown keys return the key itself. */\nexport function lookupText<Key extends string>(\n texts: TextTable<Key>,\n key: Key,\n): string {\n return (texts as Record<string, string | undefined>)[key] ?? key;\n}\n","import {\n lookupText,\n resolveTexts,\n type LocaleTables,\n} from \"@/src/lib/texts\";\n\n/**\n * Every string the checkout renders. Pass a partial object through the\n * `texts` prop of `BitsnapCheckout` to override any of them, whatever the\n * locale.\n */\nexport type CheckoutTexts = {\n /** Drawer title. */\n cart: string;\n /** Shown when the cart has no products. */\n emptyCart: string;\n /** Label next to the total price. */\n total: string;\n /** Delivery note next to the total price. */\n delivery: string;\n /** Country selector placeholder. */\n chooseCountry: string;\n /** Checkout button label. */\n nextStep: string;\n /** Error shown when some products are out of stock. */\n unavailableProducts: string;\n};\n\nexport type CheckoutTextKey = keyof CheckoutTexts;\n\nexport const CHECKOUT_TEXT_TABLES: LocaleTables<CheckoutTextKey> = {\n en: {\n cart: \"Cart\",\n emptyCart: \"Your cart is empty.\",\n total: \"Total:\",\n delivery: \"+ delivery\",\n chooseCountry: \"Choose a country\",\n nextStep: \"Next step\",\n unavailableProducts: \"Not all products are available.\",\n },\n pl: {\n cart: \"Koszyk\",\n emptyCart: \"Brak produktów w koszyku.\",\n total: \"Suma:\",\n delivery: \"+ dostawa\",\n chooseCountry: \"Wybierz kraj\",\n nextStep: \"Następny krok\",\n unavailableProducts: \"Nie wszystkie produkty są dostępne w magazynie.\",\n },\n};\n\n/** Default texts for a locale merged with the caller overrides. */\nexport function resolveCheckoutTexts(\n locale: string,\n overrides?: Partial<CheckoutTexts>,\n): CheckoutTexts {\n return resolveTexts(CHECKOUT_TEXT_TABLES, locale, overrides);\n}\n\nexport function getCheckoutText(\n key: CheckoutTextKey,\n locale: string,\n overrides?: Partial<CheckoutTexts>,\n): string {\n return lookupText(resolveCheckoutTexts(locale, overrides), key);\n}\n","import { pickLocaleTable } from \"@/src/lib/texts\";\nimport { CHECKOUT_TEXT_TABLES } from \"./texts\";\n\nexport const DEFAULT_LOCALE = \"pl-PL\" as const;\nconst localeStorageKey = \"bitsnap-locale\";\n\nlet activeLocale: string | undefined;\n\nexport function setLocale(locale?: string): void {\n const value = locale?.trim();\n activeLocale = value\n ? value.toLowerCase() === \"en\"\n ? \"en-US\"\n : value.toLowerCase() === \"pl\"\n ? DEFAULT_LOCALE\n : value\n : undefined;\n if (typeof localStorage !== \"undefined\") {\n if (activeLocale == null) {\n localStorage.removeItem(localeStorageKey);\n } else {\n localStorage.setItem(localeStorageKey, activeLocale);\n }\n }\n}\n\nexport function getLocale(): string {\n if (activeLocale != null) {\n return activeLocale;\n }\n if (typeof localStorage !== \"undefined\") {\n return localStorage.getItem(localeStorageKey) ?? DEFAULT_LOCALE;\n }\n return DEFAULT_LOCALE;\n}\n\nexport function translate(key: string, locale: string = getLocale()): string {\n return (\n (pickLocaleTable(CHECKOUT_TEXT_TABLES, locale) as Record<string, string>)[\n key\n ] ?? key\n );\n}\n","import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport type Modify<T, R> = Omit<T, keyof R> & R;\n\nexport type Partial<T> = {\n [P in keyof T]?: T[P];\n};\n"],"mappings":"yCAGO,IAAMA,EAAqB,mBACrBC,EAAsB,iBAAiBC,CAAW,GAElDC,EAAqB,YAQrBC,EAAwC,CACnD,CAACJ,CAAkB,EAAGC,CACxB,EAGaI,EAAwCC,GAAUC,IAC7DA,EAAI,OAAO,IAAIP,EAAoBC,CAAmB,EAC/CK,EAAKC,CAAG,GAIV,SAASC,EAAgBC,EAAqB,CACnD,IAAMC,EAAOD,EAAI,QAAQ,GAAG,EACtBE,EAAOD,IAAS,GAAKD,EAAMA,EAAI,MAAM,EAAGC,CAAI,EAC5CE,EAAWF,IAAS,GAAK,GAAKD,EAAI,MAAMC,CAAI,EAC5CG,EAAYF,EAAK,SAAS,GAAG,EAAK,QAAQ,KAAKA,CAAI,EAAI,GAAK,IAAO,IACzE,MAAO,GAAGA,CAAI,GAAGE,CAAS,GAAGV,CAAkB,IAAI,mBAAmBF,CAAmB,CAAC,GAAGW,CAAQ,EACvG,CC/BO,IAAME,EAAe,qBACjBC,EAAOD,EAEX,SAASE,EAAcC,EAAc,CAC1C,QAAQ,IAAI,gBAAiBA,CAAI,EACjCF,EAAOE,CACT,CCcO,SAASC,EACdC,EACAC,EACgB,CAChB,OAAOA,EAAO,YAAY,EAAE,WAAW,IAAI,EAAID,EAAO,GAAKA,EAAO,EACpE,CAMO,SAASE,EACdC,EACAC,EACgB,CAChB,IAAMC,EAAiC,CAAE,GAAGF,CAAM,EAElD,GAAIC,GAAa,KACf,OAAW,CAACE,EAAKC,CAAK,IAAK,OAAO,QAA4BH,CAAS,EACjEG,GAAS,OACXF,EAAOC,CAAG,EAAIC,GAKpB,OAAOF,CACT,CAGO,SAASG,EACdR,EACAC,EACAG,EACgB,CAChB,OAAOF,EAAWH,EAAgBC,EAAQC,CAAM,EAAGG,CAAS,CAC9D,CAGO,SAASK,EACdC,EACAJ,EACQ,CACR,OAAQI,EAA6CJ,CAAG,GAAKA,CAC/D,CCjCO,IAAMK,EAAsD,CACjE,GAAI,CACF,KAAM,OACN,UAAW,sBACX,MAAO,SACP,SAAU,aACV,cAAe,mBACf,SAAU,YACV,oBAAqB,iCACvB,EACA,GAAI,CACF,KAAM,SACN,UAAW,+BACX,MAAO,QACP,SAAU,YACV,cAAe,eACf,SAAU,qBACV,oBAAqB,2DACvB,CACF,EAGO,SAASC,EACdC,EACAC,EACe,CACf,OAAOC,EAAaJ,EAAsBE,EAAQC,CAAS,CAC7D,CAEO,SAASE,EACdC,EACAJ,EACAC,EACQ,CACR,OAAOI,EAAWN,EAAqBC,EAAQC,CAAS,EAAGG,CAAG,CAChE,CC9DO,IAAME,EAAiB,QACxBC,EAAmB,iBAErBC,EAEG,SAASC,EAAUC,EAAuB,CAC/C,IAAMC,EAAQD,GAAQ,KAAK,EAC3BF,EAAeG,EACXA,EAAM,YAAY,IAAM,KACtB,QACAA,EAAM,YAAY,IAAM,KACtBL,EACAK,EACJ,OACA,OAAO,aAAiB,MACtBH,GAAgB,KAClB,aAAa,WAAWD,CAAgB,EAExC,aAAa,QAAQA,EAAkBC,CAAY,EAGzD,CAEO,SAASI,GAAoB,CAClC,OAAIJ,IAGA,OAAO,aAAiB,IACnB,aAAa,QAAQD,CAAgB,GAAKD,EAE5CA,EACT,CClCA,OAA0B,QAAAO,MAAY,OACtC,OAAS,WAAAC,MAAe,iBAEjB,SAASC,KAAMC,EAAsB,CAC1C,OAAOF,EAAQD,EAAKG,CAAM,CAAC,CAC7B","names":["CLIENT_HEADER_NAME","CLIENT_HEADER_VALUE","SDK_VERSION","CLIENT_QUERY_PARAM","clientHeaders","clientHeaderInterceptor","next","req","withClientParam","url","hash","base","fragment","separator","DEFAULT_HOST","HOST","setCustomHost","host","pickLocaleTable","tables","locale","mergeTexts","table","overrides","result","key","value","resolveTexts","lookupText","texts","CHECKOUT_TEXT_TABLES","resolveCheckoutTexts","locale","overrides","resolveTexts","getCheckoutText","key","lookupText","DEFAULT_LOCALE","localeStorageKey","activeLocale","setLocale","locale","value","getLocale","clsx","twMerge","cn","inputs"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{a as ct,b as dt}from"./chunk-KOVKUJAW.mjs";import{c as Y,d as F,e as st,f as U,g as it,i as ut,k as D}from"./chunk-43IIYJ6J.mjs";import{f as tt,k as et,l as rt,m as ot,n as nt,p as at}from"./chunk-RLEC25JI.mjs";var It=["root","grid","card","cardLink","image","imagePlaceholder","body","name","description","price","comparePrice","badge","actions","addToCartButton","selectButton","message","pagination","paginationButton","pageIndicator","emptyState","error","retryButton","skeleton"],wt={fontFamily:"--bitsnap-products-font-family",fontSize:"--bitsnap-products-font-size",background:"--bitsnap-products-background",surface:"--bitsnap-products-surface",foreground:"--bitsnap-products-foreground",mutedForeground:"--bitsnap-products-muted-foreground",border:"--bitsnap-products-border",buttonBackground:"--bitsnap-products-button-background",buttonForeground:"--bitsnap-products-button-foreground",buttonHoverBackground:"--bitsnap-products-button-hover-background",hoverSurface:"--bitsnap-products-hover-surface",focusRing:"--bitsnap-products-focus-ring",error:"--bitsnap-products-error",badgeBackground:"--bitsnap-products-badge-background",badgeForeground:"--bitsnap-products-badge-foreground",radius:"--bitsnap-products-radius"},Et={light:{background:"#ffffff",surface:"#f9fafb",foreground:"#18181b",mutedForeground:"#71717a",border:"#e4e4e7",buttonBackground:"#18181b",buttonForeground:"#fafafa",buttonHoverBackground:"#000000",hoverSurface:"#f4f4f5",focusRing:"#71717a",error:"#dc2626",badgeBackground:"#f4f4f5",badgeForeground:"#3f3f46",radius:"8px"},dark:{background:"#18181b",surface:"#27272a",foreground:"#fafafa",mutedForeground:"#a1a1aa",border:"#3f3f46",buttonBackground:"#fafafa",buttonForeground:"#18181b",buttonHoverBackground:"#e4e4e7",hoverSurface:"#3f3f46",focusRing:"#a1a1aa",error:"#f87171",badgeBackground:"#3f3f46",badgeForeground:"#e4e4e7",radius:"8px"}},_t=[];function lt(t){let e=ct(t,{defaultTheme:"light",presets:Et,variables:wt,shadcnVariables:_t});return{theme:e.theme,tokens:e.tokens,elements:e.elements,boundaryClassName:e.theme,boundaryStyle:e.boundaryStyle}}function q(t,e){return dt(t.elements,e)}function pt(t,e){try{return new Intl.NumberFormat(t,{style:"currency",currency:e,currencyDisplay:"symbol"})}catch{return}}function H(t,e,r=D){let o=t/100,n=pt(r,e)??pt(D,e);return n!=null?n.format(o):`${o.toFixed(2)} ${e}`}import{useCallback as gt,useEffect as Bt,useMemo as jt,useState as z}from"react";var Dt=24,Ot=48,M=class extends Error{status;constructor(e,r){super(e),this.name="ProductListError",this.status=r}};function O(t){return t==null||!Number.isFinite(t)?Dt:Math.min(Ot,Math.max(1,Math.floor(t)))}function mt(t,e){return Math.max(1,Math.ceil(t/e))}function N(t){return t.replace(/\/+$/,"")}function Nt(t){let e=new URLSearchParams;e.set("limit",String(t.limit)),e.set("offset",String(t.offset)),t.locale&&e.set("locale",t.locale);let r=t.search?.trim();return r&&e.set("search",r),`${N(t.host)}/api/integrations/${encodeURIComponent(t.projectID)}/public-commerce/product-list?${e.toString()}`}function Rt(t){return typeof t=="object"&&t!=null&&Array.isArray(t.items)&&typeof t.totalCount=="number"}async function ft(t,e){let r=await fetch(Y(Nt(t)),{method:"GET",credentials:"omit",headers:{Accept:"application/json"},signal:e});if(!r.ok)throw new M(`Could not load products (${r.status})`,r.status);let o=await r.json();if(!Rt(o))throw new M("Unexpected product list response");return o}function G(t){let{projectID:e,locale:r,search:o,initialData:n}=t,[a]=z(()=>U),i=t.host??a,d=O(t.pageSize),c=JSON.stringify([e,i,r??null,o?.trim()??null,d]),[m,u]=z({queryKey:c,page:1});m.queryKey!==c&&u({queryKey:c,page:1});let[b,v]=z(0),[f,l]=z(()=>({requestKey:n!=null?`${c}|1|0`:"",data:n,error:void 0})),g=f.data?.totalCount??0,P=mt(g,d),h=m.queryKey===c?m.page:1,k=g>0?Math.min(h,P):h,C=`${c}|${k}|${b}`,T=f.requestKey===C;Bt(()=>{if(T)return;let y=new AbortController;return ft({projectID:e,host:i,locale:r,search:o,limit:d,offset:(k-1)*d},y.signal).then(x=>{y.signal.aborted||l({requestKey:C,data:x,error:void 0})},x=>{y.signal.aborted||l(w=>({requestKey:C,data:w.data,error:x instanceof Error?x:new Error(String(x))}))}),()=>y.abort()},[C,T]);let I=gt(y=>{let x=Math.max(1,Math.floor(y));u({queryKey:c,page:g>0?Math.min(x,P):x})},[c,g,P]),A=gt(()=>v(y=>y+1),[]);return jt(()=>({products:f.data?.items??[],totalCount:g,page:k,pageCount:P,setPage:I,isLoading:!T,error:T?f.error:void 0,refetch:A}),[f,g,k,P,I,T,A])}import{createContext as ht,useCallback as V,useContext as xt,useEffect as R,useMemo as B,useRef as K,useState as Mt}from"react";function Pt(t,e){if(t.projectID!=null&&t.projectID!==e.projectID)return{ok:!1,reason:`the page cart already belongs to project "${t.projectID}"`};let r=null,o=e.host?N(e.host):"",n=N(t.host);if(o&&o!==n){if(n!==N(t.defaultHost))return{ok:!1,reason:`the page cart already uses the host "${t.host}"`};r=o}return{ok:!0,setProjectID:t.projectID==null?e.projectID:null,setHost:r}}function Q(t,e){let r=e.cartItemId!=null&&e.cartItemId!==e.id?e.cartItemId:null;t.track("add_to_cart",{qty:1,value_minor:e.price,currency:e.currency},{product_id:e.id,variant_id:r})}async function bt(t,e){let[{Bitsnap:r},{getProjectID:o,setProjectID:n}]=await Promise.all([import("./methods-AOHKV7IN.mjs"),import("./CartProvider-HXLGQ4EX.mjs")]),a=Pt({projectID:o(),host:U,defaultHost:st},e);if(!a.ok)return Ft(a.reason);if(a.setProjectID!=null&&n(a.setProjectID),a.setHost!=null&&it(a.setHost),await r.addProductToCart(t.cartItemId??t.id,1,void 0,{name:t.name,price:t.price,currency:t.currency,analytics:et})!=null)throw new Error("Could not add the product to the cart");e.analytics!=null&&Q(e.analytics,t),e.showCart&&r.showCart()}function Ft(t){throw console.warn(`bitsnap-react: ProductList did not add to the cart because ${t}. Pass onAddToCart to use another cart.`),new Error("The page cart belongs to another project or host")}var $=50,Ut=1e3;function yt(t,e=Ut){let r=new Set,o=new Map,n=[],a=[],i,d,c=!1;function m(){for(d!==void 0&&clearTimeout(d),d=void 0;n.length>0;){let l=n.slice(0,$),g=a.slice(0,$);n=n.slice($),a=a.slice($);try{t({productIds:l,positions:g})}catch{}}}function u(){m();try{tt()?.flush()}catch{}}let b=()=>{typeof document<"u"&&document.visibilityState==="hidden"&&u()};function v(){c||typeof window>"u"||(c=!0,window.addEventListener("pagehide",u,!0),document.addEventListener("visibilitychange",b,!0))}function f(){if(i)return i;if(!(typeof IntersectionObserver>"u"))return i=new IntersectionObserver(l=>{for(let g of l){let P=o.get(g.target);!P||!g.isIntersecting||g.intersectionRatio<.5||(o.delete(g.target),i?.unobserve(g.target),!r.has(P.id)&&(r.add(P.id),n.push(P.id),a.push(P.position)))}n.length>0&&d===void 0&&(d=setTimeout(m,e),d.unref?.())},{threshold:[.5]}),v(),i}return{observe(l,g,P){try{if(r.has(g))return()=>{};let h=f();return h?(o.set(l,{id:g,position:P}),h.observe(l),()=>{o.delete(l),h.unobserve(l)}):()=>{}}catch{return()=>{}}},flush:m,destroy(){try{m(),i?.disconnect(),c&&(window.removeEventListener("pagehide",u,!0),document.removeEventListener("visibilitychange",b,!0),c=!1)}catch{}i=void 0,o.clear()}}}var Ht={en:{addToCart:"Add to cart",addingToCart:"Adding\u2026",addedToCart:"Added to cart",addToCartError:"Could not add to the cart. Try again.",chooseOptions:"Choose",viewProduct:"View",unavailable:"Unavailable",subscription:"Subscription",priceFrom:"from {price}",originalPrice:"Original price",loading:"Loading products",empty:"There are no products to show.",error:"Could not load the products.",retry:"Try again",pagination:"Pagination",previousPage:"Previous",nextPage:"Next",pageOf:"Page {page} of {pageCount}"},pl:{addToCart:"Dodaj do koszyka",addingToCart:"Dodawanie\u2026",addedToCart:"Dodano do koszyka",addToCartError:"Nie uda\u0142o si\u0119 doda\u0107 do koszyka. Spr\xF3buj ponownie.",chooseOptions:"Wybierz",viewProduct:"Zobacz",unavailable:"Niedost\u0119pny",subscription:"Subskrypcja",priceFrom:"od {price}",originalPrice:"Cena przed obni\u017Ck\u0105",loading:"\u0141adowanie produkt\xF3w",empty:"Brak produkt\xF3w do wy\u015Bwietlenia.",error:"Nie uda\u0142o si\u0119 za\u0142adowa\u0107 produkt\xF3w.",retry:"Spr\xF3buj ponownie",pagination:"Paginacja",previousPage:"Poprzednia",nextPage:"Nast\u0119pna",pageOf:"Strona {page} z {pageCount}"}};function Lt(t,e){return ut(Ht,t,e)}function X(t,e){return t.replace(/\{(\w+)\}/g,(r,o)=>o in e?String(e[o]):r)}import{Fragment as At,jsx as s,jsxs as S}from"react/jsx-runtime";var Tt=ht(void 0),zt={observe:()=>()=>{},clicked:()=>{}},vt=ht(zt);function j(){let t=xt(Tt);if(t==null)throw new Error("ProductList parts must be rendered inside ProductList");return t}function p(t,e){let{appearance:r}=j(),o=q(r,t);return{"data-bitsnap-products-slot":t,className:F(e,o?.className),style:o?.style}}var Z="inline-flex cursor-pointer items-center justify-center px-4 py-2 text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50",Ct=`${Z} w-full`,$t=`${Ct} no-underline`;function Kt({product:t,onAdd:e}){let{texts:r}=j(),[o,n]=Mt("idle"),a=p("addToCartButton",Ct),i=p("message","mt-2 text-xs");R(()=>{if(o!=="added")return;let m=setTimeout(()=>n("idle"),2e3);return()=>clearTimeout(m)},[o]);let d=V(async()=>{n("adding");try{await e(),n("added")}catch(m){console.error("bitsnap-react: could not add to the cart",m),n("error")}},[e]),c=o==="adding"?r.addingToCart:o==="added"?r.addedToCart:r.addToCart;return S(At,{children:[s("button",{type:"button",...a,disabled:o==="adding","aria-label":`${c}: ${t.name}`,onClick:d,children:c}),o==="error"?s("p",{...i,role:"alert",children:r.addToCartError}):null]})}function Vt({product:t,position:e,href:r,onAdd:o,renderItem:n}){let{texts:a,locale:i}=j(),d=xt(vt),c=K(null);R(()=>{if(c.current!=null)return d.observe(c.current,t.id,e)},[d,t.id,e]);let m=V(()=>d.clicked(t,e),[d,t,e]),u=p("card","relative flex flex-col overflow-hidden border transition-colors"),b=p("cardLink","flex flex-1 flex-col no-underline"),v=p("image","aspect-square w-full overflow-hidden"),f=p("imagePlaceholder","aspect-square w-full"),l=p("body","flex flex-1 flex-col gap-1 p-4"),g=p("name","text-base font-medium leading-snug"),P=p("description","line-clamp-2 text-sm"),h=p("price","mt-auto pt-2 text-base font-semibold"),k=p("comparePrice","ml-2 text-sm font-normal"),C=p("badge","absolute left-3 top-3 px-2 py-0.5 text-xs font-medium"),T=p("actions","p-4 pt-0"),I=p("selectButton",$t),A=H(t.price,t.currency,i),y=t.compareAtPrice!=null&&t.compareAtPrice>t.price?H(t.compareAtPrice,t.currency,i):null;if(n!=null)return s("li",{ref:c,...u,"data-unavailable":t.available?void 0:"",children:n(t,{href:r,price:A,compareAtPrice:y,texts:a,locale:i,addToCart:o,trackClick:m})});let x=t.image?.thumbnailUrl??t.image?.url,w=t.available?t.isRecurring?a.subscription:null:a.unavailable;return S("li",{ref:c,...u,"data-unavailable":t.available?void 0:"",children:[S("a",{...b,href:r,onClick:m,children:[x!=null?s("div",{...v,children:s("img",{src:x,alt:"",loading:"lazy",decoding:"async",className:"h-full w-full object-cover"})}):s("div",{...f,"aria-hidden":"true"}),S("div",{...l,children:[s("h3",{...g,children:t.name}),t.shortDescription?s("p",{...P,children:t.shortDescription}):null,S("p",{...h,children:[t.priceFrom?X(a.priceFrom,{price:A}):A,y!=null?S(At,{children:[S("span",{className:"sr-only",children:[a.originalPrice,": "]}),s("del",{...k,children:y})]}):null]})]})]}),w!=null?s("span",{...C,"data-kind":t.available?"subscription":"unavailable",children:w}):null,s("div",{...T,children:t.available?t.addToCart?s(Kt,{product:t,onAdd:o}):s("a",{...I,href:r,onClick:m,children:t.variantCount>1?a.chooseOptions:a.viewProduct}):s("button",{type:"button",...I,disabled:!0,children:a.unavailable})})]})}var St="m-0 grid list-none grid-cols-[repeat(auto-fill,minmax(min(100%,15rem),1fr))] gap-5 p-0";function qt({count:t,label:e}){let r=p("grid",St),o=p("skeleton","aspect-[3/4] animate-pulse");return s("div",{...r,role:"status","aria-busy":"true","aria-label":e,children:Array.from({length:t},(n,a)=>s("div",{...o,"aria-hidden":"true"},a))})}function Gt({page:t,pageCount:e,isLoading:r,onPage:o}){let{texts:n}=j(),a=p("pagination","mt-8 flex items-center justify-center gap-4"),i=p("paginationButton",Z),d=p("pageIndicator","text-sm");return S("nav",{...a,"aria-label":n.pagination,children:[s("button",{type:"button",...i,"aria-disabled":r||t<=1,onClick:()=>{!r&&t>1&&o(t-1)},children:n.previousPage}),s("span",{...d,"aria-live":"polite",children:X(n.pageOf,{page:t,pageCount:e})}),s("button",{type:"button",...i,"aria-disabled":r||t>=e,onClick:()=>{!r&&t<e&&o(t+1)},children:n.nextPage})]})}function Qt({options:t,productHref:e,renderItem:r,onAddToCart:o,showCartOnAdd:n}){let{texts:a}=j(),{products:i,page:d,pageCount:c,setPage:m,isLoading:u,error:b,refetch:v}=G(t),f=ot(),l=t.search?.trim()?"search":"catalog",g=O(t.pageSize),P=K(!1);R(()=>{P.current||u||b!=null||(P.current=!0,f.track("product_list_viewed",{list_id:l,item_count:i.length}))},[f,u,b,l,i.length]);let h=B(()=>yt(({productIds:L,positions:_})=>f.track("product_impression",{list_id:l,product_ids:L,positions:_})),[f,l]);R(()=>()=>h.destroy(),[h]);let k=B(()=>({observe:h.observe,clicked:(L,_)=>f.track("product_clicked",{list_id:l,position:_},{product_id:L.id})}),[h,f,l]),C=K(null),T=K(!1),I=V(L=>{T.current=!0,m(L)},[m]);R(()=>{u||!T.current||(T.current=!1,C.current?.focus({preventScroll:!0}),C.current?.scrollIntoView({block:"start"}))},[u]);let A=p("grid",St),y=p("emptyState","py-12 text-center text-sm"),x=p("error","flex flex-col items-center gap-3 py-12 text-center text-sm"),w=p("retryButton",Z),{projectID:J,host:W}=t,kt=V(L=>async()=>{if(o!=null){await at(()=>o(L)),Q(f,L);return}await bt(L,{projectID:J,host:W,showCart:n,analytics:f})},[o,J,W,n,f]),E;return u&&i.length===0?E=s(qt,{count:Math.min(O(t.pageSize),8),label:a.loading}):b!=null?E=S("div",{...x,role:"alert",children:[s("p",{children:a.error}),s("button",{type:"button",...w,onClick:v,children:a.retry})]}):i.length===0?E=s("p",{...y,children:a.empty}):E=s("ul",{...A,"aria-busy":u||void 0,className:F(A.className,u&&"opacity-60"),children:i.map((L,_)=>s(Vt,{product:L,position:(d-1)*g+_,href:e(L),onAdd:kt(L),renderItem:r},L.id))}),s(vt.Provider,{value:k,children:S("div",{ref:C,tabIndex:-1,className:"p-1 outline-none scroll-mt-4",children:[E,c>1&&b==null?s(Gt,{page:d,pageCount:c,isLoading:u,onPage:I}):null]})})}var Xt=t=>`/p/${encodeURIComponent(t.uniqueSlug)}`;function Zt({productHref:t=Xt,appearance:e,texts:r,renderItem:o,onAddToCart:n,showCartOnAdd:a=!0,className:i,analytics:d,...c}){let m=nt(c.projectID,d,"catalog"),u=c.locale??D,b=B(()=>lt(e),[e]),v=B(()=>Lt(u,r),[u,r]),f=B(()=>({appearance:b,texts:v,locale:u}),[b,v,u]),l=q(b,"root");return s(rt,{value:m,children:s(Tt.Provider,{value:f,children:s("div",{"data-bitsnap-products":"root","data-bitsnap-products-slot":"root",className:F("bitsnap-react",b.boundaryClassName,i,l?.className),style:{...b.boundaryStyle,...l?.style},children:s(Qt,{options:c,productHref:t,renderItem:o,onAddToCart:n,showCartOnAdd:a})})})})}export{It as a,H as b,G as c,Zt as d};
|
|
2
|
+
//# sourceMappingURL=chunk-6D3OORMK.mjs.map
|