@builder.io/sdk-react-native 0.0.1-31 → 0.0.1-35
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/button.js +22 -66
- package/src/blocks/button.lite.tsx +4 -4
- package/src/blocks/columns.js +36 -247
- package/src/blocks/columns.lite.tsx +7 -7
- package/src/blocks/custom-code.js +47 -73
- package/src/blocks/custom-code.lite.tsx +16 -12
- package/src/blocks/embed.js +47 -66
- package/src/blocks/embed.lite.tsx +13 -11
- package/src/blocks/form.js +195 -436
- package/src/blocks/form.lite.tsx +72 -65
- package/src/blocks/fragment.js +8 -16
- package/src/blocks/fragment.lite.tsx +3 -3
- package/src/blocks/image.js +80 -88
- package/src/blocks/image.lite.tsx +16 -17
- package/src/blocks/img.js +28 -45
- package/src/blocks/img.lite.tsx +7 -7
- package/src/blocks/input.js +28 -93
- package/src/blocks/input.lite.tsx +7 -5
- package/src/blocks/raw-text.js +12 -16
- package/src/blocks/raw-text.lite.tsx +3 -3
- package/src/blocks/section.js +23 -66
- package/src/blocks/section.lite.tsx +4 -4
- package/src/blocks/select.js +28 -67
- package/src/blocks/select.lite.tsx +8 -6
- package/src/blocks/submit-button.js +21 -41
- package/src/blocks/submit-button.lite.tsx +3 -3
- package/src/blocks/symbol.js +16 -18
- package/src/blocks/symbol.lite.tsx +5 -5
- package/src/blocks/text.js +27 -29
- package/src/blocks/text.lite.tsx +9 -4
- package/src/blocks/textarea.js +24 -53
- package/src/blocks/textarea.lite.tsx +3 -3
- package/src/blocks/video.js +60 -81
- package/src/blocks/video.lite.tsx +6 -6
- package/src/components/block-styles.js +4 -2
- package/src/components/block-styles.lite.tsx +3 -3
- package/src/components/error-boundary.js +9 -11
- package/src/components/error-boundary.lite.tsx +3 -3
- package/src/components/render-block.js +40 -85
- package/src/components/render-block.lite.tsx +25 -17
- package/src/components/render-blocks.js +32 -56
- package/src/components/render-blocks.lite.tsx +14 -14
- package/src/components/render-content.js +52 -100
- package/src/components/render-content.lite.tsx +41 -32
- package/src/constants/device-sizes.js +11 -8
- package/src/context/builder.context.js +4 -2
- package/src/functions/evaluate.js +9 -17
- package/src/functions/get-block-actions.js +10 -9
- package/src/functions/get-block-component-options.js +10 -11
- package/src/functions/get-block-properties.js +17 -15
- package/src/functions/get-block-styles.js +17 -20
- package/src/functions/get-block-tag.js +4 -2
- package/src/functions/get-content.js +30 -39
- package/src/functions/get-content.test.js +28 -37
- package/src/functions/get-fetch.js +7 -5
- package/src/functions/get-global-this.js +7 -5
- package/src/functions/get-processed-block.js +13 -11
- package/src/functions/get-processed-block.test.js +14 -14
- package/src/functions/get-target.js +4 -2
- package/src/functions/if-target.js +3 -1
- package/src/functions/is-browser.js +5 -3
- package/src/functions/is-editing.js +5 -3
- package/src/functions/is-iframe.js +4 -2
- package/src/functions/is-previewing.js +6 -4
- package/src/functions/is-react-native.js +4 -2
- package/src/functions/macro-eval.js +5 -2
- package/src/functions/on-change.js +7 -4
- package/src/functions/on-change.test.js +10 -10
- package/src/functions/previewing-model-name.js +5 -3
- package/src/functions/register-component.js +28 -33
- package/src/functions/register.js +10 -8
- package/src/functions/set-editor-settings.js +7 -5
- package/src/functions/set.js +4 -10
- package/src/functions/set.test.js +14 -14
- package/src/functions/track.js +8 -6
- package/src/index.js +18 -20
- package/src/scripts/init-editing.js +40 -64
package/src/blocks/form.lite.tsx
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import * as React from
|
|
2
|
-
import { View, StyleSheet, Image, Text } from
|
|
3
|
-
import { useState, useContext, useRef } from
|
|
4
|
-
import RenderBlock from
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { View, StyleSheet, Image, Text } from "react-native";
|
|
3
|
+
import { useState, useContext, useRef } from "react";
|
|
4
|
+
import RenderBlock from "../components/render-block.lite";
|
|
5
5
|
|
|
6
6
|
export default function FormComponent(props) {
|
|
7
|
-
const [state, setState] = useState(() =>
|
|
7
|
+
const [state, setState] = useState(() => "unsubmitted");
|
|
8
8
|
|
|
9
9
|
const [responseData, setResponseData] = useState(() => null);
|
|
10
10
|
|
|
11
|
-
const [formErrorMessage, setFormErrorMessage] = useState(() =>
|
|
11
|
+
const [formErrorMessage, setFormErrorMessage] = useState(() => "");
|
|
12
12
|
|
|
13
13
|
function submissionState() {
|
|
14
14
|
return (Builder.isEditing && props.previewState) || state;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
function onSubmit(event) {
|
|
18
|
-
const sendWithJs = props.sendWithJs || props.sendSubmissionsTo ===
|
|
18
|
+
const sendWithJs = props.sendWithJs || props.sendSubmissionsTo === "email";
|
|
19
19
|
|
|
20
|
-
if (props.sendSubmissionsTo ===
|
|
20
|
+
if (props.sendSubmissionsTo === "zapier") {
|
|
21
21
|
event.preventDefault();
|
|
22
22
|
} else if (sendWithJs) {
|
|
23
|
-
if (!(props.action || props.sendSubmissionsTo ===
|
|
23
|
+
if (!(props.action || props.sendSubmissionsTo === "email")) {
|
|
24
24
|
event.preventDefault();
|
|
25
25
|
return;
|
|
26
26
|
}
|
|
@@ -31,14 +31,16 @@ export default function FormComponent(props) {
|
|
|
31
31
|
let body;
|
|
32
32
|
const formData = new FormData(el); // TODO: maybe support null
|
|
33
33
|
|
|
34
|
-
const formPairs = Array.from(
|
|
35
|
-
.
|
|
36
|
-
|
|
34
|
+
const formPairs = Array.from(
|
|
35
|
+
event.currentTarget.querySelectorAll("input,select,textarea")
|
|
36
|
+
)
|
|
37
|
+
.filter((el) => !!el.name)
|
|
38
|
+
.map((el) => {
|
|
37
39
|
let value;
|
|
38
40
|
const key = el.name;
|
|
39
41
|
|
|
40
42
|
if (el instanceof HTMLInputElement) {
|
|
41
|
-
if (el.type ===
|
|
43
|
+
if (el.type === "radio") {
|
|
42
44
|
if (el.checked) {
|
|
43
45
|
value = el.name;
|
|
44
46
|
return {
|
|
@@ -46,15 +48,15 @@ export default function FormComponent(props) {
|
|
|
46
48
|
value,
|
|
47
49
|
};
|
|
48
50
|
}
|
|
49
|
-
} else if (el.type ===
|
|
51
|
+
} else if (el.type === "checkbox") {
|
|
50
52
|
value = el.checked;
|
|
51
|
-
} else if (el.type ===
|
|
53
|
+
} else if (el.type === "number" || el.type === "range") {
|
|
52
54
|
const num = el.valueAsNumber;
|
|
53
55
|
|
|
54
56
|
if (!isNaN(num)) {
|
|
55
57
|
value = num;
|
|
56
58
|
}
|
|
57
|
-
} else if (el.type ===
|
|
59
|
+
} else if (el.type === "file") {
|
|
58
60
|
// TODO: one vs multiple files
|
|
59
61
|
value = el.files;
|
|
60
62
|
} else {
|
|
@@ -71,8 +73,8 @@ export default function FormComponent(props) {
|
|
|
71
73
|
});
|
|
72
74
|
let contentType = props.contentType;
|
|
73
75
|
|
|
74
|
-
if (props.sendSubmissionsTo ===
|
|
75
|
-
contentType =
|
|
76
|
+
if (props.sendSubmissionsTo === "email") {
|
|
77
|
+
contentType = "multipart/form-data";
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
Array.from(formPairs).forEach(({ value }) => {
|
|
@@ -81,13 +83,13 @@ export default function FormComponent(props) {
|
|
|
81
83
|
(Array.isArray(value) && value[0] instanceof File) ||
|
|
82
84
|
value instanceof FileList
|
|
83
85
|
) {
|
|
84
|
-
contentType =
|
|
86
|
+
contentType = "multipart/form-data";
|
|
85
87
|
}
|
|
86
88
|
}); // TODO: send as urlEncoded or multipart by default
|
|
87
89
|
// because of ease of use and reliability in browser API
|
|
88
90
|
// for encoding the form?
|
|
89
91
|
|
|
90
|
-
if (contentType !==
|
|
92
|
+
if (contentType !== "application/json") {
|
|
91
93
|
body = formData;
|
|
92
94
|
} else {
|
|
93
95
|
// Json
|
|
@@ -98,60 +100,57 @@ export default function FormComponent(props) {
|
|
|
98
100
|
body = JSON.stringify(json);
|
|
99
101
|
}
|
|
100
102
|
|
|
101
|
-
if (contentType && contentType !==
|
|
103
|
+
if (contentType && contentType !== "multipart/form-data") {
|
|
102
104
|
if (
|
|
103
105
|
/* Zapier doesn't allow content-type header to be sent from browsers */ !(
|
|
104
|
-
sendWithJs && props.action?.includes(
|
|
106
|
+
sendWithJs && props.action?.includes("zapier.com")
|
|
105
107
|
)
|
|
106
108
|
) {
|
|
107
|
-
headers[
|
|
109
|
+
headers["content-type"] = contentType;
|
|
108
110
|
}
|
|
109
111
|
}
|
|
110
|
-
const presubmitEvent = new CustomEvent(
|
|
112
|
+
const presubmitEvent = new CustomEvent("presubmit", { detail: { body } });
|
|
111
113
|
if (formRef.current) {
|
|
112
114
|
formRef.current.dispatchEvent(presubmitEvent);
|
|
113
115
|
if (presubmitEvent.defaultPrevented) {
|
|
114
116
|
return;
|
|
115
117
|
}
|
|
116
118
|
}
|
|
117
|
-
setState(
|
|
119
|
+
setState("sending");
|
|
118
120
|
const formUrl = `${
|
|
119
|
-
builder.env ===
|
|
121
|
+
builder.env === "dev" ? "http://localhost:5000" : "https://builder.io"
|
|
120
122
|
}/api/v1/form-submit?apiKey=${builder.apiKey}&to=${btoa(
|
|
121
|
-
props.sendSubmissionsToEmail ||
|
|
122
|
-
)}&name=${encodeURIComponent(props.name ||
|
|
123
|
+
props.sendSubmissionsToEmail || ""
|
|
124
|
+
)}&name=${encodeURIComponent(props.name || "")}`;
|
|
123
125
|
fetch(
|
|
124
|
-
props.sendSubmissionsTo ===
|
|
126
|
+
props.sendSubmissionsTo === "email"
|
|
125
127
|
? formUrl
|
|
126
128
|
: props.action /* TODO: throw error if no action URL */,
|
|
127
|
-
{ body, headers, method: props.method ||
|
|
129
|
+
{ body, headers, method: props.method || "post" }
|
|
128
130
|
).then(
|
|
129
|
-
async res => {
|
|
131
|
+
async (res) => {
|
|
130
132
|
let body;
|
|
131
|
-
const contentType = res.headers.get(
|
|
132
|
-
if (contentType && contentType.indexOf(
|
|
133
|
+
const contentType = res.headers.get("content-type");
|
|
134
|
+
if (contentType && contentType.indexOf("application/json") !== -1) {
|
|
133
135
|
body = await res.json();
|
|
134
136
|
} else {
|
|
135
137
|
body = await res.text();
|
|
136
138
|
}
|
|
137
139
|
if (!res.ok && props.errorMessagePath) {
|
|
138
|
-
/* TODO: allow supplying an error formatter function */ let message =
|
|
139
|
-
body,
|
|
140
|
-
props.errorMessagePath
|
|
141
|
-
);
|
|
140
|
+
/* TODO: allow supplying an error formatter function */ let message =
|
|
141
|
+
get(body, props.errorMessagePath);
|
|
142
142
|
if (message) {
|
|
143
|
-
if (typeof message !==
|
|
144
|
-
/* TODO: ideally convert json to yaml so it woul dbe like error: - email has been taken */ message =
|
|
145
|
-
message
|
|
146
|
-
);
|
|
143
|
+
if (typeof message !== "string") {
|
|
144
|
+
/* TODO: ideally convert json to yaml so it woul dbe like error: - email has been taken */ message =
|
|
145
|
+
JSON.stringify(message);
|
|
147
146
|
}
|
|
148
147
|
setFormErrorMessage(message);
|
|
149
148
|
}
|
|
150
149
|
}
|
|
151
150
|
setResponseData(body);
|
|
152
|
-
setState(res.ok ?
|
|
151
|
+
setState(res.ok ? "success" : "error");
|
|
153
152
|
if (res.ok) {
|
|
154
|
-
const submitSuccessEvent = new CustomEvent(
|
|
153
|
+
const submitSuccessEvent = new CustomEvent("submit:success", {
|
|
155
154
|
detail: { res, body },
|
|
156
155
|
});
|
|
157
156
|
if (formRef.current) {
|
|
@@ -159,7 +158,9 @@ export default function FormComponent(props) {
|
|
|
159
158
|
if (submitSuccessEvent.defaultPrevented) {
|
|
160
159
|
return;
|
|
161
160
|
}
|
|
162
|
-
/* TODO: option to turn this on/off? */ if (
|
|
161
|
+
/* TODO: option to turn this on/off? */ if (
|
|
162
|
+
props.resetFormOnSubmit !== false
|
|
163
|
+
) {
|
|
163
164
|
formRef.current.reset();
|
|
164
165
|
}
|
|
165
166
|
}
|
|
@@ -167,7 +168,7 @@ export default function FormComponent(props) {
|
|
|
167
168
|
props.successUrl
|
|
168
169
|
) {
|
|
169
170
|
if (formRef.current) {
|
|
170
|
-
const event = new CustomEvent(
|
|
171
|
+
const event = new CustomEvent("route", {
|
|
171
172
|
detail: { url: props.successUrl },
|
|
172
173
|
});
|
|
173
174
|
formRef.current.dispatchEvent(event);
|
|
@@ -180,8 +181,8 @@ export default function FormComponent(props) {
|
|
|
180
181
|
}
|
|
181
182
|
}
|
|
182
183
|
},
|
|
183
|
-
err => {
|
|
184
|
-
const submitErrorEvent = new CustomEvent(
|
|
184
|
+
(err) => {
|
|
185
|
+
const submitErrorEvent = new CustomEvent("submit:error", {
|
|
185
186
|
detail: { error: err },
|
|
186
187
|
});
|
|
187
188
|
if (formRef.current) {
|
|
@@ -191,7 +192,7 @@ export default function FormComponent(props) {
|
|
|
191
192
|
}
|
|
192
193
|
}
|
|
193
194
|
setResponseData(err);
|
|
194
|
-
setState(
|
|
195
|
+
setState("error");
|
|
195
196
|
}
|
|
196
197
|
);
|
|
197
198
|
}
|
|
@@ -205,42 +206,48 @@ export default function FormComponent(props) {
|
|
|
205
206
|
action={!props.sendWithJs && props.action}
|
|
206
207
|
method={props.method}
|
|
207
208
|
name={props.name}
|
|
208
|
-
onSubmit={event => onSubmit(event)}
|
|
209
|
+
onSubmit={(event) => onSubmit(event)}
|
|
209
210
|
>
|
|
210
|
-
{
|
|
211
|
+
{" "}
|
|
211
212
|
{props.builderBlock && props.builderBlock.children ? (
|
|
212
213
|
<>
|
|
213
|
-
{props.builderBlock?.children?.map(block => (
|
|
214
|
+
{props.builderBlock?.children?.map((block) => (
|
|
214
215
|
<RenderBlock block={block} />
|
|
215
216
|
))}
|
|
216
217
|
</>
|
|
217
|
-
) : null}{
|
|
218
|
-
{submissionState() ===
|
|
218
|
+
) : null}{" "}
|
|
219
|
+
{submissionState() === "error" ? (
|
|
219
220
|
<>
|
|
220
221
|
<BuilderBlocks dataPath="errorMessage" blocks={props.errorMessage} />
|
|
221
222
|
</>
|
|
222
|
-
) : null}{
|
|
223
|
-
{submissionState() ===
|
|
223
|
+
) : null}{" "}
|
|
224
|
+
{submissionState() === "sending" ? (
|
|
224
225
|
<>
|
|
225
|
-
<BuilderBlocks
|
|
226
|
+
<BuilderBlocks
|
|
227
|
+
dataPath="sendingMessage"
|
|
228
|
+
blocks={props.sendingMessage}
|
|
229
|
+
/>
|
|
226
230
|
</>
|
|
227
|
-
) : null}{
|
|
228
|
-
{submissionState() ===
|
|
231
|
+
) : null}{" "}
|
|
232
|
+
{submissionState() === "error" && responseData ? (
|
|
229
233
|
<>
|
|
230
234
|
<View className="builder-form-error-text" style={styles.view1}>
|
|
231
|
-
{
|
|
232
|
-
<Text>{JSON.stringify(responseData, null, 2)}</Text>{
|
|
235
|
+
{" "}
|
|
236
|
+
<Text>{JSON.stringify(responseData, null, 2)}</Text>{" "}
|
|
233
237
|
</View>
|
|
234
238
|
</>
|
|
235
|
-
) : null}{
|
|
236
|
-
{submissionState() ===
|
|
239
|
+
) : null}{" "}
|
|
240
|
+
{submissionState() === "success" ? (
|
|
237
241
|
<>
|
|
238
|
-
<BuilderBlocks
|
|
242
|
+
<BuilderBlocks
|
|
243
|
+
dataPath="successMessage"
|
|
244
|
+
blocks={props.successMessage}
|
|
245
|
+
/>
|
|
239
246
|
</>
|
|
240
|
-
) : null}{
|
|
247
|
+
) : null}{" "}
|
|
241
248
|
</View>
|
|
242
249
|
);
|
|
243
250
|
}
|
|
244
251
|
const styles = StyleSheet.create({
|
|
245
|
-
view1: { padding: 10, color:
|
|
252
|
+
view1: { padding: 10, color: "red", textAlign: "center" },
|
|
246
253
|
});
|
package/src/blocks/fragment.js
CHANGED
|
@@ -1,21 +1,13 @@
|
|
|
1
1
|
import { registerComponent } from '../functions/register-component';
|
|
2
2
|
|
|
3
|
-
import * as React from
|
|
4
|
-
import { View, Text } from
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { View, Text } from "react-native";
|
|
5
5
|
function FragmentComponent(props) {
|
|
6
|
-
|
|
7
|
-
View,
|
|
8
|
-
null,
|
|
9
|
-
/* @__PURE__ */ React.createElement(Text, null, props.children)
|
|
10
|
-
);
|
|
6
|
+
return /* @__PURE__ */ React.createElement(View, null, /* @__PURE__ */ React.createElement(Text, null, props.children));
|
|
11
7
|
}
|
|
12
|
-
export {
|
|
8
|
+
export {
|
|
9
|
+
FragmentComponent as default
|
|
10
|
+
};
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
static: true,
|
|
17
|
-
hidden: true,
|
|
18
|
-
builtIn: true,
|
|
19
|
-
canHaveChildren: true,
|
|
20
|
-
noWrap: true,
|
|
21
|
-
});
|
|
12
|
+
|
|
13
|
+
registerComponent(FragmentComponent, {name:'Fragment',static:true,hidden:true,builtIn:true,canHaveChildren:true,noWrap:true});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import * as React from
|
|
2
|
-
import { View, StyleSheet, Image, Text } from
|
|
3
|
-
import { useContext } from
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { View, StyleSheet, Image, Text } from "react-native";
|
|
3
|
+
import { useContext } from "react";
|
|
4
4
|
|
|
5
5
|
export default function FragmentComponent(props) {
|
|
6
6
|
return (
|
package/src/blocks/image.js
CHANGED
|
@@ -1,129 +1,121 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { Image as ReactImage, View } from
|
|
3
|
-
import { registerComponent } from
|
|
2
|
+
import { Image as ReactImage, View } from "react-native";
|
|
3
|
+
import { registerComponent } from "../functions/register-component";
|
|
4
4
|
function Image(props) {
|
|
5
|
-
return /* @__PURE__ */ React.createElement(
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
},
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
width: '100%',
|
|
18
|
-
paddingTop: props.aspectRatio * 100 + '%',
|
|
19
|
-
},
|
|
20
|
-
})
|
|
21
|
-
);
|
|
5
|
+
return /* @__PURE__ */ React.createElement(View, {
|
|
6
|
+
style: { position: "relative" }
|
|
7
|
+
}, /* @__PURE__ */ React.createElement(ReactImage, {
|
|
8
|
+
resizeMode: props.backgroundSize || "contain",
|
|
9
|
+
style: { position: "absolute", top: 0, bottom: 0, left: 0, right: 0 },
|
|
10
|
+
source: { uri: props.image }
|
|
11
|
+
}), /* @__PURE__ */ React.createElement(View, {
|
|
12
|
+
style: {
|
|
13
|
+
width: "100%",
|
|
14
|
+
paddingTop: props.aspectRatio * 100 + "%"
|
|
15
|
+
}
|
|
16
|
+
}));
|
|
22
17
|
}
|
|
23
18
|
registerComponent(Image, {
|
|
24
|
-
name:
|
|
19
|
+
name: "Image",
|
|
25
20
|
static: true,
|
|
26
21
|
builtIn: true,
|
|
27
|
-
image:
|
|
28
|
-
'https://firebasestorage.googleapis.com/v0/b/builder-3b0a2.appspot.com/o/images%2Fbaseline-insert_photo-24px.svg?alt=media&token=4e5d0ef4-f5e8-4e57-b3a9-38d63a9b9dc4',
|
|
22
|
+
image: "https://firebasestorage.googleapis.com/v0/b/builder-3b0a2.appspot.com/o/images%2Fbaseline-insert_photo-24px.svg?alt=media&token=4e5d0ef4-f5e8-4e57-b3a9-38d63a9b9dc4",
|
|
29
23
|
defaultStyles: {
|
|
30
|
-
position:
|
|
31
|
-
minHeight:
|
|
32
|
-
minWidth:
|
|
33
|
-
overflow:
|
|
24
|
+
position: "relative",
|
|
25
|
+
minHeight: "20px",
|
|
26
|
+
minWidth: "20px",
|
|
27
|
+
overflow: "hidden"
|
|
34
28
|
},
|
|
35
29
|
canHaveChildren: true,
|
|
36
30
|
inputs: [
|
|
37
31
|
{
|
|
38
|
-
name:
|
|
39
|
-
type:
|
|
32
|
+
name: "image",
|
|
33
|
+
type: "file",
|
|
40
34
|
bubble: true,
|
|
41
|
-
allowedFileTypes: [
|
|
35
|
+
allowedFileTypes: ["jpeg", "jpg", "png", "svg"],
|
|
42
36
|
required: true,
|
|
43
|
-
defaultValue:
|
|
44
|
-
'
|
|
45
|
-
onChange:
|
|
46
|
-
" const DEFAULT_ASPECT_RATIO = 0.7041; options.delete('srcset'); options.delete('noWebp'); function loadImage(url, timeout) { return new Promise((resolve, reject) => { const img = document.createElement('img'); let loaded = false; img.onload = () => { loaded = true; resolve(img); }; img.addEventListener('error', event => { console.warn('Image load failed', event.error); reject(event.error); }); img.src = url; setTimeout(() => { if (!loaded) { reject(new Error('Image load timed out')); } }, timeout); }); } function round(num) { return Math.round(num * 1000) / 1000; } const value = options.get('image'); const aspectRatio = options.get('aspectRatio'); // For SVG images - don't render as webp, keep them as SVG fetch(value) .then(res => res.blob()) .then(blob => { if (blob.type.includes('svg')) { options.set('noWebp', true); } }); if (value && (!aspectRatio || aspectRatio === DEFAULT_ASPECT_RATIO)) { return loadImage(value).then(img => { const possiblyUpdatedAspectRatio = options.get('aspectRatio'); if ( options.get('image') === value && (!possiblyUpdatedAspectRatio || possiblyUpdatedAspectRatio === DEFAULT_ASPECT_RATIO) ) { if (img.width && img.height) { options.set('aspectRatio', round(img.height / img.width)); options.set('height', img.height); options.set('width', img.width); } } }); }",
|
|
37
|
+
defaultValue: "https://cdn.builder.io/api/v1/image/assets%2Fpwgjf0RoYWbdnJSbpBAjXNRMe9F2%2Ffb27a7c790324294af8be1c35fe30f4d",
|
|
38
|
+
onChange: " const DEFAULT_ASPECT_RATIO = 0.7041; options.delete('srcset'); options.delete('noWebp'); function loadImage(url, timeout) { return new Promise((resolve, reject) => { const img = document.createElement('img'); let loaded = false; img.onload = () => { loaded = true; resolve(img); }; img.addEventListener('error', event => { console.warn('Image load failed', event.error); reject(event.error); }); img.src = url; setTimeout(() => { if (!loaded) { reject(new Error('Image load timed out')); } }, timeout); }); } function round(num) { return Math.round(num * 1000) / 1000; } const value = options.get('image'); const aspectRatio = options.get('aspectRatio'); // For SVG images - don't render as webp, keep them as SVG fetch(value) .then(res => res.blob()) .then(blob => { if (blob.type.includes('svg')) { options.set('noWebp', true); } }); if (value && (!aspectRatio || aspectRatio === DEFAULT_ASPECT_RATIO)) { return loadImage(value).then(img => { const possiblyUpdatedAspectRatio = options.get('aspectRatio'); if ( options.get('image') === value && (!possiblyUpdatedAspectRatio || possiblyUpdatedAspectRatio === DEFAULT_ASPECT_RATIO) ) { if (img.width && img.height) { options.set('aspectRatio', round(img.height / img.width)); options.set('height', img.height); options.set('width', img.width); } } }); }"
|
|
47
39
|
},
|
|
48
40
|
{
|
|
49
|
-
name:
|
|
50
|
-
type:
|
|
51
|
-
defaultValue:
|
|
41
|
+
name: "backgroundSize",
|
|
42
|
+
type: "text",
|
|
43
|
+
defaultValue: "cover",
|
|
52
44
|
enum: [
|
|
53
45
|
{
|
|
54
|
-
label:
|
|
55
|
-
value:
|
|
56
|
-
helperText:
|
|
46
|
+
label: "contain",
|
|
47
|
+
value: "contain",
|
|
48
|
+
helperText: "The image should never get cropped"
|
|
57
49
|
},
|
|
58
50
|
{
|
|
59
|
-
label:
|
|
60
|
-
value:
|
|
61
|
-
helperText: `The image should fill it's box, cropping when needed
|
|
62
|
-
}
|
|
63
|
-
]
|
|
51
|
+
label: "cover",
|
|
52
|
+
value: "cover",
|
|
53
|
+
helperText: `The image should fill it's box, cropping when needed`
|
|
54
|
+
}
|
|
55
|
+
]
|
|
64
56
|
},
|
|
65
57
|
{
|
|
66
|
-
name:
|
|
67
|
-
type:
|
|
68
|
-
defaultValue:
|
|
58
|
+
name: "backgroundPosition",
|
|
59
|
+
type: "text",
|
|
60
|
+
defaultValue: "center",
|
|
69
61
|
enum: [
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
]
|
|
62
|
+
"center",
|
|
63
|
+
"top",
|
|
64
|
+
"left",
|
|
65
|
+
"right",
|
|
66
|
+
"bottom",
|
|
67
|
+
"top left",
|
|
68
|
+
"top right",
|
|
69
|
+
"bottom left",
|
|
70
|
+
"bottom right"
|
|
71
|
+
]
|
|
80
72
|
},
|
|
81
73
|
{
|
|
82
|
-
name:
|
|
83
|
-
type:
|
|
84
|
-
helperText:
|
|
74
|
+
name: "altText",
|
|
75
|
+
type: "string",
|
|
76
|
+
helperText: "Text to display when the user has images off"
|
|
85
77
|
},
|
|
86
78
|
{
|
|
87
|
-
name:
|
|
88
|
-
type:
|
|
89
|
-
hideFromUI: true
|
|
79
|
+
name: "height",
|
|
80
|
+
type: "number",
|
|
81
|
+
hideFromUI: true
|
|
90
82
|
},
|
|
91
83
|
{
|
|
92
|
-
name:
|
|
93
|
-
type:
|
|
94
|
-
hideFromUI: true
|
|
84
|
+
name: "width",
|
|
85
|
+
type: "number",
|
|
86
|
+
hideFromUI: true
|
|
95
87
|
},
|
|
96
88
|
{
|
|
97
|
-
name:
|
|
98
|
-
type:
|
|
99
|
-
hideFromUI: true
|
|
89
|
+
name: "sizes",
|
|
90
|
+
type: "string",
|
|
91
|
+
hideFromUI: true
|
|
100
92
|
},
|
|
101
93
|
{
|
|
102
|
-
name:
|
|
103
|
-
type:
|
|
104
|
-
hideFromUI: true
|
|
94
|
+
name: "srcset",
|
|
95
|
+
type: "string",
|
|
96
|
+
hideFromUI: true
|
|
105
97
|
},
|
|
106
98
|
{
|
|
107
|
-
name:
|
|
108
|
-
type:
|
|
99
|
+
name: "lazy",
|
|
100
|
+
type: "boolean",
|
|
109
101
|
defaultValue: true,
|
|
110
|
-
hideFromUI: true
|
|
102
|
+
hideFromUI: true
|
|
111
103
|
},
|
|
112
104
|
{
|
|
113
|
-
name:
|
|
114
|
-
type:
|
|
115
|
-
helperText:
|
|
116
|
-
|
|
117
|
-
defaultValue: true,
|
|
105
|
+
name: "fitContent",
|
|
106
|
+
type: "boolean",
|
|
107
|
+
helperText: "When child blocks are provided, fit to them instead of using the image's aspect ratio",
|
|
108
|
+
defaultValue: true
|
|
118
109
|
},
|
|
119
110
|
{
|
|
120
|
-
name:
|
|
121
|
-
type:
|
|
122
|
-
helperText:
|
|
123
|
-
"This is the ratio of height/width, e.g. set to 1.5 for a 300px wide and 200px tall photo. Set to 0 to not force the image to maintain it's aspect ratio",
|
|
111
|
+
name: "aspectRatio",
|
|
112
|
+
type: "number",
|
|
113
|
+
helperText: "This is the ratio of height/width, e.g. set to 1.5 for a 300px wide and 200px tall photo. Set to 0 to not force the image to maintain it's aspect ratio",
|
|
124
114
|
advanced: true,
|
|
125
|
-
defaultValue: 0.7041
|
|
126
|
-
}
|
|
127
|
-
]
|
|
115
|
+
defaultValue: 0.7041
|
|
116
|
+
}
|
|
117
|
+
]
|
|
128
118
|
});
|
|
129
|
-
export {
|
|
119
|
+
export {
|
|
120
|
+
Image as default
|
|
121
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import * as React from
|
|
2
|
-
import { View, StyleSheet, Image, Text } from
|
|
3
|
-
import { useContext } from
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { View, StyleSheet, Image, Text } from "react-native";
|
|
3
|
+
import { useContext } from "react";
|
|
4
4
|
|
|
5
5
|
export default function Image(props) {
|
|
6
6
|
return (
|
|
@@ -9,9 +9,9 @@ export default function Image(props) {
|
|
|
9
9
|
<View
|
|
10
10
|
loading="lazy"
|
|
11
11
|
alt={props.altText}
|
|
12
|
-
aria-role={props.altText ?
|
|
12
|
+
aria-role={props.altText ? "presentation" : undefined}
|
|
13
13
|
style={styles.view2}
|
|
14
|
-
className={
|
|
14
|
+
className={"builder-image" + (props.class ? " " + props.class : "")}
|
|
15
15
|
src={props.image}
|
|
16
16
|
srcset={props.srcset}
|
|
17
17
|
sizes={props.sizes}
|
|
@@ -20,16 +20,15 @@ export default function Image(props) {
|
|
|
20
20
|
<View srcSet={props.srcset} />
|
|
21
21
|
</View>
|
|
22
22
|
|
|
23
|
-
{props.aspectRatio &&
|
|
23
|
+
{props.aspectRatio &&
|
|
24
|
+
!(props.fitContent && props.builderBlock?.children?.length) ? (
|
|
24
25
|
<View className="builder-image-sizer" style={styles.view3}>
|
|
25
26
|
<Text> </Text>
|
|
26
27
|
</View>
|
|
27
28
|
) : null}
|
|
28
29
|
|
|
29
30
|
{props.builderBlock?.children?.length && props.fitContent ? (
|
|
30
|
-
|
|
31
|
-
<Text>{props.children}</Text>
|
|
32
|
-
</>
|
|
31
|
+
<Text>{props.children}</Text>
|
|
33
32
|
) : null}
|
|
34
33
|
|
|
35
34
|
{!props.fitContent ? (
|
|
@@ -44,22 +43,22 @@ export default function Image(props) {
|
|
|
44
43
|
}
|
|
45
44
|
|
|
46
45
|
const styles = StyleSheet.create({
|
|
47
|
-
view1: { position:
|
|
46
|
+
view1: { position: "relative" },
|
|
48
47
|
view2: {
|
|
49
48
|
opacity: 1,
|
|
50
|
-
transition:
|
|
51
|
-
position:
|
|
49
|
+
transition: "opacity 0.2s ease-in-out",
|
|
50
|
+
position: "absolute",
|
|
52
51
|
height: 100,
|
|
53
52
|
width: 100,
|
|
54
53
|
top: 0,
|
|
55
54
|
left: 0,
|
|
56
55
|
},
|
|
57
|
-
view3: { width: 100, pointerEvents:
|
|
56
|
+
view3: { width: 100, pointerEvents: "none", fontSize: 0 },
|
|
58
57
|
view4: {
|
|
59
|
-
display:
|
|
60
|
-
flexDirection:
|
|
61
|
-
alignItems:
|
|
62
|
-
position:
|
|
58
|
+
display: "flex",
|
|
59
|
+
flexDirection: "column",
|
|
60
|
+
alignItems: "stretch",
|
|
61
|
+
position: "absolute",
|
|
63
62
|
top: 0,
|
|
64
63
|
left: 0,
|
|
65
64
|
width: 100,
|