@ninetailed/experience.js-react 7.5.3 → 7.6.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.cjs.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./src/index";
@@ -34,7 +34,8 @@ const NinetailedProvider = props => {
34
34
  buildClientContext,
35
35
  onInitProfileId,
36
36
  componentViewTrackingThreshold,
37
- storageImpl
37
+ storageImpl,
38
+ useClientSideEvaluation
38
39
  } = props;
39
40
  return new experience_js.Ninetailed({
40
41
  clientId,
@@ -50,7 +51,8 @@ const NinetailedProvider = props => {
50
51
  buildClientContext,
51
52
  onInitProfileId,
52
53
  componentViewTrackingThreshold,
53
- storageImpl
54
+ storageImpl,
55
+ useClientSideEvaluation
54
56
  });
55
57
  }, []);
56
58
  const {
@@ -252,80 +254,18 @@ const MergeTag = ({
252
254
  }) : null;
253
255
  };
254
256
 
255
- const useExperienceSelectionMiddleware = ({
256
- experiences,
257
- baseline,
258
- profile
259
- }) => {
260
- const {
261
- plugins
262
- } = useNinetailed();
263
- const [_, setCurrentTime] = React.useState(Date.now());
264
- const {
265
- addListeners,
266
- removeListeners,
267
- middleware
268
- } = React.useMemo(() => experience_js.makeExperienceSelectMiddleware({
269
- plugins,
270
- experiences,
271
- baseline,
272
- profile,
273
- onChange: () => setCurrentTime(Date.now())
274
- }), [plugins, experiences, baseline, profile]);
275
- React.useEffect(() => {
276
- addListeners();
277
- return () => {
278
- removeListeners();
279
- };
280
- }, [addListeners, removeListeners]);
281
- return middleware;
282
- };
283
-
284
257
  const useExperience = ({
285
258
  baseline,
286
259
  experiences
287
260
  }) => {
288
- const profileState = useProfile();
261
+ const ninetailed = useNinetailed();
289
262
  const hasVariants = experiences.map(experience => experience_js.selectHasExperienceVariants(experience, baseline)).reduce((acc, curr) => acc || curr, false);
290
- const {
291
- status,
292
- profile
293
- } = profileState;
294
- const experienceSelectionMiddleware = useExperienceSelectionMiddleware({
295
- experiences,
296
- baseline,
297
- profile
298
- });
299
- const overrideResult = _a => {
300
- var {
301
- experience: originalExperience,
302
- variant: originalVariant,
303
- variantIndex: originalVariantIndex
304
- } = _a,
305
- other = __rest(_a, ["experience", "variant", "variantIndex"]);
306
- const {
307
- experience,
308
- variant,
309
- variantIndex
310
- } = experienceSelectionMiddleware({
311
- experience: originalExperience,
312
- variant: originalVariant,
313
- variantIndex: originalVariantIndex
314
- });
315
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
316
- // @ts-ignore
317
- return Object.assign(Object.assign({}, other), {
318
- audience: (experience === null || experience === void 0 ? void 0 : experience.audience) ? experience.audience : null,
319
- experience,
320
- variant,
321
- variantIndex
322
- });
323
- };
324
- const baseReturn = Object.assign(Object.assign({}, profileState), {
263
+ const [experience, setExperience] = React.useState({
325
264
  hasVariants,
326
- baseline
327
- });
328
- const emptyReturn = Object.assign(Object.assign({}, baseReturn), {
265
+ baseline,
266
+ error: null,
267
+ loading: true,
268
+ status: 'loading',
329
269
  experience: null,
330
270
  variant: baseline,
331
271
  variantIndex: 0,
@@ -333,54 +273,20 @@ const useExperience = ({
333
273
  isPersonalized: false,
334
274
  profile: null
335
275
  });
336
- if (status === 'loading') {
337
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
338
- // @ts-ignore
339
- return overrideResult(emptyReturn);
340
- }
341
- if (status === 'error') {
342
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
343
- // @ts-ignore
344
- return overrideResult(emptyReturn);
345
- }
346
- if (!profile) {
347
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
348
- // @ts-ignore
349
- return overrideResult(emptyReturn);
350
- }
351
- const experience = experience_js.selectExperience({
352
- experiences,
353
- profile
354
- });
355
- if (!experience) {
356
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
357
- // @ts-ignore
358
- return overrideResult(Object.assign(Object.assign({}, emptyReturn), {
359
- profile
360
- }));
361
- }
362
- const {
363
- variant,
364
- index
365
- } = experience_js.selectExperienceVariant({
366
- baseline,
367
- experience,
368
- profile
369
- });
370
- return overrideResult(Object.assign(Object.assign({}, baseReturn), {
371
- status: 'success',
372
- loading: false,
373
- error: null,
374
- experience,
375
- variant,
376
- variantIndex: index,
377
- audience: experience.audience ? experience.audience : null,
378
- profile,
379
- isPersonalized: true
380
- }));
276
+ React.useEffect(() => {
277
+ return ninetailed.onSelectVariant({
278
+ baseline,
279
+ experiences
280
+ }, state => {
281
+ setExperience(state);
282
+ });
283
+ }, [JSON.stringify(baseline), JSON.stringify(experiences)]);
284
+ return experience;
381
285
  };
382
286
 
383
- const ComponentMarker = /*#__PURE__*/React.forwardRef((_, ref) => {
287
+ const ComponentMarker = /*#__PURE__*/React.forwardRef(({
288
+ hidden: _hidden = false
289
+ }, ref) => {
384
290
  const {
385
291
  logger
386
292
  } = useNinetailed();
@@ -398,16 +304,16 @@ const ComponentMarker = /*#__PURE__*/React.forwardRef((_, ref) => {
398
304
  }, [logger]);
399
305
  React.useEffect(() => {
400
306
  if (markerRef.current) {
401
- const observableElement = getObservableElement(markerRef.current);
307
+ const nextSibling = getNextSibling(markerRef.current, _hidden);
402
308
  if (ref) {
403
309
  if (typeof ref === 'function') {
404
- ref(observableElement);
310
+ ref(nextSibling);
405
311
  } else {
406
- ref.current = observableElement;
312
+ ref.current = nextSibling;
407
313
  }
408
314
  }
409
315
  }
410
- }, []);
316
+ }, [_hidden]);
411
317
  return jsxRuntime.jsx("div", {
412
318
  className: "nt-cmp-marker",
413
319
  style: {
@@ -416,22 +322,9 @@ const ComponentMarker = /*#__PURE__*/React.forwardRef((_, ref) => {
416
322
  ref: markerRef
417
323
  });
418
324
  });
419
- const getObservableSibling = element => {
420
- const nextSibling = element.nextElementSibling;
421
- if (!nextSibling) {
422
- return null;
423
- }
424
- const nextSiblingStyle = getComputedStyle(nextSibling);
425
- // Elements with display: none are not observable by the IntersectionObserver
426
- if (nextSiblingStyle.display !== 'none') {
427
- return nextSibling;
428
- }
429
- return getObservableSibling(nextSibling);
430
- };
431
- const getObservableElement = element => {
432
- const observableSibling = getObservableSibling(element);
433
- if (observableSibling) {
434
- return observableSibling;
325
+ const getNextSibling = (element, recursive = false) => {
326
+ if (!recursive) {
327
+ return element.nextSibling;
435
328
  }
436
329
  const {
437
330
  parentElement
@@ -439,7 +332,10 @@ const getObservableElement = element => {
439
332
  if (!parentElement) {
440
333
  return null;
441
334
  }
442
- return getObservableElement(parentElement);
335
+ if (parentElement.nextSibling) {
336
+ return parentElement.nextSibling;
337
+ }
338
+ return getNextSibling(parentElement, true);
443
339
  };
444
340
 
445
341
  const DefaultExperienceLoadingComponent = _a => {
@@ -569,7 +465,8 @@ const Experience = _a => {
569
465
  }
570
466
  if (isVariantHidden) {
571
467
  return jsxRuntime.jsx(ComponentMarker, {
572
- ref: componentRef
468
+ ref: componentRef,
469
+ hidden: true
573
470
  }, `marker-hidden-${(experience === null || experience === void 0 ? void 0 : experience.id) || 'baseline'}-${variant.id}`);
574
471
  }
575
472
  return jsxRuntime.jsxs(jsxRuntime.Fragment, {
@@ -1,6 +1,6 @@
1
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
1
  import React, { createContext, useMemo, useContext, useState, useRef, useEffect, createElement, forwardRef } from 'react';
3
- import { Ninetailed, selectVariant, makeExperienceSelectMiddleware, selectHasExperienceVariants, selectExperience, selectExperienceVariant } from '@ninetailed/experience.js';
2
+ import { Ninetailed, selectVariant, selectHasExperienceVariants } from '@ninetailed/experience.js';
3
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
  import { logger, isBrowser } from '@ninetailed/experience.js-shared';
5
5
  import { isEqual, get } from 'radash';
6
6
  import { useInView } from 'react-intersection-observer';
@@ -26,7 +26,8 @@ const NinetailedProvider = props => {
26
26
  buildClientContext,
27
27
  onInitProfileId,
28
28
  componentViewTrackingThreshold,
29
- storageImpl
29
+ storageImpl,
30
+ useClientSideEvaluation
30
31
  } = props;
31
32
  return new Ninetailed({
32
33
  clientId,
@@ -42,17 +43,17 @@ const NinetailedProvider = props => {
42
43
  buildClientContext,
43
44
  onInitProfileId,
44
45
  componentViewTrackingThreshold,
45
- storageImpl
46
+ storageImpl,
47
+ useClientSideEvaluation
46
48
  });
47
49
  }, []);
48
50
  const {
49
51
  children
50
52
  } = props;
51
- return jsx(NinetailedContext.Provider, Object.assign({
52
- value: ninetailed
53
- }, {
53
+ return /*#__PURE__*/jsx(NinetailedContext.Provider, {
54
+ value: ninetailed,
54
55
  children: children
55
- }));
56
+ });
56
57
  };
57
58
 
58
59
  const useNinetailed = () => {
@@ -63,42 +64,30 @@ const useNinetailed = () => {
63
64
  return ninetailed;
64
65
  };
65
66
 
66
- /******************************************************************************
67
- Copyright (c) Microsoft Corporation.
68
-
69
- Permission to use, copy, modify, and/or distribute this software for any
70
- purpose with or without fee is hereby granted.
71
-
72
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
73
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
74
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
75
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
76
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
77
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
78
- PERFORMANCE OF THIS SOFTWARE.
79
- ***************************************************************************** */
80
-
81
- function __rest(s, e) {
82
- var t = {};
83
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
84
- t[p] = s[p];
85
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
86
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
87
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
88
- t[p[i]] = s[p[i]];
89
- }
90
- return t;
67
+ function _objectWithoutPropertiesLoose(source, excluded) {
68
+ if (source == null) return {};
69
+ var target = {};
70
+ var sourceKeys = Object.keys(source);
71
+ var key, i;
72
+ for (i = 0; i < sourceKeys.length; i++) {
73
+ key = sourceKeys[i];
74
+ if (excluded.indexOf(key) >= 0) continue;
75
+ target[key] = source[key];
76
+ }
77
+ return target;
91
78
  }
92
79
 
80
+ const _excluded$3 = ["experiences"];
93
81
  const useProfile = () => {
94
82
  const ninetailed = useNinetailed();
95
83
  const [profileState, setProfileState] = useState(ninetailed.profileState);
96
84
  const profileStateRef = useRef({});
97
- /**
98
- * This effect compares the old and new profile state before updating it.
99
- * We use a ref to avoid an infinite loop which can happen when an empty profile state was updated with no changes.
100
- * This behaviour occurred as the validation handling on the error property was not set properly in the "CreateProfile" and "UpdateProfile" endpoint types.
101
- * Furthermore, it was also observed, that it "only" occurred when the preview plugin was used in parallel.
85
+
86
+ /**
87
+ * This effect compares the old and new profile state before updating it.
88
+ * We use a ref to avoid an infinite loop which can happen when an empty profile state was updated with no changes.
89
+ * This behaviour occurred as the validation handling on the error property was not set properly in the "CreateProfile" and "UpdateProfile" endpoint types.
90
+ * Furthermore, it was also observed, that it "only" occurred when the preview plugin was used in parallel.
102
91
  */
103
92
  useEffect(() => {
104
93
  ninetailed.onProfileChange(profileState => {
@@ -112,8 +101,8 @@ const useProfile = () => {
112
101
  }
113
102
  });
114
103
  }, []);
115
- const profileStateWithoutExperiences = __rest(profileState, ["experiences"]);
116
- return Object.assign(Object.assign({}, profileStateWithoutExperiences), {
104
+ const profileStateWithoutExperiences = _objectWithoutPropertiesLoose(profileState, _excluded$3);
105
+ return Object.assign({}, profileStateWithoutExperiences, {
117
106
  loading: profileState.status === 'loading'
118
107
  });
119
108
  };
@@ -147,21 +136,22 @@ const TrackHasSeenComponent = ({
147
136
  });
148
137
  }
149
138
  }, [inView]);
150
- return jsxs(Fragment, {
151
- children: [jsx("div", {
139
+ return /*#__PURE__*/jsxs(Fragment, {
140
+ children: [/*#__PURE__*/jsx("div", {
152
141
  ref: ref
153
142
  }), children]
154
143
  });
155
144
  };
156
145
 
157
- const Personalize = _a => {
158
- var {
146
+ const _excluded$2 = ["component", "loadingComponent", "variants", "holdout"];
147
+ const Personalize = _ref => {
148
+ let {
159
149
  component: Component,
160
150
  loadingComponent: LoadingComponent,
161
151
  variants = [],
162
152
  holdout = -1
163
- } = _a,
164
- baseline = __rest(_a, ["component", "loadingComponent", "variants", "holdout"]);
153
+ } = _ref,
154
+ baseline = _objectWithoutPropertiesLoose(_ref, _excluded$2);
165
155
  const {
166
156
  loading,
167
157
  variant,
@@ -172,7 +162,7 @@ const Personalize = _a => {
172
162
  });
173
163
  const hasVariants = variants.length > 0;
174
164
  if (!hasVariants) {
175
- return jsx(Component, Object.assign({}, baseline, {
165
+ return /*#__PURE__*/jsx(Component, Object.assign({}, baseline, {
176
166
  ninetailed: {
177
167
  isPersonalized,
178
168
  audience
@@ -181,26 +171,24 @@ const Personalize = _a => {
181
171
  }
182
172
  if (loading) {
183
173
  if (LoadingComponent) {
184
- return jsx(LoadingComponent, {});
174
+ return /*#__PURE__*/jsx(LoadingComponent, {});
185
175
  }
186
- return jsx("div", Object.assign({
176
+ return /*#__PURE__*/jsx("div", {
187
177
  style: {
188
178
  opacity: 0
189
- }
190
- }, {
191
- children: jsx(Component, Object.assign({}, variant, {
179
+ },
180
+ children: /*#__PURE__*/jsx(Component, Object.assign({}, variant, {
192
181
  ninetailed: {
193
182
  isPersonalized,
194
183
  audience
195
184
  }
196
185
  }))
197
- }), "hide");
186
+ }, "hide");
198
187
  }
199
- return jsx(TrackHasSeenComponent, Object.assign({
188
+ return /*#__PURE__*/jsx(TrackHasSeenComponent, {
200
189
  variant: variant,
201
190
  audience: audience,
202
- isPersonalized: isPersonalized
203
- }, {
191
+ isPersonalized: isPersonalized,
204
192
  children: /*#__PURE__*/createElement(Component, Object.assign({}, variant, {
205
193
  key: `${audience.id}-${variant.id}`,
206
194
  ninetailed: {
@@ -208,7 +196,7 @@ const Personalize = _a => {
208
196
  audience
209
197
  }
210
198
  }))
211
- }));
199
+ });
212
200
  };
213
201
 
214
202
  const generateSelectors = id => {
@@ -239,85 +227,23 @@ const MergeTag = ({
239
227
  return null;
240
228
  }
241
229
  const value = selectValueFromProfile(profile, id) || fallback;
242
- return value ? jsx(Fragment, {
230
+ return value ? /*#__PURE__*/jsx(Fragment, {
243
231
  children: value
244
232
  }) : null;
245
233
  };
246
234
 
247
- const useExperienceSelectionMiddleware = ({
248
- experiences,
249
- baseline,
250
- profile
251
- }) => {
252
- const {
253
- plugins
254
- } = useNinetailed();
255
- const [_, setCurrentTime] = useState(Date.now());
256
- const {
257
- addListeners,
258
- removeListeners,
259
- middleware
260
- } = useMemo(() => makeExperienceSelectMiddleware({
261
- plugins,
262
- experiences,
263
- baseline,
264
- profile,
265
- onChange: () => setCurrentTime(Date.now())
266
- }), [plugins, experiences, baseline, profile]);
267
- useEffect(() => {
268
- addListeners();
269
- return () => {
270
- removeListeners();
271
- };
272
- }, [addListeners, removeListeners]);
273
- return middleware;
274
- };
275
-
276
235
  const useExperience = ({
277
236
  baseline,
278
237
  experiences
279
238
  }) => {
280
- const profileState = useProfile();
239
+ const ninetailed = useNinetailed();
281
240
  const hasVariants = experiences.map(experience => selectHasExperienceVariants(experience, baseline)).reduce((acc, curr) => acc || curr, false);
282
- const {
283
- status,
284
- profile
285
- } = profileState;
286
- const experienceSelectionMiddleware = useExperienceSelectionMiddleware({
287
- experiences,
288
- baseline,
289
- profile
290
- });
291
- const overrideResult = _a => {
292
- var {
293
- experience: originalExperience,
294
- variant: originalVariant,
295
- variantIndex: originalVariantIndex
296
- } = _a,
297
- other = __rest(_a, ["experience", "variant", "variantIndex"]);
298
- const {
299
- experience,
300
- variant,
301
- variantIndex
302
- } = experienceSelectionMiddleware({
303
- experience: originalExperience,
304
- variant: originalVariant,
305
- variantIndex: originalVariantIndex
306
- });
307
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
308
- // @ts-ignore
309
- return Object.assign(Object.assign({}, other), {
310
- audience: (experience === null || experience === void 0 ? void 0 : experience.audience) ? experience.audience : null,
311
- experience,
312
- variant,
313
- variantIndex
314
- });
315
- };
316
- const baseReturn = Object.assign(Object.assign({}, profileState), {
241
+ const [experience, setExperience] = useState({
317
242
  hasVariants,
318
- baseline
319
- });
320
- const emptyReturn = Object.assign(Object.assign({}, baseReturn), {
243
+ baseline,
244
+ error: null,
245
+ loading: true,
246
+ status: 'loading',
321
247
  experience: null,
322
248
  variant: baseline,
323
249
  variantIndex: 0,
@@ -325,82 +251,48 @@ const useExperience = ({
325
251
  isPersonalized: false,
326
252
  profile: null
327
253
  });
328
- if (status === 'loading') {
329
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
330
- // @ts-ignore
331
- return overrideResult(emptyReturn);
332
- }
333
- if (status === 'error') {
334
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
335
- // @ts-ignore
336
- return overrideResult(emptyReturn);
337
- }
338
- if (!profile) {
339
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
340
- // @ts-ignore
341
- return overrideResult(emptyReturn);
342
- }
343
- const experience = selectExperience({
344
- experiences,
345
- profile
346
- });
347
- if (!experience) {
348
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
349
- // @ts-ignore
350
- return overrideResult(Object.assign(Object.assign({}, emptyReturn), {
351
- profile
352
- }));
353
- }
354
- const {
355
- variant,
356
- index
357
- } = selectExperienceVariant({
358
- baseline,
359
- experience,
360
- profile
361
- });
362
- return overrideResult(Object.assign(Object.assign({}, baseReturn), {
363
- status: 'success',
364
- loading: false,
365
- error: null,
366
- experience,
367
- variant,
368
- variantIndex: index,
369
- audience: experience.audience ? experience.audience : null,
370
- profile,
371
- isPersonalized: true
372
- }));
254
+ useEffect(() => {
255
+ return ninetailed.onSelectVariant({
256
+ baseline,
257
+ experiences
258
+ }, state => {
259
+ setExperience(state);
260
+ });
261
+ }, [JSON.stringify(baseline), JSON.stringify(experiences)]);
262
+ return experience;
373
263
  };
374
264
 
375
- const ComponentMarker = /*#__PURE__*/forwardRef((_, ref) => {
265
+ const ComponentMarker = /*#__PURE__*/forwardRef(({
266
+ hidden: _hidden = false
267
+ }, ref) => {
376
268
  const {
377
269
  logger
378
270
  } = useNinetailed();
379
271
  const markerRef = useRef(null);
380
272
  useEffect(() => {
381
- var _a;
382
- /*
383
- Due to React's limitation on setting !important styles during rendering, we set the display property on the DOM element directly.
384
- See: https://github.com/facebook/react/issues/1881
273
+ var _markerRef$current;
274
+ /*
275
+ Due to React's limitation on setting !important styles during rendering, we set the display property on the DOM element directly.
276
+ See: https://github.com/facebook/react/issues/1881
385
277
  */
386
- (_a = markerRef.current) === null || _a === void 0 ? void 0 : _a.style.setProperty('display', 'none', 'important');
278
+ (_markerRef$current = markerRef.current) == null || _markerRef$current.style.setProperty('display', 'none', 'important');
387
279
  }, []);
388
280
  useEffect(() => {
389
281
  logger.debug('Using fallback mechanism to detect when experiences are seen. This can lead to inaccurate results. Consider using a forwardRef instead. See: https://docs.ninetailed.io/for-developers/experience-sdk/rendering-experiences#tracking-impressions-of-experiences');
390
282
  }, [logger]);
391
283
  useEffect(() => {
392
284
  if (markerRef.current) {
393
- const observableElement = getObservableElement(markerRef.current);
285
+ const nextSibling = getNextSibling(markerRef.current, _hidden);
394
286
  if (ref) {
395
287
  if (typeof ref === 'function') {
396
- ref(observableElement);
288
+ ref(nextSibling);
397
289
  } else {
398
- ref.current = observableElement;
290
+ ref.current = nextSibling;
399
291
  }
400
292
  }
401
293
  }
402
- }, []);
403
- return jsx("div", {
294
+ }, [_hidden]);
295
+ return /*#__PURE__*/jsx("div", {
404
296
  className: "nt-cmp-marker",
405
297
  style: {
406
298
  display: 'none'
@@ -408,22 +300,9 @@ const ComponentMarker = /*#__PURE__*/forwardRef((_, ref) => {
408
300
  ref: markerRef
409
301
  });
410
302
  });
411
- const getObservableSibling = element => {
412
- const nextSibling = element.nextElementSibling;
413
- if (!nextSibling) {
414
- return null;
415
- }
416
- const nextSiblingStyle = getComputedStyle(nextSibling);
417
- // Elements with display: none are not observable by the IntersectionObserver
418
- if (nextSiblingStyle.display !== 'none') {
419
- return nextSibling;
420
- }
421
- return getObservableSibling(nextSibling);
422
- };
423
- const getObservableElement = element => {
424
- const observableSibling = getObservableSibling(element);
425
- if (observableSibling) {
426
- return observableSibling;
303
+ const getNextSibling = (element, recursive = false) => {
304
+ if (!recursive) {
305
+ return element.nextSibling;
427
306
  }
428
307
  const {
429
308
  parentElement
@@ -431,16 +310,21 @@ const getObservableElement = element => {
431
310
  if (!parentElement) {
432
311
  return null;
433
312
  }
434
- return getObservableElement(parentElement);
313
+ if (parentElement.nextSibling) {
314
+ return parentElement.nextSibling;
315
+ }
316
+ return getNextSibling(parentElement, true);
435
317
  };
436
318
 
437
- const DefaultExperienceLoadingComponent = _a => {
438
- var {
319
+ const _excluded$1 = ["component", "unhideAfterMs", "passthroughProps"],
320
+ _excluded2 = ["experiences", "component", "loadingComponent", "passthroughProps"];
321
+ const DefaultExperienceLoadingComponent = _ref => {
322
+ let {
439
323
  component: Component,
440
324
  unhideAfterMs = 5000,
441
325
  passthroughProps
442
- } = _a,
443
- baseline = __rest(_a, ["component", "unhideAfterMs", "passthroughProps"]);
326
+ } = _ref,
327
+ baseline = _objectWithoutPropertiesLoose(_ref, _excluded$1);
444
328
  const {
445
329
  logger
446
330
  } = useNinetailed();
@@ -455,12 +339,11 @@ const DefaultExperienceLoadingComponent = _a => {
455
339
  };
456
340
  }, []);
457
341
  if (hidden) {
458
- return jsx("div", Object.assign({
342
+ return /*#__PURE__*/jsx("div", {
459
343
  style: {
460
344
  opacity: 0
461
- }
462
- }, {
463
- children: jsx(Component, Object.assign({}, passthroughProps, baseline, {
345
+ },
346
+ children: /*#__PURE__*/jsx(Component, Object.assign({}, passthroughProps, baseline, {
464
347
  ninetailed: {
465
348
  isPersonalized: false,
466
349
  audience: {
@@ -468,9 +351,9 @@ const DefaultExperienceLoadingComponent = _a => {
468
351
  }
469
352
  }
470
353
  }))
471
- }), "hide");
354
+ }, "hide");
472
355
  }
473
- return jsx(Component, Object.assign({}, passthroughProps, baseline, {
356
+ return /*#__PURE__*/jsx(Component, Object.assign({}, passthroughProps, baseline, {
474
357
  ninetailed: {
475
358
  isPersonalized: false,
476
359
  audience: {
@@ -479,14 +362,14 @@ const DefaultExperienceLoadingComponent = _a => {
479
362
  }
480
363
  }));
481
364
  };
482
- const Experience = _a => {
483
- var {
365
+ const Experience = _ref2 => {
366
+ let {
484
367
  experiences,
485
368
  component: Component,
486
369
  loadingComponent: LoadingComponent = DefaultExperienceLoadingComponent,
487
370
  passthroughProps
488
- } = _a,
489
- baseline = __rest(_a, ["experiences", "component", "loadingComponent", "passthroughProps"]);
371
+ } = _ref2,
372
+ baseline = _objectWithoutPropertiesLoose(_ref2, _excluded2);
490
373
  const {
491
374
  observeElement,
492
375
  unobserveElement,
@@ -504,9 +387,10 @@ const Experience = _a => {
504
387
  baseline,
505
388
  experiences
506
389
  });
390
+
507
391
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
508
392
  // @ts-ignore
509
- const isComponentForwardRef = isForwardRef(jsx(Component, {}));
393
+ const isComponentForwardRef = isForwardRef( /*#__PURE__*/jsx(Component, {}));
510
394
  const componentRef = useRef(null);
511
395
  const isVariantHidden = 'hidden' in variant && variant.hidden;
512
396
  useEffect(() => {
@@ -525,7 +409,7 @@ const Experience = _a => {
525
409
  element: componentElement,
526
410
  experience,
527
411
  audience,
528
- variant: isVariantHidden ? Object.assign(Object.assign({}, variant), {
412
+ variant: isVariantHidden ? Object.assign({}, variant, {
529
413
  id: `${baseline.id}-hidden`
530
414
  }) : variant,
531
415
  variantIndex
@@ -541,10 +425,10 @@ const Experience = _a => {
541
425
  };
542
426
  }, [observeElement, unobserveElement, experience, baseline, variant, variantIndex, audience, isVariantHidden]);
543
427
  if (!hasVariants) {
544
- return jsxs(Fragment, {
545
- children: [!isComponentForwardRef && jsx(ComponentMarker, {
428
+ return /*#__PURE__*/jsxs(Fragment, {
429
+ children: [!isComponentForwardRef && /*#__PURE__*/jsx(ComponentMarker, {
546
430
  ref: componentRef
547
- }, `marker-no-variants-${(experience === null || experience === void 0 ? void 0 : experience.id) || 'baseline'}-${variant.id}`), /*#__PURE__*/createElement(Component, Object.assign({}, passthroughProps, baseline, {
431
+ }, `marker-no-variants-${(experience == null ? void 0 : experience.id) || 'baseline'}-${variant.id}`), /*#__PURE__*/createElement(Component, Object.assign({}, passthroughProps, baseline, {
548
432
  key: baseline.id
549
433
  }, isComponentForwardRef ? {
550
434
  ref: componentRef
@@ -560,19 +444,20 @@ const Experience = _a => {
560
444
  }));
561
445
  }
562
446
  if (isVariantHidden) {
563
- return jsx(ComponentMarker, {
564
- ref: componentRef
565
- }, `marker-hidden-${(experience === null || experience === void 0 ? void 0 : experience.id) || 'baseline'}-${variant.id}`);
447
+ return /*#__PURE__*/jsx(ComponentMarker, {
448
+ ref: componentRef,
449
+ hidden: true
450
+ }, `marker-hidden-${(experience == null ? void 0 : experience.id) || 'baseline'}-${variant.id}`);
566
451
  }
567
- return jsxs(Fragment, {
568
- children: [!isComponentForwardRef && jsx(ComponentMarker, {
452
+ return /*#__PURE__*/jsxs(Fragment, {
453
+ children: [!isComponentForwardRef && /*#__PURE__*/jsx(ComponentMarker, {
569
454
  ref: componentRef
570
- }, `marker-${(experience === null || experience === void 0 ? void 0 : experience.id) || 'baseline'}-${variant.id}`), /*#__PURE__*/createElement(Component, Object.assign({}, Object.assign(Object.assign({}, passthroughProps), variant), {
571
- key: `${(experience === null || experience === void 0 ? void 0 : experience.id) || 'baseline'}-${variant.id}`,
455
+ }, `marker-${(experience == null ? void 0 : experience.id) || 'baseline'}-${variant.id}`), /*#__PURE__*/createElement(Component, Object.assign({}, Object.assign({}, passthroughProps, variant), {
456
+ key: `${(experience == null ? void 0 : experience.id) || 'baseline'}-${variant.id}`,
572
457
  ninetailed: {
573
458
  isPersonalized,
574
459
  audience: {
575
- id: (audience === null || audience === void 0 ? void 0 : audience.id) || 'all visitors'
460
+ id: (audience == null ? void 0 : audience.id) || 'all visitors'
576
461
  }
577
462
  }
578
463
  }, isComponentForwardRef ? {
@@ -581,18 +466,18 @@ const Experience = _a => {
581
466
  });
582
467
  };
583
468
 
469
+ const _excluded = ["experiences", "component", "passthroughProps"];
584
470
  const ESRContext = /*#__PURE__*/React.createContext(undefined);
585
471
  const ESRProvider = ({
586
472
  experienceVariantsMap,
587
473
  children
588
474
  }) => {
589
- return jsx(ESRContext.Provider, Object.assign({
475
+ return /*#__PURE__*/jsx(ESRContext.Provider, {
590
476
  value: {
591
477
  experienceVariantsMap
592
- }
593
- }, {
478
+ },
594
479
  children: children
595
- }));
480
+ });
596
481
  };
597
482
  const useESR = () => {
598
483
  const context = React.useContext(ESRContext);
@@ -603,19 +488,19 @@ const useESR = () => {
603
488
  experienceVariantsMap: context.experienceVariantsMap
604
489
  };
605
490
  };
606
- const ESRLoadingComponent = _a => {
607
- var {
491
+ const ESRLoadingComponent = _ref => {
492
+ let {
608
493
  experiences,
609
494
  component: Component,
610
495
  passthroughProps
611
- } = _a,
612
- baseline = __rest(_a, ["experiences", "component", "passthroughProps"]);
496
+ } = _ref,
497
+ baseline = _objectWithoutPropertiesLoose(_ref, _excluded);
613
498
  const {
614
499
  experienceVariantsMap
615
500
  } = useESR();
616
501
  const experience = experiences.find(experience => Object.prototype.hasOwnProperty.call(experienceVariantsMap, experience.id));
617
502
  if (!experience) {
618
- return jsx(Component, Object.assign({}, passthroughProps, baseline, {
503
+ return /*#__PURE__*/jsx(Component, Object.assign({}, passthroughProps, baseline, {
619
504
  ninetailed: {
620
505
  isPersonalized: false,
621
506
  audience: {
@@ -626,7 +511,7 @@ const ESRLoadingComponent = _a => {
626
511
  }
627
512
  const component = experience.components.find(component => component.baseline.id === baseline.id);
628
513
  if (!component) {
629
- return jsx(Component, Object.assign({}, passthroughProps, baseline, {
514
+ return /*#__PURE__*/jsx(Component, Object.assign({}, passthroughProps, baseline, {
630
515
  ninetailed: {
631
516
  isPersonalized: false,
632
517
  audience: {
@@ -636,7 +521,7 @@ const ESRLoadingComponent = _a => {
636
521
  }));
637
522
  }
638
523
  if (experienceVariantsMap[experience.id] === 0) {
639
- return jsx(Component, Object.assign({}, passthroughProps, baseline, {
524
+ return /*#__PURE__*/jsx(Component, Object.assign({}, passthroughProps, baseline, {
640
525
  ninetailed: {
641
526
  isPersonalized: false,
642
527
  audience: {
@@ -647,7 +532,7 @@ const ESRLoadingComponent = _a => {
647
532
  }
648
533
  const variant = component.variants[experienceVariantsMap[experience.id] - 1];
649
534
  if (!variant) {
650
- return jsx(Component, Object.assign({}, passthroughProps, baseline, {
535
+ return /*#__PURE__*/jsx(Component, Object.assign({}, passthroughProps, baseline, {
651
536
  ninetailed: {
652
537
  isPersonalized: false,
653
538
  audience: {
@@ -656,7 +541,7 @@ const ESRLoadingComponent = _a => {
656
541
  }
657
542
  }));
658
543
  }
659
- return jsx(Component, Object.assign({}, passthroughProps, variant, {
544
+ return /*#__PURE__*/jsx(Component, Object.assign({}, passthroughProps, variant, {
660
545
  ninetailed: {
661
546
  isPersonalized: false,
662
547
  audience: {
package/package.json CHANGED
@@ -1,7 +1,17 @@
1
1
  {
2
2
  "name": "@ninetailed/experience.js-react",
3
- "version": "7.5.3",
3
+ "version": "7.6.0-beta.2",
4
4
  "description": "Ninetailed SDK for React",
5
+ "dependencies": {
6
+ "@ninetailed/experience.js": "*",
7
+ "@ninetailed/experience.js-shared": "*",
8
+ "@ninetailed/experience.js-plugin-analytics": "*",
9
+ "radash": "10.9.0",
10
+ "react-intersection-observer": "8.34.0",
11
+ "react-is": "18.2.0",
12
+ "@testing-library/react-hooks": "7.0.2",
13
+ "@testing-library/react": "13.4.0"
14
+ },
5
15
  "peerDependencies": {
6
16
  "react": ">=16.8.0"
7
17
  },
@@ -11,15 +21,12 @@
11
21
  "url": "https://github.com/ninetailed-inc/experience.js.git",
12
22
  "directory": "packages/sdks/react"
13
23
  },
14
- "module": "./index.js",
15
- "main": "./index.cjs",
16
- "type": "module",
17
- "types": "./src/index.d.ts",
18
- "dependencies": {
19
- "@ninetailed/experience.js": "7.5.3",
20
- "@ninetailed/experience.js-shared": "7.5.3",
21
- "radash": "10.9.0",
22
- "react-intersection-observer": "8.34.0",
23
- "react-is": "18.2.0"
24
- }
24
+ "keywords": [
25
+ "react",
26
+ "ninetailed",
27
+ "personalization",
28
+ "a/b testing"
29
+ ],
30
+ "module": "./index.esm.js",
31
+ "main": "./index.cjs.js"
25
32
  }
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ type ComponentMarkerProps = {
3
+ hidden?: boolean;
4
+ };
5
+ export declare const ComponentMarker: React.ForwardRefExoticComponent<ComponentMarkerProps & React.RefAttributes<unknown>>;
6
+ export {};
@@ -12,5 +12,5 @@ export declare const ESRProvider: React.FC<PropsWithChildren<ESRProviderProps>>;
12
12
  export declare const useESR: () => {
13
13
  experienceVariantsMap: Record<string, number>;
14
14
  };
15
- export declare const ESRLoadingComponent: <P, PassThroughProps extends Partial<P> = Partial<P>, Variant extends Pick<P, Exclude<keyof P, keyof PassThroughProps>> & Reference = Pick<P, Exclude<keyof P, keyof PassThroughProps>> & Reference>({ experiences, component: Component, passthroughProps, ...baseline }: ExperienceBaseProps<P, PassThroughProps, Variant>) => JSX.Element;
15
+ export declare const ESRLoadingComponent: <P, PassThroughProps extends Partial<P> = Partial<P>, Variant extends Pick<P, Exclude<keyof P, keyof PassThroughProps>> & Reference = Pick<P, Exclude<keyof P, keyof PassThroughProps>> & Reference>({ experiences, component: Component, passthroughProps, ...baseline }: ExperienceBaseProps<P, PassThroughProps, Variant>) => import("react/jsx-runtime").JSX.Element;
16
16
  export {};
@@ -23,5 +23,5 @@ type DefaultExperienceLoadingComponentProps = ExperienceBaseProps<Record<string,
23
23
  unhideAfterMs?: number;
24
24
  };
25
25
  export declare const DefaultExperienceLoadingComponent: React.FC<DefaultExperienceLoadingComponentProps>;
26
- export declare const Experience: <P, PassThroughProps extends Partial<P> = Partial<P>, Variant extends Pick<P, Exclude<keyof P, keyof PassThroughProps>> & Reference = Pick<P, Exclude<keyof P, keyof PassThroughProps>> & Reference>({ experiences, component: Component, loadingComponent: LoadingComponent, passthroughProps, ...baseline }: ExperienceProps<P, PassThroughProps, Variant>) => JSX.Element;
26
+ export declare const Experience: <P, PassThroughProps extends Partial<P> = Partial<P>, Variant extends Pick<P, Exclude<keyof P, keyof PassThroughProps>> & Reference = Pick<P, Exclude<keyof P, keyof PassThroughProps>> & Reference>({ experiences, component: Component, loadingComponent: LoadingComponent, passthroughProps, ...baseline }: ExperienceProps<P, PassThroughProps, Variant>) => import("react/jsx-runtime").JSX.Element;
27
27
  export {};
@@ -1,24 +1,24 @@
1
- import { Baseline, ExperienceConfiguration, Profile, Reference, VariantRef } from '@ninetailed/experience.js';
2
- type Load<Variant extends Reference> = {
1
+ import { ExperienceConfiguration, Profile, Reference, VariantRef } from '@ninetailed/experience.js';
2
+ type Load<TBaseline extends Reference> = {
3
3
  status: 'loading';
4
4
  loading: boolean;
5
5
  hasVariants: boolean;
6
- baseline: Baseline;
6
+ baseline: TBaseline;
7
7
  experience: null;
8
- variant: Variant;
8
+ variant: TBaseline;
9
9
  variantIndex: 0;
10
10
  audience: null;
11
11
  isPersonalized: boolean;
12
12
  profile: null;
13
13
  error: null;
14
14
  };
15
- type Success<Variant extends Reference> = {
15
+ type Success<TBaseline extends Reference, TVariant extends Reference> = {
16
16
  status: 'success';
17
17
  loading: boolean;
18
18
  hasVariants: boolean;
19
- baseline: Baseline;
20
- experience: ExperienceConfiguration<Variant> | null;
21
- variant: Variant;
19
+ baseline: TBaseline;
20
+ experience: ExperienceConfiguration<TVariant> | null;
21
+ variant: TBaseline | TVariant;
22
22
  variantIndex: number;
23
23
  audience: {
24
24
  id: string;
@@ -27,23 +27,23 @@ type Success<Variant extends Reference> = {
27
27
  profile: Profile;
28
28
  error: null;
29
29
  };
30
- type Fail<Variant extends Reference> = {
30
+ type Fail<TBaseline extends Reference> = {
31
31
  status: 'error';
32
32
  loading: boolean;
33
33
  hasVariants: boolean;
34
- baseline: Baseline;
34
+ baseline: TBaseline;
35
35
  experience: null;
36
- variant: Variant;
36
+ variant: TBaseline;
37
37
  variantIndex: 0;
38
38
  audience: null;
39
39
  isPersonalized: boolean;
40
40
  profile: null;
41
41
  error: Error;
42
42
  };
43
- type UseExperienceArgs<Variant extends Reference> = {
44
- baseline: Baseline;
45
- experiences: ExperienceConfiguration<Variant>[];
43
+ type UseExperienceArgs<TBaseline extends Reference, TVariant extends Reference> = {
44
+ baseline: TBaseline;
45
+ experiences: ExperienceConfiguration<TVariant>[];
46
46
  };
47
- type UseExperienceResponse<Variant extends Reference> = Load<Variant | VariantRef> | Success<Variant | VariantRef> | Fail<Variant | VariantRef>;
48
- export declare const useExperience: <Variant extends Reference>({ baseline, experiences, }: UseExperienceArgs<Variant>) => UseExperienceResponse<Variant>;
47
+ type UseExperienceReturn<TBaseline extends Reference, TVariant extends Reference> = Load<TBaseline> | Success<TBaseline, TVariant | VariantRef> | Fail<TBaseline>;
48
+ export declare const useExperience: <TBaseline extends Reference, TVariant extends Reference>({ baseline, experiences, }: UseExperienceArgs<TBaseline, TVariant>) => UseExperienceReturn<TBaseline, TVariant>;
49
49
  export {};
@@ -3,5 +3,5 @@ type MergeTagProps = {
3
3
  id: string;
4
4
  fallback?: string;
5
5
  };
6
- export declare const MergeTag: ({ id, fallback, }: PropsWithChildren<MergeTagProps>) => JSX.Element | null;
6
+ export declare const MergeTag: ({ id, fallback, }: PropsWithChildren<MergeTagProps>) => import("react/jsx-runtime").JSX.Element | null;
7
7
  export {};
@@ -1,3 +1,3 @@
1
1
  /// <reference types="react" />
2
2
  import { NinetailedInstance } from '@ninetailed/experience.js';
3
- export declare const NinetailedContext: import("react").Context<NinetailedInstance | undefined>;
3
+ export declare const NinetailedContext: import("react").Context<NinetailedInstance<import("@ninetailed/experience.js").Reference, import("@ninetailed/experience.js").Reference> | undefined>;
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
- import { Ninetailed, NinetailedPlugin, OnInitProfileId, Storage } from '@ninetailed/experience.js';
2
+ import { Ninetailed, OnInitProfileId, Storage } from '@ninetailed/experience.js';
3
3
  import { Locale, OnErrorHandler, OnLogHandler, NinetailedRequestContext } from '@ninetailed/experience.js-shared';
4
+ import { NinetailedPlugin } from '@ninetailed/experience.js-plugin-analytics';
4
5
  export type NinetailedProviderInstantiationProps = {
5
6
  clientId: string;
6
7
  environment?: string;
@@ -15,8 +16,9 @@ export type NinetailedProviderInstantiationProps = {
15
16
  buildClientContext?: () => NinetailedRequestContext;
16
17
  onInitProfileId?: OnInitProfileId;
17
18
  storageImpl?: Storage;
19
+ useClientSideEvaluation?: boolean;
18
20
  };
19
21
  export type NinetailedProviderProps = NinetailedProviderInstantiationProps | {
20
22
  ninetailed: Ninetailed;
21
23
  };
22
- export declare const NinetailedProvider: (props: React.PropsWithChildren<NinetailedProviderProps>) => JSX.Element;
24
+ export declare const NinetailedProvider: (props: React.PropsWithChildren<NinetailedProviderProps>) => import("react/jsx-runtime").JSX.Element;
@@ -19,5 +19,5 @@ type PersonalizeProps<P> = Baseline<P> & {
19
19
  loadingComponent?: React.ComponentType;
20
20
  holdout?: number;
21
21
  };
22
- export declare const Personalize: <P extends object>({ component: Component, loadingComponent: LoadingComponent, variants, holdout, ...baseline }: PersonalizeProps<P>) => JSX.Element;
22
+ export declare const Personalize: <P extends object>({ component: Component, loadingComponent: LoadingComponent, variants, holdout, ...baseline }: PersonalizeProps<P>) => import("react/jsx-runtime").JSX.Element;
23
23
  export {};
@@ -1,2 +1,3 @@
1
1
  import { NinetailedInstance } from '@ninetailed/experience.js';
2
- export declare const useNinetailed: () => NinetailedInstance;
2
+ import { Reference } from '@ninetailed/experience.js-shared';
3
+ export declare const useNinetailed: <TBaseline extends Reference = Reference, TVariant extends Reference = Reference>() => NinetailedInstance<TBaseline, TVariant>;
@@ -1,2 +0,0 @@
1
- import React from 'react';
2
- export declare const ComponentMarker: React.ForwardRefExoticComponent<React.RefAttributes<unknown>>;
@@ -1 +0,0 @@
1
- export * from './ComponentMarker';
@@ -1,12 +0,0 @@
1
- import { ExperienceConfiguration, Profile, Reference } from '@ninetailed/experience.js-shared';
2
- type UseExperienceSelectionMiddlewareArg<Variant extends Reference> = {
3
- experiences: ExperienceConfiguration<Variant>[];
4
- baseline: Reference;
5
- profile: Profile | null;
6
- };
7
- export declare const useExperienceSelectionMiddleware: <Variant extends Reference>({ experiences, baseline, profile, }: UseExperienceSelectionMiddlewareArg<Variant>) => ({ experience, variant, variantIndex, }: import("@ninetailed/experience.js").ExperienceSelectionMiddlewareReturnArg<Variant | import("@ninetailed/experience.js-shared").VariantRef>) => {
8
- experience: ExperienceConfiguration<Variant | import("@ninetailed/experience.js-shared").VariantRef> | null;
9
- variant: Variant | import("@ninetailed/experience.js-shared").VariantRef;
10
- variantIndex: number;
11
- };
12
- export {};