@builder.io/sdk-react-native 0.1.1 → 0.1.3

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 (183) hide show
  1. package/dist/blocks/BaseText.js +40 -0
  2. package/dist/blocks/button/button.js +39 -0
  3. package/dist/blocks/button/component-info.js +42 -0
  4. package/dist/blocks/columns/columns.js +149 -0
  5. package/dist/blocks/columns/component-info.js +244 -0
  6. package/dist/blocks/custom-code/component-info.js +32 -0
  7. package/dist/blocks/custom-code/custom-code.js +76 -0
  8. package/dist/blocks/embed/component-info.js +47 -0
  9. package/dist/blocks/embed/embed.js +67 -0
  10. package/dist/blocks/embed/helpers.js +10 -0
  11. package/dist/blocks/form/component-info.js +263 -0
  12. package/dist/blocks/form/form.js +232 -0
  13. package/dist/blocks/fragment/component-info.js +12 -0
  14. package/dist/blocks/fragment/fragment.js +36 -0
  15. package/dist/blocks/image/component-info.js +152 -0
  16. package/dist/blocks/image/image.helpers.js +49 -0
  17. package/dist/blocks/image/image.js +81 -0
  18. package/dist/blocks/img/component-info.js +21 -0
  19. package/dist/blocks/img/img.js +35 -0
  20. package/dist/blocks/input/component-info.js +75 -0
  21. package/dist/blocks/input/input.js +32 -0
  22. package/dist/blocks/raw-text/component-info.js +17 -0
  23. package/dist/blocks/raw-text/raw-text.js +31 -0
  24. package/dist/blocks/section/component-info.js +50 -0
  25. package/dist/blocks/section/section.js +49 -0
  26. package/dist/blocks/select/component-info.js +60 -0
  27. package/dist/blocks/select/select.js +38 -0
  28. package/dist/blocks/submit-button/component-info.js +29 -0
  29. package/dist/blocks/submit-button/submit-button.js +36 -0
  30. package/dist/blocks/symbol/component-info.js +44 -0
  31. package/dist/blocks/symbol/symbol.js +97 -0
  32. package/dist/blocks/text/component-info.js +25 -0
  33. package/dist/blocks/text/text.js +112 -0
  34. package/dist/blocks/textarea/component-info.js +48 -0
  35. package/dist/blocks/textarea/textarea.js +31 -0
  36. package/dist/blocks/util.js +9 -0
  37. package/dist/blocks/video/component-info.js +107 -0
  38. package/dist/blocks/video/video.js +81 -0
  39. package/dist/components/render-block/block-styles.js +79 -0
  40. package/dist/components/render-block/render-block.helpers.js +109 -0
  41. package/dist/components/render-block/render-block.js +154 -0
  42. package/dist/components/render-block/render-component-with-context.js +45 -0
  43. package/dist/components/render-block/render-component.js +50 -0
  44. package/dist/components/render-block/render-repeated-block.js +45 -0
  45. package/dist/components/render-block/types.js +1 -0
  46. package/dist/components/render-blocks.js +76 -0
  47. package/dist/components/render-content/builder-editing.js +31 -0
  48. package/dist/components/render-content/components/render-styles.js +91 -0
  49. package/dist/components/render-content/index.js +8 -0
  50. package/dist/components/render-content/render-content.js +325 -0
  51. package/dist/components/render-inlined-styles.js +42 -0
  52. package/dist/constants/builder-registered-components.js +46 -0
  53. package/dist/constants/device-sizes.js +49 -0
  54. package/dist/constants/target.js +5 -0
  55. package/dist/context/builder.context.js +13 -0
  56. package/dist/context/types.js +1 -0
  57. package/dist/functions/camel-to-kebab-case.js +5 -0
  58. package/dist/functions/evaluate.js +25 -0
  59. package/dist/functions/event-handler-name.js +8 -0
  60. package/dist/functions/extract-text-styles.js +23 -0
  61. package/dist/functions/fast-clone.js +5 -0
  62. package/dist/functions/get-block-actions-handler.js +12 -0
  63. package/dist/functions/get-block-actions.js +19 -0
  64. package/dist/functions/get-block-component-options.js +29 -0
  65. package/dist/functions/get-block-properties.js +51 -0
  66. package/dist/functions/get-block-tag.js +8 -0
  67. package/dist/functions/get-builder-search-params/fn.test.js +15 -0
  68. package/dist/functions/get-builder-search-params/index.js +39 -0
  69. package/dist/functions/get-content/ab-testing.js +89 -0
  70. package/dist/functions/get-content/generate-content-url.js +44 -0
  71. package/dist/functions/get-content/generate-content-url.test.js +33 -0
  72. package/dist/functions/get-content/index.js +68 -0
  73. package/dist/functions/get-content/types.js +1 -0
  74. package/dist/functions/get-fetch.js +15 -0
  75. package/dist/functions/get-global-this.js +19 -0
  76. package/dist/functions/get-processed-block.js +52 -0
  77. package/dist/functions/get-processed-block.test.js +34 -0
  78. package/dist/functions/get-react-native-block-styles.js +30 -0
  79. package/dist/functions/if-target.js +13 -0
  80. package/dist/functions/is-browser.js +7 -0
  81. package/dist/functions/is-editing.js +8 -0
  82. package/dist/functions/is-iframe.js +8 -0
  83. package/dist/functions/is-previewing.js +15 -0
  84. package/dist/functions/on-change.js +28 -0
  85. package/dist/functions/on-change.test.js +21 -0
  86. package/dist/functions/register-component.js +65 -0
  87. package/dist/functions/register.js +31 -0
  88. package/dist/functions/sanitize-react-native-block-styles.js +71 -0
  89. package/dist/functions/set-editor-settings.js +16 -0
  90. package/dist/functions/set.js +12 -0
  91. package/dist/functions/set.test.js +18 -0
  92. package/dist/functions/track/helpers.js +53 -0
  93. package/dist/functions/track/index.js +124 -0
  94. package/dist/functions/track/interaction.js +51 -0
  95. package/dist/functions/track.js +120 -0
  96. package/dist/functions/transform-block-properties.js +18 -0
  97. package/dist/functions/transform-block.js +38 -0
  98. package/dist/helpers/ab-tests.js +10 -0
  99. package/dist/helpers/cookie.js +70 -0
  100. package/dist/helpers/css.js +33 -0
  101. package/dist/helpers/flatten.js +35 -0
  102. package/dist/helpers/localStorage.js +31 -0
  103. package/dist/helpers/nullable.js +5 -0
  104. package/dist/helpers/sessionId.js +51 -0
  105. package/dist/helpers/time.js +6 -0
  106. package/dist/helpers/url.js +14 -0
  107. package/dist/helpers/url.test.js +23 -0
  108. package/dist/helpers/uuid.js +14 -0
  109. package/dist/helpers/visitorId.js +33 -0
  110. package/dist/index-helpers/blocks-exports.js +22 -0
  111. package/dist/index-helpers/top-of-file.js +3 -0
  112. package/dist/index.js +29 -0
  113. package/dist/scripts/init-editing.js +97 -0
  114. package/dist/types/builder-block.js +1 -0
  115. package/dist/types/builder-content.js +1 -0
  116. package/dist/types/can-track.js +1 -0
  117. package/dist/types/components.js +1 -0
  118. package/dist/types/deep-partial.js +1 -0
  119. package/dist/types/element.js +1 -0
  120. package/dist/types/targets.js +1 -0
  121. package/dist/types/typescript.js +1 -0
  122. package/package.json +13 -5
  123. package/src/blocks/BaseText.jsx +17 -0
  124. package/src/blocks/button/button.jsx +28 -0
  125. package/src/blocks/columns/{columns.js → columns.jsx} +59 -36
  126. package/src/blocks/custom-code/{custom-code.js → custom-code.jsx} +22 -15
  127. package/src/blocks/embed/{embed.js → embed.jsx} +12 -11
  128. package/src/blocks/form/form.jsx +251 -0
  129. package/src/blocks/fragment/fragment.jsx +11 -0
  130. package/src/blocks/image/image.jsx +78 -0
  131. package/src/blocks/img/img.jsx +18 -0
  132. package/src/blocks/input/input.jsx +20 -0
  133. package/src/blocks/raw-text/raw-text.jsx +6 -0
  134. package/src/blocks/section/section.jsx +29 -0
  135. package/src/blocks/select/select.jsx +24 -0
  136. package/src/blocks/submit-button/submit-button.jsx +11 -0
  137. package/src/blocks/symbol/symbol.jsx +86 -0
  138. package/src/blocks/text/text.jsx +100 -0
  139. package/src/blocks/textarea/textarea.jsx +14 -0
  140. package/src/blocks/video/video.jsx +70 -0
  141. package/src/components/render-block/block-styles.jsx +70 -0
  142. package/src/components/render-block/render-block.helpers.js +90 -0
  143. package/src/components/render-block/render-block.jsx +183 -0
  144. package/src/components/render-block/render-component.jsx +45 -0
  145. package/src/components/render-block/render-repeated-block.jsx +22 -0
  146. package/src/components/render-blocks.jsx +86 -0
  147. package/src/components/render-content/builder-editing.jsx +6 -0
  148. package/src/components/render-content/components/{render-styles.js → render-styles.jsx} +21 -14
  149. package/src/components/render-content/render-content.jsx +373 -0
  150. package/src/components/render-inlined-styles.jsx +29 -0
  151. package/src/functions/track/helpers.js +50 -0
  152. package/src/functions/{track.js → track/index.js} +10 -6
  153. package/src/functions/track/interaction.js +53 -0
  154. package/src/helpers/localStorage.js +1 -0
  155. package/src/helpers/sessionId.js +4 -1
  156. package/src/helpers/url.js +3 -0
  157. package/src/helpers/url.test.js +6 -0
  158. package/src/helpers/visitorId.js +1 -0
  159. package/src/index.js +1 -1
  160. package/src/blocks/BaseText.js +0 -31
  161. package/src/blocks/button/button.js +0 -36
  162. package/src/blocks/form/form.js +0 -222
  163. package/src/blocks/fragment/fragment.js +0 -9
  164. package/src/blocks/image/image.js +0 -55
  165. package/src/blocks/img/img.js +0 -33
  166. package/src/blocks/input/input.js +0 -36
  167. package/src/blocks/raw-text/raw-text.js +0 -10
  168. package/src/blocks/section/section.js +0 -41
  169. package/src/blocks/select/select.js +0 -37
  170. package/src/blocks/submit-button/submit-button.js +0 -27
  171. package/src/blocks/symbol/symbol.js +0 -73
  172. package/src/blocks/text/text.js +0 -67
  173. package/src/blocks/textarea/textarea.js +0 -32
  174. package/src/blocks/video/video.js +0 -55
  175. package/src/components/render-block/block-styles.js +0 -50
  176. package/src/components/render-block/render-block.js +0 -202
  177. package/src/components/render-block/render-component-with-context.js +0 -35
  178. package/src/components/render-block/render-component.js +0 -35
  179. package/src/components/render-block/render-repeated-block.js +0 -33
  180. package/src/components/render-blocks.js +0 -63
  181. package/src/components/render-content/builder-editing.js +0 -8
  182. package/src/components/render-content/render-content.js +0 -296
  183. package/src/components/render-inlined-styles.js +0 -17
@@ -1,40 +1,50 @@
1
1
  import * as React from "react";
2
- import { View, StyleSheet } from "react-native";
2
+ import { View, StyleSheet, Image, Text } from "react-native";
3
3
  import RenderBlocks from "../../components/render-blocks.js";
4
4
  import { getSizesForBreakpoints } from "../../constants/device-sizes";
5
5
  import RenderInlinedStyles from "../../components/render-inlined-styles.js";
6
6
  import { TARGET } from "../../constants/target.js";
7
7
  import { convertStyleMapToCSS } from "../../helpers/css";
8
- function Columns(props) {
9
- var _a;
8
+
9
+ export default function Columns(props) {
10
10
  function getGutterSize() {
11
11
  return typeof props.space === "number" ? props.space || 0 : 20;
12
12
  }
13
+
13
14
  function getColumns() {
14
15
  return props.columns || [];
15
16
  }
17
+
16
18
  function getWidth(index) {
17
- var _a2;
18
19
  const columns = getColumns();
19
- return ((_a2 = columns[index]) == null ? void 0 : _a2.width) || 100 / columns.length;
20
+ return columns[index]?.width || 100 / columns.length;
20
21
  }
22
+
21
23
  function getColumnCssWidth(index) {
22
24
  const columns = getColumns();
23
25
  const gutterSize = getGutterSize();
24
- const subtractWidth = gutterSize * (columns.length - 1) / columns.length;
26
+ const subtractWidth = (gutterSize * (columns.length - 1)) / columns.length;
25
27
  return `calc(${getWidth(index)}% - ${subtractWidth}px)`;
26
28
  }
29
+
27
30
  function maybeApplyForTablet(prop) {
28
31
  const _stackColumnsAt = props.stackColumnsAt || "tablet";
29
32
  return _stackColumnsAt === "tablet" ? prop : "inherit";
30
33
  }
34
+
31
35
  function columnsCssVars() {
32
- const flexDir = props.stackColumnsAt === "never" ? "inherit" : props.reverseColumnsWhenStacked ? "column-reverse" : "column";
36
+ const flexDir =
37
+ props.stackColumnsAt === "never"
38
+ ? "inherit"
39
+ : props.reverseColumnsWhenStacked
40
+ ? "column-reverse"
41
+ : "column";
33
42
  return {
34
43
  "--flex-dir": flexDir,
35
- "--flex-dir-tablet": maybeApplyForTablet(flexDir)
44
+ "--flex-dir-tablet": maybeApplyForTablet(flexDir),
36
45
  };
37
46
  }
47
+
38
48
  function columnCssVars() {
39
49
  const width = "100%";
40
50
  const marginLeft = "0";
@@ -42,37 +52,42 @@ function Columns(props) {
42
52
  "--column-width": width,
43
53
  "--column-margin-left": marginLeft,
44
54
  "--column-width-tablet": maybeApplyForTablet(width),
45
- "--column-margin-left-tablet": maybeApplyForTablet(marginLeft)
55
+ "--column-margin-left-tablet": maybeApplyForTablet(marginLeft),
46
56
  };
47
57
  }
58
+
48
59
  function getWidthForBreakpointSize(size) {
49
- const breakpointSizes = getSizesForBreakpoints(props.customBreakpoints || {});
60
+ const breakpointSizes = getSizesForBreakpoints(
61
+ props.customBreakpoints || {}
62
+ );
50
63
  return breakpointSizes[size].max;
51
64
  }
65
+
52
66
  function columnStyleObjects() {
53
67
  return {
54
68
  columns: {
55
69
  small: {
56
70
  flexDirection: "var(--flex-dir)",
57
- alignItems: "stretch"
71
+ alignItems: "stretch",
58
72
  },
59
73
  medium: {
60
74
  flexDirection: "var(--flex-dir-tablet)",
61
- alignItems: "stretch"
62
- }
75
+ alignItems: "stretch",
76
+ },
63
77
  },
64
78
  column: {
65
79
  small: {
66
80
  width: "var(--column-width) !important",
67
- marginLeft: "var(--column-margin-left) !important"
81
+ marginLeft: "var(--column-margin-left) !important",
68
82
  },
69
83
  medium: {
70
84
  width: "var(--column-width-tablet) !important",
71
- marginLeft: "var(--column-margin-left-tablet) !important"
72
- }
73
- }
85
+ marginLeft: "var(--column-margin-left-tablet) !important",
86
+ },
87
+ },
74
88
  };
75
89
  }
90
+
76
91
  function columnsStyles() {
77
92
  return `
78
93
  @media (max-width: ${getWidthForBreakpointSize("medium")}px) {
@@ -96,32 +111,40 @@ function Columns(props) {
96
111
  },
97
112
  `;
98
113
  }
114
+
99
115
  function reactNativeColumnsStyles() {
100
116
  return columnStyleObjects.columns.small;
101
117
  }
118
+
102
119
  function reactNativeColumnStyles() {
103
120
  return columnStyleObjects.column.small;
104
121
  }
105
- return /* @__PURE__ */ React.createElement(View, {
106
- style: styles.view1
107
- }, TARGET !== "reactNative" ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(RenderInlinedStyles, {
108
- styles: columnsStyles()
109
- })) : null, (_a = props.columns) == null ? void 0 : _a.map((column, index) => /* @__PURE__ */ React.createElement(View, {
110
- style: styles.view2,
111
- key: index
112
- }, /* @__PURE__ */ React.createElement(RenderBlocks, {
113
- blocks: column.blocks,
114
- path: `component.options.columns.${index}.blocks`,
115
- parent: props.builderBlock.id,
116
- styleProp: {
117
- flexGrow: "1"
118
- }
119
- }))));
122
+
123
+ return (
124
+ <View style={styles.view1}>
125
+ {TARGET !== "reactNative" ? (
126
+ <>
127
+ <RenderInlinedStyles styles={columnsStyles()} />
128
+ </>
129
+ ) : null}
130
+
131
+ {props.columns?.map((column, index) => (
132
+ <View style={styles.view2} key={index}>
133
+ <RenderBlocks
134
+ blocks={column.blocks}
135
+ path={`component.options.columns.${index}.blocks`}
136
+ parent={props.builderBlock.id}
137
+ styleProp={{
138
+ flexGrow: "1",
139
+ }}
140
+ />
141
+ </View>
142
+ ))}
143
+ </View>
144
+ );
120
145
  }
146
+
121
147
  const styles = StyleSheet.create({
122
148
  view1: { display: "flex" },
123
- view2: { display: "flex", flexDirection: "column", alignItems: "stretch" }
149
+ view2: { display: "flex", flexDirection: "column", alignItems: "stretch" },
124
150
  });
125
- export {
126
- Columns as default
127
- };
@@ -1,12 +1,20 @@
1
1
  import * as React from "react";
2
- import { View } from "react-native";
2
+ import { View, StyleSheet, Image, Text } from "react-native";
3
3
  import { useState, useRef, useEffect } from "react";
4
- function CustomCode(props) {
4
+
5
+ export default function CustomCode(props) {
5
6
  const elem = useRef(null);
6
7
  const [scriptsInserted, setScriptsInserted] = useState(() => []);
8
+
7
9
  const [scriptsRun, setScriptsRun] = useState(() => []);
10
+
8
11
  function findAndRunScripts() {
9
- if (elem.current && elem.current.getElementsByTagName && typeof window !== "undefined") {
12
+ // TODO: Move this function to standalone one in '@builder.io/utils'
13
+ if (
14
+ elem.current &&
15
+ elem.current.getElementsByTagName &&
16
+ typeof window !== "undefined"
17
+ ) {
10
18
  const scripts = elem.current.getElementsByTagName("script");
11
19
  for (let i = 0; i < scripts.length; i++) {
12
20
  const script = scripts[i];
@@ -19,11 +27,14 @@ function CustomCode(props) {
19
27
  newScript.async = true;
20
28
  newScript.src = script.src;
21
29
  document.head.appendChild(newScript);
22
- } else if (!script.type || [
23
- "text/javascript",
24
- "application/javascript",
25
- "application/ecmascript"
26
- ].includes(script.type)) {
30
+ } else if (
31
+ !script.type ||
32
+ [
33
+ "text/javascript",
34
+ "application/javascript",
35
+ "application/ecmascript",
36
+ ].includes(script.type)
37
+ ) {
27
38
  if (scriptsRun.includes(script.innerText)) {
28
39
  continue;
29
40
  }
@@ -37,14 +48,10 @@ function CustomCode(props) {
37
48
  }
38
49
  }
39
50
  }
51
+
40
52
  useEffect(() => {
41
53
  findAndRunScripts();
42
54
  }, []);
43
- return /* @__PURE__ */ React.createElement(View, {
44
- ref: elem,
45
- dangerouslySetInnerHTML: { __html: props.code }
46
- });
55
+
56
+ return <View ref={elem} dangerouslySetInnerHTML={{ __html: props.code }} />;
47
57
  }
48
- export {
49
- CustomCode as default
50
- };
@@ -1,15 +1,18 @@
1
1
  import * as React from "react";
2
- import { View } from "react-native";
2
+ import { View, StyleSheet, Image, Text } from "react-native";
3
3
  import { useState, useRef, useEffect } from "react";
4
4
  import { isJsScript } from "./helpers.js";
5
- function Embed(props) {
5
+
6
+ export default function Embed(props) {
6
7
  const elem = useRef(null);
7
8
  const [scriptsInserted, setScriptsInserted] = useState(() => []);
9
+
8
10
  const [scriptsRun, setScriptsRun] = useState(() => []);
11
+
9
12
  const [ranInitFn, setRanInitFn] = useState(() => false);
13
+
10
14
  function findAndRunScripts() {
11
- if (!elem.current || !elem.current.getElementsByTagName)
12
- return;
15
+ if (!elem.current || !elem.current.getElementsByTagName) return;
13
16
  const scripts = elem.current.getElementsByTagName("script");
14
17
  for (let i = 0; i < scripts.length; i++) {
15
18
  const script = scripts[i];
@@ -29,17 +32,15 @@ function Embed(props) {
29
32
  }
30
33
  }
31
34
  }
35
+
32
36
  useEffect(() => {
33
37
  if (elem.current && !ranInitFn) {
34
38
  setRanInitFn(true);
35
39
  findAndRunScripts();
36
40
  }
37
41
  }, [elem.current, ranInitFn]);
38
- return /* @__PURE__ */ React.createElement(View, {
39
- ref: elem,
40
- dangerouslySetInnerHTML: { __html: props.content }
41
- });
42
+
43
+ return (
44
+ <View ref={elem} dangerouslySetInnerHTML={{ __html: props.content }} />
45
+ );
42
46
  }
43
- export {
44
- Embed as default
45
- };
@@ -0,0 +1,251 @@
1
+ import BaseText from "../BaseText";
2
+ import * as React from "react";
3
+ import { View, StyleSheet, Image, Text } from "react-native";
4
+ import { useState, useContext, useRef } from "react";
5
+ import RenderBlock from "../../components/render-block/render-block.js";
6
+ import BuilderBlocks from "../../components/render-blocks.js";
7
+ import { isEditing } from "../../functions/is-editing.js";
8
+
9
+ export default function FormComponent(props) {
10
+ const formRef = useRef(null);
11
+ const [formState, setFormState] = useState(() => "unsubmitted");
12
+
13
+ const [responseData, setResponseData] = useState(() => null);
14
+
15
+ const [formErrorMessage, setFormErrorMessage] = useState(() => "");
16
+
17
+ function submissionState() {
18
+ return (isEditing() && props.previewState) || formState;
19
+ }
20
+
21
+ function onSubmit(event) {
22
+ const sendWithJs = props.sendWithJs || props.sendSubmissionsTo === "email";
23
+ if (props.sendSubmissionsTo === "zapier") {
24
+ event.preventDefault();
25
+ } else if (sendWithJs) {
26
+ if (!(props.action || props.sendSubmissionsTo === "email")) {
27
+ event.preventDefault();
28
+ return;
29
+ }
30
+ event.preventDefault();
31
+ const el = event.currentTarget;
32
+ const headers = props.customHeaders || {};
33
+ let body;
34
+ const formData = new FormData(el);
35
+
36
+ // TODO: maybe support null
37
+ const formPairs = Array.from(
38
+ event.currentTarget.querySelectorAll("input,select,textarea")
39
+ )
40
+ .filter((el) => !!el.name)
41
+ .map((el) => {
42
+ let value;
43
+ const key = el.name;
44
+ if (el instanceof HTMLInputElement) {
45
+ if (el.type === "radio") {
46
+ if (el.checked) {
47
+ value = el.name;
48
+ return {
49
+ key,
50
+ value,
51
+ };
52
+ }
53
+ } else if (el.type === "checkbox") {
54
+ value = el.checked;
55
+ } else if (el.type === "number" || el.type === "range") {
56
+ const num = el.valueAsNumber;
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
+ return {
70
+ key,
71
+ value,
72
+ };
73
+ });
74
+ let contentType = props.contentType;
75
+ if (props.sendSubmissionsTo === "email") {
76
+ contentType = "multipart/form-data";
77
+ }
78
+ Array.from(formPairs).forEach(({ value }) => {
79
+ if (
80
+ value instanceof File ||
81
+ (Array.isArray(value) && value[0] instanceof File) ||
82
+ value instanceof FileList
83
+ ) {
84
+ contentType = "multipart/form-data";
85
+ }
86
+ });
87
+
88
+ // TODO: send as urlEncoded or multipart by default
89
+ // because of ease of use and reliability in browser API
90
+ // for encoding the form?
91
+ if (contentType !== "application/json") {
92
+ body = formData;
93
+ } else {
94
+ // Json
95
+ const json = {};
96
+ Array.from(formPairs).forEach(({ value, key }) => {
97
+ set(json, key, value);
98
+ });
99
+ body = JSON.stringify(json);
100
+ }
101
+ if (contentType && contentType !== "multipart/form-data") {
102
+ if (
103
+ /* Zapier doesn't allow content-type header to be sent from browsers */ !(
104
+ sendWithJs && props.action?.includes("zapier.com")
105
+ )
106
+ ) {
107
+ headers["content-type"] = contentType;
108
+ }
109
+ }
110
+ const presubmitEvent = new CustomEvent("presubmit", { detail: { body } });
111
+ if (formRef.current) {
112
+ formRef.current.dispatchEvent(presubmitEvent);
113
+ if (presubmitEvent.defaultPrevented) {
114
+ return;
115
+ }
116
+ }
117
+ setFormState("sending");
118
+ const formUrl = `${
119
+ builder.env === "dev" ? "http://localhost:5000" : "https://builder.io"
120
+ }/api/v1/form-submit?apiKey=${builder.apiKey}&to=${btoa(
121
+ props.sendSubmissionsToEmail || ""
122
+ )}&name=${encodeURIComponent(props.name || "")}`;
123
+ fetch(
124
+ props.sendSubmissionsTo === "email"
125
+ ? formUrl
126
+ : props.action /* TODO: throw error if no action URL */,
127
+ { body, headers, method: props.method || "post" }
128
+ ).then(
129
+ async (res) => {
130
+ let body;
131
+ const contentType = res.headers.get("content-type");
132
+ if (contentType && contentType.indexOf("application/json") !== -1) {
133
+ body = await res.json();
134
+ } else {
135
+ body = await res.text();
136
+ }
137
+ if (!res.ok && props.errorMessagePath) {
138
+ /* TODO: allow supplying an error formatter function */ let message =
139
+ get(body, props.errorMessagePath);
140
+ if (message) {
141
+ if (typeof message !== "string") {
142
+ /* TODO: ideally convert json to yaml so it woul dbe like error: - email has been taken */ message =
143
+ JSON.stringify(message);
144
+ }
145
+ setFormErrorMessage(message);
146
+ }
147
+ }
148
+ setResponseData(body);
149
+ setFormState(res.ok ? "success" : "error");
150
+ if (res.ok) {
151
+ const submitSuccessEvent = new CustomEvent("submit:success", {
152
+ detail: { res, body },
153
+ });
154
+ if (formRef.current) {
155
+ formRef.current.dispatchEvent(submitSuccessEvent);
156
+ if (submitSuccessEvent.defaultPrevented) {
157
+ return;
158
+ }
159
+ /* TODO: option to turn this on/off? */ if (
160
+ props.resetFormOnSubmit !== false
161
+ ) {
162
+ formRef.current.reset();
163
+ }
164
+ }
165
+ /* TODO: client side route event first that can be preventDefaulted */ if (
166
+ props.successUrl
167
+ ) {
168
+ if (formRef.current) {
169
+ const event = new CustomEvent("route", {
170
+ detail: { url: props.successUrl },
171
+ });
172
+ formRef.current.dispatchEvent(event);
173
+ if (!event.defaultPrevented) {
174
+ location.href = props.successUrl;
175
+ }
176
+ } else {
177
+ location.href = props.successUrl;
178
+ }
179
+ }
180
+ }
181
+ },
182
+ (err) => {
183
+ const submitErrorEvent = new CustomEvent("submit:error", {
184
+ detail: { error: err },
185
+ });
186
+ if (formRef.current) {
187
+ formRef.current.dispatchEvent(submitErrorEvent);
188
+ if (submitErrorEvent.defaultPrevented) {
189
+ return;
190
+ }
191
+ }
192
+ setResponseData(err);
193
+ setFormState("error");
194
+ }
195
+ );
196
+ }
197
+ }
198
+ const builderContext = useContext(BuilderContext);
199
+ return (
200
+ <View
201
+ validate={props.validate}
202
+ ref={formRef}
203
+ action={!props.sendWithJs && props.action}
204
+ method={props.method}
205
+ name={props.name}
206
+ onSubmit={(event) => onSubmit(event)}
207
+ {...props.attributes}
208
+ >
209
+ {" "}
210
+ {props.builderBlock && props.builderBlock.children ? (
211
+ <>
212
+ {props.builderBlock?.children?.map((block) => (
213
+ <RenderBlock block={block} context={builderContext} />
214
+ ))}
215
+ </>
216
+ ) : null}{" "}
217
+ {submissionState() === "error" ? (
218
+ <>
219
+ <BuilderBlocks dataPath="errorMessage" blocks={props.errorMessage} />
220
+ </>
221
+ ) : null}{" "}
222
+ {submissionState() === "sending" ? (
223
+ <>
224
+ <BuilderBlocks
225
+ dataPath="sendingMessage"
226
+ blocks={props.sendingMessage}
227
+ />
228
+ </>
229
+ ) : null}{" "}
230
+ {submissionState() === "error" && responseData ? (
231
+ <>
232
+ <View style={styles.view1}>
233
+ {" "}
234
+ <BaseText>{JSON.stringify(responseData, null, 2)}</BaseText>{" "}
235
+ </View>
236
+ </>
237
+ ) : null}{" "}
238
+ {submissionState() === "success" ? (
239
+ <>
240
+ <BuilderBlocks
241
+ dataPath="successMessage"
242
+ blocks={props.successMessage}
243
+ />
244
+ </>
245
+ ) : null}{" "}
246
+ </View>
247
+ );
248
+ }
249
+ const styles = StyleSheet.create({
250
+ view1: { padding: 10, color: "red", textAlign: "center" },
251
+ });
@@ -0,0 +1,11 @@
1
+ import BaseText from "../BaseText";
2
+ import * as React from "react";
3
+ import { View, StyleSheet, Image, Text } from "react-native";
4
+
5
+ export default function FragmentComponent(props) {
6
+ return (
7
+ <View>
8
+ <BaseText>{props.children}</BaseText>
9
+ </View>
10
+ );
11
+ }
@@ -0,0 +1,78 @@
1
+ import * as React from 'react';
2
+ import { Image as ReactImage, View } from 'react-native';
3
+
4
+ // Subset of Image props, many are irrelevant for native (such as altText, etc)
5
+ /**
6
+ * @typedef {{
7
+ * image: string;
8
+ * backgroundSize?: 'cover' | 'contain';
9
+ * backgroundPosition?: string;
10
+ * aspectRatio?: number;
11
+ * width?: number;
12
+ * height?: number;
13
+ * children?: any;
14
+ * }} ImageProps
15
+ */
16
+
17
+ // TODO: support children by wrapping in a View
18
+ /**
19
+ * @param {ImageProps} props
20
+ */
21
+ export default function Image(props) {
22
+ const shouldRenderUnwrappedChildren =
23
+ props.fitContent && props.builderBlock?.children?.length;
24
+
25
+ const imageStyle = props.aspectRatio
26
+ ? {
27
+ position: 'absolute',
28
+ top: 0,
29
+ bottom: 0,
30
+ left: 0,
31
+ right: 0,
32
+ }
33
+ : {
34
+ position: 'relative',
35
+ ...(props.width ? { width: props.width } : {}),
36
+ ...(props.height ? { height: props.height } : {}),
37
+ };
38
+
39
+ return (
40
+ <>
41
+ <React.Fragment>
42
+ <ReactImage
43
+ resizeMode={props.backgroundSize || 'contain'}
44
+ style={imageStyle}
45
+ source={{ uri: props.image }}
46
+ />
47
+ {props.aspectRatio && !shouldRenderUnwrappedChildren ? (
48
+ <View
49
+ style={{
50
+ width: '100%',
51
+ paddingTop: props.aspectRatio * 100 + '%',
52
+ }}
53
+ />
54
+ ) : null}
55
+ </React.Fragment>
56
+
57
+ {shouldRenderUnwrappedChildren && props.children}
58
+
59
+ {!props.fitContent && props.children && (
60
+ // When `fitContent: false`, we wrap image children ssuch that they stretch across the entire image
61
+ <View
62
+ css={{
63
+ display: 'flex',
64
+ flexDirection: 'column',
65
+ alignItems: 'stretch',
66
+ position: 'absolute',
67
+ top: '0',
68
+ left: '0',
69
+ width: '100%',
70
+ height: '100%',
71
+ }}
72
+ >
73
+ {props.children}
74
+ </View>
75
+ )}
76
+ </>
77
+ );
78
+ }
@@ -0,0 +1,18 @@
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
+ style={{
9
+ objectFit: props.backgroundSize || "cover",
10
+ objectPosition: props.backgroundPosition || "center",
11
+ }}
12
+ key={(isEditing() && props.imgSrc) || "default-key"}
13
+ alt={props.altText}
14
+ src={props.imgSrc || props.image}
15
+ {...props.attributes}
16
+ />
17
+ );
18
+ }
@@ -0,0 +1,20 @@
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
+ }
@@ -0,0 +1,6 @@
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
+ }