@builder.io/sdk-svelte 0.2.0 → 0.2.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/package/--package.json +3 -3
- package/package/CHANGELOG.md +13 -1
- package/package/blocks/symbol/symbol.svelte +2 -1
- package/package/components/render-block/block-styles.svelte +3 -0
- package/package/components/render-block/render-block.helpers.d.ts +1 -0
- package/package/components/render-block/render-block.helpers.js +16 -0
- package/package/components/render-block/render-block.svelte +33 -52
- package/package/components/render-content/render-content.svelte +8 -6
- package/package/components/render-inlined-styles.svelte +3 -3
- package/package/functions/get-block-properties.d.ts +1 -0
- package/package/functions/get-block-properties.js +31 -0
- package/package/functions/get-content/index.d.ts +1 -1
- package/package/functions/get-content/index.js +24 -17
- package/package/functions/track/index.js +2 -1
- package/package/helpers/logger.d.ts +5 -0
- package/package/helpers/logger.js +6 -0
- package/package/types/builder-content.d.ts +1 -1
- package/package/types/components.d.ts +1 -72
- package/package/types/input.d.ts +4 -0
- package/package.json +2 -2
- package/src/blocks/symbol/symbol.svelte +2 -1
- package/src/components/render-block/block-styles.svelte +4 -1
- package/src/components/render-block/render-block.helpers.ts +27 -1
- package/src/components/render-block/render-block.svelte +34 -58
- package/src/components/render-content/render-content.svelte +13 -7
- package/src/components/render-inlined-styles.svelte +3 -3
- package/src/functions/get-block-properties.ts +34 -0
- package/src/functions/get-content/index.ts +28 -22
- package/src/functions/register-component.ts +10 -10
- package/src/functions/track/index.ts +3 -2
- package/src/helpers/logger.ts +6 -0
- package/src/types/builder-content.ts +1 -1
- package/src/types/components.ts +1 -68
- package/src/types/input.ts +5 -0
- package/package/functions/get-block-tag.d.ts +0 -3
- package/package/functions/get-block-tag.js +0 -3
- package/src/functions/get-block-tag.ts +0 -7
package/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": "0.1
|
|
4
|
+
"version": "0.2.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"package",
|
|
@@ -93,7 +93,6 @@
|
|
|
93
93
|
"./functions/get-block-actions": "./functions/get-block-actions.js",
|
|
94
94
|
"./functions/get-block-component-options": "./functions/get-block-component-options.js",
|
|
95
95
|
"./functions/get-block-properties": "./functions/get-block-properties.js",
|
|
96
|
-
"./functions/get-block-tag": "./functions/get-block-tag.js",
|
|
97
96
|
"./functions/get-builder-search-params/fn.test": "./functions/get-builder-search-params/fn.test.js",
|
|
98
97
|
"./functions/get-builder-search-params": "./functions/get-builder-search-params/index.js",
|
|
99
98
|
"./functions/get-content/ab-testing": "./functions/get-content/ab-testing.js",
|
|
@@ -129,6 +128,7 @@
|
|
|
129
128
|
"./helpers/css": "./helpers/css.js",
|
|
130
129
|
"./helpers/flatten": "./helpers/flatten.js",
|
|
131
130
|
"./helpers/localStorage": "./helpers/localStorage.js",
|
|
131
|
+
"./helpers/logger": "./helpers/logger.js",
|
|
132
132
|
"./helpers/nullable": "./helpers/nullable.js",
|
|
133
133
|
"./helpers/sessionId": "./helpers/sessionId.js",
|
|
134
134
|
"./helpers/time": "./helpers/time.js",
|
|
@@ -153,7 +153,7 @@
|
|
|
153
153
|
"types": "./package/index.d.ts",
|
|
154
154
|
"devDependencies": {
|
|
155
155
|
"@sveltejs/adapter-auto": "1.0.0-next.71",
|
|
156
|
-
"@sveltejs/kit": "1.
|
|
156
|
+
"@sveltejs/kit": "^1.15.2",
|
|
157
157
|
"@sveltejs/package": "1.0.0-next.1",
|
|
158
158
|
"svelte": "^3.44.0",
|
|
159
159
|
"svelte-check": "^2.7.1",
|
package/package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
|
+
### 0.2.2
|
|
2
|
+
|
|
3
|
+
- Fix: dynamic bindings for Link URLs.
|
|
4
|
+
- Fix: previewing content that includes a symbol whose `model` property is a `page`.
|
|
5
|
+
- Fix: "Show If"/"Hide If" bindings when the initial value is `undefined`.
|
|
6
|
+
|
|
7
|
+
### 0.2.1
|
|
8
|
+
|
|
9
|
+
- No Changes.
|
|
10
|
+
|
|
1
11
|
### 0.2.0
|
|
12
|
+
|
|
2
13
|
- Sets the default `apiVersion` to `v3`.
|
|
3
14
|
|
|
4
15
|
In case you feel the need to use our older API Version `v2`, reach out to us at support@builder.io first. But you can override the default by setting `apiVersion` explicitly to `v2` as follows:
|
|
@@ -8,8 +19,9 @@ In case you feel the need to use our older API Version `v2`, reach out to us at
|
|
|
8
19
|
```
|
|
9
20
|
|
|
10
21
|
```js
|
|
11
|
-
getContent({ apiVersion:
|
|
22
|
+
getContent({ apiVersion: 'v2' });
|
|
12
23
|
```
|
|
24
|
+
|
|
13
25
|
More details on the Builder API Versions visit [this link](https://www.builder.io/c/docs/content-api-versions).
|
|
14
26
|
|
|
15
27
|
### 0.1.16
|
|
@@ -7,6 +7,7 @@ import { getContent } from "../../functions/get-content/index.js";
|
|
|
7
7
|
import "../../types/builder-content.js";
|
|
8
8
|
import "../../types/builder-block.js";
|
|
9
9
|
import { TARGET } from "../../constants/target";
|
|
10
|
+
import { logger } from "../../helpers/logger";
|
|
10
11
|
const isEvent = (attr) => attr.startsWith("on:");
|
|
11
12
|
const isNonEvent = (attr) => !attr.startsWith("on:");
|
|
12
13
|
const filterAttrs = (attrs = {}, filter) => {
|
|
@@ -66,7 +67,7 @@ function fetchContent() {
|
|
|
66
67
|
}
|
|
67
68
|
})
|
|
68
69
|
.catch((err) => {
|
|
69
|
-
|
|
70
|
+
logger.error("Could not fetch symbol content: ", err);
|
|
70
71
|
});
|
|
71
72
|
}
|
|
72
73
|
}
|
|
@@ -80,3 +80,19 @@ export const getRepeatItemData = ({ block, context, }) => {
|
|
|
80
80
|
}));
|
|
81
81
|
return repeatArray;
|
|
82
82
|
};
|
|
83
|
+
export const getProxyState = (context) => {
|
|
84
|
+
if (typeof Proxy === 'undefined') {
|
|
85
|
+
console.error('no Proxy available in this environment, cannot proxy state.');
|
|
86
|
+
return context.state;
|
|
87
|
+
}
|
|
88
|
+
const useState = new Proxy(context.state, {
|
|
89
|
+
set: (obj, prop, value) => {
|
|
90
|
+
// set the value on the state object, so that the event handler instantly gets the update.
|
|
91
|
+
obj[prop] = value;
|
|
92
|
+
// set the value in the context, so that the rest of the app gets the update.
|
|
93
|
+
context.setState?.(obj);
|
|
94
|
+
return true;
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
return useState;
|
|
98
|
+
};
|
|
@@ -4,18 +4,16 @@
|
|
|
4
4
|
import { getBlockActions } from "../../functions/get-block-actions.js";
|
|
5
5
|
import { getBlockComponentOptions } from "../../functions/get-block-component-options.js";
|
|
6
6
|
import { getBlockProperties } from "../../functions/get-block-properties.js";
|
|
7
|
-
import { getBlockTag } from "../../functions/get-block-tag.js";
|
|
8
7
|
import { getProcessedBlock } from "../../functions/get-processed-block.js";
|
|
9
8
|
import "../../types/builder-block.js";
|
|
10
9
|
import BlockStyles from "./block-styles.svelte";
|
|
11
|
-
import { getComponent, getRepeatItemData, isEmptyHtmlElement, } from "./render-block.helpers.js";
|
|
10
|
+
import { getComponent, getProxyState, getRepeatItemData, isEmptyHtmlElement, } from "./render-block.helpers.js";
|
|
12
11
|
import "./render-component.svelte";
|
|
13
12
|
import RenderRepeatedBlock from "./render-repeated-block.svelte";
|
|
14
13
|
import { TARGET } from "../../constants/target.js";
|
|
15
14
|
import { extractTextStyles } from "../../functions/extract-text-styles.js";
|
|
16
15
|
import RenderComponent from "./render-component.svelte";
|
|
17
16
|
import { getReactNativeBlockStyles } from "../../functions/get-react-native-block-styles.js";
|
|
18
|
-
import { checkIsDefined } from "../../helpers/nullable.js";
|
|
19
17
|
const setAttrs = (node, attrs = {}) => {
|
|
20
18
|
const attrKeys = Object.keys(attrs);
|
|
21
19
|
const setup = (attr) => node.addEventListener(attr.substr(3), attrs[attr]);
|
|
@@ -34,9 +32,6 @@ const setAttrs = (node, attrs = {}) => {
|
|
|
34
32
|
};
|
|
35
33
|
export let block;
|
|
36
34
|
export let context;
|
|
37
|
-
$: tag = () => {
|
|
38
|
-
return getBlockTag(useBlock());
|
|
39
|
-
};
|
|
40
35
|
$: useBlock = () => {
|
|
41
36
|
return repeatItemData
|
|
42
37
|
? block
|
|
@@ -48,34 +43,18 @@ $: useBlock = () => {
|
|
|
48
43
|
});
|
|
49
44
|
};
|
|
50
45
|
$: canShowBlock = () => {
|
|
51
|
-
if (
|
|
46
|
+
if ("hide" in useBlock()) {
|
|
52
47
|
return !useBlock().hide;
|
|
53
48
|
}
|
|
54
|
-
if (
|
|
49
|
+
if ("show" in useBlock()) {
|
|
55
50
|
return useBlock().show;
|
|
56
51
|
}
|
|
57
52
|
return true;
|
|
58
53
|
};
|
|
59
|
-
$: proxyState = () => {
|
|
60
|
-
if (typeof Proxy === "undefined") {
|
|
61
|
-
console.error("no Proxy available in this environment, cannot proxy state.");
|
|
62
|
-
return context.state;
|
|
63
|
-
}
|
|
64
|
-
const useState = new Proxy(context.state, {
|
|
65
|
-
set: (obj, prop, value) => {
|
|
66
|
-
// set the value on the state object, so that the event handler instantly gets the update.
|
|
67
|
-
obj[prop] = value;
|
|
68
|
-
// set the value in the context, so that the rest of the app gets the update.
|
|
69
|
-
context.setState?.(obj);
|
|
70
|
-
return true;
|
|
71
|
-
},
|
|
72
|
-
});
|
|
73
|
-
return useState;
|
|
74
|
-
};
|
|
75
54
|
$: actions = () => {
|
|
76
55
|
return getBlockActions({
|
|
77
56
|
block: useBlock(),
|
|
78
|
-
state: proxyState
|
|
57
|
+
state: TARGET === "qwik" ? context.state : proxyState,
|
|
79
58
|
context: context.context,
|
|
80
59
|
});
|
|
81
60
|
};
|
|
@@ -94,28 +73,6 @@ $: attributes = () => {
|
|
|
94
73
|
: {}),
|
|
95
74
|
};
|
|
96
75
|
};
|
|
97
|
-
$: renderComponentProps = () => {
|
|
98
|
-
return {
|
|
99
|
-
blockChildren: useBlock().children ?? [],
|
|
100
|
-
componentRef: component?.component,
|
|
101
|
-
componentOptions: {
|
|
102
|
-
...getBlockComponentOptions(useBlock()),
|
|
103
|
-
/**
|
|
104
|
-
* These attributes are passed to the wrapper element when there is one. If `noWrap` is set to true, then
|
|
105
|
-
* they are provided to the component itself directly.
|
|
106
|
-
*/
|
|
107
|
-
...(!component?.noWrap
|
|
108
|
-
? {}
|
|
109
|
-
: {
|
|
110
|
-
attributes: {
|
|
111
|
-
...attributes(),
|
|
112
|
-
...actions(),
|
|
113
|
-
},
|
|
114
|
-
}),
|
|
115
|
-
},
|
|
116
|
-
context: childrenContext(),
|
|
117
|
-
};
|
|
118
|
-
};
|
|
119
76
|
$: childrenWithoutParentComponent = () => {
|
|
120
77
|
/**
|
|
121
78
|
* When there is no `componentRef`, there might still be children that need to be rendered. In this case,
|
|
@@ -148,6 +105,28 @@ $: childrenContext = () => {
|
|
|
148
105
|
inheritedStyles: getInheritedTextStyles(),
|
|
149
106
|
};
|
|
150
107
|
};
|
|
108
|
+
$: renderComponentProps = () => {
|
|
109
|
+
return {
|
|
110
|
+
blockChildren: useBlock().children ?? [],
|
|
111
|
+
componentRef: component?.component,
|
|
112
|
+
componentOptions: {
|
|
113
|
+
...getBlockComponentOptions(useBlock()),
|
|
114
|
+
/**
|
|
115
|
+
* These attributes are passed to the wrapper element when there is one. If `noWrap` is set to true, then
|
|
116
|
+
* they are provided to the component itself directly.
|
|
117
|
+
*/
|
|
118
|
+
...(!component?.noWrap
|
|
119
|
+
? {}
|
|
120
|
+
: {
|
|
121
|
+
attributes: {
|
|
122
|
+
...attributes(),
|
|
123
|
+
...actions(),
|
|
124
|
+
},
|
|
125
|
+
}),
|
|
126
|
+
},
|
|
127
|
+
context: childrenContext(),
|
|
128
|
+
};
|
|
129
|
+
};
|
|
151
130
|
let component = getComponent({
|
|
152
131
|
block: block,
|
|
153
132
|
context: context,
|
|
@@ -156,22 +135,24 @@ let repeatItemData = getRepeatItemData({
|
|
|
156
135
|
block: block,
|
|
157
136
|
context: context,
|
|
158
137
|
});
|
|
138
|
+
let tag = block.tagName || "div";
|
|
139
|
+
let proxyState = getProxyState(context);
|
|
159
140
|
</script>
|
|
160
141
|
|
|
161
142
|
{#if canShowBlock()}
|
|
162
143
|
{#if !component?.noWrap}
|
|
163
|
-
{#if isEmptyHtmlElement(tag
|
|
164
|
-
<svelte:element this={tag
|
|
144
|
+
{#if isEmptyHtmlElement(tag)}
|
|
145
|
+
<svelte:element this={tag} {...attributes()} use:setAttrs={actions()} />
|
|
165
146
|
{/if}
|
|
166
147
|
|
|
167
|
-
{#if !isEmptyHtmlElement(tag
|
|
148
|
+
{#if !isEmptyHtmlElement(tag) && repeatItemData}
|
|
168
149
|
{#each repeatItemData as data, index (index)}
|
|
169
150
|
<RenderRepeatedBlock repeatContext={data.context} block={data.block} />
|
|
170
151
|
{/each}
|
|
171
152
|
{/if}
|
|
172
153
|
|
|
173
|
-
{#if !isEmptyHtmlElement(tag
|
|
174
|
-
<svelte:element this={tag
|
|
154
|
+
{#if !isEmptyHtmlElement(tag) && !repeatItemData}
|
|
155
|
+
<svelte:element this={tag} {...attributes()} use:setAttrs={actions()}>
|
|
175
156
|
<RenderComponent {...renderComponentProps()} />
|
|
176
157
|
|
|
177
158
|
{#each childrenWithoutParentComponent() as child ("render-block-" + child.id)}
|
|
@@ -20,6 +20,7 @@ import { getInteractionPropertiesForEvent } from "../../functions/track/interact
|
|
|
20
20
|
import "./render-content.types.js";
|
|
21
21
|
import { getContentInitialValue, getContextStateInitialValue, } from "./render-content.helpers.js";
|
|
22
22
|
import { TARGET } from "../../constants/target.js";
|
|
23
|
+
import { logger } from "../../helpers/logger.js";
|
|
23
24
|
export let content;
|
|
24
25
|
export let data;
|
|
25
26
|
export let canTrack;
|
|
@@ -198,7 +199,7 @@ let httpReqsData = {};
|
|
|
198
199
|
let clicked = false;
|
|
199
200
|
onMount(() => {
|
|
200
201
|
if (!apiKey) {
|
|
201
|
-
|
|
202
|
+
logger.error("No API key provided to `RenderContent` component. This can cause issues. Please provide an API key using the `apiKey` prop.");
|
|
202
203
|
}
|
|
203
204
|
if (isBrowser()) {
|
|
204
205
|
if (isEditing()) {
|
|
@@ -237,20 +238,21 @@ onMount(() => {
|
|
|
237
238
|
// override normal content in preview mode
|
|
238
239
|
if (isPreviewing()) {
|
|
239
240
|
const searchParams = new URL(location.href).searchParams;
|
|
240
|
-
const
|
|
241
|
+
const searchParamPreviewModel = searchParams.get("builder.preview");
|
|
242
|
+
const searchParamPreviewId = searchParams.get(`builder.preview.${searchParamPreviewModel}`);
|
|
241
243
|
const previewApiKey = searchParams.get("apiKey") || searchParams.get("builder.space");
|
|
242
244
|
/**
|
|
243
245
|
* Make sure that:
|
|
244
246
|
* - the preview model name is the same as the one we're rendering, since there can be multiple models rendered
|
|
245
247
|
* at the same time, e.g. header/page/footer.
|
|
246
248
|
* - the API key is the same, since we don't want to preview content from other organizations.
|
|
247
|
-
*
|
|
248
|
-
* TO-DO: should we check that the preview item ID is the same as the initial one being rendered? Or would
|
|
249
|
-
* this break scenarios where the item is not published yet?
|
|
249
|
+
* - if there is content, that the preview ID is the same as that of the one we receive.
|
|
250
250
|
*
|
|
251
251
|
* TO-DO: should we only update the state when there is a change?
|
|
252
252
|
**/
|
|
253
|
-
if (
|
|
253
|
+
if (searchParamPreviewModel === model &&
|
|
254
|
+
previewApiKey === apiKey &&
|
|
255
|
+
(!content || searchParamPreviewId === content.id)) {
|
|
254
256
|
getContent({
|
|
255
257
|
model: model,
|
|
256
258
|
apiKey: apiKey,
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
<script>import { TARGET } from "../constants/target.js";
|
|
4
4
|
export let styles;
|
|
5
|
-
$: injectedStyleScript = () => {
|
|
6
|
-
return `<${tag()}>${styles}</${tag()}>`;
|
|
7
|
-
};
|
|
8
5
|
$: tag = () => {
|
|
9
6
|
// NOTE: we have to obfusctate the name of the tag due to a limitation in the svelte-preprocessor plugin.
|
|
10
7
|
// https://github.com/sveltejs/vite-plugin-svelte/issues/315#issuecomment-1109000027
|
|
11
8
|
return "sty" + "le";
|
|
12
9
|
};
|
|
10
|
+
$: injectedStyleScript = () => {
|
|
11
|
+
return `<${tag()}>${styles}</${tag()}>`;
|
|
12
|
+
};
|
|
13
13
|
</script>
|
|
14
14
|
|
|
15
15
|
{#if TARGET === "svelte" || TARGET === "qwik"}
|
|
@@ -1,8 +1,39 @@
|
|
|
1
1
|
import { TARGET } from '../constants/target.js';
|
|
2
2
|
import { convertStyleMapToCSSArray } from '../helpers/css.js';
|
|
3
3
|
import { transformBlockProperties } from './transform-block-properties.js';
|
|
4
|
+
const extractRelevantRootBlockProperties = (block) => {
|
|
5
|
+
// currently we are only spreading the `href` property
|
|
6
|
+
// TO-DO: potentially spread all properties from block, but only if they are not already defined by BuilderBlock
|
|
7
|
+
// const {
|
|
8
|
+
// '@type': _type,
|
|
9
|
+
// '@version': _version,
|
|
10
|
+
// id: _id,
|
|
11
|
+
// tagName: _tagName,
|
|
12
|
+
// layerName: _layerName,
|
|
13
|
+
// groupLocked: _groupLocked,
|
|
14
|
+
// layerLocked: _layerLocked,
|
|
15
|
+
// class: _class,
|
|
16
|
+
// children: _children,
|
|
17
|
+
// responsiveStyles: _responsiveStyles,
|
|
18
|
+
// component: _component,
|
|
19
|
+
// bindings: _bindings,
|
|
20
|
+
// meta: _meta,
|
|
21
|
+
// actions: _actions,
|
|
22
|
+
// properties: _properties,
|
|
23
|
+
// code: _code,
|
|
24
|
+
// repeat: _repeat,
|
|
25
|
+
// animations: _animations,
|
|
26
|
+
// style: _style,
|
|
27
|
+
// hide: _hide,
|
|
28
|
+
// show: _show,
|
|
29
|
+
// // anything set by dynamic bindings outside of predefined `BuilderBlock` properties
|
|
30
|
+
// ...remainingBlockProperties
|
|
31
|
+
// } = block;
|
|
32
|
+
return { href: block.href };
|
|
33
|
+
};
|
|
4
34
|
export function getBlockProperties(block) {
|
|
5
35
|
const properties = {
|
|
36
|
+
...extractRelevantRootBlockProperties(block),
|
|
6
37
|
...block.properties,
|
|
7
38
|
'builder-id': block.id,
|
|
8
39
|
style: getStyleAttribute(block.style),
|
|
@@ -7,5 +7,5 @@ type ContentResponse = {
|
|
|
7
7
|
status: number;
|
|
8
8
|
message: string;
|
|
9
9
|
};
|
|
10
|
-
export declare function getAllContent(options: GetContentOptions): Promise<ContentResponse>;
|
|
10
|
+
export declare function getAllContent(options: GetContentOptions): Promise<ContentResponse | null>;
|
|
11
11
|
export {};
|
|
@@ -1,33 +1,40 @@
|
|
|
1
|
+
import { logger } from '../../helpers/logger.js';
|
|
1
2
|
import { fetch } from '../get-fetch.js';
|
|
2
3
|
import { handleABTesting } from './ab-testing.js';
|
|
3
4
|
import { generateContentUrl } from './generate-content-url.js';
|
|
4
5
|
export async function getContent(options) {
|
|
5
6
|
const allContent = await getAllContent({ ...options, limit: 1 });
|
|
6
|
-
if ('results' in allContent) {
|
|
7
|
+
if (allContent && 'results' in allContent) {
|
|
7
8
|
return allContent?.results[0] || null;
|
|
8
9
|
}
|
|
9
10
|
return null;
|
|
10
11
|
}
|
|
11
12
|
export async function getAllContent(options) {
|
|
12
|
-
const url = generateContentUrl(options);
|
|
13
|
-
const res = await fetch(url.href);
|
|
14
|
-
const content = await res.json();
|
|
15
|
-
if ('status' in content && !('results' in content)) {
|
|
16
|
-
console.error('[Builder.io]: Error fetching data. ', content, options);
|
|
17
|
-
return content;
|
|
18
|
-
}
|
|
19
|
-
const canTrack = options.canTrack !== false;
|
|
20
13
|
try {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
14
|
+
const url = generateContentUrl(options);
|
|
15
|
+
const res = await fetch(url.href);
|
|
16
|
+
const content = await res.json();
|
|
17
|
+
if ('status' in content && !('results' in content)) {
|
|
18
|
+
logger.error('Error fetching data. ', content, options);
|
|
19
|
+
return content;
|
|
20
|
+
}
|
|
21
|
+
const canTrack = options.canTrack !== false;
|
|
22
|
+
try {
|
|
23
|
+
if (canTrack &&
|
|
24
|
+
// This makes sure we have a non-error response with the results array.
|
|
25
|
+
Array.isArray(content.results)) {
|
|
26
|
+
for (const item of content.results) {
|
|
27
|
+
await handleABTesting({ item, canTrack });
|
|
28
|
+
}
|
|
26
29
|
}
|
|
27
30
|
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
logger.error('Could not setup A/B testing. ', e);
|
|
33
|
+
}
|
|
34
|
+
return content;
|
|
28
35
|
}
|
|
29
|
-
catch (
|
|
30
|
-
|
|
36
|
+
catch (error) {
|
|
37
|
+
logger.error('Error fetching data. ', error);
|
|
38
|
+
return null;
|
|
31
39
|
}
|
|
32
|
-
return content;
|
|
33
40
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { TARGET } from '../../constants/target.js';
|
|
2
|
+
import { logger } from '../../helpers/logger.js';
|
|
2
3
|
import { getSessionId } from '../../helpers/sessionId.js';
|
|
3
4
|
import { getVisitorId } from '../../helpers/visitorId.js';
|
|
4
5
|
import { isBrowser } from '../is-browser.js';
|
|
@@ -30,7 +31,7 @@ const createEvent = async ({ type: eventType, canTrack, apiKey, metadata, ...pro
|
|
|
30
31
|
});
|
|
31
32
|
export async function _track(eventProps) {
|
|
32
33
|
if (!eventProps.apiKey) {
|
|
33
|
-
|
|
34
|
+
logger.error('Missing API key for track call. Please provide your API key.');
|
|
34
35
|
return;
|
|
35
36
|
}
|
|
36
37
|
if (!eventProps.canTrack) {
|
|
@@ -1,76 +1,5 @@
|
|
|
1
1
|
import type { BuilderElement } from './element';
|
|
2
|
-
|
|
3
|
-
name: string;
|
|
4
|
-
friendlyName?: string;
|
|
5
|
-
description?: string;
|
|
6
|
-
defaultValue?: any;
|
|
7
|
-
type: string;
|
|
8
|
-
valueType?: {
|
|
9
|
-
type?: string;
|
|
10
|
-
};
|
|
11
|
-
required?: boolean;
|
|
12
|
-
autoFocus?: boolean;
|
|
13
|
-
subFields?: Input[];
|
|
14
|
-
helperText?: string;
|
|
15
|
-
allowedFileTypes?: string[];
|
|
16
|
-
imageHeight?: number;
|
|
17
|
-
imageWidth?: number;
|
|
18
|
-
mediaHeight?: number;
|
|
19
|
-
mediaWidth?: number;
|
|
20
|
-
hideFromUI?: boolean;
|
|
21
|
-
modelId?: string;
|
|
22
|
-
/**
|
|
23
|
-
* Number field type validation maximum accepted input
|
|
24
|
-
*/
|
|
25
|
-
max?: number;
|
|
26
|
-
/**
|
|
27
|
-
* Number field type validation minimum accepted input
|
|
28
|
-
*/
|
|
29
|
-
min?: number;
|
|
30
|
-
/**
|
|
31
|
-
* Number field type step size when using arrows
|
|
32
|
-
*/
|
|
33
|
-
step?: number;
|
|
34
|
-
/**
|
|
35
|
-
* Set this to `true` to show the editor for this input when
|
|
36
|
-
* children of this component are selected. This is useful for things
|
|
37
|
-
* like Tabs, such that users may not always select the Tabs component
|
|
38
|
-
* directly but will still be looking for how to add additional tabs
|
|
39
|
-
*/
|
|
40
|
-
broadcast?: boolean;
|
|
41
|
-
/**
|
|
42
|
-
* Set this to `true` to show the editor for this input when
|
|
43
|
-
* group locked parents of this component are selected. This is useful
|
|
44
|
-
* to bubble up important inputs for locked groups, like text and images
|
|
45
|
-
*/
|
|
46
|
-
bubble?: boolean;
|
|
47
|
-
options?: {
|
|
48
|
-
[key: string]: any;
|
|
49
|
-
};
|
|
50
|
-
enum?: string[] | {
|
|
51
|
-
label: string;
|
|
52
|
-
value: any;
|
|
53
|
-
helperText?: string;
|
|
54
|
-
}[];
|
|
55
|
-
/** Regex field validation for all string types (text, longText, html, url, etc) */
|
|
56
|
-
regex?: {
|
|
57
|
-
/** pattern to test, like "^\/[a-z]$" */
|
|
58
|
-
pattern: string;
|
|
59
|
-
/** flags for the RegExp constructor, e.g. "gi" */
|
|
60
|
-
options?: string;
|
|
61
|
-
/**
|
|
62
|
-
* Friendly message to display to end-users if the regex fails, e.g.
|
|
63
|
-
* "You must use a relative url starting with '/...' "
|
|
64
|
-
*/
|
|
65
|
-
message: string;
|
|
66
|
-
};
|
|
67
|
-
advanced?: boolean;
|
|
68
|
-
onChange?: ((options: Map<string, any>) => void | Promise<void>) | string;
|
|
69
|
-
code?: boolean;
|
|
70
|
-
richText?: boolean;
|
|
71
|
-
showIf?: ((options: Map<string, any>) => boolean) | string;
|
|
72
|
-
copyOnAdd?: boolean;
|
|
73
|
-
}
|
|
2
|
+
import type { Input } from './input';
|
|
74
3
|
export interface ComponentInfo {
|
|
75
4
|
/**
|
|
76
5
|
* Name your component something unique, e.g. 'MyButton'. You can override built-in components
|
package/package/types/input.d.ts
CHANGED
|
@@ -114,4 +114,8 @@ export interface Input {
|
|
|
114
114
|
* Use optionally with inputs of type `reference`. Restricts the content entry picker to a specific model by name.
|
|
115
115
|
*/
|
|
116
116
|
model?: string;
|
|
117
|
+
valueType?: {
|
|
118
|
+
type?: string;
|
|
119
|
+
};
|
|
120
|
+
onChange?: ((options: Map<string, any>) => void | Promise<void>) | string;
|
|
117
121
|
}
|
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": "0.2.
|
|
4
|
+
"version": "0.2.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"package",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@sveltejs/adapter-auto": "1.0.0-next.71",
|
|
42
|
-
"@sveltejs/kit": "1.
|
|
42
|
+
"@sveltejs/kit": "^1.15.2",
|
|
43
43
|
"@sveltejs/package": "1.0.0-next.1",
|
|
44
44
|
"svelte": "^3.44.0",
|
|
45
45
|
"svelte-check": "^2.7.1",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
import { BuilderContent } from "../../types/builder-content.js";
|
|
28
28
|
import { BuilderBlock } from "../../types/builder-block.js";
|
|
29
29
|
import { TARGET } from "../../constants/target";
|
|
30
|
+
import { logger } from "../../helpers/logger";
|
|
30
31
|
|
|
31
32
|
const isEvent = (attr) => attr.startsWith("on:");
|
|
32
33
|
const isNonEvent = (attr) => !attr.startsWith("on:");
|
|
@@ -93,7 +94,7 @@
|
|
|
93
94
|
}
|
|
94
95
|
})
|
|
95
96
|
.catch((err) => {
|
|
96
|
-
|
|
97
|
+
logger.error("Could not fetch symbol content: ", err);
|
|
97
98
|
});
|
|
98
99
|
}
|
|
99
100
|
}
|
|
@@ -48,7 +48,10 @@
|
|
|
48
48
|
const largeStyles = styles?.large;
|
|
49
49
|
const mediumStyles = styles?.medium;
|
|
50
50
|
const smallStyles = styles?.small;
|
|
51
|
-
const className = useBlock().id
|
|
51
|
+
const className = useBlock().id;
|
|
52
|
+
if (!className) {
|
|
53
|
+
return "";
|
|
54
|
+
}
|
|
52
55
|
const largeStylesClass = largeStyles
|
|
53
56
|
? createCssClass({
|
|
54
57
|
className,
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
BuilderContextInterface,
|
|
3
|
+
BuilderRenderState,
|
|
4
|
+
} from '../../context/types';
|
|
2
5
|
import { evaluate } from '../../functions/evaluate';
|
|
3
6
|
import { getProcessedBlock } from '../../functions/get-processed-block';
|
|
4
7
|
import type { BuilderBlock } from '../../types/builder-block';
|
|
@@ -110,3 +113,26 @@ export const getRepeatItemData = ({
|
|
|
110
113
|
|
|
111
114
|
return repeatArray;
|
|
112
115
|
};
|
|
116
|
+
|
|
117
|
+
export const getProxyState = (
|
|
118
|
+
context: BuilderContextInterface
|
|
119
|
+
): BuilderContextInterface['state'] => {
|
|
120
|
+
if (typeof Proxy === 'undefined') {
|
|
121
|
+
console.error(
|
|
122
|
+
'no Proxy available in this environment, cannot proxy state.'
|
|
123
|
+
);
|
|
124
|
+
return context.state;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const useState = new Proxy(context.state, {
|
|
128
|
+
set: (obj, prop: keyof BuilderRenderState, value) => {
|
|
129
|
+
// set the value on the state object, so that the event handler instantly gets the update.
|
|
130
|
+
obj[prop] = value;
|
|
131
|
+
|
|
132
|
+
// set the value in the context, so that the rest of the app gets the update.
|
|
133
|
+
context.setState?.(obj);
|
|
134
|
+
return true;
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
return useState;
|
|
138
|
+
};
|
|
@@ -6,19 +6,16 @@
|
|
|
6
6
|
</script>
|
|
7
7
|
|
|
8
8
|
<script lang="ts">
|
|
9
|
-
import {
|
|
10
|
-
BuilderContextInterface,
|
|
11
|
-
BuilderRenderState,
|
|
12
|
-
} from "../../context/types.js";
|
|
9
|
+
import { BuilderContextInterface } from "../../context/types.js";
|
|
13
10
|
import { getBlockActions } from "../../functions/get-block-actions.js";
|
|
14
11
|
import { getBlockComponentOptions } from "../../functions/get-block-component-options.js";
|
|
15
12
|
import { getBlockProperties } from "../../functions/get-block-properties.js";
|
|
16
|
-
import { getBlockTag } from "../../functions/get-block-tag.js";
|
|
17
13
|
import { getProcessedBlock } from "../../functions/get-processed-block.js";
|
|
18
14
|
import { BuilderBlock } from "../../types/builder-block.js";
|
|
19
15
|
import BlockStyles from "./block-styles.svelte";
|
|
20
16
|
import {
|
|
21
17
|
getComponent,
|
|
18
|
+
getProxyState,
|
|
22
19
|
getRepeatItemData,
|
|
23
20
|
isEmptyHtmlElement,
|
|
24
21
|
} from "./render-block.helpers.js";
|
|
@@ -28,7 +25,6 @@
|
|
|
28
25
|
import { extractTextStyles } from "../../functions/extract-text-styles.js";
|
|
29
26
|
import RenderComponent from "./render-component.svelte";
|
|
30
27
|
import { getReactNativeBlockStyles } from "../../functions/get-react-native-block-styles.js";
|
|
31
|
-
import { checkIsDefined } from "../../helpers/nullable.js";
|
|
32
28
|
|
|
33
29
|
const setAttrs = (node, attrs = {}) => {
|
|
34
30
|
const attrKeys = Object.keys(attrs);
|
|
@@ -51,9 +47,6 @@
|
|
|
51
47
|
export let block: RenderBlockProps["block"];
|
|
52
48
|
export let context: RenderBlockProps["context"];
|
|
53
49
|
|
|
54
|
-
$: tag = () => {
|
|
55
|
-
return getBlockTag(useBlock());
|
|
56
|
-
};
|
|
57
50
|
$: useBlock = () => {
|
|
58
51
|
return repeatItemData
|
|
59
52
|
? block
|
|
@@ -65,37 +58,18 @@
|
|
|
65
58
|
});
|
|
66
59
|
};
|
|
67
60
|
$: canShowBlock = () => {
|
|
68
|
-
if (
|
|
61
|
+
if ("hide" in useBlock()) {
|
|
69
62
|
return !useBlock().hide;
|
|
70
63
|
}
|
|
71
|
-
if (
|
|
64
|
+
if ("show" in useBlock()) {
|
|
72
65
|
return useBlock().show;
|
|
73
66
|
}
|
|
74
67
|
return true;
|
|
75
68
|
};
|
|
76
|
-
$: proxyState = () => {
|
|
77
|
-
if (typeof Proxy === "undefined") {
|
|
78
|
-
console.error(
|
|
79
|
-
"no Proxy available in this environment, cannot proxy state."
|
|
80
|
-
);
|
|
81
|
-
return context.state;
|
|
82
|
-
}
|
|
83
|
-
const useState = new Proxy(context.state, {
|
|
84
|
-
set: (obj, prop: keyof BuilderRenderState, value) => {
|
|
85
|
-
// set the value on the state object, so that the event handler instantly gets the update.
|
|
86
|
-
obj[prop] = value;
|
|
87
|
-
|
|
88
|
-
// set the value in the context, so that the rest of the app gets the update.
|
|
89
|
-
context.setState?.(obj);
|
|
90
|
-
return true;
|
|
91
|
-
},
|
|
92
|
-
});
|
|
93
|
-
return useState;
|
|
94
|
-
};
|
|
95
69
|
$: actions = () => {
|
|
96
70
|
return getBlockActions({
|
|
97
71
|
block: useBlock(),
|
|
98
|
-
state: proxyState
|
|
72
|
+
state: TARGET === "qwik" ? context.state : proxyState,
|
|
99
73
|
context: context.context,
|
|
100
74
|
});
|
|
101
75
|
};
|
|
@@ -114,28 +88,6 @@
|
|
|
114
88
|
: {}),
|
|
115
89
|
};
|
|
116
90
|
};
|
|
117
|
-
$: renderComponentProps = () => {
|
|
118
|
-
return {
|
|
119
|
-
blockChildren: useBlock().children ?? [],
|
|
120
|
-
componentRef: component?.component,
|
|
121
|
-
componentOptions: {
|
|
122
|
-
...getBlockComponentOptions(useBlock()),
|
|
123
|
-
/**
|
|
124
|
-
* These attributes are passed to the wrapper element when there is one. If `noWrap` is set to true, then
|
|
125
|
-
* they are provided to the component itself directly.
|
|
126
|
-
*/
|
|
127
|
-
...(!component?.noWrap
|
|
128
|
-
? {}
|
|
129
|
-
: {
|
|
130
|
-
attributes: {
|
|
131
|
-
...attributes(),
|
|
132
|
-
...actions(),
|
|
133
|
-
},
|
|
134
|
-
}),
|
|
135
|
-
},
|
|
136
|
-
context: childrenContext(),
|
|
137
|
-
};
|
|
138
|
-
};
|
|
139
91
|
$: childrenWithoutParentComponent = () => {
|
|
140
92
|
/**
|
|
141
93
|
* When there is no `componentRef`, there might still be children that need to be rendered. In this case,
|
|
@@ -171,6 +123,28 @@
|
|
|
171
123
|
inheritedStyles: getInheritedTextStyles(),
|
|
172
124
|
};
|
|
173
125
|
};
|
|
126
|
+
$: renderComponentProps = () => {
|
|
127
|
+
return {
|
|
128
|
+
blockChildren: useBlock().children ?? [],
|
|
129
|
+
componentRef: component?.component,
|
|
130
|
+
componentOptions: {
|
|
131
|
+
...getBlockComponentOptions(useBlock()),
|
|
132
|
+
/**
|
|
133
|
+
* These attributes are passed to the wrapper element when there is one. If `noWrap` is set to true, then
|
|
134
|
+
* they are provided to the component itself directly.
|
|
135
|
+
*/
|
|
136
|
+
...(!component?.noWrap
|
|
137
|
+
? {}
|
|
138
|
+
: {
|
|
139
|
+
attributes: {
|
|
140
|
+
...attributes(),
|
|
141
|
+
...actions(),
|
|
142
|
+
},
|
|
143
|
+
}),
|
|
144
|
+
},
|
|
145
|
+
context: childrenContext(),
|
|
146
|
+
};
|
|
147
|
+
};
|
|
174
148
|
|
|
175
149
|
let component = getComponent({
|
|
176
150
|
block: block,
|
|
@@ -180,22 +154,24 @@
|
|
|
180
154
|
block: block,
|
|
181
155
|
context: context,
|
|
182
156
|
});
|
|
157
|
+
let tag = block.tagName || "div";
|
|
158
|
+
let proxyState = getProxyState(context);
|
|
183
159
|
</script>
|
|
184
160
|
|
|
185
161
|
{#if canShowBlock()}
|
|
186
162
|
{#if !component?.noWrap}
|
|
187
|
-
{#if isEmptyHtmlElement(tag
|
|
188
|
-
<svelte:element this={tag
|
|
163
|
+
{#if isEmptyHtmlElement(tag)}
|
|
164
|
+
<svelte:element this={tag} {...attributes()} use:setAttrs={actions()} />
|
|
189
165
|
{/if}
|
|
190
166
|
|
|
191
|
-
{#if !isEmptyHtmlElement(tag
|
|
167
|
+
{#if !isEmptyHtmlElement(tag) && repeatItemData}
|
|
192
168
|
{#each repeatItemData as data, index (index)}
|
|
193
169
|
<RenderRepeatedBlock repeatContext={data.context} block={data.block} />
|
|
194
170
|
{/each}
|
|
195
171
|
{/if}
|
|
196
172
|
|
|
197
|
-
{#if !isEmptyHtmlElement(tag
|
|
198
|
-
<svelte:element this={tag
|
|
173
|
+
{#if !isEmptyHtmlElement(tag) && !repeatItemData}
|
|
174
|
+
<svelte:element this={tag} {...attributes()} use:setAttrs={actions()}>
|
|
199
175
|
<RenderComponent {...renderComponentProps()} />
|
|
200
176
|
|
|
201
177
|
{#each childrenWithoutParentComponent() as child ("render-block-" + child.id)}
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
getContextStateInitialValue,
|
|
39
39
|
} from "./render-content.helpers.js";
|
|
40
40
|
import { TARGET } from "../../constants/target.js";
|
|
41
|
+
import { logger } from "../../helpers/logger.js";
|
|
41
42
|
|
|
42
43
|
export let content: RenderContentProps["content"];
|
|
43
44
|
export let data: RenderContentProps["data"];
|
|
@@ -236,8 +237,8 @@
|
|
|
236
237
|
|
|
237
238
|
onMount(() => {
|
|
238
239
|
if (!apiKey) {
|
|
239
|
-
|
|
240
|
-
"
|
|
240
|
+
logger.error(
|
|
241
|
+
"No API key provided to `RenderContent` component. This can cause issues. Please provide an API key using the `apiKey` prop."
|
|
241
242
|
);
|
|
242
243
|
}
|
|
243
244
|
if (isBrowser()) {
|
|
@@ -283,7 +284,10 @@
|
|
|
283
284
|
// override normal content in preview mode
|
|
284
285
|
if (isPreviewing()) {
|
|
285
286
|
const searchParams = new URL(location.href).searchParams;
|
|
286
|
-
const
|
|
287
|
+
const searchParamPreviewModel = searchParams.get("builder.preview");
|
|
288
|
+
const searchParamPreviewId = searchParams.get(
|
|
289
|
+
`builder.preview.${searchParamPreviewModel}`
|
|
290
|
+
);
|
|
287
291
|
const previewApiKey =
|
|
288
292
|
searchParams.get("apiKey") || searchParams.get("builder.space");
|
|
289
293
|
|
|
@@ -292,13 +296,15 @@
|
|
|
292
296
|
* - the preview model name is the same as the one we're rendering, since there can be multiple models rendered
|
|
293
297
|
* at the same time, e.g. header/page/footer.
|
|
294
298
|
* - the API key is the same, since we don't want to preview content from other organizations.
|
|
295
|
-
*
|
|
296
|
-
* TO-DO: should we check that the preview item ID is the same as the initial one being rendered? Or would
|
|
297
|
-
* this break scenarios where the item is not published yet?
|
|
299
|
+
* - if there is content, that the preview ID is the same as that of the one we receive.
|
|
298
300
|
*
|
|
299
301
|
* TO-DO: should we only update the state when there is a change?
|
|
300
302
|
**/
|
|
301
|
-
if (
|
|
303
|
+
if (
|
|
304
|
+
searchParamPreviewModel === model &&
|
|
305
|
+
previewApiKey === apiKey &&
|
|
306
|
+
(!content || searchParamPreviewId === content.id)
|
|
307
|
+
) {
|
|
302
308
|
getContent({
|
|
303
309
|
model: model,
|
|
304
310
|
apiKey: apiKey,
|
|
@@ -9,14 +9,14 @@
|
|
|
9
9
|
|
|
10
10
|
export let styles: Props["styles"];
|
|
11
11
|
|
|
12
|
-
$: injectedStyleScript = () => {
|
|
13
|
-
return `<${tag()}>${styles}</${tag()}>`;
|
|
14
|
-
};
|
|
15
12
|
$: tag = () => {
|
|
16
13
|
// NOTE: we have to obfusctate the name of the tag due to a limitation in the svelte-preprocessor plugin.
|
|
17
14
|
// https://github.com/sveltejs/vite-plugin-svelte/issues/315#issuecomment-1109000027
|
|
18
15
|
return "sty" + "le";
|
|
19
16
|
};
|
|
17
|
+
$: injectedStyleScript = () => {
|
|
18
|
+
return `<${tag()}>${styles}</${tag()}>`;
|
|
19
|
+
};
|
|
20
20
|
</script>
|
|
21
21
|
|
|
22
22
|
{#if TARGET === "svelte" || TARGET === "qwik"}
|
|
@@ -3,8 +3,42 @@ import { convertStyleMapToCSSArray } from '../helpers/css.js';
|
|
|
3
3
|
import type { BuilderBlock } from '../types/builder-block.js';
|
|
4
4
|
import { transformBlockProperties } from './transform-block-properties.js';
|
|
5
5
|
|
|
6
|
+
const extractRelevantRootBlockProperties = (block: BuilderBlock) => {
|
|
7
|
+
// currently we are only spreading the `href` property
|
|
8
|
+
// TO-DO: potentially spread all properties from block, but only if they are not already defined by BuilderBlock
|
|
9
|
+
|
|
10
|
+
// const {
|
|
11
|
+
// '@type': _type,
|
|
12
|
+
// '@version': _version,
|
|
13
|
+
// id: _id,
|
|
14
|
+
// tagName: _tagName,
|
|
15
|
+
// layerName: _layerName,
|
|
16
|
+
// groupLocked: _groupLocked,
|
|
17
|
+
// layerLocked: _layerLocked,
|
|
18
|
+
// class: _class,
|
|
19
|
+
// children: _children,
|
|
20
|
+
// responsiveStyles: _responsiveStyles,
|
|
21
|
+
// component: _component,
|
|
22
|
+
// bindings: _bindings,
|
|
23
|
+
// meta: _meta,
|
|
24
|
+
// actions: _actions,
|
|
25
|
+
// properties: _properties,
|
|
26
|
+
// code: _code,
|
|
27
|
+
// repeat: _repeat,
|
|
28
|
+
// animations: _animations,
|
|
29
|
+
// style: _style,
|
|
30
|
+
// hide: _hide,
|
|
31
|
+
// show: _show,
|
|
32
|
+
// // anything set by dynamic bindings outside of predefined `BuilderBlock` properties
|
|
33
|
+
// ...remainingBlockProperties
|
|
34
|
+
// } = block;
|
|
35
|
+
|
|
36
|
+
return { href: (block as any).href };
|
|
37
|
+
};
|
|
38
|
+
|
|
6
39
|
export function getBlockProperties(block: BuilderBlock) {
|
|
7
40
|
const properties = {
|
|
41
|
+
...extractRelevantRootBlockProperties(block),
|
|
8
42
|
...block.properties,
|
|
9
43
|
'builder-id': block.id,
|
|
10
44
|
style: getStyleAttribute(block.style),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { logger } from '../../helpers/logger.js';
|
|
1
2
|
import type { BuilderContent } from '../../types/builder-content.js';
|
|
2
3
|
import { fetch } from '../get-fetch.js';
|
|
3
4
|
import { handleABTesting } from './ab-testing.js';
|
|
@@ -8,7 +9,7 @@ export async function getContent(
|
|
|
8
9
|
options: GetContentOptions
|
|
9
10
|
): Promise<BuilderContent | null> {
|
|
10
11
|
const allContent = await getAllContent({ ...options, limit: 1 });
|
|
11
|
-
if ('results' in allContent) {
|
|
12
|
+
if (allContent && 'results' in allContent) {
|
|
12
13
|
return allContent?.results[0] || null;
|
|
13
14
|
}
|
|
14
15
|
|
|
@@ -26,31 +27,36 @@ type ContentResponse =
|
|
|
26
27
|
|
|
27
28
|
export async function getAllContent(
|
|
28
29
|
options: GetContentOptions
|
|
29
|
-
): Promise<ContentResponse> {
|
|
30
|
-
|
|
30
|
+
): Promise<ContentResponse | null> {
|
|
31
|
+
try {
|
|
32
|
+
const url = generateContentUrl(options);
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
const res = await fetch(url.href);
|
|
35
|
+
const content = await (res.json() as Promise<ContentResponse>);
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
if ('status' in content && !('results' in content)) {
|
|
38
|
+
logger.error('Error fetching data. ', content, options);
|
|
39
|
+
return content;
|
|
40
|
+
}
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
const canTrack = options.canTrack !== false;
|
|
43
|
+
try {
|
|
44
|
+
if (
|
|
45
|
+
canTrack &&
|
|
46
|
+
// This makes sure we have a non-error response with the results array.
|
|
47
|
+
Array.isArray(content.results)
|
|
48
|
+
) {
|
|
49
|
+
for (const item of content.results) {
|
|
50
|
+
await handleABTesting({ item, canTrack });
|
|
51
|
+
}
|
|
49
52
|
}
|
|
53
|
+
} catch (e) {
|
|
54
|
+
logger.error('Could not setup A/B testing. ', e);
|
|
50
55
|
}
|
|
51
|
-
} catch (e) {
|
|
52
|
-
console.error('[Builder.io]: Could not setup A/B testing. ', e);
|
|
53
|
-
}
|
|
54
56
|
|
|
55
|
-
|
|
57
|
+
return content;
|
|
58
|
+
} catch (error) {
|
|
59
|
+
logger.error('Error fetching data. ', error);
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
56
62
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { serializeFn } from '../blocks/util.js';
|
|
2
2
|
import type { RegisteredComponent } from '../context/types.js';
|
|
3
|
-
import type { ComponentInfo
|
|
3
|
+
import type { ComponentInfo } from '../types/components.js';
|
|
4
|
+
import type { Input } from '../types/input.js';
|
|
4
5
|
import { fastClone } from './fast-clone.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -37,14 +38,13 @@ const prepareComponentInfoToSend = ({
|
|
|
37
38
|
...info
|
|
38
39
|
}: ComponentInfo): ComponentInfo => ({
|
|
39
40
|
...fastClone(info),
|
|
40
|
-
inputs: inputs?.map(
|
|
41
|
-
(input)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
)
|
|
41
|
+
inputs: inputs?.map((input) =>
|
|
42
|
+
Object.entries(input).reduce(
|
|
43
|
+
(acc, [key, value]) => ({
|
|
44
|
+
...acc,
|
|
45
|
+
[key]: serializeValue(value),
|
|
46
|
+
}),
|
|
47
|
+
{} as Input
|
|
48
|
+
)
|
|
49
49
|
),
|
|
50
50
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { TARGET } from '../../constants/target.js';
|
|
2
|
+
import { logger } from '../../helpers/logger.js';
|
|
2
3
|
import { getSessionId } from '../../helpers/sessionId.js';
|
|
3
4
|
import { getVisitorId } from '../../helpers/visitorId.js';
|
|
4
5
|
import type { CanTrack } from '../../types/can-track.js';
|
|
@@ -100,8 +101,8 @@ const createEvent = async ({
|
|
|
100
101
|
|
|
101
102
|
export async function _track(eventProps: EventProps) {
|
|
102
103
|
if (!eventProps.apiKey) {
|
|
103
|
-
|
|
104
|
-
'
|
|
104
|
+
logger.error(
|
|
105
|
+
'Missing API key for track call. Please provide your API key.'
|
|
105
106
|
);
|
|
106
107
|
return;
|
|
107
108
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
const MSG_PREFIX = '[Builder.io]: ';
|
|
2
|
+
export const logger = {
|
|
3
|
+
log: (...message: any[]) => console.log(MSG_PREFIX, ...message),
|
|
4
|
+
error: (...message: any[]) => console.error(MSG_PREFIX, ...message),
|
|
5
|
+
warn: (...message: any[]) => console.warn(MSG_PREFIX, ...message),
|
|
6
|
+
};
|
package/src/types/components.ts
CHANGED
|
@@ -1,72 +1,5 @@
|
|
|
1
1
|
import type { BuilderElement } from './element';
|
|
2
|
-
|
|
3
|
-
export interface Input {
|
|
4
|
-
name: string;
|
|
5
|
-
friendlyName?: string;
|
|
6
|
-
description?: string;
|
|
7
|
-
defaultValue?: any;
|
|
8
|
-
type: string;
|
|
9
|
-
valueType?: {
|
|
10
|
-
type?: string;
|
|
11
|
-
};
|
|
12
|
-
required?: boolean;
|
|
13
|
-
autoFocus?: boolean;
|
|
14
|
-
subFields?: Input[];
|
|
15
|
-
helperText?: string;
|
|
16
|
-
allowedFileTypes?: string[];
|
|
17
|
-
imageHeight?: number;
|
|
18
|
-
imageWidth?: number;
|
|
19
|
-
mediaHeight?: number;
|
|
20
|
-
mediaWidth?: number;
|
|
21
|
-
hideFromUI?: boolean;
|
|
22
|
-
modelId?: string;
|
|
23
|
-
/**
|
|
24
|
-
* Number field type validation maximum accepted input
|
|
25
|
-
*/
|
|
26
|
-
max?: number;
|
|
27
|
-
/**
|
|
28
|
-
* Number field type validation minimum accepted input
|
|
29
|
-
*/
|
|
30
|
-
min?: number;
|
|
31
|
-
/**
|
|
32
|
-
* Number field type step size when using arrows
|
|
33
|
-
*/
|
|
34
|
-
step?: number;
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Set this to `true` to show the editor for this input when
|
|
38
|
-
* children of this component are selected. This is useful for things
|
|
39
|
-
* like Tabs, such that users may not always select the Tabs component
|
|
40
|
-
* directly but will still be looking for how to add additional tabs
|
|
41
|
-
*/
|
|
42
|
-
broadcast?: boolean;
|
|
43
|
-
/**
|
|
44
|
-
* Set this to `true` to show the editor for this input when
|
|
45
|
-
* group locked parents of this component are selected. This is useful
|
|
46
|
-
* to bubble up important inputs for locked groups, like text and images
|
|
47
|
-
*/
|
|
48
|
-
bubble?: boolean;
|
|
49
|
-
options?: { [key: string]: any };
|
|
50
|
-
enum?: string[] | { label: string; value: any; helperText?: string }[];
|
|
51
|
-
/** Regex field validation for all string types (text, longText, html, url, etc) */
|
|
52
|
-
regex?: {
|
|
53
|
-
/** pattern to test, like "^\/[a-z]$" */
|
|
54
|
-
pattern: string;
|
|
55
|
-
/** flags for the RegExp constructor, e.g. "gi" */
|
|
56
|
-
options?: string;
|
|
57
|
-
/**
|
|
58
|
-
* Friendly message to display to end-users if the regex fails, e.g.
|
|
59
|
-
* "You must use a relative url starting with '/...' "
|
|
60
|
-
*/
|
|
61
|
-
message: string;
|
|
62
|
-
};
|
|
63
|
-
advanced?: boolean;
|
|
64
|
-
onChange?: ((options: Map<string, any>) => void | Promise<void>) | string;
|
|
65
|
-
code?: boolean;
|
|
66
|
-
richText?: boolean;
|
|
67
|
-
showIf?: ((options: Map<string, any>) => boolean) | string;
|
|
68
|
-
copyOnAdd?: boolean;
|
|
69
|
-
}
|
|
2
|
+
import type { Input } from './input';
|
|
70
3
|
|
|
71
4
|
export interface ComponentInfo {
|
|
72
5
|
/**
|
package/src/types/input.ts
CHANGED
|
@@ -117,4 +117,9 @@ export interface Input {
|
|
|
117
117
|
* Use optionally with inputs of type `reference`. Restricts the content entry picker to a specific model by name.
|
|
118
118
|
*/
|
|
119
119
|
model?: string;
|
|
120
|
+
|
|
121
|
+
valueType?: {
|
|
122
|
+
type?: string;
|
|
123
|
+
};
|
|
124
|
+
onChange?: ((options: Map<string, any>) => void | Promise<void>) | string;
|
|
120
125
|
}
|