@builder.io/sdk-svelte 4.0.8 → 4.0.9

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.
Files changed (57) hide show
  1. package/lib/browser/blocks/personalization-container/component-info.js +2 -1
  2. package/lib/browser/blocks/personalization-container/helpers/inlined-fns.d.ts +4 -1
  3. package/lib/browser/blocks/personalization-container/helpers/inlined-fns.js +45 -1
  4. package/lib/browser/blocks/personalization-container/helpers.d.ts +24 -4
  5. package/lib/browser/blocks/personalization-container/helpers.js +50 -19
  6. package/lib/browser/blocks/personalization-container/personalization-container.svelte +86 -38
  7. package/lib/browser/blocks/personalization-container/personalization-container.svelte.d.ts +2 -1
  8. package/lib/browser/blocks/personalization-container/personalization-container.types.d.ts +2 -2
  9. package/lib/browser/components/blocks/blocks.svelte +6 -4
  10. package/lib/browser/components/blocks/blocks.svelte.d.ts +5 -2
  11. package/lib/browser/components/blocks/blocks.types.d.ts +1 -1
  12. package/lib/browser/components/content-variants/content-variants.svelte +9 -0
  13. package/lib/browser/constants/builder-registered-components.js +2 -1
  14. package/lib/browser/constants/sdk-version.d.ts +1 -1
  15. package/lib/browser/constants/sdk-version.js +1 -1
  16. package/lib/edge/blocks/personalization-container/component-info.js +2 -1
  17. package/lib/edge/blocks/personalization-container/helpers/inlined-fns.d.ts +4 -1
  18. package/lib/edge/blocks/personalization-container/helpers/inlined-fns.js +45 -1
  19. package/lib/edge/blocks/personalization-container/helpers.d.ts +24 -4
  20. package/lib/edge/blocks/personalization-container/helpers.js +50 -19
  21. package/lib/edge/blocks/personalization-container/personalization-container.svelte +86 -38
  22. package/lib/edge/blocks/personalization-container/personalization-container.svelte.d.ts +2 -1
  23. package/lib/edge/blocks/personalization-container/personalization-container.types.d.ts +2 -2
  24. package/lib/edge/components/blocks/blocks.svelte +6 -4
  25. package/lib/edge/components/blocks/blocks.svelte.d.ts +5 -2
  26. package/lib/edge/components/blocks/blocks.types.d.ts +1 -1
  27. package/lib/edge/components/content-variants/content-variants.svelte +9 -0
  28. package/lib/edge/constants/builder-registered-components.js +2 -1
  29. package/lib/edge/constants/sdk-version.d.ts +1 -1
  30. package/lib/edge/constants/sdk-version.js +1 -1
  31. package/lib/node/blocks/personalization-container/component-info.js +2 -1
  32. package/lib/node/blocks/personalization-container/helpers/inlined-fns.d.ts +4 -1
  33. package/lib/node/blocks/personalization-container/helpers/inlined-fns.js +45 -1
  34. package/lib/node/blocks/personalization-container/helpers.d.ts +24 -4
  35. package/lib/node/blocks/personalization-container/helpers.js +50 -19
  36. package/lib/node/blocks/personalization-container/personalization-container.svelte +86 -38
  37. package/lib/node/blocks/personalization-container/personalization-container.svelte.d.ts +2 -1
  38. package/lib/node/blocks/personalization-container/personalization-container.types.d.ts +2 -2
  39. package/lib/node/components/blocks/blocks.svelte +6 -4
  40. package/lib/node/components/blocks/blocks.svelte.d.ts +5 -2
  41. package/lib/node/components/blocks/blocks.types.d.ts +1 -1
  42. package/lib/node/components/content-variants/content-variants.svelte +9 -0
  43. package/lib/node/constants/builder-registered-components.js +2 -1
  44. package/lib/node/constants/sdk-version.d.ts +1 -1
  45. package/lib/node/constants/sdk-version.js +1 -1
  46. package/package.json +2 -2
  47. package/src/blocks/personalization-container/component-info.ts +2 -1
  48. package/src/blocks/personalization-container/helpers/inlined-fns.ts +42 -2
  49. package/src/blocks/personalization-container/helpers.ts +60 -24
  50. package/src/blocks/personalization-container/personalization-container.svelte +95 -38
  51. package/src/blocks/personalization-container/personalization-container.types.ts +2 -2
  52. package/src/components/blocks/blocks-wrapper.svelte +13 -1
  53. package/src/components/blocks/blocks.svelte +7 -4
  54. package/src/components/blocks/blocks.types.ts +1 -1
  55. package/src/components/content-variants/content-variants.svelte +12 -0
  56. package/src/constants/builder-registered-components.ts +2 -1
  57. package/src/constants/sdk-version.ts +1 -1
@@ -83,5 +83,49 @@ export function filterWithCustomTargeting(userAttributes, query, startDate, endD
83
83
  return objectMatchesQuery(userAttributes, filter);
84
84
  });
85
85
  }
86
- export const PERSONALIZATION_SCRIPT = "function getPersonalizedVariant(variants, blockId, locale) {\n if (!navigator.cookieEnabled) {\n return;\n }\n function getCookie(name) {\n const nameEQ = name + '=';\n const ca = document.cookie.split(';');\n for (let i = 0; i < ca.length; i++) {\n let c = ca[i];\n while (c.charAt(0) == ' ') c = c.substring(1, c.length);\n if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);\n }\n return null;\n }\n function removeVariants() {\n variants?.forEach(function (_, index) {\n document.querySelector('template[data-variant-id=\"' + blockId + '-' + index + '\"]')?.remove();\n });\n document.querySelector('script[data-id=\"variants-script-' + blockId + '\"]')?.remove();\n document.querySelector('style[data-id=\"variants-styles-' + blockId + '\"]')?.remove();\n }\n const attributes = JSON.parse(getCookie('builder.userAttributes') || '{}');\n if (locale) {\n attributes.locale = locale;\n }\n const winningVariantIndex = variants?.findIndex(function (variant) {\n return filterWithCustomTargeting(attributes, variant.query, variant.startDate, variant.endDate);\n });\n const isDebug = location.href.includes('builder.debug=true');\n if (isDebug) {\n console.debug('PersonalizationContainer', {\n attributes,\n variants,\n winningVariantIndex\n });\n }\n if (winningVariantIndex !== -1) {\n const winningVariant = document.querySelector('template[data-variant-id=\"' + blockId + '-' + winningVariantIndex + '\"]');\n if (winningVariant) {\n const parentNode = winningVariant.parentNode;\n if (parentNode) {\n const newParent = parentNode.cloneNode(false);\n newParent.appendChild(winningVariant.content.firstChild);\n newParent.appendChild(winningVariant.content.lastChild);\n parentNode.parentNode?.replaceChild(newParent, parentNode);\n }\n if (isDebug) {\n console.debug('PersonalizationContainer', 'Winning variant Replaced:', winningVariant);\n }\n }\n } else if (variants && variants.length > 0) {\n removeVariants();\n }\n}";
86
+ export function updateVisibilityStylesScript(variants, blockId, isHydrationTarget, locale) {
87
+ function getCookie(name) {
88
+ const nameEQ = name + '=';
89
+ const ca = document.cookie.split(';');
90
+ for (let i = 0; i < ca.length; i++) {
91
+ let c = ca[i];
92
+ while (c.charAt(0) == ' ')
93
+ c = c.substring(1, c.length);
94
+ if (c.indexOf(nameEQ) == 0)
95
+ return c.substring(nameEQ.length, c.length);
96
+ }
97
+ return null;
98
+ }
99
+ const visibilityStylesEl = document.currentScript?.previousElementSibling;
100
+ if (!visibilityStylesEl) {
101
+ return;
102
+ }
103
+ if (isHydrationTarget) {
104
+ visibilityStylesEl.remove();
105
+ const currentScript = document.currentScript;
106
+ if (currentScript) {
107
+ currentScript.remove();
108
+ }
109
+ }
110
+ else {
111
+ const attributes = JSON.parse(getCookie('builder.userAttributes') || '{}');
112
+ if (locale) {
113
+ attributes.locale = locale;
114
+ }
115
+ const winningVariantIndex = variants?.findIndex(function (variant) {
116
+ return window.filterWithCustomTargeting(attributes, variant.query, variant.startDate, variant.endDate);
117
+ });
118
+ if (winningVariantIndex !== -1) {
119
+ let newStyleStr = variants?.map((_, index) => {
120
+ if (index === winningVariantIndex)
121
+ return '';
122
+ return `div[data-variant-id="${blockId}-${index}"] { display: none !important; } `;
123
+ }).join('') || '';
124
+ newStyleStr += `div[data-variant-id="${blockId}-default"] { display: none !important; } `;
125
+ visibilityStylesEl.innerHTML = newStyleStr;
126
+ }
127
+ }
128
+ }
129
+ export const PERSONALIZATION_SCRIPT = "function getPersonalizedVariant(variants, blockId, isHydrationTarget, locale) {\n if (!navigator.cookieEnabled) {\n return;\n }\n function getCookie(name) {\n const nameEQ = name + '=';\n const ca = document.cookie.split(';');\n for (let i = 0; i < ca.length; i++) {\n let c = ca[i];\n while (c.charAt(0) == ' ') c = c.substring(1, c.length);\n if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);\n }\n return null;\n }\n const attributes = JSON.parse(getCookie('builder.userAttributes') || '{}');\n if (locale) {\n attributes.locale = locale;\n }\n const winningVariantIndex = variants?.findIndex(function (variant) {\n return window.filterWithCustomTargeting(attributes, variant.query, variant.startDate, variant.endDate);\n });\n const parentDiv = document.currentScript?.parentElement;\n const variantId = parentDiv?.getAttribute('data-variant-id');\n const isDefaultVariant = variantId === `${blockId}-default`;\n const isWinningVariant = winningVariantIndex !== -1 && variantId === `${blockId}-${winningVariantIndex}` || winningVariantIndex === -1 && isDefaultVariant;\n if (isWinningVariant && !isDefaultVariant) {\n parentDiv?.removeAttribute('hidden');\n parentDiv?.removeAttribute('aria-hidden');\n } else if (!isWinningVariant && isDefaultVariant) {\n parentDiv?.setAttribute('hidden', 'true');\n parentDiv?.setAttribute('aria-hidden', 'true');\n }\n if (isHydrationTarget) {\n if (!isWinningVariant) {\n const itsStyleEl = parentDiv?.previousElementSibling;\n if (itsStyleEl) {\n itsStyleEl.remove();\n }\n parentDiv?.remove();\n }\n const thisScript = document.currentScript;\n if (thisScript) {\n thisScript.remove();\n }\n }\n}";
87
130
  export const FILTER_WITH_CUSTOM_TARGETING_SCRIPT = "function filterWithCustomTargeting(userAttributes, query, startDate, endDate) {\n function isString(val) {\n return typeof val === 'string';\n }\n function isNumber(val) {\n return typeof val === 'number';\n }\n function objectMatchesQuery(userattr, query) {\n const result = (() => {\n const property = query.property;\n const operator = query.operator;\n let testValue = query.value;\n if (query && query.property === 'urlPath' && query.value && typeof query.value === 'string' && query.value !== '/' && query.value.endsWith('/')) {\n testValue = query.value.slice(0, -1);\n }\n if (!(property && operator)) {\n return true;\n }\n if (Array.isArray(testValue)) {\n if (operator === 'isNot') {\n return testValue.every(val => objectMatchesQuery(userattr, {\n property,\n operator,\n value: val\n }));\n }\n return !!testValue.find(val => objectMatchesQuery(userattr, {\n property,\n operator,\n value: val\n }));\n }\n const value = userattr[property];\n if (Array.isArray(value)) {\n return value.includes(testValue);\n }\n switch (operator) {\n case 'is':\n return value === testValue;\n case 'isNot':\n return value !== testValue;\n case 'contains':\n return (isString(value) || Array.isArray(value)) && value.includes(String(testValue));\n case 'startsWith':\n return isString(value) && value.startsWith(String(testValue));\n case 'endsWith':\n return isString(value) && value.endsWith(String(testValue));\n case 'greaterThan':\n return isNumber(value) && isNumber(testValue) && value > testValue;\n case 'lessThan':\n return isNumber(value) && isNumber(testValue) && value < testValue;\n case 'greaterThanOrEqualTo':\n return isNumber(value) && isNumber(testValue) && value >= testValue;\n case 'lessThanOrEqualTo':\n return isNumber(value) && isNumber(testValue) && value <= testValue;\n default:\n return false;\n }\n })();\n return result;\n }\n const item = {\n query,\n startDate,\n endDate\n };\n const now = userAttributes.date && new Date(userAttributes.date) || new Date();\n if (item.startDate && new Date(item.startDate) > now) {\n return false;\n } else if (item.endDate && new Date(item.endDate) < now) {\n return false;\n }\n if (!item.query || !item.query.length) {\n return true;\n }\n return item.query.every(filter => {\n return objectMatchesQuery(userAttributes, filter);\n });\n}";
131
+ export const UPDATE_VISIBILITY_STYLES_SCRIPT = "function updateVisibilityStylesScript(variants, blockId, isHydrationTarget, locale) {\n function getCookie(name) {\n const nameEQ = name + '=';\n const ca = document.cookie.split(';');\n for (let i = 0; i < ca.length; i++) {\n let c = ca[i];\n while (c.charAt(0) == ' ') c = c.substring(1, c.length);\n if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);\n }\n return null;\n }\n const visibilityStylesEl = document.currentScript?.previousElementSibling;\n if (!visibilityStylesEl) {\n return;\n }\n if (isHydrationTarget) {\n visibilityStylesEl.remove();\n const currentScript = document.currentScript;\n if (currentScript) {\n currentScript.remove();\n }\n } else {\n const attributes = JSON.parse(getCookie('builder.userAttributes') || '{}');\n if (locale) {\n attributes.locale = locale;\n }\n const winningVariantIndex = variants?.findIndex(function (variant) {\n return window.filterWithCustomTargeting(attributes, variant.query, variant.startDate, variant.endDate);\n });\n if (winningVariantIndex !== -1) {\n let newStyleStr = variants?.map((_, index) => {\n if (index === winningVariantIndex) return '';\n return `div[data-variant-id=\"${blockId}-${index}\"] { display: none !important; } `;\n }).join('') || '';\n newStyleStr += `div[data-variant-id=\"${blockId}-default\"] { display: none !important; } `;\n visibilityStylesEl.innerHTML = newStyleStr;\n }\n }\n}";
@@ -1,5 +1,7 @@
1
1
  import type { BuilderBlock } from '../../types/builder-block';
2
+ import type { Target } from '../../types/targets';
2
3
  import type { PersonalizationContainerProps } from './personalization-container.types';
4
+ export declare const DEFAULT_INDEX = "default";
3
5
  export type UserAttributes = {
4
6
  date?: string | Date;
5
7
  urlPath?: string;
@@ -12,6 +14,25 @@ export type Query = {
12
14
  operator: QueryOperator;
13
15
  value: QueryValue;
14
16
  };
17
+ type BlocksToRenderReturnType = {
18
+ blocks: BuilderBlock[];
19
+ path: string;
20
+ index: number | typeof DEFAULT_INDEX;
21
+ };
22
+ /**
23
+ * SDKs that support Variant Containers
24
+ */
25
+ export declare const SDKS_SUPPORTING_PERSONALIZATION: Target[];
26
+ /**
27
+ * After hydration, we reset the tree
28
+ * These SDKs handle Personalization Container in a special way:
29
+ * - first, the inlined script will help us add `display: none`, `aria-hidden: true` and `hidden: true` to the non-winning variants
30
+ * - then, on mount / when the component is hydrated - we reset the tree with only the winning variant and deleting the entire tree
31
+ *
32
+ * This helps us to avoid flicker and show the correct / winning variant initially and then once we've hydrated we show the winning variant
33
+ * and keep a track of the cookies to update to the correct variant dynamically when the cookie updates.
34
+ */
35
+ export declare const SDKS_REQUIRING_RESET_APPROACH: Target[];
15
36
  export declare function checkShouldRenderVariants(variants: PersonalizationContainerProps['variants'], canTrack: boolean): boolean;
16
37
  export declare function getBlocksToRender({ variants, previewingIndex, isHydrated, filteredVariants, fallbackBlocks }: {
17
38
  variants: PersonalizationContainerProps['variants'];
@@ -19,9 +40,8 @@ export declare function getBlocksToRender({ variants, previewingIndex, isHydrate
19
40
  isHydrated: boolean;
20
41
  filteredVariants: PersonalizationContainerProps['variants'];
21
42
  fallbackBlocks?: BuilderBlock[];
22
- }): {
23
- blocks: BuilderBlock[];
24
- path: string | undefined;
25
- };
43
+ }): BlocksToRenderReturnType;
44
+ export declare const getInitPersonalizationVariantsFnsScriptString: () => string;
26
45
  export declare const getPersonalizationScript: (variants: PersonalizationContainerProps['variants'], blockId: string, locale?: string) => string;
46
+ export declare const getUpdateVisibilityStylesScript: (variants: PersonalizationContainerProps['variants'], blockId: string, locale?: string) => string;
27
47
  export { filterWithCustomTargeting } from './helpers/inlined-fns';
@@ -1,7 +1,25 @@
1
1
  import { TARGET } from '../../constants/target';
2
2
  import { isBrowser } from '../../functions/is-browser';
3
3
  import { isEditing } from '../../functions/is-editing';
4
- import { FILTER_WITH_CUSTOM_TARGETING_SCRIPT, PERSONALIZATION_SCRIPT } from './helpers/inlined-fns';
4
+ import { FILTER_WITH_CUSTOM_TARGETING_SCRIPT, PERSONALIZATION_SCRIPT, UPDATE_VISIBILITY_STYLES_SCRIPT } from './helpers/inlined-fns';
5
+ export const DEFAULT_INDEX = 'default';
6
+ const FILTER_WITH_CUSTOM_TARGETING_SCRIPT_FN_NAME = 'filterWithCustomTargeting';
7
+ const BUILDER_IO_PERSONALIZATION_SCRIPT_FN_NAME = 'builderIoPersonalization';
8
+ const UPDATE_VARIANT_VISIBILITY_SCRIPT_FN_NAME = 'updateVisibilityStylesScript';
9
+ /**
10
+ * SDKs that support Variant Containers
11
+ */
12
+ export const SDKS_SUPPORTING_PERSONALIZATION = ['react', 'vue', 'svelte'];
13
+ /**
14
+ * After hydration, we reset the tree
15
+ * These SDKs handle Personalization Container in a special way:
16
+ * - first, the inlined script will help us add `display: none`, `aria-hidden: true` and `hidden: true` to the non-winning variants
17
+ * - then, on mount / when the component is hydrated - we reset the tree with only the winning variant and deleting the entire tree
18
+ *
19
+ * This helps us to avoid flicker and show the correct / winning variant initially and then once we've hydrated we show the winning variant
20
+ * and keep a track of the cookies to update to the correct variant dynamically when the cookie updates.
21
+ */
22
+ export const SDKS_REQUIRING_RESET_APPROACH = ['vue', 'svelte'];
5
23
  export function checkShouldRenderVariants(variants, canTrack) {
6
24
  const hasVariants = variants && variants.length > 0;
7
25
  if (TARGET === 'reactNative')
@@ -10,7 +28,7 @@ export function checkShouldRenderVariants(variants, canTrack) {
10
28
  return false;
11
29
  if (!canTrack)
12
30
  return false;
13
- if (TARGET === 'vue' || TARGET === 'svelte')
31
+ if (SDKS_REQUIRING_RESET_APPROACH.includes(TARGET))
14
32
  return true;
15
33
  if (isBrowser())
16
34
  return false;
@@ -19,16 +37,20 @@ export function checkShouldRenderVariants(variants, canTrack) {
19
37
  export function getBlocksToRender({ variants, previewingIndex, isHydrated, filteredVariants, fallbackBlocks }) {
20
38
  const fallback = {
21
39
  blocks: fallbackBlocks ?? [],
22
- path: 'this.children'
40
+ path: 'this.children',
41
+ index: DEFAULT_INDEX
23
42
  };
24
43
  if (isHydrated && isEditing()) {
25
44
  // If editing a specific variant
26
45
  if (typeof previewingIndex === 'number' && previewingIndex < (variants?.length ?? 0)) {
27
- const variant = variants[previewingIndex];
28
- return {
29
- blocks: variant.blocks,
30
- path: `component.options.variants.${previewingIndex}.blocks`
31
- };
46
+ const variant = variants?.[previewingIndex];
47
+ if (variant) {
48
+ return {
49
+ blocks: variant.blocks,
50
+ path: `variants.${previewingIndex}.blocks`,
51
+ index: previewingIndex
52
+ };
53
+ }
32
54
  }
33
55
  // Otherwise we're editing the default variant
34
56
  return fallback;
@@ -36,23 +58,32 @@ export function getBlocksToRender({ variants, previewingIndex, isHydrated, filte
36
58
  // If we're on the browser, check if there's a winning variant
37
59
  if (isBrowser()) {
38
60
  const winningVariant = filteredVariants?.[0];
39
- if (winningVariant) {
40
- return {
41
- blocks: winningVariant.blocks,
42
- path: `component.options.variants.${variants?.indexOf(winningVariant)}.blocks`
43
- };
61
+ if (winningVariant && variants) {
62
+ const variantIndex = variants.indexOf(winningVariant);
63
+ if (variantIndex !== -1) {
64
+ return {
65
+ blocks: winningVariant.blocks,
66
+ path: `variants.${variantIndex}.blocks`,
67
+ index: variantIndex
68
+ };
69
+ }
44
70
  }
45
71
  }
46
72
  // If no winning variant or we are on the server, return the default variant
47
73
  return fallback;
48
74
  }
49
- export const getPersonalizationScript = (variants, blockId, locale) => {
75
+ export const getInitPersonalizationVariantsFnsScriptString = () => {
50
76
  return `
51
- (function() {
52
- ${FILTER_WITH_CUSTOM_TARGETING_SCRIPT}
53
- ${PERSONALIZATION_SCRIPT}
54
- getPersonalizedVariant(${JSON.stringify(variants)}, "${blockId}"${locale ? `, "${locale}"` : ''})
55
- })();
77
+ window.${FILTER_WITH_CUSTOM_TARGETING_SCRIPT_FN_NAME} = ${FILTER_WITH_CUSTOM_TARGETING_SCRIPT}
78
+ window.${BUILDER_IO_PERSONALIZATION_SCRIPT_FN_NAME} = ${PERSONALIZATION_SCRIPT}
79
+ window.${UPDATE_VARIANT_VISIBILITY_SCRIPT_FN_NAME} = ${UPDATE_VISIBILITY_STYLES_SCRIPT}
56
80
  `;
57
81
  };
82
+ const isHydrationTarget = TARGET === 'react';
83
+ export const getPersonalizationScript = (variants, blockId, locale) => {
84
+ return `window.${BUILDER_IO_PERSONALIZATION_SCRIPT_FN_NAME}(${JSON.stringify(variants)}, "${blockId}", ${isHydrationTarget}${locale ? `, "${locale}"` : ''})`;
85
+ };
86
+ export const getUpdateVisibilityStylesScript = (variants, blockId, locale) => {
87
+ return `window.${UPDATE_VARIANT_VISIBILITY_SCRIPT_FN_NAME}(${JSON.stringify(variants)}, "${blockId}", ${isHydrationTarget}${locale ? `, "${locale}"` : ''})`;
88
+ };
58
89
  export { filterWithCustomTargeting } from './helpers/inlined-fns';
@@ -2,16 +2,28 @@
2
2
  import Blocks from "../../components/blocks/blocks.svelte";
3
3
  import InlinedScript from "../../components/inlined-script.svelte";
4
4
  import InlinedStyles from "../../components/inlined-styles.svelte";
5
+ import { TARGET } from "../../constants/target";
6
+ import { getClassPropName } from "../../functions/get-class-prop-name";
5
7
  import { isEditing } from "../../functions/is-editing";
6
8
  import { isPreviewing } from "../../functions/is-previewing";
7
9
  import { getDefaultCanTrack } from "../../helpers/canTrack";
8
10
  import { userAttributesService } from "../../helpers/user-attributes";
9
- import { checkShouldRenderVariants, filterWithCustomTargeting, getBlocksToRender, getPersonalizationScript, } from "./helpers";
11
+ import { filterAttrs } from "../helpers";
12
+ import { checkShouldRenderVariants, DEFAULT_INDEX, filterWithCustomTargeting, getBlocksToRender, getPersonalizationScript, getUpdateVisibilityStylesScript, SDKS_REQUIRING_RESET_APPROACH, } from "./helpers";
13
+ import "../helpers";
10
14
  export let builderContext;
11
15
  export let variants = undefined;
12
16
  export let builderBlock;
13
- export let previewingIndex = undefined;
14
17
  export let attributes = undefined;
18
+ export let previewingIndex = undefined;
19
+ export let builderComponents;
20
+ $: attrs = () => {
21
+ return {
22
+ ...filterAttrs(attributes, "on:", false),
23
+ ...filterAttrs(attributes, "on:", true),
24
+ [getClassPropName()]: `builder-personalization-container ${attributes[getClassPropName()] || ""}`,
25
+ };
26
+ };
15
27
  $: filteredVariants = () => {
16
28
  return (variants || []).filter((variant) => {
17
29
  return filterWithCustomTargeting({
@@ -28,24 +40,25 @@ $: blocksToRender = () => {
28
40
  return getBlocksToRender({
29
41
  variants: variants,
30
42
  fallbackBlocks: builderBlock?.children,
31
- isHydrated: isHydrated,
43
+ isHydrated: shouldResetVariants,
32
44
  filteredVariants: filteredVariants(),
33
45
  previewingIndex: previewingIndex,
34
46
  });
35
47
  };
36
48
  $: hideVariantsStyleString = () => {
37
49
  return (variants || [])
38
- .map((_, index) => `[data-variant-id="${builderBlock?.id}-${index}"] { display: none; } `)
50
+ .map((_, index) => `div[data-variant-id="${builderBlock?.id}-${index}"] { display: none !important; } `)
39
51
  .join("");
40
52
  };
41
53
  let rootRef;
42
54
  let userAttributes = userAttributesService.getUserAttributes();
43
55
  let scriptStr = getPersonalizationScript(variants, builderBlock?.id || "none", $builderContext?.rootState?.locale);
56
+ let updateVisibilityStylesScript = getUpdateVisibilityStylesScript(variants, builderBlock?.id || "none", $builderContext?.rootState?.locale);
44
57
  let unsubscribers = [];
45
58
  let shouldRenderVariants = checkShouldRenderVariants(variants, getDefaultCanTrack($builderContext?.canTrack));
46
- let isHydrated = false;
59
+ let shouldResetVariants = false;
47
60
  onMount(() => {
48
- isHydrated = true;
61
+ shouldResetVariants = true;
49
62
  const unsub = userAttributesService.subscribeOnUserAttributesChange((attrs) => {
50
63
  userAttributes = attrs;
51
64
  });
@@ -54,7 +67,7 @@ onMount(() => {
54
67
  if (rootRef) {
55
68
  rootRef.dispatchEvent(new CustomEvent("builder.variantLoaded", {
56
69
  detail: {
57
- variant: variant || "default",
70
+ variant: variant || DEFAULT_INDEX,
58
71
  content: $builderContext?.content,
59
72
  },
60
73
  bubbles: true,
@@ -64,7 +77,7 @@ onMount(() => {
64
77
  if (entry.isIntersecting && rootRef) {
65
78
  rootRef.dispatchEvent(new CustomEvent("builder.variantDisplayed", {
66
79
  detail: {
67
- variant: variant || "default",
80
+ variant: variant || DEFAULT_INDEX,
68
81
  content: $builderContext?.content,
69
82
  },
70
83
  bubbles: true,
@@ -82,37 +95,72 @@ onDestroy(() => {
82
95
  });
83
96
  </script>
84
97
 
85
- <div
86
- bind:this={rootRef}
87
- {...attributes}
88
- class={`builder-personalization-container ${attributes?.className || ""}`}
89
- >
90
- {#if shouldRenderVariants}
91
- {#each variants as variant, index (index)}
92
- <svelte:element
93
- this={"template"}
94
- data-variant-id={`${builderBlock?.id}-${index}`}
95
- ><Blocks
98
+ <div bind:this={rootRef} {...attrs()}>
99
+ {#if shouldResetVariants && SDKS_REQUIRING_RESET_APPROACH.includes(TARGET)}
100
+ <Blocks
101
+ blocks={blocksToRender().blocks}
102
+ parent={builderBlock?.id}
103
+ path={blocksToRender().path}
104
+ context={builderContext}
105
+ registeredComponents={builderComponents}
106
+ BlocksWrapperProps={{
107
+ ...$builderContext?.BlocksWrapperProps,
108
+ "data-variant-id": `${builderBlock?.id}-${blocksToRender().index}`,
109
+ }}
110
+ />
111
+ {/if}
112
+ {#if (!shouldResetVariants && SDKS_REQUIRING_RESET_APPROACH.includes(TARGET)) || !SDKS_REQUIRING_RESET_APPROACH.includes(TARGET)}
113
+ {#if shouldRenderVariants}
114
+ <InlinedStyles
115
+ nonce={$builderContext?.nonce || ""}
116
+ styles={hideVariantsStyleString()}
117
+ id={`variants-styles-${builderBlock?.id}`}
118
+ />
119
+ <InlinedScript
120
+ nonce={$builderContext?.nonce || ""}
121
+ scriptStr={updateVisibilityStylesScript}
122
+ id={`variants-visibility-script-${builderBlock?.id}`}
123
+ />
124
+
125
+ {#each variants as variant, index (`${builderBlock?.id}-${index}`)}
126
+ <Blocks
127
+ BlocksWrapperProps={{
128
+ ...$builderContext?.BlocksWrapperProps,
129
+ "aria-hidden": true,
130
+ hidden: true,
131
+ "data-variant-id": `${builderBlock?.id}-${index}`,
132
+ }}
96
133
  blocks={variant.blocks}
97
134
  parent={builderBlock?.id}
98
135
  path={`component.options.variants.${index}.blocks`}
99
- /></svelte:element
100
- >
101
- {/each}
102
-
103
- <InlinedStyles
104
- nonce={$builderContext?.nonce || ""}
105
- styles={hideVariantsStyleString()}
106
- id={`variants-styles-${builderBlock?.id}`}
107
- />
108
- <InlinedScript
109
- nonce={$builderContext?.nonce || ""}
110
- {scriptStr}
111
- id={`variants-script-${builderBlock?.id}`}
112
- />
113
- {/if}<Blocks
114
- blocks={blocksToRender().blocks}
115
- parent={builderBlock?.id}
116
- path={blocksToRender().path}
117
- />
136
+ context={builderContext}
137
+ registeredComponents={builderComponents}
138
+ ><InlinedScript
139
+ nonce={$builderContext?.nonce || ""}
140
+ {scriptStr}
141
+ id={`variants-script-${builderBlock?.id}-${index}`}
142
+ /></Blocks
143
+ >
144
+ {/each}
145
+ {/if}
146
+ <Blocks
147
+ blocks={blocksToRender().blocks}
148
+ parent={builderBlock?.id}
149
+ path={blocksToRender().path}
150
+ context={builderContext}
151
+ registeredComponents={builderComponents}
152
+ BlocksWrapperProps={{
153
+ ...$builderContext?.BlocksWrapperProps,
154
+ "data-variant-id": `${builderBlock?.id}-${blocksToRender().index}`,
155
+ }}
156
+ >
157
+ {#if shouldRenderVariants}
158
+ <InlinedScript
159
+ nonce={$builderContext?.nonce || ""}
160
+ {scriptStr}
161
+ id={`variants-script-${builderBlock?.id}-${DEFAULT_INDEX}`}
162
+ />
163
+ {/if}</Blocks
164
+ >
165
+ {/if}
118
166
  </div>
@@ -5,8 +5,9 @@ declare const __propDef: {
5
5
  builderContext: PersonalizationContainerProps["builderContext"];
6
6
  variants?: PersonalizationContainerProps["variants"];
7
7
  builderBlock: PersonalizationContainerProps["builderBlock"];
8
- previewingIndex?: PersonalizationContainerProps["previewingIndex"];
9
8
  attributes?: PersonalizationContainerProps["attributes"];
9
+ previewingIndex?: PersonalizationContainerProps["previewingIndex"];
10
+ builderComponents: PersonalizationContainerProps["builderComponents"];
10
11
  };
11
12
  events: {
12
13
  [evt: string]: CustomEvent<any>;
@@ -1,5 +1,5 @@
1
1
  import type { BuilderBlock } from '../../types/builder-block';
2
- import type { BuilderDataProps } from '../../types/builder-props';
2
+ import type { BuilderComponentsProp, BuilderDataProps } from '../../types/builder-props';
3
3
  import type { Query } from './helpers';
4
4
  export type PersonalizationContainerProps = {
5
5
  children?: any;
@@ -11,4 +11,4 @@ export type PersonalizationContainerProps = {
11
11
  startDate?: string;
12
12
  endDate?: string;
13
13
  }>;
14
- } & BuilderDataProps;
14
+ } & BuilderDataProps & BuilderComponentsProp;
@@ -7,8 +7,9 @@ export let blocks;
7
7
  export let parent;
8
8
  export let path;
9
9
  export let styleProp;
10
- export let className = undefined;
10
+ export let BlocksWrapperProps;
11
11
  export let context = undefined;
12
+ export let className = undefined;
12
13
  export let linkComponent = undefined;
13
14
  export let registeredComponents = undefined;
14
15
  let builderContext = getContext(BuilderContext.key);
@@ -20,11 +21,12 @@ let componentsContext = getContext(ComponentsContext.key);
20
21
  {parent}
21
22
  {path}
22
23
  {styleProp}
24
+ BlocksWrapperProps={BlocksWrapperProps ||
25
+ $context?.BlocksWrapperProps ||
26
+ $builderContext?.BlocksWrapperProps}
23
27
  classNameProp={className}
24
28
  BlocksWrapper={$context?.BlocksWrapper || $builderContext?.BlocksWrapper}
25
- BlocksWrapperProps={$context?.BlocksWrapperProps ||
26
- $builderContext?.BlocksWrapperProps}
27
- >
29
+ ><slot />
28
30
  {#if blocks}
29
31
  {#each blocks as block (block.id)}
30
32
  <Block
@@ -6,15 +6,18 @@ declare const __propDef: {
6
6
  parent: BlocksProps["parent"];
7
7
  path: BlocksProps["path"];
8
8
  styleProp: BlocksProps["styleProp"];
9
- className?: BlocksProps["className"];
9
+ BlocksWrapperProps: BlocksProps["BlocksWrapperProps"];
10
10
  context?: BlocksProps["context"];
11
+ className?: BlocksProps["className"];
11
12
  linkComponent?: BlocksProps["linkComponent"];
12
13
  registeredComponents?: BlocksProps["registeredComponents"];
13
14
  };
14
15
  events: {
15
16
  [evt: string]: CustomEvent<any>;
16
17
  };
17
- slots: {};
18
+ slots: {
19
+ default: {};
20
+ };
18
21
  };
19
22
  type BlocksProps_ = typeof __propDef.props;
20
23
  export { BlocksProps_ as BlocksProps };
@@ -2,7 +2,7 @@
2
2
  import type { Writable } from "svelte/store";
3
3
  import type { BuilderContextInterface, RegisteredComponents } from '../../context/types';
4
4
  import type { BlocksWrapperProps } from './blocks-wrapper.svelte';
5
- export type BlocksProps = Partial<Omit<BlocksWrapperProps, 'BlocksWrapper' | 'BlocksWrapperProps' | 'classNameProp'>> & {
5
+ export type BlocksProps = Partial<Omit<BlocksWrapperProps, 'BlocksWrapper' | 'classNameProp'>> & {
6
6
  context?: Writable<BuilderContextInterface>;
7
7
  registeredComponents?: RegisteredComponents;
8
8
  linkComponent?: any;
@@ -1,6 +1,7 @@
1
1
  <script context="module"></script>
2
2
 
3
3
  <script>import { onMount } from "svelte";
4
+ import { SDKS_SUPPORTING_PERSONALIZATION, getInitPersonalizationVariantsFnsScriptString, } from "../../blocks/personalization-container/helpers";
4
5
  import { TARGET } from "../../constants/target";
5
6
  import { handleABTestingSync } from "../../helpers/ab-tests";
6
7
  import { getDefaultCanTrack } from "../../helpers/canTrack";
@@ -67,6 +68,14 @@ onMount(() => {
67
68
  scriptStr={getInitVariantsFnsScriptString()}
68
69
  nonce={nonce || ""}
69
70
  />
71
+
72
+ {#if SDKS_SUPPORTING_PERSONALIZATION.includes(TARGET)}
73
+ <InlinedScript
74
+ id="builderio-init-personalization-variants-fns"
75
+ nonce={nonce || ""}
76
+ scriptStr={getInitPersonalizationVariantsFnsScriptString()}
77
+ />
78
+ {/if}
70
79
  {/if}
71
80
 
72
81
  {#if shouldRenderVariants}
@@ -9,6 +9,7 @@ import { default as Fragment } from '../blocks/fragment/index';
9
9
  import { componentInfo as imageComponentInfo } from '../blocks/image/component-info';
10
10
  import { default as Image } from '../blocks/image/index';
11
11
  import { componentInfo as personalizationContainerComponentInfo } from '../blocks/personalization-container/component-info';
12
+ import { SDKS_SUPPORTING_PERSONALIZATION } from '../blocks/personalization-container/helpers';
12
13
  import { default as PersonalizationContainer } from '../blocks/personalization-container/index';
13
14
  import { componentInfo as sectionComponentInfo } from '../blocks/section/component-info';
14
15
  import { default as Section } from '../blocks/section/index';
@@ -50,7 +51,7 @@ export const getDefaultRegisteredComponents = () => [{
50
51
  }, {
51
52
  component: Text,
52
53
  ...textComponentInfo
53
- }, ...(TARGET === 'react' ? [{
54
+ }, ...(SDKS_SUPPORTING_PERSONALIZATION.includes(TARGET) ? [{
54
55
  component: PersonalizationContainer,
55
56
  ...personalizationContainerComponentInfo
56
57
  }] : []), ...(TARGET === 'rsc' ? [] : [{
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "4.0.8";
1
+ export declare const SDK_VERSION = "4.0.9";
@@ -1 +1 @@
1
- export const SDK_VERSION = "4.0.8";
1
+ export const SDK_VERSION = "4.0.9";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@builder.io/sdk-svelte",
3
3
  "description": "Builder.io SDK for Svelte",
4
- "version": "4.0.8",
4
+ "version": "4.0.9",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "lib",
@@ -60,7 +60,7 @@
60
60
  "svelte-preprocess": "^5.0.4",
61
61
  "tslib": "^2.3.1",
62
62
  "typescript": "^5.1.6",
63
- "vite": "^4.5.6"
63
+ "vite": "^4.5.11"
64
64
  },
65
65
  "nx": {
66
66
  "targets": {
@@ -3,7 +3,8 @@ export const componentInfo: ComponentInfo = {
3
3
  name: 'PersonalizationContainer',
4
4
  shouldReceiveBuilderProps: {
5
5
  builderBlock: true,
6
- builderContext: true
6
+ builderContext: true,
7
+ builderComponents: true
7
8
  },
8
9
  noWrap: true,
9
10
  image: 'https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2F37229ed30d8c41dfb10b8cca1992053a',
@@ -84,5 +84,45 @@ export function filterWithCustomTargeting(userAttributes: UserAttributes, query:
84
84
  return objectMatchesQuery(userAttributes, filter);
85
85
  });
86
86
  }
87
- export const PERSONALIZATION_SCRIPT = "function getPersonalizedVariant(variants, blockId, locale) {\n if (!navigator.cookieEnabled) {\n return;\n }\n function getCookie(name) {\n const nameEQ = name + '=';\n const ca = document.cookie.split(';');\n for (let i = 0; i < ca.length; i++) {\n let c = ca[i];\n while (c.charAt(0) == ' ') c = c.substring(1, c.length);\n if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);\n }\n return null;\n }\n function removeVariants() {\n variants?.forEach(function (_, index) {\n document.querySelector('template[data-variant-id=\"' + blockId + '-' + index + '\"]')?.remove();\n });\n document.querySelector('script[data-id=\"variants-script-' + blockId + '\"]')?.remove();\n document.querySelector('style[data-id=\"variants-styles-' + blockId + '\"]')?.remove();\n }\n const attributes = JSON.parse(getCookie('builder.userAttributes') || '{}');\n if (locale) {\n attributes.locale = locale;\n }\n const winningVariantIndex = variants?.findIndex(function (variant) {\n return filterWithCustomTargeting(attributes, variant.query, variant.startDate, variant.endDate);\n });\n const isDebug = location.href.includes('builder.debug=true');\n if (isDebug) {\n console.debug('PersonalizationContainer', {\n attributes,\n variants,\n winningVariantIndex\n });\n }\n if (winningVariantIndex !== -1) {\n const winningVariant = document.querySelector('template[data-variant-id=\"' + blockId + '-' + winningVariantIndex + '\"]');\n if (winningVariant) {\n const parentNode = winningVariant.parentNode;\n if (parentNode) {\n const newParent = parentNode.cloneNode(false);\n newParent.appendChild(winningVariant.content.firstChild);\n newParent.appendChild(winningVariant.content.lastChild);\n parentNode.parentNode?.replaceChild(newParent, parentNode);\n }\n if (isDebug) {\n console.debug('PersonalizationContainer', 'Winning variant Replaced:', winningVariant);\n }\n }\n } else if (variants && variants.length > 0) {\n removeVariants();\n }\n}";
88
- export const FILTER_WITH_CUSTOM_TARGETING_SCRIPT = "function filterWithCustomTargeting(userAttributes, query, startDate, endDate) {\n function isString(val) {\n return typeof val === 'string';\n }\n function isNumber(val) {\n return typeof val === 'number';\n }\n function objectMatchesQuery(userattr, query) {\n const result = (() => {\n const property = query.property;\n const operator = query.operator;\n let testValue = query.value;\n if (query && query.property === 'urlPath' && query.value && typeof query.value === 'string' && query.value !== '/' && query.value.endsWith('/')) {\n testValue = query.value.slice(0, -1);\n }\n if (!(property && operator)) {\n return true;\n }\n if (Array.isArray(testValue)) {\n if (operator === 'isNot') {\n return testValue.every(val => objectMatchesQuery(userattr, {\n property,\n operator,\n value: val\n }));\n }\n return !!testValue.find(val => objectMatchesQuery(userattr, {\n property,\n operator,\n value: val\n }));\n }\n const value = userattr[property];\n if (Array.isArray(value)) {\n return value.includes(testValue);\n }\n switch (operator) {\n case 'is':\n return value === testValue;\n case 'isNot':\n return value !== testValue;\n case 'contains':\n return (isString(value) || Array.isArray(value)) && value.includes(String(testValue));\n case 'startsWith':\n return isString(value) && value.startsWith(String(testValue));\n case 'endsWith':\n return isString(value) && value.endsWith(String(testValue));\n case 'greaterThan':\n return isNumber(value) && isNumber(testValue) && value > testValue;\n case 'lessThan':\n return isNumber(value) && isNumber(testValue) && value < testValue;\n case 'greaterThanOrEqualTo':\n return isNumber(value) && isNumber(testValue) && value >= testValue;\n case 'lessThanOrEqualTo':\n return isNumber(value) && isNumber(testValue) && value <= testValue;\n default:\n return false;\n }\n })();\n return result;\n }\n const item = {\n query,\n startDate,\n endDate\n };\n const now = userAttributes.date && new Date(userAttributes.date) || new Date();\n if (item.startDate && new Date(item.startDate) > now) {\n return false;\n } else if (item.endDate && new Date(item.endDate) < now) {\n return false;\n }\n if (!item.query || !item.query.length) {\n return true;\n }\n return item.query.every(filter => {\n return objectMatchesQuery(userAttributes, filter);\n });\n}";
87
+ export function updateVisibilityStylesScript(variants: PersonalizationContainerProps['variants'], blockId: string, isHydrationTarget: boolean, locale?: string) {
88
+ function getCookie(name: string) {
89
+ const nameEQ = name + '=';
90
+ const ca = document.cookie.split(';');
91
+ for (let i = 0; i < ca.length; i++) {
92
+ let c = ca[i];
93
+ while (c.charAt(0) == ' ') c = c.substring(1, c.length);
94
+ if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
95
+ }
96
+ return null;
97
+ }
98
+ const visibilityStylesEl = (document.currentScript?.previousElementSibling as HTMLStyleElement);
99
+ if (!visibilityStylesEl) {
100
+ return;
101
+ }
102
+ if (isHydrationTarget) {
103
+ visibilityStylesEl.remove();
104
+ const currentScript = document.currentScript;
105
+ if (currentScript) {
106
+ currentScript.remove();
107
+ }
108
+ } else {
109
+ const attributes = JSON.parse(getCookie('builder.userAttributes') || '{}');
110
+ if (locale) {
111
+ attributes.locale = locale;
112
+ }
113
+ const winningVariantIndex = variants?.findIndex(function (variant) {
114
+ return (window as any).filterWithCustomTargeting(attributes, variant.query, variant.startDate, variant.endDate);
115
+ });
116
+ if (winningVariantIndex !== -1) {
117
+ let newStyleStr = variants?.map((_, index) => {
118
+ if (index === winningVariantIndex) return '';
119
+ return `div[data-variant-id="${blockId}-${index}"] { display: none !important; } `;
120
+ }).join('') || '';
121
+ newStyleStr += `div[data-variant-id="${blockId}-default"] { display: none !important; } `;
122
+ visibilityStylesEl.innerHTML = newStyleStr;
123
+ }
124
+ }
125
+ }
126
+ export const PERSONALIZATION_SCRIPT = "function getPersonalizedVariant(variants, blockId, isHydrationTarget, locale) {\n if (!navigator.cookieEnabled) {\n return;\n }\n function getCookie(name) {\n const nameEQ = name + '=';\n const ca = document.cookie.split(';');\n for (let i = 0; i < ca.length; i++) {\n let c = ca[i];\n while (c.charAt(0) == ' ') c = c.substring(1, c.length);\n if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);\n }\n return null;\n }\n const attributes = JSON.parse(getCookie('builder.userAttributes') || '{}');\n if (locale) {\n attributes.locale = locale;\n }\n const winningVariantIndex = variants?.findIndex(function (variant) {\n return window.filterWithCustomTargeting(attributes, variant.query, variant.startDate, variant.endDate);\n });\n const parentDiv = document.currentScript?.parentElement;\n const variantId = parentDiv?.getAttribute('data-variant-id');\n const isDefaultVariant = variantId === `${blockId}-default`;\n const isWinningVariant = winningVariantIndex !== -1 && variantId === `${blockId}-${winningVariantIndex}` || winningVariantIndex === -1 && isDefaultVariant;\n if (isWinningVariant && !isDefaultVariant) {\n parentDiv?.removeAttribute('hidden');\n parentDiv?.removeAttribute('aria-hidden');\n } else if (!isWinningVariant && isDefaultVariant) {\n parentDiv?.setAttribute('hidden', 'true');\n parentDiv?.setAttribute('aria-hidden', 'true');\n }\n if (isHydrationTarget) {\n if (!isWinningVariant) {\n const itsStyleEl = parentDiv?.previousElementSibling;\n if (itsStyleEl) {\n itsStyleEl.remove();\n }\n parentDiv?.remove();\n }\n const thisScript = document.currentScript;\n if (thisScript) {\n thisScript.remove();\n }\n }\n}";
127
+ export const FILTER_WITH_CUSTOM_TARGETING_SCRIPT = "function filterWithCustomTargeting(userAttributes, query, startDate, endDate) {\n function isString(val) {\n return typeof val === 'string';\n }\n function isNumber(val) {\n return typeof val === 'number';\n }\n function objectMatchesQuery(userattr, query) {\n const result = (() => {\n const property = query.property;\n const operator = query.operator;\n let testValue = query.value;\n if (query && query.property === 'urlPath' && query.value && typeof query.value === 'string' && query.value !== '/' && query.value.endsWith('/')) {\n testValue = query.value.slice(0, -1);\n }\n if (!(property && operator)) {\n return true;\n }\n if (Array.isArray(testValue)) {\n if (operator === 'isNot') {\n return testValue.every(val => objectMatchesQuery(userattr, {\n property,\n operator,\n value: val\n }));\n }\n return !!testValue.find(val => objectMatchesQuery(userattr, {\n property,\n operator,\n value: val\n }));\n }\n const value = userattr[property];\n if (Array.isArray(value)) {\n return value.includes(testValue);\n }\n switch (operator) {\n case 'is':\n return value === testValue;\n case 'isNot':\n return value !== testValue;\n case 'contains':\n return (isString(value) || Array.isArray(value)) && value.includes(String(testValue));\n case 'startsWith':\n return isString(value) && value.startsWith(String(testValue));\n case 'endsWith':\n return isString(value) && value.endsWith(String(testValue));\n case 'greaterThan':\n return isNumber(value) && isNumber(testValue) && value > testValue;\n case 'lessThan':\n return isNumber(value) && isNumber(testValue) && value < testValue;\n case 'greaterThanOrEqualTo':\n return isNumber(value) && isNumber(testValue) && value >= testValue;\n case 'lessThanOrEqualTo':\n return isNumber(value) && isNumber(testValue) && value <= testValue;\n default:\n return false;\n }\n })();\n return result;\n }\n const item = {\n query,\n startDate,\n endDate\n };\n const now = userAttributes.date && new Date(userAttributes.date) || new Date();\n if (item.startDate && new Date(item.startDate) > now) {\n return false;\n } else if (item.endDate && new Date(item.endDate) < now) {\n return false;\n }\n if (!item.query || !item.query.length) {\n return true;\n }\n return item.query.every(filter => {\n return objectMatchesQuery(userAttributes, filter);\n });\n}";
128
+ export const UPDATE_VISIBILITY_STYLES_SCRIPT = "function updateVisibilityStylesScript(variants, blockId, isHydrationTarget, locale) {\n function getCookie(name) {\n const nameEQ = name + '=';\n const ca = document.cookie.split(';');\n for (let i = 0; i < ca.length; i++) {\n let c = ca[i];\n while (c.charAt(0) == ' ') c = c.substring(1, c.length);\n if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);\n }\n return null;\n }\n const visibilityStylesEl = document.currentScript?.previousElementSibling;\n if (!visibilityStylesEl) {\n return;\n }\n if (isHydrationTarget) {\n visibilityStylesEl.remove();\n const currentScript = document.currentScript;\n if (currentScript) {\n currentScript.remove();\n }\n } else {\n const attributes = JSON.parse(getCookie('builder.userAttributes') || '{}');\n if (locale) {\n attributes.locale = locale;\n }\n const winningVariantIndex = variants?.findIndex(function (variant) {\n return window.filterWithCustomTargeting(attributes, variant.query, variant.startDate, variant.endDate);\n });\n if (winningVariantIndex !== -1) {\n let newStyleStr = variants?.map((_, index) => {\n if (index === winningVariantIndex) return '';\n return `div[data-variant-id=\"${blockId}-${index}\"] { display: none !important; } `;\n }).join('') || '';\n newStyleStr += `div[data-variant-id=\"${blockId}-default\"] { display: none !important; } `;\n visibilityStylesEl.innerHTML = newStyleStr;\n }\n }\n}";