@builder.io/sdk-solid 0.0.8-15 → 0.0.8-18
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.json +1 -1
- package/src/blocks/columns/component-info.js +25 -1
- package/src/blocks/custom-code/custom-code.jsx +2 -2
- package/src/blocks/embed/component-info.js +20 -1
- package/src/blocks/embed/embed.jsx +30 -33
- package/src/blocks/embed/helpers.js +9 -0
- package/src/blocks/form/form.jsx +2 -2
- package/src/blocks/image/component-info.js +47 -1
- package/src/blocks/image/image.helpers.js +48 -0
- package/src/blocks/image/image.jsx +40 -2
- package/src/blocks/symbol/symbol.jsx +30 -2
- package/src/blocks/video/video.jsx +1 -1
- package/src/components/render-block/render-block.jsx +21 -22
- package/src/components/render-block/types.js +0 -0
- package/src/components/render-content/render-content.jsx +30 -10
- package/src/constants/builder-registered-components.js +4 -1
- package/src/functions/get-builder-search-params/index.js +12 -1
- package/src/functions/get-content/ab-testing.js +38 -0
- package/src/functions/get-content/index.js +16 -58
- package/src/functions/get-content/types.js +0 -0
- package/src/functions/register-component.js +20 -18
- package/src/helpers/flatten.js +34 -0
- package/src/functions/fast-clone.js +0 -4
- package/src/functions/previewing-model-name.js +0 -11
package/package.json
CHANGED
|
@@ -186,7 +186,31 @@ const componentInfo = {
|
|
|
186
186
|
]
|
|
187
187
|
}
|
|
188
188
|
],
|
|
189
|
-
onChange
|
|
189
|
+
onChange(options) {
|
|
190
|
+
function clearWidths() {
|
|
191
|
+
columns.forEach((col) => {
|
|
192
|
+
col.delete("width");
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
const columns = options.get("columns");
|
|
196
|
+
if (Array.isArray(columns)) {
|
|
197
|
+
const containsColumnWithWidth = !!columns.find((col) => col.get("width"));
|
|
198
|
+
if (containsColumnWithWidth) {
|
|
199
|
+
const containsColumnWithoutWidth = !!columns.find((col) => !col.get("width"));
|
|
200
|
+
if (containsColumnWithoutWidth) {
|
|
201
|
+
clearWidths();
|
|
202
|
+
} else {
|
|
203
|
+
const sumWidths = columns.reduce((memo, col) => {
|
|
204
|
+
return memo + col.get("width");
|
|
205
|
+
}, 0);
|
|
206
|
+
const widthsDontAddUp = sumWidths !== 100;
|
|
207
|
+
if (widthsDontAddUp) {
|
|
208
|
+
clearWidths();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
190
214
|
},
|
|
191
215
|
{
|
|
192
216
|
name: "space",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { onMount
|
|
1
|
+
import { onMount } from "solid-js";
|
|
2
2
|
import { createMutable } from "solid-js/store";
|
|
3
3
|
|
|
4
4
|
function CustomCode(props) {
|
|
@@ -41,7 +41,7 @@ function CustomCode(props) {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
});
|
|
44
|
-
|
|
44
|
+
let elem;
|
|
45
45
|
onMount(() => {
|
|
46
46
|
state.findAndRunScripts();
|
|
47
47
|
});
|
|
@@ -9,7 +9,26 @@ const componentInfo = {
|
|
|
9
9
|
required: true,
|
|
10
10
|
defaultValue: "",
|
|
11
11
|
helperText: "e.g. enter a youtube url, google map, etc",
|
|
12
|
-
onChange
|
|
12
|
+
onChange(options) {
|
|
13
|
+
const url = options.get("url");
|
|
14
|
+
if (url) {
|
|
15
|
+
options.set("content", "Loading...");
|
|
16
|
+
const apiKey = "ae0e60e78201a3f2b0de4b";
|
|
17
|
+
return fetch(`https://iframe.ly/api/iframely?url=${url}&api_key=${apiKey}`).then((res) => res.json()).then((data) => {
|
|
18
|
+
if (options.get("url") === url) {
|
|
19
|
+
if (data.html) {
|
|
20
|
+
options.set("content", data.html);
|
|
21
|
+
} else {
|
|
22
|
+
options.set("content", "Invalid url, please try another");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}).catch((_err) => {
|
|
26
|
+
options.set("content", "There was an error embedding this URL, please try again or another URL");
|
|
27
|
+
});
|
|
28
|
+
} else {
|
|
29
|
+
options.delete("content");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
13
32
|
},
|
|
14
33
|
{
|
|
15
34
|
name: "content",
|
|
@@ -1,50 +1,47 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { on, createEffect } from "solid-js";
|
|
2
2
|
import { createMutable } from "solid-js/store";
|
|
3
|
+
import { isJsScript } from "./helpers";
|
|
3
4
|
|
|
4
5
|
function Embed(props) {
|
|
5
6
|
const state = createMutable({
|
|
6
7
|
scriptsInserted: [],
|
|
7
8
|
scriptsRun: [],
|
|
9
|
+
ranInitFn: false,
|
|
8
10
|
|
|
9
11
|
findAndRunScripts() {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
} else if (!script.type || ["text/javascript", "application/javascript", "application/ecmascript"].includes(script.type)) {
|
|
28
|
-
if (state.scriptsRun.includes(script.innerText)) {
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
state.scriptsRun.push(script.innerText);
|
|
34
|
-
new Function(script.innerText)();
|
|
35
|
-
} catch (error) {
|
|
36
|
-
console.warn("`Embed`: Error running script:", error);
|
|
37
|
-
}
|
|
12
|
+
const scripts = elem.getElementsByTagName("script");
|
|
13
|
+
|
|
14
|
+
for (let i = 0; i < scripts.length; i++) {
|
|
15
|
+
const script = scripts[i];
|
|
16
|
+
|
|
17
|
+
if (script.src && !state.scriptsInserted.includes(script.src)) {
|
|
18
|
+
state.scriptsInserted.push(script.src);
|
|
19
|
+
const newScript = document.createElement("script");
|
|
20
|
+
newScript.async = true;
|
|
21
|
+
newScript.src = script.src;
|
|
22
|
+
document.head.appendChild(newScript);
|
|
23
|
+
} else if (isJsScript(script) && !state.scriptsRun.includes(script.innerText)) {
|
|
24
|
+
try {
|
|
25
|
+
state.scriptsRun.push(script.innerText);
|
|
26
|
+
new Function(script.innerText)();
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.warn("`Embed`: Error running script:", error);
|
|
38
29
|
}
|
|
39
30
|
}
|
|
40
31
|
}
|
|
41
32
|
}
|
|
42
33
|
|
|
43
34
|
});
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
35
|
+
let elem;
|
|
36
|
+
|
|
37
|
+
function onUpdateFn_0() {
|
|
38
|
+
if (elem && !state.ranInitFn) {
|
|
39
|
+
state.ranInitFn = true;
|
|
40
|
+
state.findAndRunScripts();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
createEffect(on(() => [elem, state.ranInitFn], onUpdateFn_0));
|
|
48
45
|
return <div class="builder-embed" ref={elem} innerHTML={props.content}></div>;
|
|
49
46
|
}
|
|
50
47
|
|
package/src/blocks/form/form.jsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Show, For
|
|
1
|
+
import { Show, For } from "solid-js";
|
|
2
2
|
import { createMutable } from "solid-js/store";
|
|
3
3
|
import { css } from "solid-styled-components";
|
|
4
4
|
import RenderBlock from "../../components/render-block/render-block.jsx";
|
|
@@ -218,7 +218,7 @@ function FormComponent(props) {
|
|
|
218
218
|
}
|
|
219
219
|
|
|
220
220
|
});
|
|
221
|
-
|
|
221
|
+
let formRef;
|
|
222
222
|
return <form {...props.attributes} validate={props.validate} ref={formRef} action={!props.sendWithJs && props.action} method={props.method} name={props.name} onSubmit={event => state.onSubmit(event)}>
|
|
223
223
|
<Show when={props.builderBlock && props.builderBlock.children}>
|
|
224
224
|
<For each={props.builderBlock?.children}>
|
|
@@ -18,7 +18,53 @@ const componentInfo = {
|
|
|
18
18
|
allowedFileTypes: ["jpeg", "jpg", "png", "svg"],
|
|
19
19
|
required: true,
|
|
20
20
|
defaultValue: "https://cdn.builder.io/api/v1/image/assets%2Fpwgjf0RoYWbdnJSbpBAjXNRMe9F2%2Ffb27a7c790324294af8be1c35fe30f4d",
|
|
21
|
-
onChange
|
|
21
|
+
onChange(options) {
|
|
22
|
+
const DEFAULT_ASPECT_RATIO = 0.7041;
|
|
23
|
+
options.delete("srcset");
|
|
24
|
+
options.delete("noWebp");
|
|
25
|
+
function loadImage(url, timeout = 6e4) {
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
const img = document.createElement("img");
|
|
28
|
+
let loaded = false;
|
|
29
|
+
img.onload = () => {
|
|
30
|
+
loaded = true;
|
|
31
|
+
resolve(img);
|
|
32
|
+
};
|
|
33
|
+
img.addEventListener("error", (event) => {
|
|
34
|
+
console.warn("Image load failed", event.error);
|
|
35
|
+
reject(event.error);
|
|
36
|
+
});
|
|
37
|
+
img.src = url;
|
|
38
|
+
setTimeout(() => {
|
|
39
|
+
if (!loaded) {
|
|
40
|
+
reject(new Error("Image load timed out"));
|
|
41
|
+
}
|
|
42
|
+
}, timeout);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function round(num) {
|
|
46
|
+
return Math.round(num * 1e3) / 1e3;
|
|
47
|
+
}
|
|
48
|
+
const value = options.get("image");
|
|
49
|
+
const aspectRatio = options.get("aspectRatio");
|
|
50
|
+
fetch(value).then((res) => res.blob()).then((blob) => {
|
|
51
|
+
if (blob.type.includes("svg")) {
|
|
52
|
+
options.set("noWebp", true);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
if (value && (!aspectRatio || aspectRatio === DEFAULT_ASPECT_RATIO)) {
|
|
56
|
+
return loadImage(value).then((img) => {
|
|
57
|
+
const possiblyUpdatedAspectRatio = options.get("aspectRatio");
|
|
58
|
+
if (options.get("image") === value && (!possiblyUpdatedAspectRatio || possiblyUpdatedAspectRatio === DEFAULT_ASPECT_RATIO)) {
|
|
59
|
+
if (img.width && img.height) {
|
|
60
|
+
options.set("aspectRatio", round(img.height / img.width));
|
|
61
|
+
options.set("height", img.height);
|
|
62
|
+
options.set("width", img.width);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
22
68
|
},
|
|
23
69
|
{
|
|
24
70
|
name: "backgroundSize",
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
function removeProtocol(path) {
|
|
2
|
+
return path.replace(/http(s)?:/, "");
|
|
3
|
+
}
|
|
4
|
+
function updateQueryParam(uri = "", key, value) {
|
|
5
|
+
const re = new RegExp("([?&])" + key + "=.*?(&|$)", "i");
|
|
6
|
+
const separator = uri.indexOf("?") !== -1 ? "&" : "?";
|
|
7
|
+
if (uri.match(re)) {
|
|
8
|
+
return uri.replace(re, "$1" + key + "=" + encodeURIComponent(value) + "$2");
|
|
9
|
+
}
|
|
10
|
+
return uri + separator + key + "=" + encodeURIComponent(value);
|
|
11
|
+
}
|
|
12
|
+
function getShopifyImageUrl(src, size) {
|
|
13
|
+
if (!src || !(src == null ? void 0 : src.match(/cdn\.shopify\.com/)) || !size) {
|
|
14
|
+
return src;
|
|
15
|
+
}
|
|
16
|
+
if (size === "master") {
|
|
17
|
+
return removeProtocol(src);
|
|
18
|
+
}
|
|
19
|
+
const match = src.match(/(_\d+x(\d+)?)?(\.(jpg|jpeg|gif|png|bmp|bitmap|tiff|tif)(\?v=\d+)?)/i);
|
|
20
|
+
if (match) {
|
|
21
|
+
const prefix = src.split(match[0]);
|
|
22
|
+
const suffix = match[3];
|
|
23
|
+
const useSize = size.match("x") ? size : `${size}x`;
|
|
24
|
+
return removeProtocol(`${prefix[0]}_${useSize}${suffix}`);
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
function getSrcSet(url) {
|
|
29
|
+
if (!url) {
|
|
30
|
+
return url;
|
|
31
|
+
}
|
|
32
|
+
const sizes = [100, 200, 400, 800, 1200, 1600, 2e3];
|
|
33
|
+
if (url.match(/builder\.io/)) {
|
|
34
|
+
let srcUrl = url;
|
|
35
|
+
const widthInSrc = Number(url.split("?width=")[1]);
|
|
36
|
+
if (!isNaN(widthInSrc)) {
|
|
37
|
+
srcUrl = `${srcUrl} ${widthInSrc}w`;
|
|
38
|
+
}
|
|
39
|
+
return sizes.filter((size) => size !== widthInSrc).map((size) => `${updateQueryParam(url, "width", size)} ${size}w`).concat([srcUrl]).join(", ");
|
|
40
|
+
}
|
|
41
|
+
if (url.match(/cdn\.shopify\.com/)) {
|
|
42
|
+
return sizes.map((size) => [getShopifyImageUrl(url, `${size}x${size}`), size]).filter(([sizeUrl]) => !!sizeUrl).map(([sizeUrl, size]) => `${sizeUrl} ${size}w`).concat([url]).join(", ");
|
|
43
|
+
}
|
|
44
|
+
return url;
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
getSrcSet
|
|
48
|
+
};
|
|
@@ -1,11 +1,48 @@
|
|
|
1
1
|
import { Show } from "solid-js";
|
|
2
|
+
import { createMutable } from "solid-js/store";
|
|
2
3
|
import { css } from "solid-styled-components";
|
|
4
|
+
import { getSrcSet } from "./image.helpers";
|
|
3
5
|
|
|
4
6
|
function Image(props) {
|
|
7
|
+
const state = createMutable({
|
|
8
|
+
get srcSetToUse() {
|
|
9
|
+
const imageToUse = props.image || props.src;
|
|
10
|
+
const url = imageToUse;
|
|
11
|
+
|
|
12
|
+
if (!url || // We can auto add srcset for cdn.builder.io and shopify
|
|
13
|
+
// images, otherwise you can supply this prop manually
|
|
14
|
+
!(url.match(/builder\.io/) || url.match(/cdn\.shopify\.com/))) {
|
|
15
|
+
return props.srcset;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (props.srcset && props.image?.includes("builder.io/api/v1/image")) {
|
|
19
|
+
if (!props.srcset.includes(props.image.split("?")[0])) {
|
|
20
|
+
console.debug("Removed given srcset");
|
|
21
|
+
return getSrcSet(url);
|
|
22
|
+
}
|
|
23
|
+
} else if (props.image && !props.srcset) {
|
|
24
|
+
return getSrcSet(url);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return getSrcSet(url);
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
get webpSrcSet() {
|
|
31
|
+
if (state.srcSetToUse?.match(/builder\.io/) && !props.noWebp) {
|
|
32
|
+
return state.srcSetToUse.replace(/\?/g, "?format=webp&");
|
|
33
|
+
} else {
|
|
34
|
+
return "";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
});
|
|
5
39
|
return <div class={css({
|
|
6
40
|
position: "relative"
|
|
7
41
|
})}>
|
|
8
42
|
<picture>
|
|
43
|
+
<Show when={state.webpSrcSet}>
|
|
44
|
+
<source type="image/webp" srcset={state.webpSrcSet} />
|
|
45
|
+
</Show>
|
|
9
46
|
<img class={"builder-image" + (props.className ? " " + props.className : "") + " " + css({
|
|
10
47
|
opacity: "1",
|
|
11
48
|
transition: "opacity 0.2s ease-in-out",
|
|
@@ -17,8 +54,8 @@ function Image(props) {
|
|
|
17
54
|
})} loading="lazy" alt={props.altText} role={props.altText ? "presentation" : undefined} style={{
|
|
18
55
|
"object-position": props.backgroundSize || "center",
|
|
19
56
|
"object-fit": props.backgroundSize || "cover"
|
|
20
|
-
}} src={props.image} srcset={
|
|
21
|
-
<source srcset={
|
|
57
|
+
}} src={props.image} srcset={state.srcSetToUse} sizes={props.sizes} />
|
|
58
|
+
<source srcset={state.srcSetToUse} />
|
|
22
59
|
</picture>
|
|
23
60
|
<Show when={props.aspectRatio && !(props.fitContent && props.builderBlock?.children?.length)}>
|
|
24
61
|
<div class={"builder-image-sizer " + css({
|
|
@@ -26,6 +63,7 @@ function Image(props) {
|
|
|
26
63
|
pointerEvents: "none",
|
|
27
64
|
fontSize: "0"
|
|
28
65
|
})} style={{
|
|
66
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
29
67
|
"padding-top": props.aspectRatio * 100 + "%"
|
|
30
68
|
}}></div>
|
|
31
69
|
</Show>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { useContext, onMount } from "solid-js";
|
|
1
|
+
import { useContext, onMount, on, createEffect } from "solid-js";
|
|
2
2
|
import { createMutable } from "solid-js/store";
|
|
3
3
|
import RenderContent from "../../components/render-content/render-content.jsx";
|
|
4
4
|
import BuilderContext from "../../context/builder.context";
|
|
5
|
+
import { getContent } from "../../functions/get-content/index.js";
|
|
5
6
|
|
|
6
7
|
function Symbol(props) {
|
|
7
8
|
const state = createMutable({
|
|
@@ -12,10 +13,37 @@ function Symbol(props) {
|
|
|
12
13
|
onMount(() => {
|
|
13
14
|
state.content = props.symbol?.content;
|
|
14
15
|
});
|
|
16
|
+
|
|
17
|
+
function onUpdateFn_0() {
|
|
18
|
+
const symbolToUse = props.symbol;
|
|
19
|
+
/**
|
|
20
|
+
* If:
|
|
21
|
+
* - we have a symbol prop
|
|
22
|
+
* - yet it does not have any content
|
|
23
|
+
* - and we have not already stored content from before
|
|
24
|
+
* - and it has a model name
|
|
25
|
+
*
|
|
26
|
+
* then we want to re-fetch the symbol content.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
if (symbolToUse && !symbolToUse.content && !state.content && symbolToUse.model) {
|
|
30
|
+
getContent({
|
|
31
|
+
model: symbolToUse.model,
|
|
32
|
+
apiKey: builderContext.apiKey,
|
|
33
|
+
query: {
|
|
34
|
+
id: symbolToUse.entry
|
|
35
|
+
}
|
|
36
|
+
}).then(response => {
|
|
37
|
+
state.content = response;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
createEffect(on(() => [props.symbol?.content, props.symbol?.model, props.symbol?.entry, state.content], onUpdateFn_0));
|
|
15
43
|
return <div class={state.className} {...props.attributes} dataSet={{
|
|
16
44
|
class: state.className
|
|
17
45
|
}}>
|
|
18
|
-
<RenderContent apiKey={builderContext.apiKey} context={builderContext.context} data={{ ...props.symbol?.data,
|
|
46
|
+
<RenderContent apiKey={builderContext.apiKey} context={builderContext.context} customComponents={Object.values(builderContext.registeredComponents)} data={{ ...props.symbol?.data,
|
|
19
47
|
...builderContext.state,
|
|
20
48
|
...props.symbol?.content?.data?.state
|
|
21
49
|
}} model={props.symbol?.model} content={state.content}></RenderContent>
|
|
@@ -8,7 +8,7 @@ function Video(props) {
|
|
|
8
8
|
// Hack to get object fit to work as expected and
|
|
9
9
|
// not have the video overflow
|
|
10
10
|
"border-radius": 1
|
|
11
|
-
}}
|
|
11
|
+
}} src={props.video || "no-src"} poster={props.posterImage} autoPlay={props.autoPlay} muted={props.muted} controls={props.controls} loop={props.loop}></video>;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export default Video;
|
|
@@ -139,34 +139,30 @@ function RenderBlock(props) {
|
|
|
139
139
|
return undefined;
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
const {
|
|
143
|
-
collection,
|
|
144
|
-
itemName
|
|
145
|
-
} = repeat;
|
|
146
142
|
const itemsArray = evaluate({
|
|
147
|
-
code: collection,
|
|
143
|
+
code: repeat.collection,
|
|
148
144
|
state: builderContext.state,
|
|
149
145
|
context: builderContext.context
|
|
150
146
|
});
|
|
151
147
|
|
|
152
|
-
if (Array.isArray(itemsArray)) {
|
|
153
|
-
const collectionName = collection.split(".").pop();
|
|
154
|
-
const itemNameToUse = itemName || (collectionName ? collectionName + "Item" : "item");
|
|
155
|
-
const repeatArray = itemsArray.map((item, index) => ({
|
|
156
|
-
context: { ...builderContext,
|
|
157
|
-
state: { ...builderContext.state,
|
|
158
|
-
$index: index,
|
|
159
|
-
$item: item,
|
|
160
|
-
[itemNameToUse]: item,
|
|
161
|
-
[`$${itemNameToUse}Index`]: index
|
|
162
|
-
}
|
|
163
|
-
},
|
|
164
|
-
block: blockWithoutRepeat
|
|
165
|
-
}));
|
|
166
|
-
return repeatArray;
|
|
167
|
-
} else {
|
|
148
|
+
if (!Array.isArray(itemsArray)) {
|
|
168
149
|
return undefined;
|
|
169
150
|
}
|
|
151
|
+
|
|
152
|
+
const collectionName = repeat.collection.split(".").pop();
|
|
153
|
+
const itemNameToUse = repeat.itemName || (collectionName ? collectionName + "Item" : "item");
|
|
154
|
+
const repeatArray = itemsArray.map((item, index) => ({
|
|
155
|
+
context: { ...builderContext,
|
|
156
|
+
state: { ...builderContext.state,
|
|
157
|
+
$index: index,
|
|
158
|
+
$item: item,
|
|
159
|
+
[itemNameToUse]: item,
|
|
160
|
+
[`$${itemNameToUse}Index`]: index
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
block: blockWithoutRepeat
|
|
164
|
+
}));
|
|
165
|
+
return repeatArray;
|
|
170
166
|
}
|
|
171
167
|
|
|
172
168
|
});
|
|
@@ -174,7 +170,7 @@ function RenderBlock(props) {
|
|
|
174
170
|
return <Show fallback={<RenderComponent {...state.renderComponentProps}></RenderComponent>} when={state.shouldWrap}>
|
|
175
171
|
<Show fallback={<Dynamic {...state.attributes} component={state.tagName}></Dynamic>} when={!isEmptyHtmlElement(state.tagName)}>
|
|
176
172
|
<Dynamic {...state.attributes} component={state.tagName}>
|
|
177
|
-
<Show
|
|
173
|
+
<Show when={state.repeatItemData}>
|
|
178
174
|
<For each={state.repeatItemData}>
|
|
179
175
|
{(data, _index) => {
|
|
180
176
|
const index = _index();
|
|
@@ -183,6 +179,9 @@ function RenderBlock(props) {
|
|
|
183
179
|
}}
|
|
184
180
|
</For>
|
|
185
181
|
</Show>
|
|
182
|
+
<Show when={!state.repeatItemData}>
|
|
183
|
+
<RenderComponent {...state.renderComponentProps}></RenderComponent>
|
|
184
|
+
</Show>
|
|
186
185
|
<For each={state.childrenWithoutParentComponent}>
|
|
187
186
|
{(child, _index) => {
|
|
188
187
|
const index = _index();
|
|
File without changes
|
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
import { Show, onMount } from "solid-js";
|
|
1
|
+
import { Show, onMount, on, createEffect } from "solid-js";
|
|
2
2
|
import { Dynamic } from "solid-js/web";
|
|
3
3
|
import { createMutable } from "solid-js/store";
|
|
4
4
|
import { getDefaultRegisteredComponents } from "../../constants/builder-registered-components.js";
|
|
5
5
|
import { TARGET } from "../../constants/target.js";
|
|
6
6
|
import BuilderContext from "../../context/builder.context";
|
|
7
7
|
import { evaluate } from "../../functions/evaluate.js";
|
|
8
|
-
import { convertSearchParamsToQueryObject, getBuilderSearchParams } from "../../functions/get-builder-search-params/index.js";
|
|
9
8
|
import { getContent } from "../../functions/get-content/index.js";
|
|
10
9
|
import { getFetch } from "../../functions/get-fetch.js";
|
|
11
10
|
import { isBrowser } from "../../functions/is-browser.js";
|
|
12
11
|
import { isEditing } from "../../functions/is-editing.js";
|
|
13
12
|
import { isPreviewing } from "../../functions/is-previewing.js";
|
|
14
|
-
import { previewingModelName } from "../../functions/previewing-model-name.js";
|
|
15
13
|
import { components, createRegisterComponentMessage } from "../../functions/register-component.js";
|
|
16
14
|
import { track } from "../../functions/track.js";
|
|
17
15
|
import RenderBlocks from "../render-blocks.jsx";
|
|
@@ -153,6 +151,10 @@ function RenderContent(props) {
|
|
|
153
151
|
}
|
|
154
152
|
}));
|
|
155
153
|
}
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
get shouldRenderContentStyles() {
|
|
157
|
+
return Boolean((state.useContent?.data?.cssCode || state.useContent?.data?.customFonts?.length) && TARGET !== "reactNative");
|
|
156
158
|
}
|
|
157
159
|
|
|
158
160
|
});
|
|
@@ -175,15 +177,15 @@ function RenderContent(props) {
|
|
|
175
177
|
|
|
176
178
|
|
|
177
179
|
if (isPreviewing()) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
180
|
+
const searchParams = new URL(location.href).searchParams;
|
|
181
|
+
|
|
182
|
+
if (props.model && searchParams.get("builder.preview") === props.model) {
|
|
183
|
+
const previewApiKey = searchParams.get("apiKey") || searchParams.get("builder.space");
|
|
181
184
|
|
|
182
185
|
if (previewApiKey) {
|
|
183
186
|
getContent({
|
|
184
187
|
model: props.model,
|
|
185
|
-
apiKey: previewApiKey
|
|
186
|
-
options: getBuilderSearchParams(convertSearchParamsToQueryObject(currentUrl.searchParams))
|
|
188
|
+
apiKey: previewApiKey
|
|
187
189
|
}).then(content => {
|
|
188
190
|
if (content) {
|
|
189
191
|
state.overrideContent = content;
|
|
@@ -198,6 +200,24 @@ function RenderContent(props) {
|
|
|
198
200
|
state.emitStateUpdate();
|
|
199
201
|
}
|
|
200
202
|
});
|
|
203
|
+
|
|
204
|
+
function onUpdateFn_0() {
|
|
205
|
+
state.evaluateJsCode();
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
createEffect(on(() => [state.useContent?.data?.jsCode], onUpdateFn_0));
|
|
209
|
+
|
|
210
|
+
function onUpdateFn_1() {
|
|
211
|
+
state.runHttpRequests();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
createEffect(on(() => [state.useContent?.data?.httpRequests], onUpdateFn_1));
|
|
215
|
+
|
|
216
|
+
function onUpdateFn_2() {
|
|
217
|
+
state.emitStateUpdate();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
createEffect(on(() => [state.contentState], onUpdateFn_2));
|
|
201
221
|
return <Dynamic value={{
|
|
202
222
|
get content() {
|
|
203
223
|
return state.useContent;
|
|
@@ -224,8 +244,8 @@ function RenderContent(props) {
|
|
|
224
244
|
<div onClick={event => track("click", {
|
|
225
245
|
contentId: state.useContent.id
|
|
226
246
|
})} data-builder-content-id={state.useContent?.id}>
|
|
227
|
-
<Show when={
|
|
228
|
-
<RenderContentStyles cssCode={state.useContent
|
|
247
|
+
<Show when={state.shouldRenderContentStyles}>
|
|
248
|
+
<RenderContentStyles cssCode={state.useContent?.data?.cssCode} customFonts={state.useContent?.data?.customFonts}></RenderContentStyles>
|
|
229
249
|
</Show>
|
|
230
250
|
<RenderBlocks blocks={state.useContent?.data?.blocks}></RenderBlocks>
|
|
231
251
|
</div>
|
|
@@ -30,6 +30,8 @@ import { componentInfo as textComponentInfo } from "../blocks/text/component-inf
|
|
|
30
30
|
import { default as Text } from "../blocks/text/text.jsx";
|
|
31
31
|
import { componentInfo as videoComponentInfo } from "../blocks/video/component-info";
|
|
32
32
|
import { default as Video } from "../blocks/video/video.jsx";
|
|
33
|
+
import { componentInfo as embedComponentInfo } from "../blocks/embed/component-info";
|
|
34
|
+
import { default as embed } from "../blocks/embed/embed.jsx";
|
|
33
35
|
const getDefaultRegisteredComponents = () => [
|
|
34
36
|
__spreadValues({ component: Columns }, columnsComponentInfo),
|
|
35
37
|
__spreadValues({ component: Image }, imageComponentInfo),
|
|
@@ -38,7 +40,8 @@ const getDefaultRegisteredComponents = () => [
|
|
|
38
40
|
__spreadValues({ component: Symbol }, symbolComponentInfo),
|
|
39
41
|
__spreadValues({ component: Button }, buttonComponentInfo),
|
|
40
42
|
__spreadValues({ component: Section }, sectionComponentInfo),
|
|
41
|
-
__spreadValues({ component: Fragment }, fragmentComponentInfo)
|
|
43
|
+
__spreadValues({ component: Fragment }, fragmentComponentInfo),
|
|
44
|
+
__spreadValues({ component: embed }, embedComponentInfo)
|
|
42
45
|
];
|
|
43
46
|
export {
|
|
44
47
|
getDefaultRegisteredComponents
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isBrowser } from "../is-browser";
|
|
1
2
|
const BUILDER_SEARCHPARAMS_PREFIX = "builder.";
|
|
2
3
|
const convertSearchParamsToQueryObject = (searchParams) => {
|
|
3
4
|
const options = {};
|
|
@@ -16,7 +17,17 @@ const getBuilderSearchParams = (options) => {
|
|
|
16
17
|
});
|
|
17
18
|
return newOptions;
|
|
18
19
|
};
|
|
20
|
+
const getBuilderSearchParamsFromWindow = () => {
|
|
21
|
+
if (!isBrowser()) {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
const searchParams = new URLSearchParams(window.location.search);
|
|
25
|
+
return getBuilderSearchParams(convertSearchParamsToQueryObject(searchParams));
|
|
26
|
+
};
|
|
27
|
+
const normalizeSearchParams = (searchParams) => searchParams instanceof URLSearchParams ? convertSearchParamsToQueryObject(searchParams) : searchParams;
|
|
19
28
|
export {
|
|
20
29
|
convertSearchParamsToQueryObject,
|
|
21
|
-
getBuilderSearchParams
|
|
30
|
+
getBuilderSearchParams,
|
|
31
|
+
getBuilderSearchParamsFromWindow,
|
|
32
|
+
normalizeSearchParams
|
|
22
33
|
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const handleABTesting = (item, testGroups) => {
|
|
2
|
+
if (item.variations && Object.keys(item.variations).length) {
|
|
3
|
+
const testGroup = item.id ? testGroups[item.id] : void 0;
|
|
4
|
+
const variationValue = testGroup ? item.variations[testGroup] : void 0;
|
|
5
|
+
if (testGroup && variationValue) {
|
|
6
|
+
item.data = variationValue.data;
|
|
7
|
+
item.testVariationId = variationValue.id;
|
|
8
|
+
item.testVariationName = variationValue.name;
|
|
9
|
+
} else {
|
|
10
|
+
let n = 0;
|
|
11
|
+
const random = Math.random();
|
|
12
|
+
let set = false;
|
|
13
|
+
for (const id in item.variations) {
|
|
14
|
+
const variation = item.variations[id];
|
|
15
|
+
const testRatio = variation.testRatio;
|
|
16
|
+
n += testRatio;
|
|
17
|
+
if (random < n) {
|
|
18
|
+
const variationName = variation.name || (variation.id === item.id ? "Default variation" : "");
|
|
19
|
+
set = true;
|
|
20
|
+
Object.assign(item, {
|
|
21
|
+
data: variation.data,
|
|
22
|
+
testVariationId: variation.id,
|
|
23
|
+
testVariationName: variationName
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (!set) {
|
|
28
|
+
Object.assign(item, {
|
|
29
|
+
testVariationId: item.id,
|
|
30
|
+
testVariationName: "Default"
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
export {
|
|
37
|
+
handleABTesting
|
|
38
|
+
};
|
|
@@ -37,20 +37,14 @@ var __async = (__this, __arguments, generator) => {
|
|
|
37
37
|
step((generator = generator.apply(__this, __arguments)).next());
|
|
38
38
|
});
|
|
39
39
|
};
|
|
40
|
+
import { flatten } from "../../helpers/flatten.js";
|
|
41
|
+
import {
|
|
42
|
+
getBuilderSearchParamsFromWindow,
|
|
43
|
+
normalizeSearchParams
|
|
44
|
+
} from "../get-builder-search-params/index.js";
|
|
40
45
|
import { getFetch } from "../get-fetch.js";
|
|
46
|
+
import { handleABTesting } from "./ab-testing.js";
|
|
41
47
|
const fetch$ = getFetch();
|
|
42
|
-
function flatten(object, path = null, separator = ".") {
|
|
43
|
-
return Object.keys(object).reduce((acc, key) => {
|
|
44
|
-
const value = object[key];
|
|
45
|
-
const newPath = [path, key].filter(Boolean).join(separator);
|
|
46
|
-
const isObject = [
|
|
47
|
-
typeof value === "object",
|
|
48
|
-
value !== null,
|
|
49
|
-
!(Array.isArray(value) && value.length === 0)
|
|
50
|
-
].every(Boolean);
|
|
51
|
-
return isObject ? __spreadValues(__spreadValues({}, acc), flatten(value, newPath, separator)) : __spreadProps(__spreadValues({}, acc), { [newPath]: value });
|
|
52
|
-
}, {});
|
|
53
|
-
}
|
|
54
48
|
function getContent(options) {
|
|
55
49
|
return __async(this, null, function* () {
|
|
56
50
|
return (yield getAllContent(__spreadProps(__spreadValues({}, options), { limit: 1 }))).results[0] || null;
|
|
@@ -66,67 +60,31 @@ const generateContentUrl = (options) => {
|
|
|
66
60
|
apiKey
|
|
67
61
|
} = options;
|
|
68
62
|
const url = new URL(`https://cdn.builder.io/api/v2/content/${model}?apiKey=${apiKey}&limit=${limit}&noTraverse=${noTraverse}`);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
63
|
+
const queryOptions = __spreadValues(__spreadValues({}, getBuilderSearchParamsFromWindow()), normalizeSearchParams(options.options || {}));
|
|
64
|
+
const flattened = flatten(queryOptions);
|
|
65
|
+
for (const key in flattened) {
|
|
66
|
+
url.searchParams.set(key, String(flattened[key]));
|
|
74
67
|
}
|
|
75
68
|
if (userAttributes) {
|
|
76
69
|
url.searchParams.set("userAttributes", JSON.stringify(userAttributes));
|
|
77
70
|
}
|
|
78
71
|
if (query) {
|
|
79
|
-
const
|
|
80
|
-
for (const key in
|
|
81
|
-
url.searchParams.set(key, JSON.stringify(
|
|
72
|
+
const flattened2 = flatten({ query });
|
|
73
|
+
for (const key in flattened2) {
|
|
74
|
+
url.searchParams.set(key, JSON.stringify(flattened2[key]));
|
|
82
75
|
}
|
|
83
76
|
}
|
|
84
77
|
return url;
|
|
85
78
|
};
|
|
86
|
-
const handleABTesting = (content, testGroups) => {
|
|
87
|
-
for (const item of content.results) {
|
|
88
|
-
if (item.variations && Object.keys(item.variations).length) {
|
|
89
|
-
const testGroup = testGroups[item.id];
|
|
90
|
-
const variationValue = item.variations[testGroup];
|
|
91
|
-
if (testGroup && variationValue) {
|
|
92
|
-
item.data = variationValue.data;
|
|
93
|
-
item.testVariationId = variationValue.id;
|
|
94
|
-
item.testVariationName = variationValue.name;
|
|
95
|
-
} else {
|
|
96
|
-
let n = 0;
|
|
97
|
-
const random = Math.random();
|
|
98
|
-
let set = false;
|
|
99
|
-
for (const id in item.variations) {
|
|
100
|
-
const variation = item.variations[id];
|
|
101
|
-
const testRatio = variation.testRatio;
|
|
102
|
-
n += testRatio;
|
|
103
|
-
if (random < n) {
|
|
104
|
-
const variationName = variation.name || (variation.id === item.id ? "Default variation" : "");
|
|
105
|
-
set = true;
|
|
106
|
-
Object.assign(item, {
|
|
107
|
-
data: variation.data,
|
|
108
|
-
testVariationId: variation.id,
|
|
109
|
-
testVariationName: variationName
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
if (!set) {
|
|
114
|
-
Object.assign(item, {
|
|
115
|
-
testVariationId: item.id,
|
|
116
|
-
testVariationName: "Default"
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
79
|
function getAllContent(options) {
|
|
124
80
|
return __async(this, null, function* () {
|
|
125
81
|
const url = generateContentUrl(options);
|
|
126
82
|
const fetch = yield fetch$;
|
|
127
83
|
const content = yield fetch(url.href).then((res) => res.json());
|
|
128
84
|
if (options.testGroups) {
|
|
129
|
-
|
|
85
|
+
for (const item of content.results) {
|
|
86
|
+
handleABTesting(item, options.testGroups);
|
|
87
|
+
}
|
|
130
88
|
}
|
|
131
89
|
return content;
|
|
132
90
|
});
|
|
File without changes
|
|
@@ -29,7 +29,6 @@ var __objRest = (source, exclude) => {
|
|
|
29
29
|
}
|
|
30
30
|
return target;
|
|
31
31
|
};
|
|
32
|
-
import { fastClone } from "./fast-clone.js";
|
|
33
32
|
const components = [];
|
|
34
33
|
function registerComponent(component, info) {
|
|
35
34
|
components.push(__spreadValues({ component }, info));
|
|
@@ -38,31 +37,34 @@ function registerComponent(component, info) {
|
|
|
38
37
|
}
|
|
39
38
|
const createRegisterComponentMessage = (_a) => {
|
|
40
39
|
var _b = _a, {
|
|
41
|
-
component
|
|
40
|
+
component: _
|
|
42
41
|
} = _b, info = __objRest(_b, [
|
|
43
42
|
"component"
|
|
44
43
|
]);
|
|
45
44
|
return {
|
|
46
45
|
type: "builder.registerComponent",
|
|
47
|
-
data: prepareComponentInfoToSend(
|
|
46
|
+
data: prepareComponentInfoToSend(info)
|
|
48
47
|
};
|
|
49
48
|
};
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
49
|
+
const fastClone = (obj) => JSON.parse(JSON.stringify(obj));
|
|
50
|
+
const serializeValue = (value) => typeof value === "function" ? serializeFn(value) : fastClone(value);
|
|
51
|
+
const serializeFn = (fnValue) => {
|
|
52
|
+
const fnStr = fnValue.toString().trim();
|
|
53
|
+
const appendFunction = !fnStr.startsWith("function") && !fnStr.startsWith("(");
|
|
54
|
+
return `return (${appendFunction ? "function " : ""}${fnStr}).apply(this, arguments)`;
|
|
55
|
+
};
|
|
56
|
+
const prepareComponentInfoToSend = (_c) => {
|
|
57
|
+
var _d = _c, {
|
|
58
|
+
inputs
|
|
59
|
+
} = _d, info = __objRest(_d, [
|
|
60
|
+
"inputs"
|
|
61
|
+
]);
|
|
62
|
+
return __spreadProps(__spreadValues({}, fastClone(info)), {
|
|
63
|
+
inputs: inputs == null ? void 0 : inputs.map((input) => Object.entries(input).reduce((acc, [key, value]) => __spreadProps(__spreadValues({}, acc), {
|
|
64
|
+
[key]: serializeValue(value)
|
|
65
|
+
}), {}))
|
|
64
66
|
});
|
|
65
|
-
}
|
|
67
|
+
};
|
|
66
68
|
export {
|
|
67
69
|
components,
|
|
68
70
|
createRegisterComponentMessage,
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defProps = Object.defineProperties;
|
|
3
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
+
var __spreadValues = (a, b) => {
|
|
9
|
+
for (var prop in b || (b = {}))
|
|
10
|
+
if (__hasOwnProp.call(b, prop))
|
|
11
|
+
__defNormalProp(a, prop, b[prop]);
|
|
12
|
+
if (__getOwnPropSymbols)
|
|
13
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
14
|
+
if (__propIsEnum.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
}
|
|
17
|
+
return a;
|
|
18
|
+
};
|
|
19
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
+
function flatten(object, path = null, separator = ".") {
|
|
21
|
+
return Object.keys(object).reduce((acc, key) => {
|
|
22
|
+
const value = object[key];
|
|
23
|
+
const newPath = [path, key].filter(Boolean).join(separator);
|
|
24
|
+
const isObject = [
|
|
25
|
+
typeof value === "object",
|
|
26
|
+
value !== null,
|
|
27
|
+
!(Array.isArray(value) && value.length === 0)
|
|
28
|
+
].every(Boolean);
|
|
29
|
+
return isObject ? __spreadValues(__spreadValues({}, acc), flatten(value, newPath, separator)) : __spreadProps(__spreadValues({}, acc), { [newPath]: value });
|
|
30
|
+
}, {});
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
flatten
|
|
34
|
+
};
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { isPreviewing } from "./is-previewing.js";
|
|
2
|
-
function previewingModelName() {
|
|
3
|
-
if (!isPreviewing()) {
|
|
4
|
-
return null;
|
|
5
|
-
}
|
|
6
|
-
const url = new URL(location.href);
|
|
7
|
-
return url.searchParams.get("builder.preview");
|
|
8
|
-
}
|
|
9
|
-
export {
|
|
10
|
-
previewingModelName
|
|
11
|
-
};
|