@builder.io/sdk-solid 0.1.2 → 0.1.3-0

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 (34) hide show
  1. package/dist/sdk-solid.cjs +34 -0
  2. package/dist/sdk-solid.js +3863 -0
  3. package/package.json +5 -6
  4. package/src/blocks/BaseText.jsx +5 -1
  5. package/src/blocks/button/button.jsx +25 -7
  6. package/src/blocks/columns/columns.jsx +84 -38
  7. package/src/blocks/custom-code/custom-code.jsx +25 -3
  8. package/src/blocks/embed/embed.jsx +14 -2
  9. package/src/blocks/form/form.jsx +175 -121
  10. package/src/blocks/fragment/fragment.jsx +2 -1
  11. package/src/blocks/image/image.jsx +75 -38
  12. package/src/blocks/img/img.jsx +15 -5
  13. package/src/blocks/input/input.jsx +17 -2
  14. package/src/blocks/raw-text/raw-text.jsx +8 -2
  15. package/src/blocks/section/section.jsx +24 -14
  16. package/src/blocks/select/select.jsx +21 -6
  17. package/src/blocks/submit-button/submit-button.jsx +6 -3
  18. package/src/blocks/symbol/symbol.jsx +54 -15
  19. package/src/blocks/text/text.jsx +2 -1
  20. package/src/blocks/textarea/textarea.jsx +11 -2
  21. package/src/blocks/video/video.jsx +49 -27
  22. package/src/components/render-block/block-styles.jsx +45 -21
  23. package/src/components/render-block/render-block.helpers.js +90 -0
  24. package/src/components/render-block/render-block.jsx +110 -131
  25. package/src/components/render-block/render-component.jsx +26 -9
  26. package/src/components/render-block/render-repeated-block.jsx +20 -24
  27. package/src/components/render-blocks.jsx +70 -30
  28. package/src/components/render-content/builder-editing.jsx +2 -1
  29. package/src/components/render-content/components/render-styles.jsx +17 -8
  30. package/src/components/render-content/render-content.jsx +224 -146
  31. package/src/components/render-inlined-styles.jsx +14 -4
  32. package/vite.config.ts +18 -0
  33. package/solid-index.jsx +0 -5
  34. package/src/components/render-block/render-component-with-context.jsx +0 -28
@@ -1,6 +1,7 @@
1
1
  import { Show, onMount, on, createEffect, createSignal } from "solid-js";
2
- import { Dynamic } from "solid-js/web";
2
+
3
3
  import { createStore, reconcile } from "solid-js/store";
4
+
4
5
  import { getDefaultRegisteredComponents } from "../../constants/builder-registered-components.js";
5
6
  import { TARGET } from "../../constants/target.js";
6
7
  import { evaluate } from "../../functions/evaluate.js";
@@ -9,89 +10,108 @@ import { fetch } from "../../functions/get-fetch.js";
9
10
  import { isBrowser } from "../../functions/is-browser.js";
10
11
  import { isEditing } from "../../functions/is-editing.js";
11
12
  import { isPreviewing } from "../../functions/is-previewing.js";
12
- import { components, createRegisterComponentMessage } from "../../functions/register-component.js";
13
+ import {
14
+ components,
15
+ createRegisterComponentMessage,
16
+ } from "../../functions/register-component.js";
13
17
  import { _track } from "../../functions/track.js";
14
18
  import RenderBlocks from "../render-blocks.jsx";
15
19
  import RenderContentStyles from "./components/render-styles.jsx";
16
- import BuilderContext from "../../context/builder.context.js";
17
- import { registerInsertMenu, setupBrowserForEditing } from "../../scripts/init-editing.js";
20
+ import builderContext from "../../context/builder.context.js";
21
+ import {
22
+ registerInsertMenu,
23
+ setupBrowserForEditing,
24
+ } from "../../scripts/init-editing.js";
18
25
  import { checkIsDefined } from "../../helpers/nullable.js";
26
+
19
27
  function RenderContent(props) {
20
28
  const [forceReRenderCount, setForceReRenderCount] = createSignal(0);
29
+
21
30
  const [overrideContent, setOverrideContent] = createSignal(null);
31
+
22
32
  const [update, setUpdate] = createSignal(0);
33
+
23
34
  const [useBreakpoints, setUseBreakpoints] = createSignal(null);
35
+
36
+ const [canTrackToUse, setCanTrackToUse] = createSignal(
37
+ checkIsDefined(props.canTrack) ? props.canTrack : true
38
+ );
39
+
24
40
  const [overrideState, setOverrideState] = createSignal({});
25
- function canTrackToUse() {
26
- return checkIsDefined(props.canTrack) ? props.canTrack : true;
27
- }
41
+
42
+ const [contextContext, setContextContext] = createSignal(props.context || {});
43
+
44
+ const [allRegisteredComponents, setAllRegisteredComponents] = createSignal(
45
+ [
46
+ ...getDefaultRegisteredComponents(),
47
+ // While this `components` object is deprecated, we must maintain support for it.
48
+ // Since users are able to override our default components, we need to make sure that we do not break such
49
+ // existing usage.
50
+ // This is why we spread `components` after the default Builder.io components, but before the `props.customComponents`,
51
+ // which is the new standard way of providing custom components, and must therefore take precedence.
52
+ ...components,
53
+ ...(props.customComponents || []),
54
+ ].reduce(
55
+ (acc, curr) => ({
56
+ ...acc,
57
+ [curr.name]: curr,
58
+ }),
59
+ {}
60
+ )
61
+ );
62
+
63
+ const [httpReqsData, setHttpReqsData] = createSignal({});
64
+
28
65
  function contentState() {
29
66
  return {
30
67
  ...props.content?.data?.state,
31
68
  ...props.data,
32
- ...(props.locale ? {
33
- locale: props.locale
34
- } : {}),
35
- ...overrideState()
69
+ ...(props.locale
70
+ ? {
71
+ locale: props.locale,
72
+ }
73
+ : {}),
74
+ ...overrideState(),
36
75
  };
37
76
  }
38
- function contextContext() {
39
- return props.context || {};
40
- }
41
- function allRegisteredComponents() {
42
- const allComponentsArray = [...getDefaultRegisteredComponents(),
43
- // While this `components` object is deprecated, we must maintain support for it.
44
- // Since users are able to override our default components, we need to make sure that we do not break such
45
- // existing usage.
46
- // This is why we spread `components` after the default Builder.io components, but before the `props.customComponents`,
47
- // which is the new standard way of providing custom components, and must therefore take precedence.
48
- ...components, ...(props.customComponents || [])];
49
- const allComponents = allComponentsArray.reduce((acc, curr) => ({
50
- ...acc,
51
- [curr.name]: curr
52
- }), {});
53
- return allComponents;
54
- }
77
+
55
78
  function processMessage(event) {
56
- const {
57
- data
58
- } = event;
79
+ const { data } = event;
59
80
  if (data) {
60
81
  switch (data.type) {
61
- case "builder.configureSdk":
62
- {
63
- const messageContent = data.data;
64
- const {
65
- breakpoints,
66
- contentId
67
- } = messageContent;
68
- if (!contentId || contentId !== useContent?.id) {
69
- return;
70
- }
71
- setUseBreakpoints(breakpoints);
82
+ case "builder.configureSdk": {
83
+ const messageContent = data.data;
84
+ const { breakpoints, contentId } = messageContent;
85
+ if (!contentId || contentId !== useContent?.id) {
86
+ return;
87
+ }
88
+ setUseBreakpoints(breakpoints);
89
+ setForceReRenderCount(forceReRenderCount() + 1); // This is a hack to force Qwik to re-render.
90
+ break;
91
+ }
92
+ case "builder.contentUpdate": {
93
+ const messageContent = data.data;
94
+ const key =
95
+ messageContent.key ||
96
+ messageContent.alias ||
97
+ messageContent.entry ||
98
+ messageContent.modelName;
99
+ const contentData = messageContent.data;
100
+ if (key === props.model) {
101
+ setOverrideContent(contentData);
72
102
  setForceReRenderCount(forceReRenderCount() + 1); // This is a hack to force Qwik to re-render.
73
- break;
74
103
  }
75
- case "builder.contentUpdate":
76
- {
77
- const messageContent = data.data;
78
- const key = messageContent.key || messageContent.alias || messageContent.entry || messageContent.modelName;
79
- const contentData = messageContent.data;
80
- if (key === props.model) {
81
- setOverrideContent(contentData);
82
- setForceReRenderCount(forceReRenderCount() + 1); // This is a hack to force Qwik to re-render.
83
- }
84
104
 
85
- break;
86
- }
87
- case "builder.patchUpdates":
88
- {
89
- // TODO
90
- break;
91
- }
105
+ break;
106
+ }
107
+ case "builder.patchUpdates": {
108
+ // TODO
109
+ break;
110
+ }
92
111
  }
93
112
  }
94
113
  }
114
+
95
115
  function evaluateJsCode() {
96
116
  // run any dynamic JS code attached to content
97
117
  const jsCode = useContent?.data?.jsCode;
@@ -99,13 +119,11 @@ function RenderContent(props) {
99
119
  evaluate({
100
120
  code: jsCode,
101
121
  context: contextContext(),
102
- state: contentState()
122
+ state: contentState(),
103
123
  });
104
124
  }
105
125
  }
106
- function httpReqsData() {
107
- return {};
108
- }
126
+
109
127
  function onClick(_event) {
110
128
  if (useContent) {
111
129
  const variationId = useContent?.testVariationId;
@@ -115,31 +133,36 @@ function RenderContent(props) {
115
133
  canTrack: canTrackToUse(),
116
134
  contentId,
117
135
  apiKey: props.apiKey,
118
- variationId: variationId !== contentId ? variationId : undefined
136
+ variationId: variationId !== contentId ? variationId : undefined,
119
137
  });
120
138
  }
121
139
  }
140
+
122
141
  function evalExpression(expression) {
123
- return expression.replace(/{{([^}]+)}}/g, (_match, group) => evaluate({
124
- code: group,
125
- context: contextContext(),
126
- state: contentState()
127
- }));
142
+ return expression.replace(/{{([^}]+)}}/g, (_match, group) =>
143
+ evaluate({
144
+ code: group,
145
+ context: contextContext(),
146
+ state: contentState(),
147
+ })
148
+ );
128
149
  }
129
- function handleRequest({
130
- url,
131
- key
132
- }) {
133
- fetch(url).then(response => response.json()).then(json => {
134
- const newOverrideState = {
135
- ...overrideState(),
136
- [key]: json
137
- };
138
- setOverrideState(newOverrideState);
139
- }).catch(err => {
140
- console.log("error fetching dynamic data", url, err);
141
- });
150
+
151
+ function handleRequest({ url, key }) {
152
+ fetch(url)
153
+ .then((response) => response.json())
154
+ .then((json) => {
155
+ const newOverrideState = {
156
+ ...overrideState(),
157
+ [key]: json,
158
+ };
159
+ setOverrideState(newOverrideState);
160
+ })
161
+ .catch((err) => {
162
+ console.log("error fetching dynamic data", url, err);
163
+ });
142
164
  }
165
+
143
166
  function runHttpRequests() {
144
167
  const requests = useContent?.data?.httpRequests ?? {};
145
168
  Object.entries(requests).forEach(([key, url]) => {
@@ -147,71 +170,99 @@ function RenderContent(props) {
147
170
  const evaluatedUrl = evalExpression(url);
148
171
  handleRequest({
149
172
  url: evaluatedUrl,
150
- key
173
+ key,
151
174
  });
152
175
  }
153
176
  });
154
177
  }
178
+
155
179
  function emitStateUpdate() {
156
180
  if (isEditing()) {
157
- window.dispatchEvent(new CustomEvent("builder:component:stateChange", {
158
- detail: {
159
- state: contentState(),
160
- ref: {
161
- name: props.model
162
- }
163
- }
164
- }));
181
+ window.dispatchEvent(
182
+ new CustomEvent("builder:component:stateChange", {
183
+ detail: {
184
+ state: contentState(),
185
+ ref: {
186
+ name: props.model,
187
+ },
188
+ },
189
+ })
190
+ );
165
191
  }
166
192
  }
193
+
167
194
  function shouldRenderContentStyles() {
168
- return Boolean((useContent?.data?.cssCode || useContent?.data?.customFonts?.length) && TARGET !== "reactNative");
195
+ return Boolean(
196
+ (useContent?.data?.cssCode || useContent?.data?.customFonts?.length) &&
197
+ TARGET !== "reactNative"
198
+ );
169
199
  }
200
+
170
201
  const updateUseContent = function useContent() {
171
202
  if (!props.content && !overrideContent()) {
172
203
  return undefined;
173
204
  }
174
- const mergedContent = {
205
+ return {
175
206
  ...props.content,
176
207
  ...overrideContent(),
177
208
  data: {
178
209
  ...props.content?.data,
179
210
  ...props.data,
180
- ...overrideContent()?.data
211
+ ...overrideContent()?.data,
181
212
  },
182
213
  meta: {
183
214
  ...props.content?.meta,
184
215
  ...overrideContent()?.meta,
185
- breakpoints: useBreakpoints() || overrideContent()?.meta?.breakpoints || props.content?.meta?.breakpoints
186
- }
216
+ breakpoints:
217
+ useBreakpoints() ||
218
+ overrideContent()?.meta?.breakpoints ||
219
+ props.content?.meta?.breakpoints,
220
+ },
187
221
  };
188
- return mergedContent;
189
222
  };
190
223
  const [useContent, setUseContent] = createStore(updateUseContent());
191
- createEffect(on(() => [overrideContent(), useBreakpoints(), props.content, props.data], () => setUseContent(reconcile(updateUseContent()))));
224
+ createEffect(
225
+ on(
226
+ () => [overrideContent(), useBreakpoints(), props.content, props.data],
227
+ () => setUseContent(reconcile(updateUseContent()))
228
+ )
229
+ );
230
+
192
231
  let elementRef;
232
+
193
233
  onMount(() => {
194
234
  if (!props.apiKey) {
195
- console.error("[Builder.io]: No API key provided to `RenderContent` component. This can cause issues. Please provide an API key using the `apiKey` prop.");
235
+ console.error(
236
+ "[Builder.io]: No API key provided to `RenderContent` component. This can cause issues. Please provide an API key using the `apiKey` prop."
237
+ );
196
238
  }
197
239
  if (isBrowser()) {
198
240
  if (isEditing()) {
199
241
  setForceReRenderCount(forceReRenderCount() + 1);
200
242
  registerInsertMenu();
201
243
  setupBrowserForEditing({
202
- ...(props.locale ? {
203
- locale: props.locale
204
- } : {}),
205
- ...(props.includeRefs ? {
206
- includeRefs: props.includeRefs
207
- } : {})
208
- });
209
- Object.values(allRegisteredComponents()).forEach(registeredComponent => {
210
- const message = createRegisterComponentMessage(registeredComponent);
211
- window.parent?.postMessage(message, "*");
244
+ ...(props.locale
245
+ ? {
246
+ locale: props.locale,
247
+ }
248
+ : {}),
249
+ ...(props.includeRefs
250
+ ? {
251
+ includeRefs: props.includeRefs,
252
+ }
253
+ : {}),
212
254
  });
255
+ Object.values(allRegisteredComponents()).forEach(
256
+ (registeredComponent) => {
257
+ const message = createRegisterComponentMessage(registeredComponent);
258
+ window.parent?.postMessage(message, "*");
259
+ }
260
+ );
213
261
  window.addEventListener("message", processMessage);
214
- window.addEventListener("builder:component:stateChangeListenerActivated", emitStateUpdate);
262
+ window.addEventListener(
263
+ "builder:component:stateChangeListenerActivated",
264
+ emitStateUpdate
265
+ );
215
266
  }
216
267
  if (useContent) {
217
268
  const variationId = useContent?.testVariationId;
@@ -221,25 +272,40 @@ function RenderContent(props) {
221
272
  canTrack: canTrackToUse(),
222
273
  contentId,
223
274
  apiKey: props.apiKey,
224
- variationId: variationId !== contentId ? variationId : undefined
275
+ variationId: variationId !== contentId ? variationId : undefined,
225
276
  });
226
277
  }
227
278
 
228
279
  // override normal content in preview mode
229
280
  if (isPreviewing()) {
230
281
  const searchParams = new URL(location.href).searchParams;
231
- if (props.model && searchParams.get("builder.preview") === props.model) {
232
- const previewApiKey = searchParams.get("apiKey") || searchParams.get("builder.space");
233
- if (previewApiKey) {
234
- getContent({
235
- model: props.model,
236
- apiKey: previewApiKey
237
- }).then(content => {
238
- if (content) {
239
- setOverrideContent(content);
240
- }
241
- });
242
- }
282
+ const searchParamPreview = searchParams.get("builder.preview");
283
+ const previewApiKey =
284
+ searchParams.get("apiKey") || searchParams.get("builder.space");
285
+
286
+ /**
287
+ * Make sure that:
288
+ * - the preview model name is the same as the one we're rendering, since there can be multiple models rendered
289
+ * at the same time, e.g. header/page/footer.
290
+ * - the API key is the same, since we don't want to preview content from other organizations.
291
+ *
292
+ * TO-DO: should we check that the preview item ID is the same as the initial one being rendered? Or would
293
+ * this break scenarios where the item is not published yet?
294
+ *
295
+ * TO-DO: should we only update the state when there is a change?
296
+ **/
297
+ if (
298
+ searchParamPreview === props.model &&
299
+ previewApiKey === props.apiKey
300
+ ) {
301
+ getContent({
302
+ model: props.model,
303
+ apiKey: props.apiKey,
304
+ }).then((content) => {
305
+ if (content) {
306
+ setOverrideContent(content);
307
+ }
308
+ });
243
309
  }
244
310
  }
245
311
  evaluateJsCode();
@@ -247,43 +313,55 @@ function RenderContent(props) {
247
313
  emitStateUpdate();
248
314
  }
249
315
  });
316
+
250
317
  function onUpdateFn_0() {
251
318
  evaluateJsCode();
252
319
  }
253
- createEffect(on(() => [useContent?.data?.jsCode, contentState()], onUpdateFn_0));
320
+ createEffect(
321
+ on(() => [useContent?.data?.jsCode, contentState()], onUpdateFn_0)
322
+ );
323
+
254
324
  function onUpdateFn_1() {
255
325
  runHttpRequests();
256
326
  }
257
327
  createEffect(on(() => [useContent?.data?.httpRequests], onUpdateFn_1));
328
+
258
329
  function onUpdateFn_2() {
259
330
  emitStateUpdate();
260
331
  }
261
332
  createEffect(on(() => [contentState()], onUpdateFn_2));
262
- return <Dynamic value={{
263
- get content() {
264
- return useContent;
265
- },
266
- get state() {
267
- return contentState();
268
- },
269
- get context() {
270
- return contextContext();
271
- },
272
- get apiKey() {
273
- return props.apiKey;
274
- },
275
- get registeredComponents() {
276
- return allRegisteredComponents();
277
- }
278
- }} component={BuilderContext.Provider}>
333
+
334
+ return (
335
+ <builderContext.Provider
336
+ value={{
337
+ content: useContent,
338
+ state: contentState(),
339
+ context: contextContext(),
340
+ apiKey: props.apiKey,
341
+ registeredComponents: allRegisteredComponents(),
342
+ }}
343
+ >
279
344
  <Show when={useContent}>
280
- <div ref={elementRef} onClick={event => onClick(event)} builder-content-id={useContent?.id} builder-model={props.model}>
345
+ <div
346
+ ref={elementRef}
347
+ onClick={(event) => onClick(event)}
348
+ builder-content-id={useContent?.id}
349
+ builder-model={props.model}
350
+ >
281
351
  <Show when={shouldRenderContentStyles()}>
282
- <RenderContentStyles cssCode={useContent?.data?.cssCode} customFonts={useContent?.data?.customFonts}></RenderContentStyles>
352
+ <RenderContentStyles
353
+ cssCode={useContent?.data?.cssCode}
354
+ customFonts={useContent?.data?.customFonts}
355
+ ></RenderContentStyles>
283
356
  </Show>
284
- <RenderBlocks blocks={useContent?.data?.blocks} key={forceReRenderCount()}></RenderBlocks>
357
+ <RenderBlocks
358
+ blocks={useContent?.data?.blocks}
359
+ key={forceReRenderCount()}
360
+ ></RenderBlocks>
285
361
  </div>
286
362
  </Show>
287
- </Dynamic>;
363
+ </builderContext.Provider>
364
+ );
288
365
  }
289
- export default RenderContent;
366
+
367
+ export default RenderContent;
@@ -1,17 +1,27 @@
1
- import { Show } from "solid-js";
1
+ import { Show, createSignal } from "solid-js";
2
2
  import { Dynamic } from "solid-js/web";
3
+
3
4
  import { TARGET } from "../constants/target.js";
5
+
4
6
  function RenderInlinedStyles(props) {
5
7
  function injectedStyleScript() {
6
8
  return `<${tag()}>${props.styles}</${tag()}>`;
7
9
  }
10
+
8
11
  function tag() {
9
12
  // NOTE: we have to obfusctate the name of the tag due to a limitation in the svelte-preprocessor plugin.
10
13
  // https://github.com/sveltejs/vite-plugin-svelte/issues/315#issuecomment-1109000027
11
14
  return "sty" + "le";
12
15
  }
13
- return <Show fallback={<Dynamic component={tag()}>{props.styles}</Dynamic>} when={TARGET === "svelte"}>
16
+
17
+ return (
18
+ <Show
19
+ fallback={<Dynamic component={tag()}>{props.styles}</Dynamic>}
20
+ when={TARGET === "svelte"}
21
+ >
14
22
  <div innerHTML={injectedStyleScript()}></div>
15
- </Show>;
23
+ </Show>
24
+ );
16
25
  }
17
- export default RenderInlinedStyles;
26
+
27
+ export default RenderInlinedStyles;
package/vite.config.ts ADDED
@@ -0,0 +1,18 @@
1
+ import { resolve } from 'path';
2
+ import { defineConfig } from 'vite';
3
+ import solidPlugin from 'vite-plugin-solid';
4
+ // import dts from 'vite-plugin-dts';
5
+
6
+ // https://vitejs.dev/config/
7
+ export default defineConfig({
8
+ plugins: [
9
+ solidPlugin(),
10
+ // dts({ insertTypesEntry: true })
11
+ ],
12
+ build: {
13
+ lib: {
14
+ entry: resolve(__dirname, 'src/index.js'),
15
+ formats: ['cjs', 'es'],
16
+ },
17
+ },
18
+ });
package/solid-index.jsx DELETED
@@ -1,5 +0,0 @@
1
- /**
2
- * We need to have a `.jsx` entry point for our SolidJS SDK. To avoid enforcing this to all other frameworks,
3
- * we add this file and use it to re-export the index.js file from the Mitosis-generated code.
4
- */
5
- export * from './src/index.js';
@@ -1,28 +0,0 @@
1
- import { Dynamic } from "solid-js/web";
2
- import BuilderContext from "../../context/builder.context.js";
3
- import RenderComponent from "./render-component.jsx";
4
- function RenderComponentWithContext(props) {
5
- return <Dynamic value={{
6
- get content() {
7
- return props.context.content;
8
- },
9
- get state() {
10
- return props.context.state;
11
- },
12
- get context() {
13
- return props.context.context;
14
- },
15
- get apiKey() {
16
- return props.context.apiKey;
17
- },
18
- get registeredComponents() {
19
- return props.context.registeredComponents;
20
- },
21
- get inheritedStyles() {
22
- return props.context.inheritedStyles;
23
- }
24
- }} component={BuilderContext.Provider}>
25
- <RenderComponent componentRef={props.componentRef} componentOptions={props.componentOptions} blockChildren={props.blockChildren} context={props.context}></RenderComponent>
26
- </Dynamic>;
27
- }
28
- export default RenderComponentWithContext;