@builder.io/sdk-react-native 0.0.1-55 → 0.0.1-58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/CHANGELOG.md +10 -1
  2. package/README.md +4 -0
  3. package/package.json +1 -1
  4. package/src/blocks/custom-code/custom-code.js +1 -1
  5. package/src/blocks/embed/embed.js +1 -1
  6. package/src/blocks/form/form.js +1 -1
  7. package/src/components/render-block/block-styles.js +16 -4
  8. package/src/components/render-block/render-block.js +26 -29
  9. package/src/components/render-block/render-component.js +33 -0
  10. package/src/components/render-blocks.js +10 -6
  11. package/src/functions/get-block-component-options.js +6 -1
  12. package/src/blocks/button/button.lite.tsx +0 -25
  13. package/src/blocks/columns/columns.lite.tsx +0 -70
  14. package/src/blocks/custom-code/custom-code.lite.tsx +0 -59
  15. package/src/blocks/embed/embed.lite.tsx +0 -61
  16. package/src/blocks/form/form.lite.tsx +0 -254
  17. package/src/blocks/fragment/fragment.lite.tsx +0 -10
  18. package/src/blocks/image/image.lite.tsx +0 -67
  19. package/src/blocks/img/img.lite.tsx +0 -18
  20. package/src/blocks/input/input.lite.tsx +0 -20
  21. package/src/blocks/raw-text/raw-text.lite.tsx +0 -6
  22. package/src/blocks/section/section.lite.tsx +0 -19
  23. package/src/blocks/select/select.lite.tsx +0 -23
  24. package/src/blocks/submit-button/submit-button.lite.tsx +0 -10
  25. package/src/blocks/symbol/symbol.lite.tsx +0 -60
  26. package/src/blocks/text/text.lite.tsx +0 -6
  27. package/src/blocks/textarea/textarea.lite.tsx +0 -14
  28. package/src/blocks/video/video.lite.tsx +0 -27
  29. package/src/components/error-boundary.lite.tsx +0 -6
  30. package/src/components/render-block/block-styles.lite.tsx +0 -35
  31. package/src/components/render-block/render-block.lite.tsx +0 -144
  32. package/src/components/render-blocks.lite.tsx +0 -65
  33. package/src/components/render-content/components/render-styles.lite.tsx +0 -70
  34. package/src/components/render-content/render-content.lite.tsx +0 -283
  35. package/src/components/render-inlined-styles.lite.tsx +0 -31
@@ -1,254 +0,0 @@
1
- import * as React from "react";
2
- import { View, StyleSheet, Image, Text } from "react-native";
3
- import { useState, useRef } from "react";
4
- import RenderBlock from "../../components/render-block/render-block.lite";
5
- import { isEditing } from "../../functions/is-editing.js";
6
-
7
- export default function FormComponent(props) {
8
- const [formState, setFormState] = useState(() => "unsubmitted");
9
-
10
- const [responseData, setResponseData] = useState(() => null);
11
-
12
- const [formErrorMessage, setFormErrorMessage] = useState(() => "");
13
-
14
- function submissionState() {
15
- return (isEditing() && props.previewState) || formState;
16
- }
17
-
18
- function onSubmit(event) {
19
- const sendWithJs = props.sendWithJs || props.sendSubmissionsTo === "email";
20
-
21
- if (props.sendSubmissionsTo === "zapier") {
22
- event.preventDefault();
23
- } else if (sendWithJs) {
24
- if (!(props.action || props.sendSubmissionsTo === "email")) {
25
- event.preventDefault();
26
- return;
27
- }
28
-
29
- event.preventDefault();
30
- const el = event.currentTarget;
31
- const headers = props.customHeaders || {};
32
- let body;
33
- const formData = new FormData(el); // TODO: maybe support null
34
-
35
- const formPairs = Array.from(
36
- event.currentTarget.querySelectorAll("input,select,textarea")
37
- )
38
- .filter((el) => !!el.name)
39
- .map((el) => {
40
- let value;
41
- const key = el.name;
42
-
43
- if (el instanceof HTMLInputElement) {
44
- if (el.type === "radio") {
45
- if (el.checked) {
46
- value = el.name;
47
- return {
48
- key,
49
- value,
50
- };
51
- }
52
- } else if (el.type === "checkbox") {
53
- value = el.checked;
54
- } else if (el.type === "number" || el.type === "range") {
55
- const num = el.valueAsNumber;
56
-
57
- if (!isNaN(num)) {
58
- value = num;
59
- }
60
- } else if (el.type === "file") {
61
- // TODO: one vs multiple files
62
- value = el.files;
63
- } else {
64
- value = el.value;
65
- }
66
- } else {
67
- value = el.value;
68
- }
69
-
70
- return {
71
- key,
72
- value,
73
- };
74
- });
75
- let contentType = props.contentType;
76
-
77
- if (props.sendSubmissionsTo === "email") {
78
- contentType = "multipart/form-data";
79
- }
80
-
81
- Array.from(formPairs).forEach(({ value }) => {
82
- if (
83
- value instanceof File ||
84
- (Array.isArray(value) && value[0] instanceof File) ||
85
- value instanceof FileList
86
- ) {
87
- contentType = "multipart/form-data";
88
- }
89
- }); // TODO: send as urlEncoded or multipart by default
90
- // because of ease of use and reliability in browser API
91
- // for encoding the form?
92
-
93
- if (contentType !== "application/json") {
94
- body = formData;
95
- } else {
96
- // Json
97
- const json = {};
98
- Array.from(formPairs).forEach(({ value, key }) => {
99
- set(json, key, value);
100
- });
101
- body = JSON.stringify(json);
102
- }
103
-
104
- if (contentType && contentType !== "multipart/form-data") {
105
- if (
106
- /* Zapier doesn't allow content-type header to be sent from browsers */ !(
107
- sendWithJs && props.action?.includes("zapier.com")
108
- )
109
- ) {
110
- headers["content-type"] = contentType;
111
- }
112
- }
113
- const presubmitEvent = new CustomEvent("presubmit", { detail: { body } });
114
- if (formRef.current) {
115
- formRef.current.dispatchEvent(presubmitEvent);
116
- if (presubmitEvent.defaultPrevented) {
117
- return;
118
- }
119
- }
120
- setFormState("sending");
121
- const formUrl = `${
122
- builder.env === "dev" ? "http://localhost:5000" : "https://builder.io"
123
- }/api/v1/form-submit?apiKey=${builder.apiKey}&to=${btoa(
124
- props.sendSubmissionsToEmail || ""
125
- )}&name=${encodeURIComponent(props.name || "")}`;
126
- fetch(
127
- props.sendSubmissionsTo === "email"
128
- ? formUrl
129
- : props.action /* TODO: throw error if no action URL */,
130
- { body, headers, method: props.method || "post" }
131
- ).then(
132
- async (res) => {
133
- let body;
134
- const contentType = res.headers.get("content-type");
135
- if (contentType && contentType.indexOf("application/json") !== -1) {
136
- body = await res.json();
137
- } else {
138
- body = await res.text();
139
- }
140
- if (!res.ok && props.errorMessagePath) {
141
- /* TODO: allow supplying an error formatter function */ let message =
142
- get(body, props.errorMessagePath);
143
- if (message) {
144
- if (typeof message !== "string") {
145
- /* TODO: ideally convert json to yaml so it woul dbe like error: - email has been taken */ message =
146
- JSON.stringify(message);
147
- }
148
- setFormErrorMessage(message);
149
- }
150
- }
151
- setResponseData(body);
152
- setFormState(res.ok ? "success" : "error");
153
- if (res.ok) {
154
- const submitSuccessEvent = new CustomEvent("submit:success", {
155
- detail: { res, body },
156
- });
157
- if (formRef.current) {
158
- formRef.current.dispatchEvent(submitSuccessEvent);
159
- if (submitSuccessEvent.defaultPrevented) {
160
- return;
161
- }
162
- /* TODO: option to turn this on/off? */ if (
163
- props.resetFormOnSubmit !== false
164
- ) {
165
- formRef.current.reset();
166
- }
167
- }
168
- /* TODO: client side route event first that can be preventDefaulted */ if (
169
- props.successUrl
170
- ) {
171
- if (formRef.current) {
172
- const event = new CustomEvent("route", {
173
- detail: { url: props.successUrl },
174
- });
175
- formRef.current.dispatchEvent(event);
176
- if (!event.defaultPrevented) {
177
- location.href = props.successUrl;
178
- }
179
- } else {
180
- location.href = props.successUrl;
181
- }
182
- }
183
- }
184
- },
185
- (err) => {
186
- const submitErrorEvent = new CustomEvent("submit:error", {
187
- detail: { error: err },
188
- });
189
- if (formRef.current) {
190
- formRef.current.dispatchEvent(submitErrorEvent);
191
- if (submitErrorEvent.defaultPrevented) {
192
- return;
193
- }
194
- }
195
- setResponseData(err);
196
- setFormState("error");
197
- }
198
- );
199
- }
200
- }
201
- const formRef = useRef();
202
- return (
203
- <View
204
- {...props.attributes}
205
- validate={props.validate}
206
- ref={formRef}
207
- action={!props.sendWithJs && props.action}
208
- method={props.method}
209
- name={props.name}
210
- onSubmit={(event) => onSubmit(event)}
211
- >
212
- {" "}
213
- {props.builderBlock && props.builderBlock.children ? (
214
- <>
215
- {props.builderBlock?.children?.map((block) => (
216
- <RenderBlock block={block} />
217
- ))}
218
- </>
219
- ) : null}{" "}
220
- {submissionState() === "error" ? (
221
- <>
222
- <BuilderBlocks dataPath="errorMessage" blocks={props.errorMessage} />
223
- </>
224
- ) : null}{" "}
225
- {submissionState() === "sending" ? (
226
- <>
227
- <BuilderBlocks
228
- dataPath="sendingMessage"
229
- blocks={props.sendingMessage}
230
- />
231
- </>
232
- ) : null}{" "}
233
- {submissionState() === "error" && responseData ? (
234
- <>
235
- <View style={styles.view1}>
236
- {" "}
237
- <Text>{JSON.stringify(responseData, null, 2)}</Text>{" "}
238
- </View>
239
- </>
240
- ) : null}{" "}
241
- {submissionState() === "success" ? (
242
- <>
243
- <BuilderBlocks
244
- dataPath="successMessage"
245
- blocks={props.successMessage}
246
- />
247
- </>
248
- ) : null}{" "}
249
- </View>
250
- );
251
- }
252
- const styles = StyleSheet.create({
253
- view1: { padding: 10, color: "red", textAlign: "center" },
254
- });
@@ -1,10 +0,0 @@
1
- import * as React from "react";
2
- import { View, StyleSheet, Image, Text } from "react-native";
3
-
4
- export default function FragmentComponent(props) {
5
- return (
6
- <View>
7
- <Text>{props.children}</Text>
8
- </View>
9
- );
10
- }
@@ -1,67 +0,0 @@
1
- import * as React from "react";
2
- import { View, StyleSheet, Image, Text } from "react-native";
3
-
4
- export default function Image(props) {
5
- return (
6
- <View style={styles.view1}>
7
- <View>
8
- <View
9
- loading="lazy"
10
- alt={props.altText}
11
- role={props.altText ? "presentation" : undefined}
12
- style={styles.view2}
13
- src={props.image}
14
- srcSet={props.srcset}
15
- sizes={props.sizes}
16
- />
17
-
18
- <View srcSet={props.srcset} />
19
- </View>
20
-
21
- {props.aspectRatio &&
22
- !(props.fitContent && props.builderBlock?.children?.length) ? (
23
- <View style={styles.view3}>
24
- <Text> </Text>
25
- </View>
26
- ) : null}
27
-
28
- {props.builderBlock?.children?.length && props.fitContent ? (
29
- <>
30
- <Text>{props.children}</Text>
31
- </>
32
- ) : null}
33
-
34
- {!props.fitContent ? (
35
- <>
36
- <View style={styles.view4}>
37
- <Text>{props.children}</Text>
38
- </View>
39
- </>
40
- ) : null}
41
- </View>
42
- );
43
- }
44
-
45
- const styles = StyleSheet.create({
46
- view1: { position: "relative" },
47
- view2: {
48
- opacity: 1,
49
- transition: "opacity 0.2s ease-in-out",
50
- position: "absolute",
51
- height: 100,
52
- width: 100,
53
- top: 0,
54
- left: 0,
55
- },
56
- view3: { width: 100, pointerEvents: "none", fontSize: 0 },
57
- view4: {
58
- display: "flex",
59
- flexDirection: "column",
60
- alignItems: "stretch",
61
- position: "absolute",
62
- top: 0,
63
- left: 0,
64
- width: 100,
65
- height: 100,
66
- },
67
- });
@@ -1,18 +0,0 @@
1
- import * as React from "react";
2
- import { View, StyleSheet, Image, Text } from "react-native";
3
- import { isEditing } from "../../functions/is-editing.js";
4
-
5
- export default function ImgComponent(props) {
6
- return (
7
- <View
8
- {...props.attributes}
9
- style={{
10
- objectFit: props.backgroundSize || "cover",
11
- objectPosition: props.backgroundPosition || "center",
12
- }}
13
- key={(isEditing() && props.imgSrc) || "default-key"}
14
- alt={props.altText}
15
- src={props.imgSrc}
16
- />
17
- );
18
- }
@@ -1,20 +0,0 @@
1
- import * as React from "react";
2
- import { View, StyleSheet, Image, Text } from "react-native";
3
- import { isEditing } from "../../functions/is-editing.js";
4
-
5
- export default function FormInputComponent(props) {
6
- return (
7
- <View
8
- {...props.attributes}
9
- key={
10
- isEditing() && props.defaultValue ? props.defaultValue : "default-key"
11
- }
12
- placeholder={props.placeholder}
13
- type={props.type}
14
- name={props.name}
15
- value={props.value}
16
- defaultValue={props.defaultValue}
17
- required={props.required}
18
- />
19
- );
20
- }
@@ -1,6 +0,0 @@
1
- import * as React from "react";
2
- import { View, StyleSheet, Image, Text } from "react-native";
3
-
4
- export default function RawText(props) {
5
- return <View dangerouslySetInnerHTML={{ __html: props.text || "" }} />;
6
- }
@@ -1,19 +0,0 @@
1
- import * as React from "react";
2
- import { View, StyleSheet, Image, Text } from "react-native";
3
-
4
- export default function SectionComponent(props) {
5
- return (
6
- <View
7
- {...props.attributes}
8
- style={
9
- props.maxWidth && typeof props.maxWidth === "number"
10
- ? {
11
- maxWidth: props.maxWidth,
12
- }
13
- : undefined
14
- }
15
- >
16
- <Text>{props.children}</Text>
17
- </View>
18
- );
19
- }
@@ -1,23 +0,0 @@
1
- import * as React from "react";
2
- import { View, StyleSheet, Image, Text } from "react-native";
3
- import { isEditing } from "../../functions/is-editing.js";
4
-
5
- export default function SelectComponent(props) {
6
- return (
7
- <View
8
- {...props.attributes}
9
- value={props.value}
10
- key={
11
- isEditing() && props.defaultValue ? props.defaultValue : "default-key"
12
- }
13
- defaultValue={props.defaultValue}
14
- name={props.name}
15
- >
16
- {props.options?.map((option) => (
17
- <View value={option.value}>
18
- <Text>{option.name || option.value}</Text>
19
- </View>
20
- ))}
21
- </View>
22
- );
23
- }
@@ -1,10 +0,0 @@
1
- import * as React from "react";
2
- import { View, StyleSheet, Image, Text } from "react-native";
3
-
4
- export default function SubmitButton(props) {
5
- return (
6
- <View {...props.attributes} type="submit">
7
- <Text>{props.text}</Text>
8
- </View>
9
- );
10
- }
@@ -1,60 +0,0 @@
1
- import * as React from "react";
2
- import { View, StyleSheet, Image, Text } from "react-native";
3
- import { useState, useContext, useEffect } from "react";
4
- import RenderContent from "../../components/render-content/render-content.lite";
5
- import BuilderContext from "../../context/builder.context";
6
- import { getContent } from "../../functions/get-content/index.js";
7
-
8
- export default function Symbol(props) {
9
- const [className, setClassName] = useState(() => "builder-symbol");
10
-
11
- const [content, setContent] = useState(() => null);
12
-
13
- const builderContext = useContext(BuilderContext);
14
-
15
- useEffect(() => {
16
- setContent(props.symbol?.content);
17
- }, []);
18
-
19
- useEffect(() => {
20
- const symbolToUse = props.symbol;
21
-
22
- if (symbolToUse && !symbolToUse.content && !content && symbolToUse.model) {
23
- getContent({
24
- model: symbolToUse.model,
25
- apiKey: builderContext.apiKey,
26
- options: {
27
- entry: symbolToUse.entry,
28
- },
29
- }).then((response) => {
30
- setContent(response);
31
- });
32
- }
33
- }, [
34
- props.symbol?.content,
35
- props.symbol?.model,
36
- props.symbol?.entry,
37
- content,
38
- ]);
39
-
40
- return (
41
- <View
42
- {...props.attributes}
43
- dataSet={{
44
- class: className,
45
- }}
46
- >
47
- <RenderContent
48
- apiKey={builderContext.apiKey}
49
- context={builderContext.context}
50
- data={{
51
- ...props.symbol?.data,
52
- ...builderContext.state,
53
- ...props.symbol?.content?.data?.state,
54
- }}
55
- model={props.symbol?.model}
56
- content={content}
57
- />
58
- </View>
59
- );
60
- }
@@ -1,6 +0,0 @@
1
- import * as React from "react";
2
- import { View, StyleSheet, Image, Text } from "react-native";
3
-
4
- export default function Text(props) {
5
- return <View dangerouslySetInnerHTML={{ __html: props.text }} />;
6
- }
@@ -1,14 +0,0 @@
1
- import * as React from "react";
2
- import { View, StyleSheet, Image, Text } from "react-native";
3
-
4
- export default function Textarea(props) {
5
- return (
6
- <View
7
- {...props.attributes}
8
- placeholder={props.placeholder}
9
- name={props.name}
10
- value={props.value}
11
- defaultValue={props.defaultValue}
12
- />
13
- );
14
- }
@@ -1,27 +0,0 @@
1
- import * as React from "react";
2
- import { View, StyleSheet, Image, Text } from "react-native";
3
-
4
- export default function Video(props) {
5
- return (
6
- <View
7
- {...props.attributes}
8
- preload="none"
9
- style={{
10
- width: "100%",
11
- height: "100%",
12
- ...props.attributes?.style,
13
- objectFit: props.fit,
14
- objectPosition: props.position,
15
- // Hack to get object fit to work as expected and
16
- // not have the video overflow
17
- borderRadius: 1,
18
- }}
19
- key={props.video || "no-src"}
20
- poster={props.posterImage}
21
- autoPlay={props.autoPlay}
22
- muted={props.muted}
23
- controls={props.controls}
24
- loop={props.loop}
25
- />
26
- );
27
- }
@@ -1,6 +0,0 @@
1
- import * as React from "react";
2
- import { View, StyleSheet, Image, Text } from "react-native";
3
-
4
- export default function ErrorBoundary(props) {
5
- return <></>;
6
- }
@@ -1,35 +0,0 @@
1
- import * as React from "react";
2
- import { View, StyleSheet, Image, Text } from "react-native";
3
- import RenderInlinedStyles from "../render-inlined-styles.lite";
4
-
5
- export default function BlockStyles(props) {
6
- function camelToKebabCase(string) {
7
- return string
8
- .replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, "$1-$2")
9
- .toLowerCase();
10
- }
11
-
12
- function css() {
13
- // TODO: media queries
14
- const styleObject = props.block.responsiveStyles?.large;
15
-
16
- if (!styleObject) {
17
- return "";
18
- }
19
-
20
- let str = `.${props.block.id} {`;
21
-
22
- for (const key in styleObject) {
23
- const value = styleObject[key];
24
-
25
- if (typeof value === "string") {
26
- str += `${camelToKebabCase(key)}: ${value};`;
27
- }
28
- }
29
-
30
- str += "}";
31
- return str;
32
- }
33
-
34
- return <RenderInlinedStyles styles={css()} />;
35
- }