@nosto/nosto-react 2.6.0 → 2.7.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 CHANGED
@@ -1,5 +1,10 @@
1
1
  # Nosto React
2
2
 
3
+ [![](https://img.shields.io/github/package-json/v/nosto/nosto-react)](https://github.com/Nosto/nosto-react/releases)
4
+ [![](https://github.com/Nosto/nosto-react/actions/workflows/ci.yml/badge.svg)](https://github.com/Nosto/nosto-react/actions/workflows/ci.yml)
5
+ [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
6
+ [![npm license](https://img.shields.io/npm/l/@nosto/nosto-react.svg)](https://github.com/Nosto/nosto-react/blob/master/LICENSE)
7
+
3
8
  Nosto React is a React component library to make it even easier to implement Nosto.
4
9
 
5
10
  The library provides you everything to get started with personalisation on your React site. It's dead simple, and beginner friendly.
@@ -64,14 +69,10 @@ There’s one very specific widget in Nosto React and it is the `NostoProvider`
64
69
  This widget is what we call the Nosto root widget, which is responsible for adding the actual Nosto script and the JS API stub. This widget wraps all other React Nosto widgets. Here’s how:
65
70
 
66
71
  ```jsx
67
- import { NostoProvider } from "@nosto/nosto-react";
68
-
69
- <NostoProvider
70
- account="your-nosto-account-id"
71
- recommendationComponent={<NostoSlot />}
72
- >
72
+ import { NostoProvider } from "@nosto/nosto-react"
73
+ <NostoProvider account="your-nosto-account-id" recommendationComponent={<NostoSlot />}>
73
74
  <App />
74
- </NostoProvider>;
75
+ </NostoProvider>
75
76
  ```
76
77
 
77
78
  **Note:** the component also accepts a prop to configure the host `host="connect.nosto.com"`. In advanced use-cases, the need to configure the host may surface.
@@ -97,8 +98,7 @@ The `NostoSession` component makes it very easy to keep the session up to date s
97
98
  The `cart` prop requires a value that adheres to the type `Cart`, while the `customer` prop requires a value that adheres to the type `Customer`.
98
99
 
99
100
  ```jsx
100
- import { NostoSession } from "@nosto/nosto-react";
101
-
101
+ import { NostoSession } from "@nosto/nosto-react"
102
102
  <>
103
103
  <Meta />
104
104
  <header>
@@ -107,7 +107,7 @@ import { NostoSession } from "@nosto/nosto-react";
107
107
  </header>
108
108
  <Routes />
109
109
  <Footer />
110
- </>;
110
+ </>
111
111
  ```
112
112
 
113
113
  ### Adding Personalisation
@@ -123,8 +123,7 @@ By default, your account, when created, has <u>four</u> front-page placements na
123
123
  The `<NostoHome \>` component needs to be added after the placements. Content and recommendations will be rendered through this component.
124
124
 
125
125
  ```jsx
126
- import { NostoHome, NostoPlacement } from "@nosto/nosto-react";
127
-
126
+ import { NostoHome, NostoPlacement } from "@nosto/nosto-react"
128
127
  <div className="front-page">
129
128
  ... ... ...
130
129
  <NostoPlacement id="frontpage-nosto-1" />
@@ -132,7 +131,7 @@ import { NostoHome, NostoPlacement } from "@nosto/nosto-react";
132
131
  <NostoPlacement id="frontpage-nosto-3" />
133
132
  <NostoPlacement id="frontpage-nosto-4" />
134
133
  <NostoHome />
135
- </div>;
134
+ </div>
136
135
  ```
137
136
 
138
137
  ##### Personalising your product pages
@@ -144,15 +143,14 @@ By default, your account, when created, has <u>three</u> product-page placements
144
143
  The `<NostoProduct \>` component needs to be added after the placements. Content and recommendations will be rendered through this component. Pass in the product ID via the `product` prop to pass this information back to Nosto.
145
144
 
146
145
  ```jsx
147
- import { NostoPlacement, NostoProduct } from "@nosto/nosto-react";
148
-
146
+ import { NostoPlacement, NostoProduct } from "@nosto/nosto-react"
149
147
  <div className="product-page">
150
148
  ... ... ...
151
149
  <NostoPlacement id="productpage-nosto-1" />
152
150
  <NostoPlacement id="productpage-nosto-2" />
153
151
  <NostoPlacement id="productpage-nosto-3" />
154
152
  <NostoProduct product={product.id} />
155
- </div>;
153
+ </div>
156
154
  ```
157
155
 
158
156
  ##### Personalising your search result pages
@@ -162,14 +160,13 @@ You can personalise your search pages by using the `NostoSearch` component. The
162
160
  By default, your account, when created, has <u>two</u> search-page placements named `searchpage-nosto-1` and `searchpage-nosto-2`. You may omit these and use any identifier you need. The identifiers used here are simply provided to illustrate the example.
163
161
 
164
162
  ```jsx
165
- import { NostoPlacement, NostoSearch } from "@nosto/nosto-react";
166
-
163
+ import { NostoPlacement, NostoSearch } from "@nosto/nosto-react"
167
164
  <div className="search-page">
168
165
  ... ... ...
169
166
  <NostoPlacement id="searchpage-nosto-1" />
170
167
  <NostoPlacement id="searchpage-nosto-2" />
171
168
  <NostoSearch query={search} />
172
- </div>;
169
+ </div>
173
170
  ```
174
171
 
175
172
  **Note:** Do not encode the search term in any way. It should be provided an unencoded string. A query for "black shoes" must be provided as-is and not as "black+shoes". Doing so will lead to invalid results.
@@ -181,14 +178,13 @@ You can personalise your category and collection pages by using the `NostoCatego
181
178
  By default, your account, when created, has <u>two</u> category placements named `categorypage-nosto-1` and `categorypage-nosto-2`. You may omit these and use any identifier you need. The identifiers used here are simply provided to illustrate the example.
182
179
 
183
180
  ```jsx
184
- import { NostoCategory, NostoPlacement } from "@nosto/nosto-react";
185
-
181
+ import { NostoCategory, NostoPlacement } from "@nosto/nosto-react"
186
182
  <div className="category-page">
187
183
  ... ... ...
188
184
  <NostoPlacement id="categorypage-nosto-1" />
189
185
  <NostoPlacement id="categorypage-nosto-2" />
190
186
  <NostoCategory category={category.name} />
191
- </div>;
187
+ </div>
192
188
  ```
193
189
 
194
190
  **Note:** Be sure to pass in the correct category representation. If the category being viewed is Mens >> Jackets, you must provide the name as `/Mens/Jackets` . You must ensure that the category path provided here matches that of the categories tagged in your products.
@@ -200,14 +196,13 @@ You can personalise your cart and checkout pages by using the `NostoCheckout` co
200
196
  By default, your account, when created, has <u>two</u> cart-page placements named `categorypage-nosto-1` and `categorypage-nosto-2`. You may omit these and use any identifier you need. The identifiers used here are simply provided to illustrate the example.
201
197
 
202
198
  ```jsx
203
- import { NostoCheckout, NostoPlacement } from "@nosto/nosto-react";
204
-
199
+ import { NostoCheckout, NostoPlacement } from "@nosto/nosto-react"
205
200
  <div className="checkout-page">
206
201
  ... ... ...
207
202
  <NostoPlacement id="checkout-nosto-1" />
208
203
  <NostoPlacement id="checkout-nosto-2" />
209
204
  <NostoCheckout />
210
- </div>;
205
+ </div>
211
206
  ```
212
207
 
213
208
  ##### Personalising your 404 error pages
@@ -217,15 +212,14 @@ You can personalise not found pages by using the `Nosto404` component. The compo
217
212
  By default, your account, when created, has three 404-page placements named `notfound-nosto-1`, `notfound-nosto-2` and `notfound-nosto-2`. You may omit these and use any identifier you need. The identifiers used here are simply provided to illustrate the example.
218
213
 
219
214
  ```jsx
220
- import { Nosto404, NostoPlacement } from "@nosto/nosto-react";
221
-
215
+ import { Nosto404, NostoPlacement } from "@nosto/nosto-react"
222
216
  <div className="notfound-page">
223
217
  ... ... ...
224
218
  <NostoPlacement id="notfound-nosto-1" />
225
219
  <NostoPlacement id="notfound-nosto-2" />
226
220
  <NostoPlacement id="notfound-nosto-3" />
227
221
  <Nosto404 />
228
- </div>;
222
+ </div>
229
223
  ```
230
224
 
231
225
  ##### Personalising your miscellaneous pages
@@ -235,14 +229,13 @@ You can personalise your miscellaneous pages by using the `NostoOther` component
235
229
  By default, your account, when created, has two other-page placements named `other-nosto-1` and `other-nosto-2`. You may omit these and use any identifier you need. The identifiers used here are simply provided to illustrate the example.
236
230
 
237
231
  ```jsx
238
- import { NostoOther, NostoPlacement } from "@nosto/nosto-react";
239
-
232
+ import { NostoOther, NostoPlacement } from "@nosto/nosto-react"
240
233
  <div className="other-page">
241
234
  ... ... ...
242
235
  <NostoPlacement id="other-nosto-1" />
243
236
  <NostoPlacement id="other-nosto-2" />
244
237
  <NostoOther />
245
- </div>;
238
+ </div>
246
239
  ```
247
240
 
248
241
  ##### Personalising your order confirmation page
@@ -252,16 +245,12 @@ You can personalise your order-confirmation/thank-you page by using the `NostoOr
252
245
  By default, your account, when created, has one other-page placement named `thankyou-nosto-1`. You may omit this and use any identifier you need. The identifier used here is simply provided to illustrate the example.
253
246
 
254
247
  ```jsx
255
- import {
256
- NostoOrder,
257
- NostoPlacement,
258
- } from "@nosto/nosto-react";
259
-
248
+ import { NostoOrder, NostoPlacement } from "@nosto/nosto-react"
260
249
  <div className="thankyou-page">
261
250
  ... ... ...
262
251
  <NostoPlacement id="thankyou-nosto-1" />
263
- <NostoOrder order={ order } />
264
- </div>;
252
+ <NostoOrder order={order} />
253
+ </div>
265
254
  ```
266
255
 
267
256
  ### Hook alternatives
@@ -278,6 +267,7 @@ For all the page type specific components hooks are also provided with the same
278
267
  - useNostoHome
279
268
 
280
269
  ### Detailed technical documentation
270
+
281
271
  Find our latest technical specs and documentation hosted [here](https://nosto.github.io/nosto-react).
282
272
 
283
273
  ### Feedback
@@ -292,4 +282,4 @@ Please take a moment to review the guidelines for contributing.
292
282
 
293
283
  ### License
294
284
 
295
- MIT
285
+ BSD 3-Clause License
package/dist/index.d.ts CHANGED
@@ -223,7 +223,7 @@ export declare type NostoOtherProps = {
223
223
  *
224
224
  * @group Components
225
225
  */
226
- export declare function NostoPlacement({ id, pageType }: NostoPlacementProps): JSX.Element;
226
+ export declare function NostoPlacement({ id, pageType, children }: NostoPlacementProps): JSX.Element;
227
227
 
228
228
  /**
229
229
  * @group Components
@@ -231,6 +231,7 @@ export declare function NostoPlacement({ id, pageType }: NostoPlacementProps): J
231
231
  export declare type NostoPlacementProps = {
232
232
  id: string;
233
233
  pageType?: string;
234
+ children?: React.ReactNode;
234
235
  };
235
236
 
236
237
  /**
@@ -295,7 +296,7 @@ export declare function NostoProvider(props: NostoProviderProps): JSX.Element;
295
296
  /**
296
297
  * @group Components
297
298
  */
298
- export declare interface NostoProviderProps {
299
+ declare interface NostoProviderProps {
299
300
  /**
300
301
  * Indicates merchant id
301
302
  */
@@ -320,6 +321,10 @@ export declare interface NostoProviderProps {
320
321
  * Recommendation component which holds nostoRecommendation object
321
322
  */
322
323
  recommendationComponent?: RecommendationComponent;
324
+ /**
325
+ * Recommendation render mode. See {@link https://nosto.github.io/nosto-js/types/client.RenderMode.html}
326
+ */
327
+ renderMode?: RenderMode;
323
328
  /**
324
329
  * Enables Shopify markets with language and market id
325
330
  */
package/dist/index.es.js CHANGED
@@ -1,6 +1,6 @@
1
- import { createContext as k, useContext as x, useEffect as S, useRef as h, useMemo as I, cloneElement as _, useState as T, isValidElement as D } from "react";
1
+ import { createContext as k, useContext as x, useEffect as S, useRef as h, useMemo as I, cloneElement as _, useState as M, isValidElement as T } from "react";
2
2
  import { jsx as v } from "react/jsx-runtime";
3
- import { createRoot as M } from "react-dom/client";
3
+ import { createRoot as D } from "react-dom/client";
4
4
  const O = k({
5
5
  account: "",
6
6
  currentVariation: "",
@@ -10,13 +10,13 @@ const O = k({
10
10
  function P() {
11
11
  return x(O);
12
12
  }
13
- const N = (e) => String(e) === "[object Object]";
13
+ const g = (e) => String(e) === "[object Object]";
14
14
  function L(e) {
15
- if (!N(e)) return !1;
15
+ if (!g(e)) return !1;
16
16
  const t = e.constructor;
17
17
  if (t === void 0) return !0;
18
18
  const n = t.prototype;
19
- return !(!N(n) || !n.hasOwnProperty("isPrototypeOf"));
19
+ return !(!g(n) || !n.hasOwnProperty("isPrototypeOf"));
20
20
  }
21
21
  function y(e, t) {
22
22
  if (e === t)
@@ -31,66 +31,69 @@ function y(e, t) {
31
31
  }
32
32
  return !1;
33
33
  }
34
- function g(e, t) {
34
+ function j(e, t) {
35
35
  return S(e, V(t));
36
36
  }
37
37
  function V(e) {
38
38
  const t = h(e), n = h(0);
39
39
  return y(e, t.current) || (t.current = e, n.current += 1), I(() => t.current, [n.current]);
40
40
  }
41
- function j() {
41
+ function N() {
42
42
  window.nostojs = window.nostojs ?? function(e) {
43
43
  (window.nostojs.q = window.nostojs.q ?? []).push(e);
44
44
  };
45
45
  }
46
- function H() {
46
+ async function m(e) {
47
+ return window.nostojs(e);
48
+ }
49
+ typeof window < "u" && (N(), m((e) => {
50
+ e.internal.getSettings();
51
+ }));
52
+ function q() {
47
53
  return typeof window.nosto < "u";
48
54
  }
49
- const q = {
55
+ const H = {
50
56
  production: "https://connect.nosto.com/",
51
57
  staging: "https://connect.staging.nosto.com/",
52
58
  local: "https://connect.nosto.com/"
53
59
  };
54
60
  function R(e) {
55
- return q[e ?? "production"];
61
+ return H[e ?? "production"];
56
62
  }
57
63
  function F({ merchantId: e, env: t, options: n, shopifyInternational: o, scriptLoader: r }) {
58
- var c, a;
59
- const s = document.querySelector("script[nosto-language], script[nosto-market-id]"), i = String((o == null ? void 0 : o.marketId) || ""), l = (o == null ? void 0 : o.language) || "", C = (s == null ? void 0 : s.getAttribute("nosto-language")) !== l || (s == null ? void 0 : s.getAttribute("nosto-market-id")) !== i;
64
+ var c, i;
65
+ const s = document.querySelector("script[nosto-language], script[nosto-market-id]"), a = String((o == null ? void 0 : o.marketId) || ""), u = (o == null ? void 0 : o.language) || "", C = (s == null ? void 0 : s.getAttribute("nosto-language")) !== u || (s == null ? void 0 : s.getAttribute("nosto-market-id")) !== a;
60
66
  if (!s || C) {
61
- const m = document.querySelector("#nosto-sandbox");
62
- (c = s == null ? void 0 : s.parentNode) == null || c.removeChild(s), (a = m == null ? void 0 : m.parentNode) == null || a.removeChild(m);
67
+ const f = document.querySelector("#nosto-sandbox");
68
+ (c = s == null ? void 0 : s.parentNode) == null || c.removeChild(s), (i = f == null ? void 0 : f.parentNode) == null || i.removeChild(f);
63
69
  const p = new URL("/script/shopify/market/nosto.js", R(t));
64
- p.searchParams.append("merchant", e), p.searchParams.append("market", i), p.searchParams.append("locale", l.toLowerCase());
65
- const A = {
70
+ p.searchParams.append("merchant", e), p.searchParams.append("market", a), p.searchParams.append("locale", u.toLowerCase());
71
+ const b = {
66
72
  ...n == null ? void 0 : n.attributes,
67
- "nosto-language": l,
68
- "nosto-market-id": i
73
+ "nosto-language": u,
74
+ "nosto-market-id": a
69
75
  };
70
- return (r ?? E)(p.toString(), { ...n, attributes: A });
76
+ return (r ?? E)(p.toString(), { ...n, attributes: b });
71
77
  }
72
78
  return Promise.resolve();
73
79
  }
74
80
  function z(e) {
75
81
  if (e.shopifyInternational)
76
82
  return F(e);
77
- const { merchantId: t, env: n, options: o, scriptLoader: r } = e, c = r ?? E, a = new URL(`/include/${t}`, R(n));
78
- return c(a.toString(), o);
83
+ const { merchantId: t, env: n, options: o, scriptLoader: r } = e, c = r ?? E, i = new URL(`/include/${t}`, R(n));
84
+ return c(i.toString(), o);
79
85
  }
80
86
  function E(e, t) {
81
87
  return new Promise((n, o) => {
82
88
  const r = document.createElement("script");
83
- r.src = e, r.async = !0, r.type = "text/javascript", r.onload = () => n(), r.onerror = () => o(), Object.entries((t == null ? void 0 : t.attributes) ?? {}).forEach(([c, a]) => r.setAttribute(c, a)), (t == null ? void 0 : t.position) === "head" ? document.head.appendChild(r) : document.body.appendChild(r);
89
+ r.src = e, r.async = !0, r.type = "text/javascript", r.onload = () => n(), r.onerror = () => o(), Object.entries((t == null ? void 0 : t.attributes) ?? {}).forEach(([c, i]) => r.setAttribute(c, i)), (t == null ? void 0 : t.position) === "head" ? document.head.appendChild(r) : document.body.appendChild(r);
84
90
  });
85
91
  }
86
- async function f(e) {
87
- return window.nostojs(e);
88
- }
89
- typeof window < "u" && j();
90
- function u(e, t, n) {
92
+ typeof window < "u" && N();
93
+ function d(e, t, n) {
91
94
  const { clientScriptLoaded: o } = P();
92
- (n != null && n.deep ? g : S)(() => {
93
- o && f(e);
95
+ (n != null && n.deep ? j : S)(() => {
96
+ o && m(e);
94
97
  }, [o, ...t ?? []]);
95
98
  }
96
99
  function U(e) {
@@ -99,30 +102,32 @@ function U(e) {
99
102
  nostoRecommendation: e.nostoRecommendation
100
103
  });
101
104
  }
102
- function b(e) {
103
- f((t) => t.placements.injectCampaigns(e));
105
+ function A(e) {
106
+ m((t) => t.placements.injectCampaigns(e));
104
107
  }
105
108
  function $(e) {
106
109
  if (!window.nostojs)
107
110
  throw new Error("Nosto has not yet been initialized");
108
- b(e.recommendations);
111
+ A(e.recommendations);
109
112
  }
110
- function d() {
113
+ function l() {
111
114
  const { responseMode: e, recommendationComponent: t } = P(), n = h({});
112
115
  if (e == "HTML")
113
116
  return { renderCampaigns: $ };
117
+ if (!t)
118
+ throw new Error("recommendationComponent is required for client-side rendering using hook");
114
119
  function o(r) {
115
- var a, s;
116
- b(((a = r.campaigns) == null ? void 0 : a.content) ?? {});
120
+ var i, s;
121
+ A(((i = r.campaigns) == null ? void 0 : i.content) ?? {});
117
122
  const c = ((s = r.campaigns) == null ? void 0 : s.recommendations) ?? {};
118
- for (const i in c) {
119
- const l = c[i], C = "#" + i, m = document.querySelector(C);
120
- m && (n.current[i] || (n.current[i] = M(m)), n.current[i].render(
123
+ for (const a in c) {
124
+ const u = c[a], C = "#" + a, f = document.querySelector(C);
125
+ f && (n.current[a] || (n.current[a] = D(f)), n.current[a].render(
121
126
  /* @__PURE__ */ v(
122
127
  U,
123
128
  {
124
129
  recommendationComponent: t,
125
- nostoRecommendation: l
130
+ nostoRecommendation: u
126
131
  }
127
132
  )
128
133
  ));
@@ -131,18 +136,18 @@ function d() {
131
136
  return { renderCampaigns: o };
132
137
  }
133
138
  function G(e) {
134
- const { renderCampaigns: t } = d();
135
- u(async (n) => {
139
+ const { renderCampaigns: t } = l();
140
+ d(async (n) => {
136
141
  const o = await n.defaultSession().viewNotFound().setPlacements((e == null ? void 0 : e.placements) || n.placements.getPlacements()).load();
137
142
  t(o);
138
143
  });
139
144
  }
140
- function ie(e) {
145
+ function ae(e) {
141
146
  return G(e), null;
142
147
  }
143
148
  function J({ category: e, placements: t }) {
144
- const { renderCampaigns: n } = d();
145
- u(
149
+ const { renderCampaigns: n } = l();
150
+ d(
146
151
  async (o) => {
147
152
  const r = await o.defaultSession().viewCategory(e).setPlacements(t || o.placements.getPlacements()).load();
148
153
  n(r);
@@ -154,8 +159,8 @@ function ue(e) {
154
159
  return J(e), null;
155
160
  }
156
161
  function W(e) {
157
- const { renderCampaigns: t } = d();
158
- u(async (n) => {
162
+ const { renderCampaigns: t } = l();
163
+ d(async (n) => {
159
164
  const o = await n.defaultSession().viewCart().setPlacements((e == null ? void 0 : e.placements) || n.placements.getPlacements()).load();
160
165
  t(o);
161
166
  });
@@ -164,8 +169,8 @@ function de(e) {
164
169
  return W(e), null;
165
170
  }
166
171
  function Z(e) {
167
- const { renderCampaigns: t } = d();
168
- u(async (n) => {
172
+ const { renderCampaigns: t } = l();
173
+ d(async (n) => {
169
174
  const o = await n.defaultSession().viewFrontPage().setPlacements((e == null ? void 0 : e.placements) || n.placements.getPlacements()).load();
170
175
  t(o);
171
176
  });
@@ -186,8 +191,8 @@ function K(e) {
186
191
  return Object.prototype.toString.call(e) === "[object RegExp]";
187
192
  }
188
193
  function Q({ order: e, placements: t }) {
189
- const { renderCampaigns: n } = d();
190
- u(
194
+ const { renderCampaigns: n } = l();
195
+ d(
191
196
  async (o) => {
192
197
  const r = await o.defaultSession().addOrder(w(e)).setPlacements(t || o.placements.getPlacements()).load();
193
198
  n(r);
@@ -200,8 +205,8 @@ function me(e) {
200
205
  return Q(e), null;
201
206
  }
202
207
  function X(e) {
203
- const { renderCampaigns: t } = d();
204
- u(async (n) => {
208
+ const { renderCampaigns: t } = l();
209
+ d(async (n) => {
205
210
  const o = await n.defaultSession().viewOther().setPlacements((e == null ? void 0 : e.placements) || n.placements.getPlacements()).load();
206
211
  t(o);
207
212
  });
@@ -209,20 +214,20 @@ function X(e) {
209
214
  function fe(e) {
210
215
  return X(e), null;
211
216
  }
212
- function pe({ id: e, pageType: t }) {
213
- return /* @__PURE__ */ v("div", { className: "nosto_element", id: e }, e + (t || ""));
217
+ function pe({ id: e, pageType: t, children: n }) {
218
+ return /* @__PURE__ */ v("div", { className: "nosto_element", id: e, children: n }, e + (t || ""));
214
219
  }
215
220
  function Y({ product: e, tagging: t, placements: n, reference: o }) {
216
- const { renderCampaigns: r } = d();
221
+ const { renderCampaigns: r } = l();
217
222
  if (t && !t.product_id)
218
223
  throw new Error("The product object must contain a product_id property");
219
224
  const c = (t == null ? void 0 : t.product_id) ?? e;
220
- u(
221
- async (a) => {
222
- const s = a.defaultSession().viewProduct(t ?? e).setPlacements(n || a.placements.getPlacements());
225
+ d(
226
+ async (i) => {
227
+ const s = i.defaultSession().viewProduct(t ?? e).setPlacements(n || i.placements.getPlacements());
223
228
  o && s.setRef(c, o);
224
- const i = await s.load();
225
- r(i);
229
+ const a = await s.load();
230
+ r(a);
226
231
  },
227
232
  [c, t == null ? void 0 : t.selected_sku_id]
228
233
  );
@@ -233,7 +238,7 @@ function we(e) {
233
238
  function ee(e, t) {
234
239
  return new Promise((n, o) => {
235
240
  const r = document.createElement("script");
236
- r.type = "text/javascript", r.src = e, r.async = !0, r.onload = () => n(), r.onerror = () => o(), Object.entries((t == null ? void 0 : t.attributes) ?? {}).forEach(([c, a]) => r.setAttribute(c, a)), (t == null ? void 0 : t.position) === "head" ? document.head.appendChild(r) : document.body.appendChild(r);
241
+ r.type = "text/javascript", r.src = e, r.async = !0, r.onload = () => n(), r.onerror = () => o(), Object.entries((t == null ? void 0 : t.attributes) ?? {}).forEach(([c, i]) => r.setAttribute(c, i)), (t == null ? void 0 : t.position) === "head" ? document.head.appendChild(r) : document.body.appendChild(r);
237
242
  });
238
243
  }
239
244
  const te = { "nosto-client-script": "" };
@@ -243,16 +248,16 @@ function ne(e) {
243
248
  account: n,
244
249
  shopifyMarkets: o,
245
250
  loadScript: r = !0
246
- } = e, [c, a] = T(!1);
251
+ } = e, [c, i] = M(!1);
247
252
  return S(() => {
248
253
  function s() {
249
- a(!0);
254
+ i(!0);
250
255
  }
251
- if (j(), f((l) => l.setAutoLoad(!1)), !r) {
252
- f(s);
256
+ if (N(), m((u) => u.setAutoLoad(!1)), !r) {
257
+ m(s);
253
258
  return;
254
259
  }
255
- async function i() {
260
+ async function a() {
256
261
  await z({
257
262
  merchantId: n,
258
263
  shopifyInternational: o,
@@ -262,26 +267,26 @@ function ne(e) {
262
267
  scriptLoader: t
263
268
  }), s();
264
269
  }
265
- (!H() || o) && i();
270
+ (!q() || o) && a();
266
271
  }, [o == null ? void 0 : o.marketId, o == null ? void 0 : o.language]), { clientScriptLoaded: c };
267
272
  }
268
273
  function Ce(e) {
269
- const { account: t, multiCurrency: n = !1, children: o, recommendationComponent: r } = e, c = n ? e.currentVariation : "";
270
- if (r && !D(r))
274
+ const { account: t, multiCurrency: n = !1, children: o, recommendationComponent: r, renderMode: c } = e, i = n ? e.currentVariation : "";
275
+ if (r && !T(r))
271
276
  throw new Error(
272
277
  "The recommendationComponent prop must be a valid React element. Please provide a valid React element."
273
278
  );
274
- const a = r ? "JSON_ORIGINAL" : "HTML", { clientScriptLoaded: s } = ne(e);
275
- return s && f((i) => {
276
- i.defaultSession().setVariation(c).setResponseMode(a);
279
+ const s = c || (r ? "JSON_ORIGINAL" : "HTML"), { clientScriptLoaded: a } = ne(e);
280
+ return a && m((u) => {
281
+ u.defaultSession().setVariation(i).setResponseMode(s);
277
282
  }), /* @__PURE__ */ v(
278
283
  O.Provider,
279
284
  {
280
285
  value: {
281
286
  account: t,
282
- clientScriptLoaded: s,
283
- currentVariation: c,
284
- responseMode: a,
287
+ clientScriptLoaded: a,
288
+ currentVariation: i,
289
+ responseMode: s,
285
290
  recommendationComponent: r
286
291
  },
287
292
  children: o
@@ -289,8 +294,8 @@ function Ce(e) {
289
294
  );
290
295
  }
291
296
  function oe({ query: e, placements: t }) {
292
- const { renderCampaigns: n } = d();
293
- u(
297
+ const { renderCampaigns: n } = l();
298
+ d(
294
299
  async (o) => {
295
300
  const r = await o.defaultSession().viewSearch(e).setPlacements(t || o.placements.getPlacements()).load();
296
301
  n(r);
@@ -303,9 +308,9 @@ function he(e) {
303
308
  }
304
309
  function re({ cart: e, customer: t } = {}) {
305
310
  const { clientScriptLoaded: n } = P();
306
- g(() => {
311
+ j(() => {
307
312
  const o = e ? w(e) : void 0, r = t ? w(t) : void 0;
308
- n && f((c) => {
313
+ n && m((c) => {
309
314
  c.defaultSession().setCart(o).setCustomer(r).viewOther().load({ skipPageViews: !0 });
310
315
  });
311
316
  }, [n, e, t]);
@@ -314,7 +319,7 @@ function ye(e) {
314
319
  return re(e), null;
315
320
  }
316
321
  export {
317
- ie as Nosto404,
322
+ ae as Nosto404,
318
323
  ue as NostoCategory,
319
324
  de as NostoCheckout,
320
325
  O as NostoContext,
package/dist/index.umd.js CHANGED
@@ -1 +1 @@
1
- (function(r,u){typeof exports=="object"&&typeof module<"u"?u(exports,require("react"),require("react/jsx-runtime"),require("react-dom/client")):typeof define=="function"&&define.amd?define(["exports","react","react/jsx-runtime","react-dom/client"],u):(r=typeof globalThis<"u"?globalThis:r||self,u(r["@nosto/nosto-react"]={},r.React,r["react/jsx-runtime"],r.client))})(this,function(r,u,y,V){"use strict";const S=u.createContext({account:"",currentVariation:"",responseMode:"HTML",clientScriptLoaded:!1});function w(){return u.useContext(S)}const O=e=>String(e)==="[object Object]";function j(e){if(!O(e))return!1;const t=e.constructor;if(t===void 0)return!0;const n=t.prototype;return!(!O(n)||!n.hasOwnProperty("isPrototypeOf"))}function P(e,t){if(e===t)return!0;if(e instanceof Date&&t instanceof Date)return e.getTime()===t.getTime();if(e instanceof Array&&t instanceof Array)return e.length!==t.length?!1:e.every((n,o)=>P(n,t[o]));if(j(e)&&j(t)){const n=Object.entries(e);return n.length!==Object.keys(t).length?!1:n.every(([o,s])=>P(s,t[o]))}return!1}function L(e,t){return u.useEffect(e,F(t))}function F(e){const t=u.useRef(e),n=u.useRef(0);return P(e,t.current)||(t.current=e,n.current+=1),u.useMemo(()=>t.current,[n.current])}function g(){window.nostojs=window.nostojs??function(e){(window.nostojs.q=window.nostojs.q??[]).push(e)}}function z(){return typeof window.nosto<"u"}const U={production:"https://connect.nosto.com/",staging:"https://connect.staging.nosto.com/",local:"https://connect.nosto.com/"};function R(e){return U[e??"production"]}function $({merchantId:e,env:t,options:n,shopifyInternational:o,scriptLoader:s}){var c,a;const i=document.querySelector("script[nosto-language], script[nosto-market-id]"),d=String((o==null?void 0:o.marketId)||""),f=(o==null?void 0:o.language)||"",v=(i==null?void 0:i.getAttribute("nosto-language"))!==f||(i==null?void 0:i.getAttribute("nosto-market-id"))!==d;if(!i||v){const h=document.querySelector("#nosto-sandbox");(c=i==null?void 0:i.parentNode)==null||c.removeChild(i),(a=h==null?void 0:h.parentNode)==null||a.removeChild(h);const N=new URL("/script/shopify/market/nosto.js",R(t));N.searchParams.append("merchant",e),N.searchParams.append("market",d),N.searchParams.append("locale",f.toLowerCase());const ue={...n==null?void 0:n.attributes,"nosto-language":f,"nosto-market-id":d};return(s??E)(N.toString(),{...n,attributes:ue})}return Promise.resolve()}function G(e){if(e.shopifyInternational)return $(e);const{merchantId:t,env:n,options:o,scriptLoader:s}=e,c=s??E,a=new URL(`/include/${t}`,R(n));return c(a.toString(),o)}function E(e,t){return new Promise((n,o)=>{const s=document.createElement("script");s.src=e,s.async=!0,s.type="text/javascript",s.onload=()=>n(),s.onerror=()=>o(),Object.entries((t==null?void 0:t.attributes)??{}).forEach(([c,a])=>s.setAttribute(c,a)),(t==null?void 0:t.position)==="head"?document.head.appendChild(s):document.body.appendChild(s)})}async function p(e){return window.nostojs(e)}typeof window<"u"&&g();function l(e,t,n){const{clientScriptLoaded:o}=w();(n!=null&&n.deep?L:u.useEffect)(()=>{o&&p(e)},[o,...t??[]])}function J(e){return u.cloneElement(e.recommendationComponent,{nostoRecommendation:e.nostoRecommendation})}function b(e){p(t=>t.placements.injectCampaigns(e))}function W(e){if(!window.nostojs)throw new Error("Nosto has not yet been initialized");b(e.recommendations)}function m(){const{responseMode:e,recommendationComponent:t}=w(),n=u.useRef({});if(e=="HTML")return{renderCampaigns:W};function o(s){var a,i;b(((a=s.campaigns)==null?void 0:a.content)??{});const c=((i=s.campaigns)==null?void 0:i.recommendations)??{};for(const d in c){const f=c[d],v="#"+d,h=document.querySelector(v);h&&(n.current[d]||(n.current[d]=V.createRoot(h)),n.current[d].render(y.jsx(J,{recommendationComponent:t,nostoRecommendation:f})))}}return{renderCampaigns:o}}function A(e){const{renderCampaigns:t}=m();l(async n=>{const o=await n.defaultSession().viewNotFound().setPlacements((e==null?void 0:e.placements)||n.placements.getPlacements()).load();t(o)})}function Z(e){return A(e),null}function k({category:e,placements:t}){const{renderCampaigns:n}=m();l(async o=>{const s=await o.defaultSession().viewCategory(e).setPlacements(t||o.placements.getPlacements()).load();n(s)},[e])}function B(e){return k(e),null}function I(e){const{renderCampaigns:t}=m();l(async n=>{const o=await n.defaultSession().viewCart().setPlacements((e==null?void 0:e.placements)||n.placements.getPlacements()).load();t(o)})}function K(e){return I(e),null}function T(e){const{renderCampaigns:t}=m();l(async n=>{const o=await n.defaultSession().viewFrontPage().setPlacements((e==null?void 0:e.placements)||n.placements.getPlacements()).load();t(o)})}function Q(e){return T(e),null}function C(e){return!e||typeof e!="object"||X(e)||Y(e)?e:Array.isArray(e)?e.map(C):Object.keys(e).reduce((t,n)=>{const o=n[0].toLowerCase()+n.slice(1).replace(/([A-Z]+)/g,(s,c)=>"_"+c.toLowerCase());return t[o]=C(e[n]),t},{})}function X(e){return Object.prototype.toString.call(e)==="[object Date]"}function Y(e){return Object.prototype.toString.call(e)==="[object RegExp]"}function _({order:e,placements:t}){const{renderCampaigns:n}=m();l(async o=>{const s=await o.defaultSession().addOrder(C(e)).setPlacements(t||o.placements.getPlacements()).load();n(s)},[e],{deep:!0})}function x(e){return _(e),null}function q(e){const{renderCampaigns:t}=m();l(async n=>{const o=await n.defaultSession().viewOther().setPlacements((e==null?void 0:e.placements)||n.placements.getPlacements()).load();t(o)})}function ee(e){return q(e),null}function te({id:e,pageType:t}){return y.jsx("div",{className:"nosto_element",id:e},e+(t||""))}function H({product:e,tagging:t,placements:n,reference:o}){const{renderCampaigns:s}=m();if(t&&!t.product_id)throw new Error("The product object must contain a product_id property");const c=(t==null?void 0:t.product_id)??e;l(async a=>{const i=a.defaultSession().viewProduct(t??e).setPlacements(n||a.placements.getPlacements());o&&i.setRef(c,o);const d=await i.load();s(d)},[c,t==null?void 0:t.selected_sku_id])}function ne(e){return H(e),null}function oe(e,t){return new Promise((n,o)=>{const s=document.createElement("script");s.type="text/javascript",s.src=e,s.async=!0,s.onload=()=>n(),s.onerror=()=>o(),Object.entries((t==null?void 0:t.attributes)??{}).forEach(([c,a])=>s.setAttribute(c,a)),(t==null?void 0:t.position)==="head"?document.head.appendChild(s):document.body.appendChild(s)})}const se={"nosto-client-script":""};function re(e){const{scriptLoader:t=oe,account:n,shopifyMarkets:o,loadScript:s=!0}=e,[c,a]=u.useState(!1);return u.useEffect(()=>{function i(){a(!0)}if(g(),p(f=>f.setAutoLoad(!1)),!s){p(i);return}async function d(){await G({merchantId:n,shopifyInternational:o,options:{attributes:se},scriptLoader:t}),i()}(!z()||o)&&d()},[o==null?void 0:o.marketId,o==null?void 0:o.language]),{clientScriptLoaded:c}}function ce(e){const{account:t,multiCurrency:n=!1,children:o,recommendationComponent:s}=e,c=n?e.currentVariation:"";if(s&&!u.isValidElement(s))throw new Error("The recommendationComponent prop must be a valid React element. Please provide a valid React element.");const a=s?"JSON_ORIGINAL":"HTML",{clientScriptLoaded:i}=re(e);return i&&p(d=>{d.defaultSession().setVariation(c).setResponseMode(a)}),y.jsx(S.Provider,{value:{account:t,clientScriptLoaded:i,currentVariation:c,responseMode:a,recommendationComponent:s},children:o})}function M({query:e,placements:t}){const{renderCampaigns:n}=m();l(async o=>{const s=await o.defaultSession().viewSearch(e).setPlacements(t||o.placements.getPlacements()).load();n(s)},[e])}function ie(e){return M(e),null}function D({cart:e,customer:t}={}){const{clientScriptLoaded:n}=w();L(()=>{const o=e?C(e):void 0,s=t?C(t):void 0;n&&p(c=>{c.defaultSession().setCart(o).setCustomer(s).viewOther().load({skipPageViews:!0})})},[n,e,t])}function ae(e){return D(e),null}r.Nosto404=Z,r.NostoCategory=B,r.NostoCheckout=K,r.NostoContext=S,r.NostoHome=Q,r.NostoOrder=x,r.NostoOther=ee,r.NostoPlacement=te,r.NostoProduct=ne,r.NostoProvider=ce,r.NostoSearch=ie,r.NostoSession=ae,r.useNosto404=A,r.useNostoCategory=k,r.useNostoCheckout=I,r.useNostoContext=w,r.useNostoHome=T,r.useNostoOrder=_,r.useNostoOther=q,r.useNostoProduct=H,r.useNostoSearch=M,r.useNostoSession=D,Object.defineProperty(r,Symbol.toStringTag,{value:"Module"})});
1
+ (function(s,u){typeof exports=="object"&&typeof module<"u"?u(exports,require("react"),require("react/jsx-runtime"),require("react-dom/client")):typeof define=="function"&&define.amd?define(["exports","react","react/jsx-runtime","react-dom/client"],u):(s=typeof globalThis<"u"?globalThis:s||self,u(s["@nosto/nosto-react"]={},s.React,s["react/jsx-runtime"],s.client))})(this,function(s,u,y,V){"use strict";const S=u.createContext({account:"",currentVariation:"",responseMode:"HTML",clientScriptLoaded:!1});function N(){return u.useContext(S)}const g=e=>String(e)==="[object Object]";function j(e){if(!g(e))return!1;const t=e.constructor;if(t===void 0)return!0;const n=t.prototype;return!(!g(n)||!n.hasOwnProperty("isPrototypeOf"))}function P(e,t){if(e===t)return!0;if(e instanceof Date&&t instanceof Date)return e.getTime()===t.getTime();if(e instanceof Array&&t instanceof Array)return e.length!==t.length?!1:e.every((n,o)=>P(n,t[o]));if(j(e)&&j(t)){const n=Object.entries(e);return n.length!==Object.keys(t).length?!1:n.every(([o,r])=>P(r,t[o]))}return!1}function L(e,t){return u.useEffect(e,F(t))}function F(e){const t=u.useRef(e),n=u.useRef(0);return P(e,t.current)||(t.current=e,n.current+=1),u.useMemo(()=>t.current,[n.current])}function v(){window.nostojs=window.nostojs??function(e){(window.nostojs.q=window.nostojs.q??[]).push(e)}}async function p(e){return window.nostojs(e)}typeof window<"u"&&(v(),p(e=>{e.internal.getSettings()}));function z(){return typeof window.nosto<"u"}const U={production:"https://connect.nosto.com/",staging:"https://connect.staging.nosto.com/",local:"https://connect.nosto.com/"};function R(e){return U[e??"production"]}function $({merchantId:e,env:t,options:n,shopifyInternational:o,scriptLoader:r}){var c,a;const i=document.querySelector("script[nosto-language], script[nosto-market-id]"),d=String((o==null?void 0:o.marketId)||""),l=(o==null?void 0:o.language)||"",O=(i==null?void 0:i.getAttribute("nosto-language"))!==l||(i==null?void 0:i.getAttribute("nosto-market-id"))!==d;if(!i||O){const h=document.querySelector("#nosto-sandbox");(c=i==null?void 0:i.parentNode)==null||c.removeChild(i),(a=h==null?void 0:h.parentNode)==null||a.removeChild(h);const w=new URL("/script/shopify/market/nosto.js",R(t));w.searchParams.append("merchant",e),w.searchParams.append("market",d),w.searchParams.append("locale",l.toLowerCase());const ue={...n==null?void 0:n.attributes,"nosto-language":l,"nosto-market-id":d};return(r??E)(w.toString(),{...n,attributes:ue})}return Promise.resolve()}function G(e){if(e.shopifyInternational)return $(e);const{merchantId:t,env:n,options:o,scriptLoader:r}=e,c=r??E,a=new URL(`/include/${t}`,R(n));return c(a.toString(),o)}function E(e,t){return new Promise((n,o)=>{const r=document.createElement("script");r.src=e,r.async=!0,r.type="text/javascript",r.onload=()=>n(),r.onerror=()=>o(),Object.entries((t==null?void 0:t.attributes)??{}).forEach(([c,a])=>r.setAttribute(c,a)),(t==null?void 0:t.position)==="head"?document.head.appendChild(r):document.body.appendChild(r)})}typeof window<"u"&&v();function m(e,t,n){const{clientScriptLoaded:o}=N();(n!=null&&n.deep?L:u.useEffect)(()=>{o&&p(e)},[o,...t??[]])}function J(e){return u.cloneElement(e.recommendationComponent,{nostoRecommendation:e.nostoRecommendation})}function A(e){p(t=>t.placements.injectCampaigns(e))}function W(e){if(!window.nostojs)throw new Error("Nosto has not yet been initialized");A(e.recommendations)}function f(){const{responseMode:e,recommendationComponent:t}=N(),n=u.useRef({});if(e=="HTML")return{renderCampaigns:W};if(!t)throw new Error("recommendationComponent is required for client-side rendering using hook");function o(r){var a,i;A(((a=r.campaigns)==null?void 0:a.content)??{});const c=((i=r.campaigns)==null?void 0:i.recommendations)??{};for(const d in c){const l=c[d],O="#"+d,h=document.querySelector(O);h&&(n.current[d]||(n.current[d]=V.createRoot(h)),n.current[d].render(y.jsx(J,{recommendationComponent:t,nostoRecommendation:l})))}}return{renderCampaigns:o}}function b(e){const{renderCampaigns:t}=f();m(async n=>{const o=await n.defaultSession().viewNotFound().setPlacements((e==null?void 0:e.placements)||n.placements.getPlacements()).load();t(o)})}function Z(e){return b(e),null}function k({category:e,placements:t}){const{renderCampaigns:n}=f();m(async o=>{const r=await o.defaultSession().viewCategory(e).setPlacements(t||o.placements.getPlacements()).load();n(r)},[e])}function B(e){return k(e),null}function I(e){const{renderCampaigns:t}=f();m(async n=>{const o=await n.defaultSession().viewCart().setPlacements((e==null?void 0:e.placements)||n.placements.getPlacements()).load();t(o)})}function K(e){return I(e),null}function T(e){const{renderCampaigns:t}=f();m(async n=>{const o=await n.defaultSession().viewFrontPage().setPlacements((e==null?void 0:e.placements)||n.placements.getPlacements()).load();t(o)})}function Q(e){return T(e),null}function C(e){return!e||typeof e!="object"||X(e)||Y(e)?e:Array.isArray(e)?e.map(C):Object.keys(e).reduce((t,n)=>{const o=n[0].toLowerCase()+n.slice(1).replace(/([A-Z]+)/g,(r,c)=>"_"+c.toLowerCase());return t[o]=C(e[n]),t},{})}function X(e){return Object.prototype.toString.call(e)==="[object Date]"}function Y(e){return Object.prototype.toString.call(e)==="[object RegExp]"}function _({order:e,placements:t}){const{renderCampaigns:n}=f();m(async o=>{const r=await o.defaultSession().addOrder(C(e)).setPlacements(t||o.placements.getPlacements()).load();n(r)},[e],{deep:!0})}function x(e){return _(e),null}function q(e){const{renderCampaigns:t}=f();m(async n=>{const o=await n.defaultSession().viewOther().setPlacements((e==null?void 0:e.placements)||n.placements.getPlacements()).load();t(o)})}function ee(e){return q(e),null}function te({id:e,pageType:t,children:n}){return y.jsx("div",{className:"nosto_element",id:e,children:n},e+(t||""))}function M({product:e,tagging:t,placements:n,reference:o}){const{renderCampaigns:r}=f();if(t&&!t.product_id)throw new Error("The product object must contain a product_id property");const c=(t==null?void 0:t.product_id)??e;m(async a=>{const i=a.defaultSession().viewProduct(t??e).setPlacements(n||a.placements.getPlacements());o&&i.setRef(c,o);const d=await i.load();r(d)},[c,t==null?void 0:t.selected_sku_id])}function ne(e){return M(e),null}function oe(e,t){return new Promise((n,o)=>{const r=document.createElement("script");r.type="text/javascript",r.src=e,r.async=!0,r.onload=()=>n(),r.onerror=()=>o(),Object.entries((t==null?void 0:t.attributes)??{}).forEach(([c,a])=>r.setAttribute(c,a)),(t==null?void 0:t.position)==="head"?document.head.appendChild(r):document.body.appendChild(r)})}const re={"nosto-client-script":""};function se(e){const{scriptLoader:t=oe,account:n,shopifyMarkets:o,loadScript:r=!0}=e,[c,a]=u.useState(!1);return u.useEffect(()=>{function i(){a(!0)}if(v(),p(l=>l.setAutoLoad(!1)),!r){p(i);return}async function d(){await G({merchantId:n,shopifyInternational:o,options:{attributes:re},scriptLoader:t}),i()}(!z()||o)&&d()},[o==null?void 0:o.marketId,o==null?void 0:o.language]),{clientScriptLoaded:c}}function ce(e){const{account:t,multiCurrency:n=!1,children:o,recommendationComponent:r,renderMode:c}=e,a=n?e.currentVariation:"";if(r&&!u.isValidElement(r))throw new Error("The recommendationComponent prop must be a valid React element. Please provide a valid React element.");const i=c||(r?"JSON_ORIGINAL":"HTML"),{clientScriptLoaded:d}=se(e);return d&&p(l=>{l.defaultSession().setVariation(a).setResponseMode(i)}),y.jsx(S.Provider,{value:{account:t,clientScriptLoaded:d,currentVariation:a,responseMode:i,recommendationComponent:r},children:o})}function H({query:e,placements:t}){const{renderCampaigns:n}=f();m(async o=>{const r=await o.defaultSession().viewSearch(e).setPlacements(t||o.placements.getPlacements()).load();n(r)},[e])}function ie(e){return H(e),null}function D({cart:e,customer:t}={}){const{clientScriptLoaded:n}=N();L(()=>{const o=e?C(e):void 0,r=t?C(t):void 0;n&&p(c=>{c.defaultSession().setCart(o).setCustomer(r).viewOther().load({skipPageViews:!0})})},[n,e,t])}function ae(e){return D(e),null}s.Nosto404=Z,s.NostoCategory=B,s.NostoCheckout=K,s.NostoContext=S,s.NostoHome=Q,s.NostoOrder=x,s.NostoOther=ee,s.NostoPlacement=te,s.NostoProduct=ne,s.NostoProvider=ce,s.NostoSearch=ie,s.NostoSession=ae,s.useNosto404=b,s.useNostoCategory=k,s.useNostoCheckout=I,s.useNostoContext=N,s.useNostoHome=T,s.useNostoOrder=_,s.useNostoOther=q,s.useNostoProduct=M,s.useNostoSearch=H,s.useNostoSession=D,Object.defineProperty(s,Symbol.toStringTag,{value:"Module"})});
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nosto/nosto-react",
3
3
  "description": "Component library to simply implementing Nosto on React.",
4
- "version": "2.6.0",
4
+ "version": "2.7.0",
5
5
  "author": "Mridang Agarwalla, Dominik Gilg",
6
6
  "license": "ISC",
7
7
  "repository": {
@@ -44,6 +44,7 @@
44
44
  "@types/user-event": "^4.1.1",
45
45
  "@vitejs/plugin-react": "^4.3.1",
46
46
  "eslint": "^9.13.0",
47
+ "eslint-plugin-barrel-files": "^3.0.1",
47
48
  "eslint-plugin-promise": "^7.1.0",
48
49
  "eslint-plugin-react": "^7.33.2",
49
50
  "prettier": "^3.3.3",
@@ -58,7 +59,7 @@
58
59
  "typescript-eslint": "^8.13.0",
59
60
  "vite": "^6.0.3",
60
61
  "vite-plugin-dts": "^4.2.2",
61
- "vitest": "^2.0.5"
62
+ "vitest": "^3.0.2"
62
63
  },
63
64
  "main": "./dist/index.umd.js",
64
65
  "module": "./dist/index.es.js",
@@ -73,5 +74,7 @@
73
74
  "bugs": {
74
75
  "url": "https://github.com/Nosto/nosto-react/issues"
75
76
  },
76
- "homepage": "https://github.com/Nosto/nosto-react#readme"
77
+ "publishConfig": {
78
+ "access": "public"
79
+ }
77
80
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @group Components
3
3
  */
4
- export type NostoPlacementProps = { id: string; pageType?: string }
4
+ export type NostoPlacementProps = { id: string; pageType?: string; children?: React.ReactNode }
5
5
 
6
6
  /**
7
7
  * Nosto React has a special component called NostoPlacement.
@@ -16,6 +16,10 @@ export type NostoPlacementProps = { id: string; pageType?: string }
16
16
  *
17
17
  * @group Components
18
18
  */
19
- export function NostoPlacement({ id, pageType }: NostoPlacementProps) {
20
- return <div className="nosto_element" id={id} key={id + (pageType || "")} />
19
+ export function NostoPlacement({ id, pageType, children }: NostoPlacementProps) {
20
+ return (
21
+ <div className="nosto_element" id={id} key={id + (pageType || "")}>
22
+ {children}
23
+ </div>
24
+ )
21
25
  }
@@ -4,6 +4,7 @@ import type { ReactNode } from "react"
4
4
  import { ScriptLoadOptions } from "../hooks/scriptLoader"
5
5
  import { useLoadClientScript } from "../hooks/useLoadClientScript"
6
6
  import { nostojs } from "@nosto/nosto-js"
7
+ import { RenderMode } from "@nosto/nosto-js/client"
7
8
 
8
9
  /**
9
10
  * @group Components
@@ -33,6 +34,10 @@ export interface NostoProviderProps {
33
34
  * Recommendation component which holds nostoRecommendation object
34
35
  */
35
36
  recommendationComponent?: RecommendationComponent
37
+ /**
38
+ * Recommendation render mode. See {@link https://nosto.github.io/nosto-js/types/client.RenderMode.html}
39
+ */
40
+ renderMode?: RenderMode
36
41
  /**
37
42
  * Enables Shopify markets with language and market id
38
43
  */
@@ -72,7 +77,7 @@ export interface NostoProviderProps {
72
77
  * @group Components
73
78
  */
74
79
  export function NostoProvider(props: NostoProviderProps) {
75
- const { account, multiCurrency = false, children, recommendationComponent } = props
80
+ const { account, multiCurrency = false, children, recommendationComponent, renderMode } = props
76
81
 
77
82
  // Pass currentVariation as empty string if multiCurrency is disabled
78
83
  const currentVariation = multiCurrency ? props.currentVariation : ""
@@ -84,7 +89,7 @@ export function NostoProvider(props: NostoProviderProps) {
84
89
  }
85
90
 
86
91
  // Set responseMode for loading campaigns:
87
- const responseMode = recommendationComponent ? "JSON_ORIGINAL" : "HTML"
92
+ const responseMode = renderMode || (recommendationComponent ? "JSON_ORIGINAL" : "HTML")
88
93
 
89
94
  const { clientScriptLoaded } = useLoadClientScript(props)
90
95
 
@@ -22,4 +22,4 @@ export function useNostoHome(props?: NostoHomeProps) {
22
22
  .load()
23
23
  renderCampaigns(data)
24
24
  })
25
- }
25
+ }
@@ -20,7 +20,7 @@ function RecommendationComponentWrapper(props: {
20
20
  }
21
21
 
22
22
  function injectPlacements(data: Record<string, unknown>) {
23
- nostojs(api => api.placements.injectCampaigns(data as Parameters<API['placements']['injectCampaigns']>[0]))
23
+ nostojs(api => api.placements.injectCampaigns(data as Parameters<API["placements"]["injectCampaigns"]>[0]))
24
24
  }
25
25
 
26
26
  function injectCampaigns(data: CampaignData) {
@@ -39,6 +39,10 @@ export function useRenderCampaigns() {
39
39
  return { renderCampaigns: injectCampaigns }
40
40
  }
41
41
 
42
+ if (!recommendationComponent) {
43
+ throw new Error("recommendationComponent is required for client-side rendering using hook")
44
+ }
45
+
42
46
  function renderCampaigns(data: CampaignData) {
43
47
  // inject Onsite content campaigns directly
44
48
  injectPlacements(data.campaigns?.content ?? {})
package/src/index.ts CHANGED
@@ -1,25 +1,26 @@
1
+ /* eslint-disable barrel-files/avoid-barrel-files */
1
2
  export type { Product, PushedCustomer as Customer, Cart, WebsiteOrder as Order } from "@nosto/nosto-js/client"
2
3
  export type { Recommendation } from "./types"
3
4
  export { type ScriptLoadOptions } from "./hooks/scriptLoader"
4
5
  export { NostoContext, type NostoContextType } from "./context"
5
6
  export { useNostoContext } from "./hooks/useNostoContext"
6
- export * from "./components/Nosto404"
7
- export * from "./components/NostoCategory"
8
- export * from "./components/NostoCheckout"
9
- export * from "./components/NostoHome"
10
- export * from "./components/NostoOrder"
11
- export * from "./components/NostoOther"
12
- export * from "./components/NostoPlacement"
13
- export * from "./components/NostoProduct"
14
- export * from "./components/NostoProvider"
15
- export * from "./components/NostoSearch"
16
- export * from "./components/NostoSession"
17
- export * from "./hooks/useNosto404"
18
- export * from "./hooks/useNostoCategory"
19
- export * from "./hooks/useNostoCheckout"
20
- export * from "./hooks/useNostoHome"
21
- export * from "./hooks/useNostoOrder"
22
- export * from "./hooks/useNostoOther"
23
- export * from "./hooks/useNostoProduct"
24
- export * from "./hooks/useNostoSearch"
25
- export * from "./hooks/useNostoSession"
7
+ export { Nosto404 } from "./components/Nosto404"
8
+ export { NostoCategory } from "./components/NostoCategory"
9
+ export { NostoCheckout } from "./components/NostoCheckout"
10
+ export { NostoHome } from "./components/NostoHome"
11
+ export { NostoOrder } from "./components/NostoOrder"
12
+ export { NostoOther } from "./components/NostoOther"
13
+ export { NostoPlacement, type NostoPlacementProps } from "./components/NostoPlacement"
14
+ export { NostoProduct } from "./components/NostoProduct"
15
+ export { NostoProvider } from "./components/NostoProvider"
16
+ export { NostoSearch } from "./components/NostoSearch"
17
+ export { NostoSession } from "./components/NostoSession"
18
+ export { useNosto404, type Nosto404Props } from "./hooks/useNosto404"
19
+ export { useNostoCategory, type NostoCategoryProps } from "./hooks/useNostoCategory"
20
+ export { useNostoCheckout, type NostoCheckoutProps } from "./hooks/useNostoCheckout"
21
+ export { useNostoHome, type NostoHomeProps } from "./hooks/useNostoHome"
22
+ export { useNostoOrder, type NostoOrderProps } from "./hooks/useNostoOrder"
23
+ export { useNostoOther, type NostoOtherProps } from "./hooks/useNostoOther"
24
+ export { useNostoProduct, type NostoProductProps } from "./hooks/useNostoProduct"
25
+ export { useNostoSearch, type NostoSearchProps }from "./hooks/useNostoSearch"
26
+ export { useNostoSession, type NostoSessionProps } from "./hooks/useNostoSession"