@builder.io/sdk-react-native 0.1.11 → 0.1.13
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/dist/blocks/columns/columns.js +67 -64
- package/dist/blocks/symbol/symbol.js +30 -23
- package/dist/components/render-blocks.js +1 -1
- package/dist/components/render-content/render-content.js +7 -0
- package/dist/context/builder.context.js +1 -0
- package/dist/functions/get-content/generate-content-url.js +5 -2
- package/dist/functions/get-content/generate-content-url.test.js +42 -0
- package/dist/types/api-version.js +1 -0
- package/package.json +1 -1
- package/src/blocks/columns/columns.jsx +84 -69
- package/src/blocks/symbol/symbol.jsx +35 -27
- package/src/components/render-blocks.jsx +1 -1
- package/src/components/render-content/render-content.jsx +7 -0
- package/src/context/builder.context.js +1 -0
- package/src/functions/get-content/generate-content-url.js +6 -2
- package/src/functions/get-content/generate-content-url.test.js +42 -0
- package/src/types/api-version.js +0 -0
|
@@ -33,50 +33,75 @@ const render_blocks_1 = __importDefault(require("../../components/render-blocks"
|
|
|
33
33
|
const device_sizes_1 = require("../../constants/device-sizes");
|
|
34
34
|
const render_inlined_styles_1 = __importDefault(require("../../components/render-inlined-styles"));
|
|
35
35
|
const target_js_1 = require("../../constants/target.js");
|
|
36
|
-
const css_1 = require("../../helpers/css");
|
|
37
36
|
const builder_context_js_1 = __importDefault(require("../../context/builder.context.js"));
|
|
38
37
|
function Columns(props) {
|
|
39
38
|
var _a;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
function getColumns() {
|
|
44
|
-
return props.columns || [];
|
|
45
|
-
}
|
|
39
|
+
const [gutterSize, setGutterSize] = (0, react_1.useState)(() => typeof props.space === "number" ? props.space || 0 : 20);
|
|
40
|
+
const [cols, setCols] = (0, react_1.useState)(() => props.columns || []);
|
|
41
|
+
const [stackAt, setStackAt] = (0, react_1.useState)(() => props.stackColumnsAt || "tablet");
|
|
46
42
|
function getWidth(index) {
|
|
47
43
|
var _a;
|
|
48
|
-
|
|
49
|
-
return ((_a = columns[index]) === null || _a === void 0 ? void 0 : _a.width) || 100 / columns.length;
|
|
44
|
+
return ((_a = cols[index]) === null || _a === void 0 ? void 0 : _a.width) || 100 / cols.length;
|
|
50
45
|
}
|
|
51
46
|
function getColumnCssWidth(index) {
|
|
52
|
-
const
|
|
53
|
-
const gutterSize = getGutterSize();
|
|
54
|
-
const subtractWidth = (gutterSize * (columns.length - 1)) / columns.length;
|
|
47
|
+
const subtractWidth = (gutterSize * (cols.length - 1)) / cols.length;
|
|
55
48
|
return `calc(${getWidth(index)}% - ${subtractWidth}px)`;
|
|
56
49
|
}
|
|
57
|
-
function
|
|
58
|
-
|
|
59
|
-
return _stackColumnsAt === "tablet" ? prop : "inherit";
|
|
50
|
+
function getTabletStyle({ stackedStyle, desktopStyle }) {
|
|
51
|
+
return stackAt === "tablet" ? stackedStyle : desktopStyle;
|
|
60
52
|
}
|
|
53
|
+
function getMobileStyle({ stackedStyle, desktopStyle }) {
|
|
54
|
+
return stackAt === "never" ? desktopStyle : stackedStyle;
|
|
55
|
+
}
|
|
56
|
+
const [flexDir, setFlexDir] = (0, react_1.useState)(() => props.stackColumnsAt === "never"
|
|
57
|
+
? "row"
|
|
58
|
+
: props.reverseColumnsWhenStacked
|
|
59
|
+
? "column-reverse"
|
|
60
|
+
: "column");
|
|
61
61
|
function columnsCssVars() {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
if (target_js_1.TARGET === "reactNative") {
|
|
63
|
+
return {
|
|
64
|
+
flexDirection: flexDir,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
67
|
return {
|
|
68
68
|
"--flex-dir": flexDir,
|
|
69
|
-
"--flex-dir-tablet":
|
|
69
|
+
"--flex-dir-tablet": getTabletStyle({
|
|
70
|
+
stackedStyle: flexDir,
|
|
71
|
+
desktopStyle: "row",
|
|
72
|
+
}),
|
|
70
73
|
};
|
|
71
74
|
}
|
|
72
|
-
function columnCssVars() {
|
|
73
|
-
const width =
|
|
74
|
-
const
|
|
75
|
+
function columnCssVars(index) {
|
|
76
|
+
const width = getColumnCssWidth(index);
|
|
77
|
+
const gutter = `${index === 0 ? 0 : gutterSize}px`;
|
|
78
|
+
if (target_js_1.TARGET === "reactNative") {
|
|
79
|
+
return {
|
|
80
|
+
width,
|
|
81
|
+
marginLeft: props.stackColumnsAt === "never" ? gutter : "0",
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
const mobileWidth = "100%";
|
|
85
|
+
const mobileMarginLeft = "0";
|
|
75
86
|
return {
|
|
76
|
-
|
|
77
|
-
"
|
|
78
|
-
"--column-width-
|
|
79
|
-
|
|
87
|
+
width,
|
|
88
|
+
"margin-left": gutter,
|
|
89
|
+
"--column-width-mobile": getMobileStyle({
|
|
90
|
+
stackedStyle: mobileWidth,
|
|
91
|
+
desktopStyle: width,
|
|
92
|
+
}),
|
|
93
|
+
"--column-margin-left-mobile": getMobileStyle({
|
|
94
|
+
stackedStyle: mobileMarginLeft,
|
|
95
|
+
desktopStyle: gutter,
|
|
96
|
+
}),
|
|
97
|
+
"--column-width-tablet": getTabletStyle({
|
|
98
|
+
stackedStyle: mobileWidth,
|
|
99
|
+
desktopStyle: width,
|
|
100
|
+
}),
|
|
101
|
+
"--column-margin-left-tablet": getTabletStyle({
|
|
102
|
+
stackedStyle: mobileMarginLeft,
|
|
103
|
+
desktopStyle: gutter,
|
|
104
|
+
}),
|
|
80
105
|
};
|
|
81
106
|
}
|
|
82
107
|
function getWidthForBreakpointSize(size) {
|
|
@@ -84,64 +109,42 @@ function Columns(props) {
|
|
|
84
109
|
const breakpointSizes = (0, device_sizes_1.getSizesForBreakpoints)(((_b = (_a = builderContext.content) === null || _a === void 0 ? void 0 : _a.meta) === null || _b === void 0 ? void 0 : _b.breakpoints) || {});
|
|
85
110
|
return breakpointSizes[size].max;
|
|
86
111
|
}
|
|
87
|
-
function columnStyleObjects() {
|
|
88
|
-
return {
|
|
89
|
-
columns: {
|
|
90
|
-
small: {
|
|
91
|
-
flexDirection: "var(--flex-dir)",
|
|
92
|
-
alignItems: "stretch",
|
|
93
|
-
},
|
|
94
|
-
medium: {
|
|
95
|
-
flexDirection: "var(--flex-dir-tablet)",
|
|
96
|
-
alignItems: "stretch",
|
|
97
|
-
},
|
|
98
|
-
},
|
|
99
|
-
column: {
|
|
100
|
-
small: {
|
|
101
|
-
width: "var(--column-width) !important",
|
|
102
|
-
marginLeft: "var(--column-margin-left) !important",
|
|
103
|
-
},
|
|
104
|
-
medium: {
|
|
105
|
-
width: "var(--column-width-tablet) !important",
|
|
106
|
-
marginLeft: "var(--column-margin-left-tablet) !important",
|
|
107
|
-
},
|
|
108
|
-
},
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
112
|
function columnsStyles() {
|
|
112
113
|
return `
|
|
113
114
|
@media (max-width: ${getWidthForBreakpointSize("medium")}px) {
|
|
114
115
|
.${props.builderBlock.id}-breakpoints {
|
|
115
|
-
|
|
116
|
+
flex-direction: var(--flex-dir-tablet);
|
|
117
|
+
align-items: stretch;
|
|
116
118
|
}
|
|
117
119
|
|
|
118
120
|
.${props.builderBlock.id}-breakpoints > .builder-column {
|
|
119
|
-
|
|
121
|
+
width: var(--column-width-tablet) !important;
|
|
122
|
+
margin-left: var(--column-margin-left-tablet) !important;
|
|
120
123
|
}
|
|
121
124
|
}
|
|
122
125
|
|
|
123
126
|
@media (max-width: ${getWidthForBreakpointSize("small")}px) {
|
|
124
127
|
.${props.builderBlock.id}-breakpoints {
|
|
125
|
-
|
|
128
|
+
flex-direction: var(--flex-dir);
|
|
129
|
+
align-items: stretch;
|
|
126
130
|
}
|
|
127
131
|
|
|
128
132
|
.${props.builderBlock.id}-breakpoints > .builder-column {
|
|
129
|
-
|
|
133
|
+
width: var(--column-width-mobile) !important;
|
|
134
|
+
margin-left: var(--column-margin-left-mobile) !important;
|
|
130
135
|
}
|
|
131
136
|
},
|
|
132
137
|
`;
|
|
133
138
|
}
|
|
134
|
-
function reactNativeColumnsStyles() {
|
|
135
|
-
return columnStyleObjects.columns.small;
|
|
136
|
-
}
|
|
137
|
-
function reactNativeColumnStyles() {
|
|
138
|
-
return columnStyleObjects.column.small;
|
|
139
|
-
}
|
|
140
139
|
const builderContext = (0, react_1.useContext)(builder_context_js_1.default);
|
|
141
|
-
return (React.createElement(react_native_1.View, { style:
|
|
140
|
+
return (React.createElement(react_native_1.View, { style: columnsCssVars(), dataSet: {
|
|
141
|
+
"builder-block-name": "builder-columns",
|
|
142
|
+
} },
|
|
142
143
|
target_js_1.TARGET !== "reactNative" ? (React.createElement(React.Fragment, null,
|
|
143
144
|
React.createElement(render_inlined_styles_1.default, { styles: columnsStyles() }))) : null, (_a = props.columns) === null || _a === void 0 ? void 0 :
|
|
144
|
-
_a.map((column, index) => (React.createElement(react_native_1.View, { style:
|
|
145
|
+
_a.map((column, index) => (React.createElement(react_native_1.View, { style: columnCssVars(index), dataSet: {
|
|
146
|
+
"builder-block-name": "builder-column",
|
|
147
|
+
}, key: index },
|
|
145
148
|
React.createElement(render_blocks_1.default, { blocks: column.blocks, path: `component.options.columns.${index}.blocks`, parent: props.builderBlock.id, styleProp: {
|
|
146
149
|
flexGrow: "1",
|
|
147
150
|
} }))))));
|
|
@@ -34,8 +34,8 @@ const builder_context_js_1 = __importDefault(require("../../context/builder.cont
|
|
|
34
34
|
const index_js_1 = require("../../functions/get-content/index.js");
|
|
35
35
|
const target_1 = require("../../constants/target");
|
|
36
36
|
function Symbol(props) {
|
|
37
|
-
var _a, _b, _c
|
|
38
|
-
|
|
37
|
+
var _a, _b, _c;
|
|
38
|
+
const [className, setClassName] = (0, react_1.useState)(() => {
|
|
39
39
|
var _a, _b;
|
|
40
40
|
return [
|
|
41
41
|
...(target_1.TARGET === "vue2" || target_1.TARGET === "vue3"
|
|
@@ -49,15 +49,10 @@ function Symbol(props) {
|
|
|
49
49
|
]
|
|
50
50
|
.filter(Boolean)
|
|
51
51
|
.join(" ");
|
|
52
|
-
}
|
|
53
|
-
const [
|
|
54
|
-
function
|
|
52
|
+
});
|
|
53
|
+
const [contentToUse, setContentToUse] = (0, react_1.useState)(() => { var _a; return (_a = props.symbol) === null || _a === void 0 ? void 0 : _a.content; });
|
|
54
|
+
function fetchContent() {
|
|
55
55
|
var _a;
|
|
56
|
-
return ((_a = props.symbol) === null || _a === void 0 ? void 0 : _a.content) || fetchedContent;
|
|
57
|
-
}
|
|
58
|
-
const builderContext = (0, react_1.useContext)(builder_context_js_1.default);
|
|
59
|
-
(0, react_1.useEffect)(() => {
|
|
60
|
-
const symbolToUse = props.symbol;
|
|
61
56
|
/**
|
|
62
57
|
* If:
|
|
63
58
|
* - we have a symbol prop
|
|
@@ -67,31 +62,43 @@ function Symbol(props) {
|
|
|
67
62
|
*
|
|
68
63
|
* then we want to re-fetch the symbol content.
|
|
69
64
|
*/
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
!fetchedContent &&
|
|
73
|
-
symbolToUse.model &&
|
|
65
|
+
if (!contentToUse &&
|
|
66
|
+
((_a = props.symbol) === null || _a === void 0 ? void 0 : _a.model) &&
|
|
74
67
|
(
|
|
75
68
|
// This is a hack, we should not need to check for this, but it is needed for Svelte.
|
|
76
69
|
builderContext === null || builderContext === void 0 ? void 0 : builderContext.apiKey)) {
|
|
77
70
|
(0, index_js_1.getContent)({
|
|
78
|
-
model:
|
|
71
|
+
model: props.symbol.model,
|
|
79
72
|
apiKey: builderContext.apiKey,
|
|
73
|
+
apiVersion: builderContext.apiVersion,
|
|
80
74
|
query: {
|
|
81
|
-
id:
|
|
75
|
+
id: props.symbol.entry,
|
|
82
76
|
},
|
|
83
|
-
})
|
|
84
|
-
|
|
77
|
+
})
|
|
78
|
+
.then((response) => {
|
|
79
|
+
if (response) {
|
|
80
|
+
setContentToUse(response);
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
.catch((err) => {
|
|
84
|
+
console.error("[Builder.io]: Could not fetch symbol content: ", err);
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
|
-
}
|
|
87
|
+
}
|
|
88
|
+
const builderContext = (0, react_1.useContext)(builder_context_js_1.default);
|
|
89
|
+
(0, react_1.useEffect)(() => {
|
|
90
|
+
fetchContent();
|
|
91
|
+
}, []);
|
|
92
|
+
(0, react_1.useEffect)(() => {
|
|
93
|
+
fetchContent();
|
|
94
|
+
}, [props.symbol]);
|
|
88
95
|
return (React.createElement(react_native_1.View, { ...props.attributes, dataSet: {
|
|
89
|
-
class: className
|
|
96
|
+
class: className,
|
|
90
97
|
} },
|
|
91
|
-
React.createElement(render_content_1.default, { apiKey: builderContext.apiKey, context: builderContext.context, customComponents: Object.values(builderContext.registeredComponents), data: {
|
|
98
|
+
React.createElement(render_content_1.default, { apiVersion: builderContext.apiVersion, apiKey: builderContext.apiKey, context: builderContext.context, customComponents: Object.values(builderContext.registeredComponents), data: {
|
|
92
99
|
...(_a = props.symbol) === null || _a === void 0 ? void 0 : _a.data,
|
|
93
100
|
...builderContext.state,
|
|
94
|
-
...(
|
|
95
|
-
}, model: (
|
|
101
|
+
...(_b = contentToUse === null || contentToUse === void 0 ? void 0 : contentToUse.data) === null || _b === void 0 ? void 0 : _b.state,
|
|
102
|
+
}, model: (_c = props.symbol) === null || _c === void 0 ? void 0 : _c.model, content: contentToUse })));
|
|
96
103
|
}
|
|
97
104
|
exports.default = Symbol;
|
|
@@ -66,7 +66,7 @@ function RenderBlocks(props) {
|
|
|
66
66
|
const builderContext = (0, react_1.useContext)(builder_context_js_1.default);
|
|
67
67
|
return (React.createElement(react_native_1.ScrollView, { "builder-path": props.path, "builder-parent-id": props.parent, dataSet: {
|
|
68
68
|
class: className(),
|
|
69
|
-
}, contentContainerStyle:
|
|
69
|
+
}, contentContainerStyle: props.styleProp, onClick: (event) => onClick(), onMouseEnter: (event) => onMouseEnter() },
|
|
70
70
|
props.blocks ? (React.createElement(React.Fragment, null, (_a = props.blocks) === null || _a === void 0 ? void 0 : _a.map((block) => (React.createElement(render_block_1.default, { key: "render-block-" + block.id, block: block, context: builderContext }))))) : null,
|
|
71
71
|
props.blocks ? (React.createElement(React.Fragment, null, (_b = props.blocks) === null || _b === void 0 ? void 0 : _b.map((block) => (React.createElement(block_styles_1.default, { key: "block-style-" + block.id, block: block, context: builderContext }))))) : null));
|
|
72
72
|
}
|
|
@@ -275,6 +275,7 @@ function RenderContent(props) {
|
|
|
275
275
|
(0, index_js_1.getContent)({
|
|
276
276
|
model: props.model,
|
|
277
277
|
apiKey: props.apiKey,
|
|
278
|
+
apiVersion: props.apiVersion,
|
|
278
279
|
}).then((content) => {
|
|
279
280
|
if (content) {
|
|
280
281
|
mergeNewContent(content);
|
|
@@ -287,6 +288,11 @@ function RenderContent(props) {
|
|
|
287
288
|
emitStateUpdate();
|
|
288
289
|
}
|
|
289
290
|
}, []);
|
|
291
|
+
(0, react_1.useEffect)(() => {
|
|
292
|
+
if (props.content) {
|
|
293
|
+
mergeNewContent(props.content);
|
|
294
|
+
}
|
|
295
|
+
}, [props.content]);
|
|
290
296
|
(0, react_1.useEffect)(() => {
|
|
291
297
|
evaluateJsCode();
|
|
292
298
|
}, [(_a = useContent === null || useContent === void 0 ? void 0 : useContent.data) === null || _a === void 0 ? void 0 : _a.jsCode, contentState]);
|
|
@@ -310,6 +316,7 @@ function RenderContent(props) {
|
|
|
310
316
|
setState: setContextState,
|
|
311
317
|
context: props.context || {},
|
|
312
318
|
apiKey: props.apiKey,
|
|
319
|
+
apiVersion: props.apiVersion,
|
|
313
320
|
registeredComponents: allRegisteredComponents,
|
|
314
321
|
} }, useContent ? (React.createElement(React.Fragment, null,
|
|
315
322
|
React.createElement(react_native_1.ScrollView, { ref: elementRef, onClick: (event) => onClick(event), "builder-content-id": useContent === null || useContent === void 0 ? void 0 : useContent.id, "builder-model": props.model },
|
|
@@ -20,11 +20,14 @@ var __spreadValues = (a, b) => {
|
|
|
20
20
|
const flatten_js_1 = require("../../helpers/flatten.js");
|
|
21
21
|
const index_js_1 = require("../get-builder-search-params/index.js");
|
|
22
22
|
const generateContentUrl = (options) => {
|
|
23
|
-
const { limit = 30, userAttributes, query, noTraverse = false, model, apiKey, includeRefs = true, locale } = options;
|
|
23
|
+
const { limit = 30, userAttributes, query, noTraverse = false, model, apiKey, includeRefs = true, locale, apiVersion = "v2" } = options;
|
|
24
24
|
if (!apiKey) {
|
|
25
25
|
throw new Error("Missing API key");
|
|
26
26
|
}
|
|
27
|
-
|
|
27
|
+
if (!["v2", "v3"].includes(apiVersion)) {
|
|
28
|
+
throw new Error(`Invalid apiVersion: expected 'v2' or 'v3', received '${apiVersion}'`);
|
|
29
|
+
}
|
|
30
|
+
const url = new URL(`https://cdn.builder.io/api/${apiVersion}/content/${model}?apiKey=${apiKey}&limit=${limit}&noTraverse=${noTraverse}&includeRefs=${includeRefs}${locale ? `&locale=${locale}` : ""}`);
|
|
28
31
|
const queryOptions = __spreadValues(__spreadValues({}, (0, index_js_1.getBuilderSearchParamsFromWindow)()), (0, index_js_1.normalizeSearchParams)(options.options || {}));
|
|
29
32
|
const flattened = (0, flatten_js_1.flatten)(queryOptions);
|
|
30
33
|
for (const key in flattened) {
|
|
@@ -30,4 +30,46 @@ describe("Generate Content URL", () => {
|
|
|
30
30
|
});
|
|
31
31
|
expect(output).toMatchSnapshot();
|
|
32
32
|
});
|
|
33
|
+
test("generate content url with apiVersion as v2", () => {
|
|
34
|
+
const output = (0, generate_content_url_1.generateContentUrl)({
|
|
35
|
+
apiKey: testKey,
|
|
36
|
+
model: testModel,
|
|
37
|
+
query: { id: testId },
|
|
38
|
+
options,
|
|
39
|
+
apiVersion: "v2"
|
|
40
|
+
});
|
|
41
|
+
expect(output).toMatchSnapshot();
|
|
42
|
+
});
|
|
43
|
+
test("generate content url with apiVersion as v3", () => {
|
|
44
|
+
const output = (0, generate_content_url_1.generateContentUrl)({
|
|
45
|
+
apiKey: testKey,
|
|
46
|
+
model: testModel,
|
|
47
|
+
query: { id: testId },
|
|
48
|
+
options,
|
|
49
|
+
apiVersion: "v3"
|
|
50
|
+
});
|
|
51
|
+
expect(output).toMatchSnapshot();
|
|
52
|
+
});
|
|
53
|
+
test("throw error when trying to generate content url with apiVersion as v1", () => {
|
|
54
|
+
expect(() => {
|
|
55
|
+
(0, generate_content_url_1.generateContentUrl)({
|
|
56
|
+
apiKey: testKey,
|
|
57
|
+
model: testModel,
|
|
58
|
+
query: { id: testId },
|
|
59
|
+
options,
|
|
60
|
+
apiVersion: "v1"
|
|
61
|
+
});
|
|
62
|
+
}).toThrow(`Invalid apiVersion: expected 'v2' or 'v3', received 'v1'`);
|
|
63
|
+
});
|
|
64
|
+
test("throw error when trying to generate content url with an invalid apiVersion value", () => {
|
|
65
|
+
expect(() => {
|
|
66
|
+
(0, generate_content_url_1.generateContentUrl)({
|
|
67
|
+
apiKey: testKey,
|
|
68
|
+
model: testModel,
|
|
69
|
+
query: { id: testId },
|
|
70
|
+
options,
|
|
71
|
+
apiVersion: "INVALID_API_VERSION"
|
|
72
|
+
});
|
|
73
|
+
}).toThrow(`Invalid apiVersion: expected 'v2' or 'v3', received 'INVALID_API_VERSION'`);
|
|
74
|
+
});
|
|
33
75
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
package/package.json
CHANGED
|
@@ -7,61 +7,94 @@ import {
|
|
|
7
7
|
Image,
|
|
8
8
|
Text,
|
|
9
9
|
} from "react-native";
|
|
10
|
-
import { useContext } from "react";
|
|
10
|
+
import { useState, useContext } from "react";
|
|
11
11
|
import RenderBlocks from "../../components/render-blocks";
|
|
12
12
|
import { getSizesForBreakpoints } from "../../constants/device-sizes";
|
|
13
13
|
import RenderInlinedStyles from "../../components/render-inlined-styles";
|
|
14
14
|
import { TARGET } from "../../constants/target.js";
|
|
15
|
-
import { convertStyleMapToCSS } from "../../helpers/css";
|
|
16
15
|
import BuilderContext from "../../context/builder.context.js";
|
|
17
16
|
|
|
18
17
|
function Columns(props) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
const [gutterSize, setGutterSize] = useState(() =>
|
|
19
|
+
typeof props.space === "number" ? props.space || 0 : 20
|
|
20
|
+
);
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
const [cols, setCols] = useState(() => props.columns || []);
|
|
23
|
+
|
|
24
|
+
const [stackAt, setStackAt] = useState(
|
|
25
|
+
() => props.stackColumnsAt || "tablet"
|
|
26
|
+
);
|
|
26
27
|
|
|
27
28
|
function getWidth(index) {
|
|
28
|
-
|
|
29
|
-
return columns[index]?.width || 100 / columns.length;
|
|
29
|
+
return cols[index]?.width || 100 / cols.length;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
function getColumnCssWidth(index) {
|
|
33
|
-
const
|
|
34
|
-
const gutterSize = getGutterSize();
|
|
35
|
-
const subtractWidth = (gutterSize * (columns.length - 1)) / columns.length;
|
|
33
|
+
const subtractWidth = (gutterSize * (cols.length - 1)) / cols.length;
|
|
36
34
|
return `calc(${getWidth(index)}% - ${subtractWidth}px)`;
|
|
37
35
|
}
|
|
38
36
|
|
|
39
|
-
function
|
|
40
|
-
|
|
41
|
-
return _stackColumnsAt === "tablet" ? prop : "inherit";
|
|
37
|
+
function getTabletStyle({ stackedStyle, desktopStyle }) {
|
|
38
|
+
return stackAt === "tablet" ? stackedStyle : desktopStyle;
|
|
42
39
|
}
|
|
43
40
|
|
|
41
|
+
function getMobileStyle({ stackedStyle, desktopStyle }) {
|
|
42
|
+
return stackAt === "never" ? desktopStyle : stackedStyle;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const [flexDir, setFlexDir] = useState(() =>
|
|
46
|
+
props.stackColumnsAt === "never"
|
|
47
|
+
? "row"
|
|
48
|
+
: props.reverseColumnsWhenStacked
|
|
49
|
+
? "column-reverse"
|
|
50
|
+
: "column"
|
|
51
|
+
);
|
|
52
|
+
|
|
44
53
|
function columnsCssVars() {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
: "column";
|
|
54
|
+
if (TARGET === "reactNative") {
|
|
55
|
+
return {
|
|
56
|
+
flexDirection: flexDir,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
51
59
|
return {
|
|
52
60
|
"--flex-dir": flexDir,
|
|
53
|
-
"--flex-dir-tablet":
|
|
61
|
+
"--flex-dir-tablet": getTabletStyle({
|
|
62
|
+
stackedStyle: flexDir,
|
|
63
|
+
desktopStyle: "row",
|
|
64
|
+
}),
|
|
54
65
|
};
|
|
55
66
|
}
|
|
56
67
|
|
|
57
|
-
function columnCssVars() {
|
|
58
|
-
const width =
|
|
59
|
-
const
|
|
68
|
+
function columnCssVars(index) {
|
|
69
|
+
const width = getColumnCssWidth(index);
|
|
70
|
+
const gutter = `${index === 0 ? 0 : gutterSize}px`;
|
|
71
|
+
if (TARGET === "reactNative") {
|
|
72
|
+
return {
|
|
73
|
+
width,
|
|
74
|
+
marginLeft: props.stackColumnsAt === "never" ? gutter : "0",
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const mobileWidth = "100%";
|
|
78
|
+
const mobileMarginLeft = "0";
|
|
60
79
|
return {
|
|
61
|
-
|
|
62
|
-
"
|
|
63
|
-
"--column-width-
|
|
64
|
-
|
|
80
|
+
width,
|
|
81
|
+
"margin-left": gutter,
|
|
82
|
+
"--column-width-mobile": getMobileStyle({
|
|
83
|
+
stackedStyle: mobileWidth,
|
|
84
|
+
desktopStyle: width,
|
|
85
|
+
}),
|
|
86
|
+
"--column-margin-left-mobile": getMobileStyle({
|
|
87
|
+
stackedStyle: mobileMarginLeft,
|
|
88
|
+
desktopStyle: gutter,
|
|
89
|
+
}),
|
|
90
|
+
"--column-width-tablet": getTabletStyle({
|
|
91
|
+
stackedStyle: mobileWidth,
|
|
92
|
+
desktopStyle: width,
|
|
93
|
+
}),
|
|
94
|
+
"--column-margin-left-tablet": getTabletStyle({
|
|
95
|
+
stackedStyle: mobileMarginLeft,
|
|
96
|
+
desktopStyle: gutter,
|
|
97
|
+
}),
|
|
65
98
|
};
|
|
66
99
|
}
|
|
67
100
|
|
|
@@ -72,67 +105,43 @@ function Columns(props) {
|
|
|
72
105
|
return breakpointSizes[size].max;
|
|
73
106
|
}
|
|
74
107
|
|
|
75
|
-
function columnStyleObjects() {
|
|
76
|
-
return {
|
|
77
|
-
columns: {
|
|
78
|
-
small: {
|
|
79
|
-
flexDirection: "var(--flex-dir)",
|
|
80
|
-
alignItems: "stretch",
|
|
81
|
-
},
|
|
82
|
-
medium: {
|
|
83
|
-
flexDirection: "var(--flex-dir-tablet)",
|
|
84
|
-
alignItems: "stretch",
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
column: {
|
|
88
|
-
small: {
|
|
89
|
-
width: "var(--column-width) !important",
|
|
90
|
-
marginLeft: "var(--column-margin-left) !important",
|
|
91
|
-
},
|
|
92
|
-
medium: {
|
|
93
|
-
width: "var(--column-width-tablet) !important",
|
|
94
|
-
marginLeft: "var(--column-margin-left-tablet) !important",
|
|
95
|
-
},
|
|
96
|
-
},
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
|
|
100
108
|
function columnsStyles() {
|
|
101
109
|
return `
|
|
102
110
|
@media (max-width: ${getWidthForBreakpointSize("medium")}px) {
|
|
103
111
|
.${props.builderBlock.id}-breakpoints {
|
|
104
|
-
|
|
112
|
+
flex-direction: var(--flex-dir-tablet);
|
|
113
|
+
align-items: stretch;
|
|
105
114
|
}
|
|
106
115
|
|
|
107
116
|
.${props.builderBlock.id}-breakpoints > .builder-column {
|
|
108
|
-
|
|
117
|
+
width: var(--column-width-tablet) !important;
|
|
118
|
+
margin-left: var(--column-margin-left-tablet) !important;
|
|
109
119
|
}
|
|
110
120
|
}
|
|
111
121
|
|
|
112
122
|
@media (max-width: ${getWidthForBreakpointSize("small")}px) {
|
|
113
123
|
.${props.builderBlock.id}-breakpoints {
|
|
114
|
-
|
|
124
|
+
flex-direction: var(--flex-dir);
|
|
125
|
+
align-items: stretch;
|
|
115
126
|
}
|
|
116
127
|
|
|
117
128
|
.${props.builderBlock.id}-breakpoints > .builder-column {
|
|
118
|
-
|
|
129
|
+
width: var(--column-width-mobile) !important;
|
|
130
|
+
margin-left: var(--column-margin-left-mobile) !important;
|
|
119
131
|
}
|
|
120
132
|
},
|
|
121
133
|
`;
|
|
122
134
|
}
|
|
123
135
|
|
|
124
|
-
function reactNativeColumnsStyles() {
|
|
125
|
-
return columnStyleObjects.columns.small;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function reactNativeColumnStyles() {
|
|
129
|
-
return columnStyleObjects.column.small;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
136
|
const builderContext = useContext(BuilderContext);
|
|
133
137
|
|
|
134
138
|
return (
|
|
135
|
-
<View
|
|
139
|
+
<View
|
|
140
|
+
style={columnsCssVars()}
|
|
141
|
+
dataSet={{
|
|
142
|
+
"builder-block-name": "builder-columns",
|
|
143
|
+
}}
|
|
144
|
+
>
|
|
136
145
|
{TARGET !== "reactNative" ? (
|
|
137
146
|
<>
|
|
138
147
|
<RenderInlinedStyles styles={columnsStyles()} />
|
|
@@ -140,7 +149,13 @@ function Columns(props) {
|
|
|
140
149
|
) : null}
|
|
141
150
|
|
|
142
151
|
{props.columns?.map((column, index) => (
|
|
143
|
-
<View
|
|
152
|
+
<View
|
|
153
|
+
style={columnCssVars(index)}
|
|
154
|
+
dataSet={{
|
|
155
|
+
"builder-block-name": "builder-column",
|
|
156
|
+
}}
|
|
157
|
+
key={index}
|
|
158
|
+
>
|
|
144
159
|
<RenderBlocks
|
|
145
160
|
blocks={column.blocks}
|
|
146
161
|
path={`component.options.columns.${index}.blocks`}
|
|
@@ -14,8 +14,8 @@ import { getContent } from "../../functions/get-content/index.js";
|
|
|
14
14
|
import { TARGET } from "../../constants/target";
|
|
15
15
|
|
|
16
16
|
function Symbol(props) {
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
const [className, setClassName] = useState(() =>
|
|
18
|
+
[
|
|
19
19
|
...(TARGET === "vue2" || TARGET === "vue3"
|
|
20
20
|
? Object.keys(props.attributes.class)
|
|
21
21
|
: [props.attributes.class]),
|
|
@@ -26,20 +26,12 @@ function Symbol(props) {
|
|
|
26
26
|
: undefined,
|
|
27
27
|
]
|
|
28
28
|
.filter(Boolean)
|
|
29
|
-
.join(" ")
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const [fetchedContent, setFetchedContent] = useState(() => null);
|
|
33
|
-
|
|
34
|
-
function contentToUse() {
|
|
35
|
-
return props.symbol?.content || fetchedContent;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const builderContext = useContext(BuilderContext);
|
|
29
|
+
.join(" ")
|
|
30
|
+
);
|
|
39
31
|
|
|
40
|
-
|
|
41
|
-
const symbolToUse = props.symbol;
|
|
32
|
+
const [contentToUse, setContentToUse] = useState(() => props.symbol?.content);
|
|
42
33
|
|
|
34
|
+
function fetchContent() {
|
|
43
35
|
/**
|
|
44
36
|
* If:
|
|
45
37
|
* - we have a symbol prop
|
|
@@ -50,43 +42,59 @@ function Symbol(props) {
|
|
|
50
42
|
* then we want to re-fetch the symbol content.
|
|
51
43
|
*/
|
|
52
44
|
if (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
!fetchedContent &&
|
|
56
|
-
symbolToUse.model &&
|
|
45
|
+
!contentToUse &&
|
|
46
|
+
props.symbol?.model &&
|
|
57
47
|
// This is a hack, we should not need to check for this, but it is needed for Svelte.
|
|
58
48
|
builderContext?.apiKey
|
|
59
49
|
) {
|
|
60
50
|
getContent({
|
|
61
|
-
model:
|
|
51
|
+
model: props.symbol.model,
|
|
62
52
|
apiKey: builderContext.apiKey,
|
|
53
|
+
apiVersion: builderContext.apiVersion,
|
|
63
54
|
query: {
|
|
64
|
-
id:
|
|
55
|
+
id: props.symbol.entry,
|
|
65
56
|
},
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
|
|
57
|
+
})
|
|
58
|
+
.then((response) => {
|
|
59
|
+
if (response) {
|
|
60
|
+
setContentToUse(response);
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
.catch((err) => {
|
|
64
|
+
console.error("[Builder.io]: Could not fetch symbol content: ", err);
|
|
65
|
+
});
|
|
69
66
|
}
|
|
70
|
-
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const builderContext = useContext(BuilderContext);
|
|
70
|
+
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
fetchContent();
|
|
73
|
+
}, []);
|
|
74
|
+
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
fetchContent();
|
|
77
|
+
}, [props.symbol]);
|
|
71
78
|
|
|
72
79
|
return (
|
|
73
80
|
<View
|
|
74
81
|
{...props.attributes}
|
|
75
82
|
dataSet={{
|
|
76
|
-
class: className
|
|
83
|
+
class: className,
|
|
77
84
|
}}
|
|
78
85
|
>
|
|
79
86
|
<RenderContent
|
|
87
|
+
apiVersion={builderContext.apiVersion}
|
|
80
88
|
apiKey={builderContext.apiKey}
|
|
81
89
|
context={builderContext.context}
|
|
82
90
|
customComponents={Object.values(builderContext.registeredComponents)}
|
|
83
91
|
data={{
|
|
84
92
|
...props.symbol?.data,
|
|
85
93
|
...builderContext.state,
|
|
86
|
-
...
|
|
94
|
+
...contentToUse?.data?.state,
|
|
87
95
|
}}
|
|
88
96
|
model={props.symbol?.model}
|
|
89
|
-
content={contentToUse
|
|
97
|
+
content={contentToUse}
|
|
90
98
|
/>
|
|
91
99
|
</View>
|
|
92
100
|
);
|
|
@@ -308,6 +308,7 @@ function RenderContent(props) {
|
|
|
308
308
|
getContent({
|
|
309
309
|
model: props.model,
|
|
310
310
|
apiKey: props.apiKey,
|
|
311
|
+
apiVersion: props.apiVersion,
|
|
311
312
|
}).then((content) => {
|
|
312
313
|
if (content) {
|
|
313
314
|
mergeNewContent(content);
|
|
@@ -321,6 +322,11 @@ function RenderContent(props) {
|
|
|
321
322
|
}
|
|
322
323
|
}, []);
|
|
323
324
|
|
|
325
|
+
useEffect(() => {
|
|
326
|
+
if (props.content) {
|
|
327
|
+
mergeNewContent(props.content);
|
|
328
|
+
}
|
|
329
|
+
}, [props.content]);
|
|
324
330
|
useEffect(() => {
|
|
325
331
|
evaluateJsCode();
|
|
326
332
|
}, [useContent?.data?.jsCode, contentState]);
|
|
@@ -351,6 +357,7 @@ function RenderContent(props) {
|
|
|
351
357
|
setState: setContextState,
|
|
352
358
|
context: props.context || {},
|
|
353
359
|
apiKey: props.apiKey,
|
|
360
|
+
apiVersion: props.apiVersion,
|
|
354
361
|
registeredComponents: allRegisteredComponents,
|
|
355
362
|
}}
|
|
356
363
|
>
|
|
@@ -28,12 +28,16 @@ const generateContentUrl = (options) => {
|
|
|
28
28
|
model,
|
|
29
29
|
apiKey,
|
|
30
30
|
includeRefs = true,
|
|
31
|
-
locale
|
|
31
|
+
locale,
|
|
32
|
+
apiVersion = "v2"
|
|
32
33
|
} = options;
|
|
33
34
|
if (!apiKey) {
|
|
34
35
|
throw new Error("Missing API key");
|
|
35
36
|
}
|
|
36
|
-
|
|
37
|
+
if (!["v2", "v3"].includes(apiVersion)) {
|
|
38
|
+
throw new Error(`Invalid apiVersion: expected 'v2' or 'v3', received '${apiVersion}'`);
|
|
39
|
+
}
|
|
40
|
+
const url = new URL(`https://cdn.builder.io/api/${apiVersion}/content/${model}?apiKey=${apiKey}&limit=${limit}&noTraverse=${noTraverse}&includeRefs=${includeRefs}${locale ? `&locale=${locale}` : ""}`);
|
|
37
41
|
const queryOptions = __spreadValues(__spreadValues({}, getBuilderSearchParamsFromWindow()), normalizeSearchParams(options.options || {}));
|
|
38
42
|
const flattened = flatten(queryOptions);
|
|
39
43
|
for (const key in flattened) {
|
|
@@ -28,4 +28,46 @@ describe("Generate Content URL", () => {
|
|
|
28
28
|
});
|
|
29
29
|
expect(output).toMatchSnapshot();
|
|
30
30
|
});
|
|
31
|
+
test("generate content url with apiVersion as v2", () => {
|
|
32
|
+
const output = generateContentUrl({
|
|
33
|
+
apiKey: testKey,
|
|
34
|
+
model: testModel,
|
|
35
|
+
query: { id: testId },
|
|
36
|
+
options,
|
|
37
|
+
apiVersion: "v2"
|
|
38
|
+
});
|
|
39
|
+
expect(output).toMatchSnapshot();
|
|
40
|
+
});
|
|
41
|
+
test("generate content url with apiVersion as v3", () => {
|
|
42
|
+
const output = generateContentUrl({
|
|
43
|
+
apiKey: testKey,
|
|
44
|
+
model: testModel,
|
|
45
|
+
query: { id: testId },
|
|
46
|
+
options,
|
|
47
|
+
apiVersion: "v3"
|
|
48
|
+
});
|
|
49
|
+
expect(output).toMatchSnapshot();
|
|
50
|
+
});
|
|
51
|
+
test("throw error when trying to generate content url with apiVersion as v1", () => {
|
|
52
|
+
expect(() => {
|
|
53
|
+
generateContentUrl({
|
|
54
|
+
apiKey: testKey,
|
|
55
|
+
model: testModel,
|
|
56
|
+
query: { id: testId },
|
|
57
|
+
options,
|
|
58
|
+
apiVersion: "v1"
|
|
59
|
+
});
|
|
60
|
+
}).toThrow(`Invalid apiVersion: expected 'v2' or 'v3', received 'v1'`);
|
|
61
|
+
});
|
|
62
|
+
test("throw error when trying to generate content url with an invalid apiVersion value", () => {
|
|
63
|
+
expect(() => {
|
|
64
|
+
generateContentUrl({
|
|
65
|
+
apiKey: testKey,
|
|
66
|
+
model: testModel,
|
|
67
|
+
query: { id: testId },
|
|
68
|
+
options,
|
|
69
|
+
apiVersion: "INVALID_API_VERSION"
|
|
70
|
+
});
|
|
71
|
+
}).toThrow(`Invalid apiVersion: expected 'v2' or 'v3', received 'INVALID_API_VERSION'`);
|
|
72
|
+
});
|
|
31
73
|
});
|
|
File without changes
|