@builder.io/sdk-svelte 5.2.6 → 5.2.8

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 (30) hide show
  1. package/lib/browser/blocks/image/image.svelte +1 -1
  2. package/lib/browser/blocks/personalization-container/helpers.d.ts +2 -0
  3. package/lib/browser/blocks/personalization-container/helpers.js +30 -3
  4. package/lib/browser/components/content-variants/content-variants.svelte +4 -3
  5. package/lib/browser/components/content-variants/content-variants.svelte.d.ts +1 -1
  6. package/lib/browser/constants/sdk-version.d.ts +1 -1
  7. package/lib/browser/constants/sdk-version.js +1 -1
  8. package/lib/browser/types/components.d.ts +14 -0
  9. package/lib/edge/blocks/image/image.svelte +1 -1
  10. package/lib/edge/blocks/personalization-container/helpers.d.ts +2 -0
  11. package/lib/edge/blocks/personalization-container/helpers.js +30 -3
  12. package/lib/edge/components/content-variants/content-variants.svelte +4 -3
  13. package/lib/edge/components/content-variants/content-variants.svelte.d.ts +1 -1
  14. package/lib/edge/constants/sdk-version.d.ts +1 -1
  15. package/lib/edge/constants/sdk-version.js +1 -1
  16. package/lib/edge/types/components.d.ts +14 -0
  17. package/lib/node/blocks/image/image.svelte +1 -1
  18. package/lib/node/blocks/personalization-container/helpers.d.ts +2 -0
  19. package/lib/node/blocks/personalization-container/helpers.js +30 -3
  20. package/lib/node/components/content-variants/content-variants.svelte +4 -3
  21. package/lib/node/components/content-variants/content-variants.svelte.d.ts +1 -1
  22. package/lib/node/constants/sdk-version.d.ts +1 -1
  23. package/lib/node/constants/sdk-version.js +1 -1
  24. package/lib/node/types/components.d.ts +14 -0
  25. package/package.json +1 -1
  26. package/src/blocks/image/image.svelte +1 -1
  27. package/src/blocks/personalization-container/helpers.ts +31 -3
  28. package/src/components/content-variants/content-variants.svelte +6 -3
  29. package/src/constants/sdk-version.ts +1 -1
  30. package/src/types/components.ts +15 -0
@@ -84,7 +84,7 @@ onMount(() => { });
84
84
  })}
85
85
  loading={highPriority ? "eager" : "lazy"}
86
86
  fetchpriority={highPriority ? "high" : "auto"}
87
- alt={altText}
87
+ alt={altText || ""}
88
88
  {title}
89
89
  role={altText ? undefined : "presentation"}
90
90
  class={"builder-image" + (className ? " " + className : "") + " img"}
@@ -1,4 +1,5 @@
1
1
  import type { BuilderBlock } from '../../types/builder-block';
2
+ import type { BuilderContent } from '../../types/builder-content';
2
3
  import type { Target } from '../../types/targets';
3
4
  import type { PersonalizationContainerProps } from './personalization-container.types';
4
5
  export declare const DEFAULT_INDEX = "default";
@@ -41,6 +42,7 @@ export declare function getBlocksToRender({ variants, previewingIndex, isHydrate
41
42
  filteredVariants: PersonalizationContainerProps['variants'];
42
43
  fallbackBlocks?: BuilderBlock[];
43
44
  }): BlocksToRenderReturnType;
45
+ export declare const hasPersonalizationContainer: (content: BuilderContent | null | undefined) => boolean;
44
46
  export declare const getInitPersonalizationVariantsFnsScriptString: () => string;
45
47
  export declare const getPersonalizationScript: (variants: PersonalizationContainerProps['variants'], blockId: string, locale?: string) => string;
46
48
  export declare const getUpdateVisibilityStylesScript: (variants: PersonalizationContainerProps['variants'], blockId: string, locale?: string) => string;
@@ -6,6 +6,7 @@ export const DEFAULT_INDEX = 'default';
6
6
  const FILTER_WITH_CUSTOM_TARGETING_SCRIPT_FN_NAME = 'filterWithCustomTargeting';
7
7
  const BUILDER_IO_PERSONALIZATION_SCRIPT_FN_NAME = 'builderIoPersonalization';
8
8
  const UPDATE_VARIANT_VISIBILITY_SCRIPT_FN_NAME = 'updateVisibilityStylesScript';
9
+ const PERSONALIZATION_CONTAINER_COMPONENT_NAME = 'PersonalizationContainer';
9
10
  /**
10
11
  * SDKs that support Variant Containers
11
12
  */
@@ -72,11 +73,37 @@ export function getBlocksToRender({ variants, previewingIndex, isHydrated, filte
72
73
  // If no winning variant or we are on the server, return the default variant
73
74
  return fallback;
74
75
  }
76
+ /**
77
+ * Walks the whole content rather than a known set of paths. A/B variation blocks live on
78
+ * `variations[*].data.blocks`, a sibling of `data.blocks`, and containers can sit inside columns,
79
+ * tabs, slots or inlined symbols. A missed container leaves the inlined fns undefined at parse
80
+ * time, so every variant renders at once.
81
+ */
82
+ const containsPersonalizationContainer = (value) => {
83
+ if (Array.isArray(value)) {
84
+ return value.some(containsPersonalizationContainer);
85
+ }
86
+ if (!value || typeof value !== 'object')
87
+ return false;
88
+ if (value.component?.name === PERSONALIZATION_CONTAINER_COMPONENT_NAME) {
89
+ return true;
90
+ }
91
+ return Object.values(value).some(containsPersonalizationContainer);
92
+ };
93
+ export const hasPersonalizationContainer = (content) => containsPersonalizationContainer(content);
75
94
  export const getInitPersonalizationVariantsFnsScriptString = () => {
76
95
  return `
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}
96
+ (function() {
97
+ if (!window.${FILTER_WITH_CUSTOM_TARGETING_SCRIPT_FN_NAME}) {
98
+ window.${FILTER_WITH_CUSTOM_TARGETING_SCRIPT_FN_NAME} = ${FILTER_WITH_CUSTOM_TARGETING_SCRIPT};
99
+ }
100
+ if (!window.${BUILDER_IO_PERSONALIZATION_SCRIPT_FN_NAME}) {
101
+ window.${BUILDER_IO_PERSONALIZATION_SCRIPT_FN_NAME} = ${PERSONALIZATION_SCRIPT};
102
+ }
103
+ if (!window.${UPDATE_VARIANT_VISIBILITY_SCRIPT_FN_NAME}) {
104
+ window.${UPDATE_VARIANT_VISIBILITY_SCRIPT_FN_NAME} = ${UPDATE_VISIBILITY_STYLES_SCRIPT};
105
+ }
106
+ })();
80
107
  `;
81
108
  };
82
109
  const isHydrationTarget = TARGET === 'react';
@@ -1,7 +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
+ import { SDKS_SUPPORTING_PERSONALIZATION, getInitPersonalizationVariantsFnsScriptString, hasPersonalizationContainer, } from "../../blocks/personalization-container/helpers";
5
5
  import { TARGET } from "../../constants/target";
6
6
  import { handleABTestingSync } from "../../helpers/ab-tests";
7
7
  import { getDefaultCanTrack } from "../../helpers/canTrack";
@@ -11,9 +11,9 @@ import InlinedStyles from "../inlined-styles.svelte";
11
11
  import { checkShouldRenderVariants, getInitVariantsFnsScriptString, getUpdateCookieAndStylesScript, getVariants, } from "./helpers";
12
12
  export let canTrack = undefined;
13
13
  export let content;
14
- export let isNestedRender = undefined;
15
14
  export let nonce = undefined;
16
15
  export let apiHost = undefined;
16
+ export let isNestedRender = undefined;
17
17
  export let model;
18
18
  export let data = undefined;
19
19
  export let context = undefined;
@@ -54,6 +54,7 @@ let shouldRenderVariants = checkShouldRenderVariants({
54
54
  canTrack: getDefaultCanTrack(canTrack),
55
55
  content: content,
56
56
  });
57
+ let shouldEmitPersonalizationFns = TARGET !== "reactNative" && hasPersonalizationContainer(content);
57
58
  onMount(() => {
58
59
  /**
59
60
  * For Solid/Svelte: we unmount the non-winning variants post-hydration.
@@ -62,7 +63,7 @@ onMount(() => {
62
63
  });
63
64
  </script>
64
65
 
65
- {#if !isNestedRender && TARGET !== "reactNative"}
66
+ {#if shouldEmitPersonalizationFns}
66
67
  {#if SDKS_SUPPORTING_PERSONALIZATION.includes(TARGET)}
67
68
  <InlinedScript
68
69
  id="builderio-init-personalization-variants-fns"
@@ -10,9 +10,9 @@ declare const __propDef: {
10
10
  props: {
11
11
  canTrack?: VariantsProviderProps["canTrack"];
12
12
  content: VariantsProviderProps["content"];
13
- isNestedRender?: VariantsProviderProps["isNestedRender"];
14
13
  nonce?: VariantsProviderProps["nonce"];
15
14
  apiHost?: VariantsProviderProps["apiHost"];
15
+ isNestedRender?: VariantsProviderProps["isNestedRender"];
16
16
  model: VariantsProviderProps["model"];
17
17
  data?: VariantsProviderProps["data"];
18
18
  context?: VariantsProviderProps["context"];
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "5.2.6";
1
+ export declare const SDK_VERSION = "5.2.8";
@@ -1 +1 @@
1
- export const SDK_VERSION = "5.2.6";
1
+ export const SDK_VERSION = "5.2.8";
@@ -98,6 +98,20 @@ export interface ComponentInfo {
98
98
  query?: any;
99
99
  };
100
100
  friendlyName?: string;
101
+ /**
102
+ * Group custom components into their own accordion sections in the editor's insert menu.
103
+ * Components with the same `group` value are grouped together; components without a `group`
104
+ * (or with a blank value) fall back to the default "Custom Components" accordion.
105
+ *
106
+ * @example
107
+ * ```js
108
+ * Builder.registerComponent(MyStepComponent, {
109
+ * name: 'Step',
110
+ * group: 'Steps',
111
+ * })
112
+ * ```
113
+ */
114
+ group?: string;
101
115
  /**
102
116
  * Use to restrict access to your component based on a the current user permissions
103
117
  * By default components will show to all users
@@ -84,7 +84,7 @@ onMount(() => { });
84
84
  })}
85
85
  loading={highPriority ? "eager" : "lazy"}
86
86
  fetchpriority={highPriority ? "high" : "auto"}
87
- alt={altText}
87
+ alt={altText || ""}
88
88
  {title}
89
89
  role={altText ? undefined : "presentation"}
90
90
  class={"builder-image" + (className ? " " + className : "") + " img"}
@@ -1,4 +1,5 @@
1
1
  import type { BuilderBlock } from '../../types/builder-block';
2
+ import type { BuilderContent } from '../../types/builder-content';
2
3
  import type { Target } from '../../types/targets';
3
4
  import type { PersonalizationContainerProps } from './personalization-container.types';
4
5
  export declare const DEFAULT_INDEX = "default";
@@ -41,6 +42,7 @@ export declare function getBlocksToRender({ variants, previewingIndex, isHydrate
41
42
  filteredVariants: PersonalizationContainerProps['variants'];
42
43
  fallbackBlocks?: BuilderBlock[];
43
44
  }): BlocksToRenderReturnType;
45
+ export declare const hasPersonalizationContainer: (content: BuilderContent | null | undefined) => boolean;
44
46
  export declare const getInitPersonalizationVariantsFnsScriptString: () => string;
45
47
  export declare const getPersonalizationScript: (variants: PersonalizationContainerProps['variants'], blockId: string, locale?: string) => string;
46
48
  export declare const getUpdateVisibilityStylesScript: (variants: PersonalizationContainerProps['variants'], blockId: string, locale?: string) => string;
@@ -6,6 +6,7 @@ export const DEFAULT_INDEX = 'default';
6
6
  const FILTER_WITH_CUSTOM_TARGETING_SCRIPT_FN_NAME = 'filterWithCustomTargeting';
7
7
  const BUILDER_IO_PERSONALIZATION_SCRIPT_FN_NAME = 'builderIoPersonalization';
8
8
  const UPDATE_VARIANT_VISIBILITY_SCRIPT_FN_NAME = 'updateVisibilityStylesScript';
9
+ const PERSONALIZATION_CONTAINER_COMPONENT_NAME = 'PersonalizationContainer';
9
10
  /**
10
11
  * SDKs that support Variant Containers
11
12
  */
@@ -72,11 +73,37 @@ export function getBlocksToRender({ variants, previewingIndex, isHydrated, filte
72
73
  // If no winning variant or we are on the server, return the default variant
73
74
  return fallback;
74
75
  }
76
+ /**
77
+ * Walks the whole content rather than a known set of paths. A/B variation blocks live on
78
+ * `variations[*].data.blocks`, a sibling of `data.blocks`, and containers can sit inside columns,
79
+ * tabs, slots or inlined symbols. A missed container leaves the inlined fns undefined at parse
80
+ * time, so every variant renders at once.
81
+ */
82
+ const containsPersonalizationContainer = (value) => {
83
+ if (Array.isArray(value)) {
84
+ return value.some(containsPersonalizationContainer);
85
+ }
86
+ if (!value || typeof value !== 'object')
87
+ return false;
88
+ if (value.component?.name === PERSONALIZATION_CONTAINER_COMPONENT_NAME) {
89
+ return true;
90
+ }
91
+ return Object.values(value).some(containsPersonalizationContainer);
92
+ };
93
+ export const hasPersonalizationContainer = (content) => containsPersonalizationContainer(content);
75
94
  export const getInitPersonalizationVariantsFnsScriptString = () => {
76
95
  return `
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}
96
+ (function() {
97
+ if (!window.${FILTER_WITH_CUSTOM_TARGETING_SCRIPT_FN_NAME}) {
98
+ window.${FILTER_WITH_CUSTOM_TARGETING_SCRIPT_FN_NAME} = ${FILTER_WITH_CUSTOM_TARGETING_SCRIPT};
99
+ }
100
+ if (!window.${BUILDER_IO_PERSONALIZATION_SCRIPT_FN_NAME}) {
101
+ window.${BUILDER_IO_PERSONALIZATION_SCRIPT_FN_NAME} = ${PERSONALIZATION_SCRIPT};
102
+ }
103
+ if (!window.${UPDATE_VARIANT_VISIBILITY_SCRIPT_FN_NAME}) {
104
+ window.${UPDATE_VARIANT_VISIBILITY_SCRIPT_FN_NAME} = ${UPDATE_VISIBILITY_STYLES_SCRIPT};
105
+ }
106
+ })();
80
107
  `;
81
108
  };
82
109
  const isHydrationTarget = TARGET === 'react';
@@ -1,7 +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
+ import { SDKS_SUPPORTING_PERSONALIZATION, getInitPersonalizationVariantsFnsScriptString, hasPersonalizationContainer, } from "../../blocks/personalization-container/helpers";
5
5
  import { TARGET } from "../../constants/target";
6
6
  import { handleABTestingSync } from "../../helpers/ab-tests";
7
7
  import { getDefaultCanTrack } from "../../helpers/canTrack";
@@ -11,9 +11,9 @@ import InlinedStyles from "../inlined-styles.svelte";
11
11
  import { checkShouldRenderVariants, getInitVariantsFnsScriptString, getUpdateCookieAndStylesScript, getVariants, } from "./helpers";
12
12
  export let canTrack = undefined;
13
13
  export let content;
14
- export let isNestedRender = undefined;
15
14
  export let nonce = undefined;
16
15
  export let apiHost = undefined;
16
+ export let isNestedRender = undefined;
17
17
  export let model;
18
18
  export let data = undefined;
19
19
  export let context = undefined;
@@ -54,6 +54,7 @@ let shouldRenderVariants = checkShouldRenderVariants({
54
54
  canTrack: getDefaultCanTrack(canTrack),
55
55
  content: content,
56
56
  });
57
+ let shouldEmitPersonalizationFns = TARGET !== "reactNative" && hasPersonalizationContainer(content);
57
58
  onMount(() => {
58
59
  /**
59
60
  * For Solid/Svelte: we unmount the non-winning variants post-hydration.
@@ -62,7 +63,7 @@ onMount(() => {
62
63
  });
63
64
  </script>
64
65
 
65
- {#if !isNestedRender && TARGET !== "reactNative"}
66
+ {#if shouldEmitPersonalizationFns}
66
67
  {#if SDKS_SUPPORTING_PERSONALIZATION.includes(TARGET)}
67
68
  <InlinedScript
68
69
  id="builderio-init-personalization-variants-fns"
@@ -10,9 +10,9 @@ declare const __propDef: {
10
10
  props: {
11
11
  canTrack?: VariantsProviderProps["canTrack"];
12
12
  content: VariantsProviderProps["content"];
13
- isNestedRender?: VariantsProviderProps["isNestedRender"];
14
13
  nonce?: VariantsProviderProps["nonce"];
15
14
  apiHost?: VariantsProviderProps["apiHost"];
15
+ isNestedRender?: VariantsProviderProps["isNestedRender"];
16
16
  model: VariantsProviderProps["model"];
17
17
  data?: VariantsProviderProps["data"];
18
18
  context?: VariantsProviderProps["context"];
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "5.2.6";
1
+ export declare const SDK_VERSION = "5.2.8";
@@ -1 +1 @@
1
- export const SDK_VERSION = "5.2.6";
1
+ export const SDK_VERSION = "5.2.8";
@@ -98,6 +98,20 @@ export interface ComponentInfo {
98
98
  query?: any;
99
99
  };
100
100
  friendlyName?: string;
101
+ /**
102
+ * Group custom components into their own accordion sections in the editor's insert menu.
103
+ * Components with the same `group` value are grouped together; components without a `group`
104
+ * (or with a blank value) fall back to the default "Custom Components" accordion.
105
+ *
106
+ * @example
107
+ * ```js
108
+ * Builder.registerComponent(MyStepComponent, {
109
+ * name: 'Step',
110
+ * group: 'Steps',
111
+ * })
112
+ * ```
113
+ */
114
+ group?: string;
101
115
  /**
102
116
  * Use to restrict access to your component based on a the current user permissions
103
117
  * By default components will show to all users
@@ -84,7 +84,7 @@ onMount(() => { });
84
84
  })}
85
85
  loading={highPriority ? "eager" : "lazy"}
86
86
  fetchpriority={highPriority ? "high" : "auto"}
87
- alt={altText}
87
+ alt={altText || ""}
88
88
  {title}
89
89
  role={altText ? undefined : "presentation"}
90
90
  class={"builder-image" + (className ? " " + className : "") + " img"}
@@ -1,4 +1,5 @@
1
1
  import type { BuilderBlock } from '../../types/builder-block';
2
+ import type { BuilderContent } from '../../types/builder-content';
2
3
  import type { Target } from '../../types/targets';
3
4
  import type { PersonalizationContainerProps } from './personalization-container.types';
4
5
  export declare const DEFAULT_INDEX = "default";
@@ -41,6 +42,7 @@ export declare function getBlocksToRender({ variants, previewingIndex, isHydrate
41
42
  filteredVariants: PersonalizationContainerProps['variants'];
42
43
  fallbackBlocks?: BuilderBlock[];
43
44
  }): BlocksToRenderReturnType;
45
+ export declare const hasPersonalizationContainer: (content: BuilderContent | null | undefined) => boolean;
44
46
  export declare const getInitPersonalizationVariantsFnsScriptString: () => string;
45
47
  export declare const getPersonalizationScript: (variants: PersonalizationContainerProps['variants'], blockId: string, locale?: string) => string;
46
48
  export declare const getUpdateVisibilityStylesScript: (variants: PersonalizationContainerProps['variants'], blockId: string, locale?: string) => string;
@@ -6,6 +6,7 @@ export const DEFAULT_INDEX = 'default';
6
6
  const FILTER_WITH_CUSTOM_TARGETING_SCRIPT_FN_NAME = 'filterWithCustomTargeting';
7
7
  const BUILDER_IO_PERSONALIZATION_SCRIPT_FN_NAME = 'builderIoPersonalization';
8
8
  const UPDATE_VARIANT_VISIBILITY_SCRIPT_FN_NAME = 'updateVisibilityStylesScript';
9
+ const PERSONALIZATION_CONTAINER_COMPONENT_NAME = 'PersonalizationContainer';
9
10
  /**
10
11
  * SDKs that support Variant Containers
11
12
  */
@@ -72,11 +73,37 @@ export function getBlocksToRender({ variants, previewingIndex, isHydrated, filte
72
73
  // If no winning variant or we are on the server, return the default variant
73
74
  return fallback;
74
75
  }
76
+ /**
77
+ * Walks the whole content rather than a known set of paths. A/B variation blocks live on
78
+ * `variations[*].data.blocks`, a sibling of `data.blocks`, and containers can sit inside columns,
79
+ * tabs, slots or inlined symbols. A missed container leaves the inlined fns undefined at parse
80
+ * time, so every variant renders at once.
81
+ */
82
+ const containsPersonalizationContainer = (value) => {
83
+ if (Array.isArray(value)) {
84
+ return value.some(containsPersonalizationContainer);
85
+ }
86
+ if (!value || typeof value !== 'object')
87
+ return false;
88
+ if (value.component?.name === PERSONALIZATION_CONTAINER_COMPONENT_NAME) {
89
+ return true;
90
+ }
91
+ return Object.values(value).some(containsPersonalizationContainer);
92
+ };
93
+ export const hasPersonalizationContainer = (content) => containsPersonalizationContainer(content);
75
94
  export const getInitPersonalizationVariantsFnsScriptString = () => {
76
95
  return `
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}
96
+ (function() {
97
+ if (!window.${FILTER_WITH_CUSTOM_TARGETING_SCRIPT_FN_NAME}) {
98
+ window.${FILTER_WITH_CUSTOM_TARGETING_SCRIPT_FN_NAME} = ${FILTER_WITH_CUSTOM_TARGETING_SCRIPT};
99
+ }
100
+ if (!window.${BUILDER_IO_PERSONALIZATION_SCRIPT_FN_NAME}) {
101
+ window.${BUILDER_IO_PERSONALIZATION_SCRIPT_FN_NAME} = ${PERSONALIZATION_SCRIPT};
102
+ }
103
+ if (!window.${UPDATE_VARIANT_VISIBILITY_SCRIPT_FN_NAME}) {
104
+ window.${UPDATE_VARIANT_VISIBILITY_SCRIPT_FN_NAME} = ${UPDATE_VISIBILITY_STYLES_SCRIPT};
105
+ }
106
+ })();
80
107
  `;
81
108
  };
82
109
  const isHydrationTarget = TARGET === 'react';
@@ -1,7 +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
+ import { SDKS_SUPPORTING_PERSONALIZATION, getInitPersonalizationVariantsFnsScriptString, hasPersonalizationContainer, } from "../../blocks/personalization-container/helpers";
5
5
  import { TARGET } from "../../constants/target";
6
6
  import { handleABTestingSync } from "../../helpers/ab-tests";
7
7
  import { getDefaultCanTrack } from "../../helpers/canTrack";
@@ -11,9 +11,9 @@ import InlinedStyles from "../inlined-styles.svelte";
11
11
  import { checkShouldRenderVariants, getInitVariantsFnsScriptString, getUpdateCookieAndStylesScript, getVariants, } from "./helpers";
12
12
  export let canTrack = undefined;
13
13
  export let content;
14
- export let isNestedRender = undefined;
15
14
  export let nonce = undefined;
16
15
  export let apiHost = undefined;
16
+ export let isNestedRender = undefined;
17
17
  export let model;
18
18
  export let data = undefined;
19
19
  export let context = undefined;
@@ -54,6 +54,7 @@ let shouldRenderVariants = checkShouldRenderVariants({
54
54
  canTrack: getDefaultCanTrack(canTrack),
55
55
  content: content,
56
56
  });
57
+ let shouldEmitPersonalizationFns = TARGET !== "reactNative" && hasPersonalizationContainer(content);
57
58
  onMount(() => {
58
59
  /**
59
60
  * For Solid/Svelte: we unmount the non-winning variants post-hydration.
@@ -62,7 +63,7 @@ onMount(() => {
62
63
  });
63
64
  </script>
64
65
 
65
- {#if !isNestedRender && TARGET !== "reactNative"}
66
+ {#if shouldEmitPersonalizationFns}
66
67
  {#if SDKS_SUPPORTING_PERSONALIZATION.includes(TARGET)}
67
68
  <InlinedScript
68
69
  id="builderio-init-personalization-variants-fns"
@@ -10,9 +10,9 @@ declare const __propDef: {
10
10
  props: {
11
11
  canTrack?: VariantsProviderProps["canTrack"];
12
12
  content: VariantsProviderProps["content"];
13
- isNestedRender?: VariantsProviderProps["isNestedRender"];
14
13
  nonce?: VariantsProviderProps["nonce"];
15
14
  apiHost?: VariantsProviderProps["apiHost"];
15
+ isNestedRender?: VariantsProviderProps["isNestedRender"];
16
16
  model: VariantsProviderProps["model"];
17
17
  data?: VariantsProviderProps["data"];
18
18
  context?: VariantsProviderProps["context"];
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "5.2.6";
1
+ export declare const SDK_VERSION = "5.2.8";
@@ -1 +1 @@
1
- export const SDK_VERSION = "5.2.6";
1
+ export const SDK_VERSION = "5.2.8";
@@ -98,6 +98,20 @@ export interface ComponentInfo {
98
98
  query?: any;
99
99
  };
100
100
  friendlyName?: string;
101
+ /**
102
+ * Group custom components into their own accordion sections in the editor's insert menu.
103
+ * Components with the same `group` value are grouped together; components without a `group`
104
+ * (or with a blank value) fall back to the default "Custom Components" accordion.
105
+ *
106
+ * @example
107
+ * ```js
108
+ * Builder.registerComponent(MyStepComponent, {
109
+ * name: 'Step',
110
+ * group: 'Steps',
111
+ * })
112
+ * ```
113
+ */
114
+ group?: string;
101
115
  /**
102
116
  * Use to restrict access to your component based on a the current user permissions
103
117
  * By default components will show to all users
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": "5.2.6",
4
+ "version": "5.2.8",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "lib",
@@ -93,7 +93,7 @@
93
93
  })}
94
94
  loading={highPriority ? "eager" : "lazy"}
95
95
  fetchpriority={highPriority ? "high" : "auto"}
96
- alt={altText}
96
+ alt={altText || ""}
97
97
  {title}
98
98
  role={altText ? undefined : "presentation"}
99
99
  class={"builder-image" + (className ? " " + className : "") + " img"}
@@ -2,6 +2,7 @@ import { TARGET } from '../../constants/target';
2
2
  import { isBrowser } from '../../functions/is-browser';
3
3
  import { isEditing } from '../../functions/is-editing';
4
4
  import type { BuilderBlock } from '../../types/builder-block';
5
+ import type { BuilderContent } from '../../types/builder-content';
5
6
  import type { Target } from '../../types/targets';
6
7
  import { FILTER_WITH_CUSTOM_TARGETING_SCRIPT, PERSONALIZATION_SCRIPT, UPDATE_VISIBILITY_STYLES_SCRIPT } from './helpers/inlined-fns';
7
8
  import type { PersonalizationContainerProps } from './personalization-container.types';
@@ -9,6 +10,7 @@ export const DEFAULT_INDEX = 'default';
9
10
  const FILTER_WITH_CUSTOM_TARGETING_SCRIPT_FN_NAME = 'filterWithCustomTargeting';
10
11
  const BUILDER_IO_PERSONALIZATION_SCRIPT_FN_NAME = 'builderIoPersonalization';
11
12
  const UPDATE_VARIANT_VISIBILITY_SCRIPT_FN_NAME = 'updateVisibilityStylesScript';
13
+ const PERSONALIZATION_CONTAINER_COMPONENT_NAME = 'PersonalizationContainer';
12
14
  export type UserAttributes = {
13
15
  date?: string | Date;
14
16
  urlPath?: string;
@@ -103,11 +105,37 @@ export function getBlocksToRender({
103
105
  // If no winning variant or we are on the server, return the default variant
104
106
  return fallback;
105
107
  }
108
+
109
+ /**
110
+ * Walks the whole content rather than a known set of paths. A/B variation blocks live on
111
+ * `variations[*].data.blocks`, a sibling of `data.blocks`, and containers can sit inside columns,
112
+ * tabs, slots or inlined symbols. A missed container leaves the inlined fns undefined at parse
113
+ * time, so every variant renders at once.
114
+ */
115
+ const containsPersonalizationContainer = (value: unknown): boolean => {
116
+ if (Array.isArray(value)) {
117
+ return value.some(containsPersonalizationContainer);
118
+ }
119
+ if (!value || typeof value !== 'object') return false;
120
+ if ((value as BuilderBlock).component?.name === PERSONALIZATION_CONTAINER_COMPONENT_NAME) {
121
+ return true;
122
+ }
123
+ return Object.values(value).some(containsPersonalizationContainer);
124
+ };
125
+ export const hasPersonalizationContainer = (content: BuilderContent | null | undefined) => containsPersonalizationContainer(content);
106
126
  export const getInitPersonalizationVariantsFnsScriptString = () => {
107
127
  return `
108
- window.${FILTER_WITH_CUSTOM_TARGETING_SCRIPT_FN_NAME} = ${FILTER_WITH_CUSTOM_TARGETING_SCRIPT}
109
- window.${BUILDER_IO_PERSONALIZATION_SCRIPT_FN_NAME} = ${PERSONALIZATION_SCRIPT}
110
- window.${UPDATE_VARIANT_VISIBILITY_SCRIPT_FN_NAME} = ${UPDATE_VISIBILITY_STYLES_SCRIPT}
128
+ (function() {
129
+ if (!window.${FILTER_WITH_CUSTOM_TARGETING_SCRIPT_FN_NAME}) {
130
+ window.${FILTER_WITH_CUSTOM_TARGETING_SCRIPT_FN_NAME} = ${FILTER_WITH_CUSTOM_TARGETING_SCRIPT};
131
+ }
132
+ if (!window.${BUILDER_IO_PERSONALIZATION_SCRIPT_FN_NAME}) {
133
+ window.${BUILDER_IO_PERSONALIZATION_SCRIPT_FN_NAME} = ${PERSONALIZATION_SCRIPT};
134
+ }
135
+ if (!window.${UPDATE_VARIANT_VISIBILITY_SCRIPT_FN_NAME}) {
136
+ window.${UPDATE_VARIANT_VISIBILITY_SCRIPT_FN_NAME} = ${UPDATE_VISIBILITY_STYLES_SCRIPT};
137
+ }
138
+ })();
111
139
  `;
112
140
  };
113
141
  const isHydrationTarget = TARGET === 'react';
@@ -13,6 +13,7 @@
13
13
  import {
14
14
  SDKS_SUPPORTING_PERSONALIZATION,
15
15
  getInitPersonalizationVariantsFnsScriptString,
16
+ hasPersonalizationContainer,
16
17
  } from "../../blocks/personalization-container/helpers";
17
18
  import { TARGET } from "../../constants/target";
18
19
  import { handleABTestingSync } from "../../helpers/ab-tests";
@@ -30,10 +31,10 @@
30
31
 
31
32
  export let canTrack: VariantsProviderProps["canTrack"] = undefined;
32
33
  export let content: VariantsProviderProps["content"];
33
- export let isNestedRender: VariantsProviderProps["isNestedRender"] =
34
- undefined;
35
34
  export let nonce: VariantsProviderProps["nonce"] = undefined;
36
35
  export let apiHost: VariantsProviderProps["apiHost"] = undefined;
36
+ export let isNestedRender: VariantsProviderProps["isNestedRender"] =
37
+ undefined;
37
38
  export let model: VariantsProviderProps["model"];
38
39
  export let data: VariantsProviderProps["data"] = undefined;
39
40
  export let context: VariantsProviderProps["context"] = undefined;
@@ -83,6 +84,8 @@
83
84
  canTrack: getDefaultCanTrack(canTrack),
84
85
  content: content,
85
86
  });
87
+ let shouldEmitPersonalizationFns =
88
+ TARGET !== "reactNative" && hasPersonalizationContainer(content);
86
89
 
87
90
  onMount(() => {
88
91
  /**
@@ -92,7 +95,7 @@
92
95
  });
93
96
  </script>
94
97
 
95
- {#if !isNestedRender && TARGET !== "reactNative"}
98
+ {#if shouldEmitPersonalizationFns}
96
99
  {#if SDKS_SUPPORTING_PERSONALIZATION.includes(TARGET)}
97
100
  <InlinedScript
98
101
  id="builderio-init-personalization-variants-fns"
@@ -1 +1 @@
1
- export const SDK_VERSION = "5.2.6"
1
+ export const SDK_VERSION = "5.2.8"
@@ -106,6 +106,21 @@ export interface ComponentInfo {
106
106
  };
107
107
  friendlyName?: string;
108
108
 
109
+ /**
110
+ * Group custom components into their own accordion sections in the editor's insert menu.
111
+ * Components with the same `group` value are grouped together; components without a `group`
112
+ * (or with a blank value) fall back to the default "Custom Components" accordion.
113
+ *
114
+ * @example
115
+ * ```js
116
+ * Builder.registerComponent(MyStepComponent, {
117
+ * name: 'Step',
118
+ * group: 'Steps',
119
+ * })
120
+ * ```
121
+ */
122
+ group?: string;
123
+
109
124
  /**
110
125
  * Use to restrict access to your component based on a the current user permissions
111
126
  * By default components will show to all users