@builder.io/react 7.0.0 → 8.0.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 (50) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/builder-react-lite.cjs.js +1 -1
  3. package/dist/builder-react-lite.cjs.js.map +1 -1
  4. package/dist/builder-react-lite.esm.js +1 -1
  5. package/dist/builder-react-lite.esm.js.map +1 -1
  6. package/dist/builder-react.browser.js +2 -2
  7. package/dist/builder-react.browser.js.map +1 -1
  8. package/dist/builder-react.cjs.js +1 -1
  9. package/dist/builder-react.cjs.js.map +1 -1
  10. package/dist/builder-react.es5.js +1 -1
  11. package/dist/builder-react.es5.js.map +1 -1
  12. package/dist/builder-react.unpkg.js +2 -2
  13. package/dist/builder-react.unpkg.js.map +1 -1
  14. package/dist/lib/package.json +1 -1
  15. package/dist/lib/src/blocks/Image.js +3 -2
  16. package/dist/lib/src/blocks/Image.js.map +1 -1
  17. package/dist/lib/src/blocks/Symbol.js +23 -2
  18. package/dist/lib/src/blocks/Symbol.js.map +1 -1
  19. package/dist/lib/src/blocks/Video.js +3 -2
  20. package/dist/lib/src/blocks/Video.js.map +1 -1
  21. package/dist/lib/src/blocks/raw/Img.js +2 -1
  22. package/dist/lib/src/blocks/raw/Img.js.map +1 -1
  23. package/dist/lib/src/components/builder-block.component.js +14 -5
  24. package/dist/lib/src/components/builder-block.component.js.map +1 -1
  25. package/dist/lib/src/components/builder-blocks.component.js +5 -1
  26. package/dist/lib/src/components/builder-blocks.component.js.map +1 -1
  27. package/dist/lib/src/constants/file-types.constant.js +72 -0
  28. package/dist/lib/src/constants/file-types.constant.js.map +1 -0
  29. package/dist/lib/src/functions/extract-localized-values.js +35 -0
  30. package/dist/lib/src/functions/extract-localized-values.js.map +1 -0
  31. package/dist/lib/src/functions/traverse.js +72 -0
  32. package/dist/lib/src/functions/traverse.js.map +1 -0
  33. package/dist/lib/src/sdk-version.js +1 -1
  34. package/dist/types/src/components/builder-block.component.d.ts +1 -0
  35. package/dist/types/src/components/builder-blocks.component.d.ts +2 -0
  36. package/dist/types/src/components/builder-content.component.d.ts +0 -3
  37. package/dist/types/src/constants/file-types.constant.d.ts +2 -0
  38. package/dist/types/src/functions/extract-localized-values.d.ts +2 -0
  39. package/dist/types/src/functions/traverse.d.ts +34 -0
  40. package/dist/types/src/sdk-version.d.ts +1 -1
  41. package/package.json +2 -2
  42. package/src/blocks/Image.tsx +3 -2
  43. package/src/blocks/Symbol.tsx +31 -3
  44. package/src/blocks/Video.tsx +3 -2
  45. package/src/blocks/raw/Img.tsx +2 -1
  46. package/src/components/builder-block.component.tsx +22 -3
  47. package/src/components/builder-blocks.component.tsx +6 -2
  48. package/src/constants/file-types.constant.ts +69 -0
  49. package/src/functions/extract-localized-values.ts +33 -0
  50. package/src/functions/traverse.ts +72 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-localized-values.js","sourceRoot":"","sources":["../../../../src/functions/extract-localized-values.ts"],"names":[],"mappings":";;;AAAA,uCAAsC;AAEtC,IAAM,gBAAgB,GAAG,UAAC,KAAU;IAClC,OAAO,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,iCAAiC,CAAC;AACpG,CAAC,CAAC;AAEK,IAAM,uBAAuB,GAAG,UAAC,IAAyB;IAC/D,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE;QACrD,OAAO,KAAK,CAAC;KACd;IACD,IAAI,kBAAkB,GAAG,KAAK,CAAC;IAC/B,IAAA,mBAAQ,EAAC,IAAI,EAAE,UAAA,KAAK;QAClB,IAAI,gBAAgB,CAAC,KAAK,CAAC,EAAE;YAC3B,kBAAkB,GAAG,IAAI,CAAC;YAC1B,OAAO;SACR;IACH,CAAC,CAAC,CAAC;IACH,OAAO,kBAAkB,CAAC;AAC5B,CAAC,CAAC;AAZW,QAAA,uBAAuB,2BAYlC;AAEK,IAAM,sBAAsB,GAAG,UAAC,IAAyB,EAAE,MAAc;IAC9E,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE;QACrD,OAAO,EAAE,CAAC;KACX;IAED,IAAA,mBAAQ,EAAC,IAAI,EAAE,UAAC,KAAK,EAAE,MAAM;;QAC3B,IAAI,gBAAgB,CAAC,KAAK,CAAC,EAAE;YAC3B,MAAM,CAAC,MAAA,KAAK,CAAC,MAAM,CAAC,mCAAI,SAAS,CAAC,CAAC;SACpC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAZW,QAAA,sBAAsB,0BAYjC"}
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.traverse = void 0;
4
+ /**
5
+ * Recursively traverses an object or array, invoking a callback on each value.
6
+ *
7
+ * @param {any} obj - The object or array to traverse. Can also handle primitives, null, or undefined.
8
+ * @param {TraverseCallback} callback - The function to invoke on each value. Receives the current value
9
+ * and an `update` function to modify the value in its parent container.
10
+ * @param {any} [parent=null] - The parent object or array of the current value. Used internally.
11
+ * @param {any} [key=null] - The key or index of the current value in its parent. Used internally.
12
+ * @param {WeakSet} [visited=new WeakSet()] - Tracks visited objects to handle circular references. Used internally.
13
+ *
14
+ * @example
15
+ * // Example: Doubling all numbers in an object
16
+ * const obj = { a: 1, b: [2, 3, { c: 4 }] };
17
+ * traverse(obj, (value, update) => {
18
+ * if (typeof value === 'number') {
19
+ * update(value * 2);
20
+ * }
21
+ * });
22
+ * console.log(obj); // { a: 2, b: [4, 6, { c: 8 }] }
23
+ *
24
+ * @example
25
+ * // Example: Handling circular references
26
+ * const obj = { a: 1 };
27
+ * obj.self = obj;
28
+ * traverse(obj, (value, update) => {
29
+ * if (typeof value === 'number') {
30
+ * update(value * 2);
31
+ * }
32
+ * });
33
+ * console.log(obj.a); // 2
34
+ */
35
+ function traverse(obj, callback, parent, key, visited) {
36
+ if (parent === void 0) { parent = null; }
37
+ if (key === void 0) { key = null; }
38
+ if (visited === void 0) { visited = new WeakSet(); }
39
+ if (obj == null || typeof obj !== 'object') {
40
+ callback(obj, function (newValue) {
41
+ if (parent !== null && key !== null) {
42
+ parent[key] = newValue;
43
+ }
44
+ });
45
+ return;
46
+ }
47
+ if (visited.has(obj)) {
48
+ return;
49
+ }
50
+ visited.add(obj);
51
+ if (Array.isArray(obj)) {
52
+ obj.forEach(function (item, index) {
53
+ var update = function (newValue) {
54
+ obj[index] = newValue;
55
+ };
56
+ callback(item, update);
57
+ traverse(item, callback, obj, index, visited);
58
+ });
59
+ }
60
+ else {
61
+ Object.entries(obj).forEach(function (_a) {
62
+ var key = _a[0], value = _a[1];
63
+ var update = function (newValue) {
64
+ obj[key] = newValue;
65
+ };
66
+ callback(value, update);
67
+ traverse(value, callback, obj, key, visited);
68
+ });
69
+ }
70
+ }
71
+ exports.traverse = traverse;
72
+ //# sourceMappingURL=traverse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traverse.js","sourceRoot":"","sources":["../../../../src/functions/traverse.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,SAAgB,QAAQ,CACtB,GAAQ,EACR,QAA0B,EAC1B,MAAkB,EAClB,GAAe,EACf,OAAuB;IAFvB,uBAAA,EAAA,aAAkB;IAClB,oBAAA,EAAA,UAAe;IACf,wBAAA,EAAA,cAAc,OAAO,EAAE;IAEvB,IAAI,GAAG,IAAI,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE;QAC1C,QAAQ,CAAC,GAAG,EAAE,UAAC,QAAa;YAC1B,IAAI,MAAM,KAAK,IAAI,IAAI,GAAG,KAAK,IAAI,EAAE;gBACnC,MAAM,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC;aACxB;QACH,CAAC,CAAC,CAAC;QACH,OAAO;KACR;IAED,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;QACpB,OAAO;KACR;IACD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAEjB,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;QACtB,GAAG,CAAC,OAAO,CAAC,UAAC,IAAI,EAAE,KAAK;YACtB,IAAM,MAAM,GAAG,UAAC,QAAa;gBAC3B,GAAG,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC;YACxB,CAAC,CAAC;YACF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACvB,QAAQ,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;KACJ;SAAM;QACL,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,UAAC,EAAY;gBAAX,GAAG,QAAA,EAAE,KAAK,QAAA;YACtC,IAAM,MAAM,GAAG,UAAC,QAAa;gBAC3B,GAAG,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC;YACtB,CAAC,CAAC;YACF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YACxB,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;KACJ;AACH,CAAC;AAtCD,4BAsCC"}
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SDK_VERSION = void 0;
4
- exports.SDK_VERSION = '7.0.0';
4
+ exports.SDK_VERSION = '8.0.0';
5
5
  //# sourceMappingURL=sdk-version.js.map
@@ -24,6 +24,7 @@ export declare class BuilderBlock extends React.Component<BuilderBlockProps, {
24
24
  private _asyncRequests?;
25
25
  private _errors?;
26
26
  private _logs?;
27
+ hydrated: boolean;
27
28
  state: {
28
29
  hasError: boolean;
29
30
  updates: number;
@@ -15,10 +15,12 @@ export interface BuilderBlocksProps {
15
15
  interface BuilderBlocksState {
16
16
  }
17
17
  export declare class BuilderBlocks extends React.Component<BuilderBlocksProps, BuilderBlocksState> {
18
+ hydrated: boolean;
18
19
  get isRoot(): boolean;
19
20
  get noBlocks(): boolean;
20
21
  get path(): string;
21
22
  get parentId(): any;
23
+ componentDidMount(): void;
22
24
  onClickEmptyBlocks: () => void;
23
25
  onHoverEmptyBlocks: () => void;
24
26
  render(): JSX.Element;
@@ -57,7 +57,6 @@ export declare class BuilderContent<ContentType extends object = any> extends Re
57
57
  get renderedVariantId(): any;
58
58
  get options(): {
59
59
  apiVersion?: "v1" | undefined;
60
- apiEndpoint?: "content" | "query" | undefined;
61
60
  fetchOptions?: object | undefined;
62
61
  userAttributes?: import("@builder.io/sdk/dist/src/builder.class").UserAttributes | undefined;
63
62
  url?: string | undefined;
@@ -97,7 +96,6 @@ export declare class BuilderContent<ContentType extends object = any> extends Re
97
96
  } | {
98
97
  apiVersion?: "v3" | undefined;
99
98
  enrich?: boolean | undefined;
100
- apiEndpoint?: "content" | "query" | undefined;
101
99
  fetchOptions?: object | undefined;
102
100
  userAttributes?: import("@builder.io/sdk/dist/src/builder.class").UserAttributes | undefined;
103
101
  url?: string | undefined;
@@ -137,7 +135,6 @@ export declare class BuilderContent<ContentType extends object = any> extends Re
137
135
  } | {
138
136
  apiVersion?: undefined;
139
137
  enrich?: boolean | undefined;
140
- apiEndpoint?: "content" | "query" | undefined;
141
138
  fetchOptions?: object | undefined;
142
139
  userAttributes?: import("@builder.io/sdk/dist/src/builder.class").UserAttributes | undefined;
143
140
  url?: string | undefined;
@@ -0,0 +1,2 @@
1
+ export declare const IMAGE_FILE_TYPES: string[];
2
+ export declare const VIDEO_FILE_TYPES: string[];
@@ -0,0 +1,2 @@
1
+ export declare const containsLocalizedValues: (data: Record<string, any>) => boolean;
2
+ export declare const extractLocalizedValues: (data: Record<string, any>, locale: string) => Record<string, any>;
@@ -0,0 +1,34 @@
1
+ type TraverseCallback = (value: any, update: (newValue: any) => void) => void;
2
+ /**
3
+ * Recursively traverses an object or array, invoking a callback on each value.
4
+ *
5
+ * @param {any} obj - The object or array to traverse. Can also handle primitives, null, or undefined.
6
+ * @param {TraverseCallback} callback - The function to invoke on each value. Receives the current value
7
+ * and an `update` function to modify the value in its parent container.
8
+ * @param {any} [parent=null] - The parent object or array of the current value. Used internally.
9
+ * @param {any} [key=null] - The key or index of the current value in its parent. Used internally.
10
+ * @param {WeakSet} [visited=new WeakSet()] - Tracks visited objects to handle circular references. Used internally.
11
+ *
12
+ * @example
13
+ * // Example: Doubling all numbers in an object
14
+ * const obj = { a: 1, b: [2, 3, { c: 4 }] };
15
+ * traverse(obj, (value, update) => {
16
+ * if (typeof value === 'number') {
17
+ * update(value * 2);
18
+ * }
19
+ * });
20
+ * console.log(obj); // { a: 2, b: [4, 6, { c: 8 }] }
21
+ *
22
+ * @example
23
+ * // Example: Handling circular references
24
+ * const obj = { a: 1 };
25
+ * obj.self = obj;
26
+ * traverse(obj, (value, update) => {
27
+ * if (typeof value === 'number') {
28
+ * update(value * 2);
29
+ * }
30
+ * });
31
+ * console.log(obj.a); // 2
32
+ */
33
+ export declare function traverse(obj: any, callback: TraverseCallback, parent?: any, key?: any, visited?: WeakSet<object>): void;
34
+ export {};
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "7.0.0";
1
+ export declare const SDK_VERSION = "8.0.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@builder.io/react",
3
- "version": "7.0.0",
3
+ "version": "8.0.0",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "main": "dist/builder-react.cjs.js",
@@ -97,7 +97,7 @@
97
97
  "react-dom": ">=16.8.0 || ^19.0.0-rc"
98
98
  },
99
99
  "dependencies": {
100
- "@builder.io/sdk": "5.0.0",
100
+ "@builder.io/sdk": "6.0.0",
101
101
  "@emotion/core": "^10.0.17",
102
102
  "hash-sum": "^2.0.0",
103
103
  "isolated-vm": "^5.0.0",
@@ -8,6 +8,7 @@ import { BuilderMetaContext } from '../store/builder-meta';
8
8
  import { withBuilder } from '../functions/with-builder';
9
9
  import { throttle } from '../functions/throttle';
10
10
  import { Breakpoints, getSizesForBreakpoints } from '../constants/device-sizes.constant';
11
+ import { IMAGE_FILE_TYPES } from 'src/constants/file-types.constant';
11
12
 
12
13
  // Taken from (and modified) the shopify theme script repo
13
14
  // https://github.com/Shopify/theme-scripts/blob/bcfb471f2a57d439e2f964a1bb65b67708cc90c3/packages/theme-images/images.js#L59
@@ -262,7 +263,7 @@ class ImageComponent extends React.Component<any, { imageLoaded: boolean; load:
262
263
 
263
264
  getSrcSet(): string | undefined {
264
265
  const url = this.image;
265
- if (!url) {
266
+ if (!url || typeof url !== 'string') {
266
267
  return;
267
268
  }
268
269
 
@@ -442,7 +443,7 @@ export const Image = withBuilder(ImageComponent, {
442
443
  name: 'image',
443
444
  type: 'file',
444
445
  bubble: true,
445
- allowedFileTypes: ['jpeg', 'jpg', 'png', 'svg', 'webp'],
446
+ allowedFileTypes: IMAGE_FILE_TYPES,
446
447
  required: true,
447
448
  defaultValue:
448
449
  'https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2F72c80f114dc149019051b6852a9e3b7a',
@@ -2,7 +2,7 @@
2
2
  import { jsx } from '@emotion/core';
3
3
  import React, { PropsWithChildren } from 'react';
4
4
  import { BuilderComponent } from '../components/builder-component.component';
5
- import { Builder, BuilderElement } from '@builder.io/sdk';
5
+ import { Builder, BuilderElement, builder } from '@builder.io/sdk';
6
6
  import hash from 'hash-sum';
7
7
  import { NoWrap } from '../components/no-wrap';
8
8
  import { BuilderStoreContext } from '../store/builder-store';
@@ -50,6 +50,7 @@ export interface SymbolProps {
50
50
  class SymbolComponent extends React.Component<PropsWithChildren<SymbolProps>> {
51
51
  ref: BuilderComponent | null = null;
52
52
  staticRef: HTMLDivElement | null = null;
53
+ isEditingThisSymbol = false;
53
54
 
54
55
  get placeholder() {
55
56
  return (
@@ -64,6 +65,16 @@ class SymbolComponent extends React.Component<PropsWithChildren<SymbolProps>> {
64
65
  if (this.useStatic && this.staticRef && refs[this.props.builderBlock?.id!]) {
65
66
  this.staticRef.parentNode?.replaceChild(refs[this.props.builderBlock?.id!], this.staticRef);
66
67
  }
68
+ Builder.nextTick(() => {
69
+ const { model, entry } = this.props.symbol || {};
70
+ // allows editing of symbols in the context of a parent page
71
+ this.isEditingThisSymbol = Boolean(
72
+ Builder.isEditing &&
73
+ model === builder.editingModel &&
74
+ entry &&
75
+ location.search.includes(`overrides.${entry}`)
76
+ );
77
+ });
67
78
  }
68
79
 
69
80
  get useStatic() {
@@ -97,6 +108,10 @@ class SymbolComponent extends React.Component<PropsWithChildren<SymbolProps>> {
97
108
  showPlaceholder = true;
98
109
  }
99
110
 
111
+ if (this.isEditingThisSymbol) {
112
+ showPlaceholder = false;
113
+ }
114
+
100
115
  let key = dynamic ? undefined : [model, entry].join(':');
101
116
  const dataString = Builder.isEditing ? null : data && size(data) && hash(data);
102
117
 
@@ -105,7 +120,9 @@ class SymbolComponent extends React.Component<PropsWithChildren<SymbolProps>> {
105
120
  }
106
121
  const attributes = this.props.attributes || {};
107
122
  return (
108
- <BuilderStoreContext.Consumer key={(model || 'no model') + ':' + (entry || 'no entry')}>
123
+ <BuilderStoreContext.Consumer
124
+ key={(model || 'no model') + ':' + (entry || 'no entry' + this.isEditingThisSymbol)}
125
+ >
109
126
  {state => {
110
127
  const builderComponentKey = `${key}_${state?.state?.locale || 'Default'}`;
111
128
  return (
@@ -139,7 +156,18 @@ class SymbolComponent extends React.Component<PropsWithChildren<SymbolProps>> {
139
156
  inlineContent={symbol?.inline}
140
157
  {...(content && { content })}
141
158
  key={builderComponentKey}
142
- options={{ key: builderComponentKey, noEditorUpdates: true }}
159
+ options={{
160
+ ...(!this.isEditingThisSymbol && {
161
+ key: builderComponentKey,
162
+ noEditorUpdates: true,
163
+ }),
164
+ ...(Builder.singletonInstance.apiEndpoint === 'content' &&
165
+ entry && {
166
+ query: {
167
+ id: entry,
168
+ },
169
+ }),
170
+ }}
143
171
  codegen={!!content?.data?.blocksJs}
144
172
  hydrate={state.state?._hydrate}
145
173
  builderBlock={this.props.builderBlock}
@@ -5,6 +5,7 @@ import React, { PropsWithChildren } from 'react';
5
5
  import { throttle } from '../functions/throttle';
6
6
  import { withChildren } from '../functions/with-children';
7
7
  import { Builder } from '@builder.io/sdk';
8
+ import { IMAGE_FILE_TYPES, VIDEO_FILE_TYPES } from 'src/constants/file-types.constant';
8
9
 
9
10
  const DEFAULT_ASPECT_RATIO = 0.7004048582995948;
10
11
 
@@ -195,7 +196,7 @@ export const Video = Builder.registerComponent(withChildren(VideoComponent), {
195
196
  {
196
197
  name: 'video',
197
198
  type: 'file',
198
- allowedFileTypes: ['mp4'],
199
+ allowedFileTypes: VIDEO_FILE_TYPES,
199
200
  bubble: true,
200
201
  defaultValue:
201
202
  'https://cdn.builder.io/o/assets%2FYJIGb4i01jvw0SRdL5Bt%2Fd27731a526464deba0016216f5f9e570%2Fcompressed?apiKey=YJIGb4i01jvw0SRdL5Bt&token=d27731a526464deba0016216f5f9e570&alt=media&optimized=true',
@@ -204,7 +205,7 @@ export const Video = Builder.registerComponent(withChildren(VideoComponent), {
204
205
  {
205
206
  name: 'posterImage',
206
207
  type: 'file',
207
- allowedFileTypes: ['jpeg', 'png'],
208
+ allowedFileTypes: IMAGE_FILE_TYPES,
208
209
  helperText: 'Image to show before the video plays',
209
210
  },
210
211
  {
@@ -2,6 +2,7 @@
2
2
  import React from 'react';
3
3
  import { BuilderElement } from '@builder.io/sdk';
4
4
  import { withBuilder } from '../../functions/with-builder';
5
+ import { IMAGE_FILE_TYPES } from 'src/constants/file-types.constant';
5
6
 
6
7
  export interface ImgProps {
7
8
  attributes?: any;
@@ -36,7 +37,7 @@ export const Img = withBuilder(ImgComponent, {
36
37
  name: 'image',
37
38
  bubble: true,
38
39
  type: 'file',
39
- allowedFileTypes: ['jpeg', 'jpg', 'png', 'svg', 'webp'],
40
+ allowedFileTypes: IMAGE_FILE_TYPES,
40
41
  required: true,
41
42
  },
42
43
  ],
@@ -11,6 +11,10 @@ import { applyPatchWithMinimalMutationChain } from '../functions/apply-patch-wit
11
11
  import { blockToHtmlString } from '../functions/block-to-html-string';
12
12
  import { Link } from './Link';
13
13
  import { fastClone } from '../functions/utils';
14
+ import {
15
+ containsLocalizedValues,
16
+ extractLocalizedValues,
17
+ } from 'src/functions/extract-localized-values';
14
18
 
15
19
  const camelCaseToKebabCase = (str?: string) =>
16
20
  str ? str.replace(/([A-Z])/g, g => `-${g[0].toLowerCase()}`) : '';
@@ -124,6 +128,8 @@ export class BuilderBlock extends React.Component<
124
128
  private _errors?: Error[];
125
129
  private _logs?: string[];
126
130
 
131
+ hydrated = false;
132
+
127
133
  state = {
128
134
  hasError: false,
129
135
  updates: 0,
@@ -283,6 +289,7 @@ export class BuilderBlock extends React.Component<
283
289
  };
284
290
 
285
291
  componentDidMount() {
292
+ this.hydrated = true;
286
293
  const block = this.block;
287
294
  const animations = block && block.animations;
288
295
 
@@ -474,11 +481,23 @@ export class BuilderBlock extends React.Component<
474
481
  }
475
482
  }
476
483
 
477
- const innerComponentProperties = (options.component || options.options) && {
484
+ let innerComponentProperties = (options.component || options.options) && {
478
485
  ...options.options,
479
486
  ...(options.component.options || options.component.data),
480
487
  };
481
488
 
489
+ if (containsLocalizedValues(innerComponentProperties)) {
490
+ if (!this.privateState.state.locale) {
491
+ console.warn(
492
+ '[Builder.io] In order to use localized fields in Builder, you must pass a locale prop to the BuilderComponent or to options object while fetching the content to resolve localized fields. Learn more: https://www.builder.io/c/docs/localization-inline#targeting-and-inline-localization'
493
+ );
494
+ }
495
+ innerComponentProperties = extractLocalizedValues(
496
+ innerComponentProperties,
497
+ this.privateState.state.locale ?? 'Default'
498
+ );
499
+ }
500
+
482
501
  const isVoid = voidElements.has(TagName);
483
502
 
484
503
  const noWrap = componentInfo && (componentInfo.fragment || componentInfo.noWrap);
@@ -521,7 +540,7 @@ export class BuilderBlock extends React.Component<
521
540
  : ''
522
541
  }` +
523
542
  (options.class ? ' ' + options.class : '') +
524
- (Builder.isEditing && this.privateState.state._spacer?.parent === block.id
543
+ (this.hydrated && Builder.isEditing && this.privateState.state._spacer?.parent === block.id
525
544
  ? ' builder-spacer-parent'
526
545
  : ''),
527
546
  key: this.id + index,
@@ -541,7 +560,7 @@ export class BuilderBlock extends React.Component<
541
560
  // tslint:disable-next-line:comment-format
542
561
  ///REACT15ONLY finalOptions.className = finalOptions.class
543
562
 
544
- if (Builder.isEditing) {
563
+ if (Builder.isEditing && this.hydrated) {
545
564
  // TODO: removed bc JS can add styles inline too?
546
565
  (finalOptions as any)['builder-inline-styles'] = !(options.attr && options.attr.style)
547
566
  ? ''
@@ -29,6 +29,7 @@ interface BuilderBlocksState {
29
29
 
30
30
  // TODO: options to set direciotn
31
31
  export class BuilderBlocks extends React.Component<BuilderBlocksProps, BuilderBlocksState> {
32
+ hydrated = false;
32
33
  get isRoot() {
33
34
  return !this.props.child;
34
35
  }
@@ -58,6 +59,10 @@ export class BuilderBlocks extends React.Component<BuilderBlocksProps, BuilderBl
58
59
  return this.props.parent && this.props.parent.id;
59
60
  }
60
61
 
62
+ componentDidMount() {
63
+ this.hydrated = true;
64
+ }
65
+
61
66
  onClickEmptyBlocks = () => {
62
67
  if (Builder.isIframe && this.noBlocks) {
63
68
  window.parent?.postMessage(
@@ -105,8 +110,7 @@ export class BuilderBlocks extends React.Component<BuilderBlocksProps, BuilderBl
105
110
  (this.props.className ? ' ' + this.props.className : '')
106
111
  }
107
112
  builder-type="blocks"
108
- // TODO: only fi in iframe?
109
- builder-path={Builder.isIframe ? this.path : undefined}
113
+ builder-path={Builder.isIframe && this.hydrated ? this.path : undefined}
110
114
  builder-parent-id={this.parentId}
111
115
  css={
112
116
  {
@@ -0,0 +1,69 @@
1
+ export const IMAGE_FILE_TYPES = [
2
+ 'jpeg',
3
+ 'jpg',
4
+ 'png',
5
+ 'svg',
6
+ 'webp',
7
+ 'gif',
8
+ 'jfif',
9
+ 'pjpeg',
10
+ 'pjp',
11
+ 'apng',
12
+ 'avif',
13
+ 'tif',
14
+ 'tiff',
15
+ 'heif',
16
+ 'bmp',
17
+ 'eps',
18
+ 'raw',
19
+ 'cr2',
20
+ 'nef',
21
+ 'orf',
22
+ 'sr2',
23
+ 'psd',
24
+ 'heic',
25
+ 'dib',
26
+ 'ai',
27
+ ];
28
+
29
+ export const VIDEO_FILE_TYPES = [
30
+ 'mp4',
31
+ 'webm',
32
+ 'mkv',
33
+ 'flv',
34
+ 'vob',
35
+ 'ogv',
36
+ 'ogg',
37
+ 'drc',
38
+ 'gif',
39
+ 'gifv',
40
+ 'mng',
41
+ 'avi',
42
+ 'mov',
43
+ 'qt',
44
+ 'mts',
45
+ 'm2ts',
46
+ 'ts',
47
+ 'wmv',
48
+ 'yuv',
49
+ 'rm',
50
+ 'rmvb',
51
+ 'viv',
52
+ 'asf',
53
+ 'amv',
54
+ 'm4p',
55
+ 'mpeg',
56
+ 'mpe',
57
+ 'm2v',
58
+ 'm4v',
59
+ 'svi',
60
+ '3gp',
61
+ '3g2',
62
+ 'mxf',
63
+ 'roq',
64
+ 'nsv',
65
+ 'f4v',
66
+ 'f4p',
67
+ 'f4a',
68
+ 'f4b',
69
+ ];
@@ -0,0 +1,33 @@
1
+ import { traverse } from './traverse';
2
+
3
+ const isLocalizedField = (value: any) => {
4
+ return value && typeof value === 'object' && value['@type'] === '@builder.io/core:LocalizedValue';
5
+ };
6
+
7
+ export const containsLocalizedValues = (data: Record<string, any>) => {
8
+ if (!data || !Object.getOwnPropertyNames(data).length) {
9
+ return false;
10
+ }
11
+ let hasLocalizedValues = false;
12
+ traverse(data, value => {
13
+ if (isLocalizedField(value)) {
14
+ hasLocalizedValues = true;
15
+ return;
16
+ }
17
+ });
18
+ return hasLocalizedValues;
19
+ };
20
+
21
+ export const extractLocalizedValues = (data: Record<string, any>, locale: string) => {
22
+ if (!data || !Object.getOwnPropertyNames(data).length) {
23
+ return {};
24
+ }
25
+
26
+ traverse(data, (value, update) => {
27
+ if (isLocalizedField(value)) {
28
+ update(value[locale] ?? undefined);
29
+ }
30
+ });
31
+
32
+ return data;
33
+ };
@@ -0,0 +1,72 @@
1
+ type TraverseCallback = (value: any, update: (newValue: any) => void) => void;
2
+
3
+ /**
4
+ * Recursively traverses an object or array, invoking a callback on each value.
5
+ *
6
+ * @param {any} obj - The object or array to traverse. Can also handle primitives, null, or undefined.
7
+ * @param {TraverseCallback} callback - The function to invoke on each value. Receives the current value
8
+ * and an `update` function to modify the value in its parent container.
9
+ * @param {any} [parent=null] - The parent object or array of the current value. Used internally.
10
+ * @param {any} [key=null] - The key or index of the current value in its parent. Used internally.
11
+ * @param {WeakSet} [visited=new WeakSet()] - Tracks visited objects to handle circular references. Used internally.
12
+ *
13
+ * @example
14
+ * // Example: Doubling all numbers in an object
15
+ * const obj = { a: 1, b: [2, 3, { c: 4 }] };
16
+ * traverse(obj, (value, update) => {
17
+ * if (typeof value === 'number') {
18
+ * update(value * 2);
19
+ * }
20
+ * });
21
+ * console.log(obj); // { a: 2, b: [4, 6, { c: 8 }] }
22
+ *
23
+ * @example
24
+ * // Example: Handling circular references
25
+ * const obj = { a: 1 };
26
+ * obj.self = obj;
27
+ * traverse(obj, (value, update) => {
28
+ * if (typeof value === 'number') {
29
+ * update(value * 2);
30
+ * }
31
+ * });
32
+ * console.log(obj.a); // 2
33
+ */
34
+ export function traverse(
35
+ obj: any,
36
+ callback: TraverseCallback,
37
+ parent: any = null,
38
+ key: any = null,
39
+ visited = new WeakSet()
40
+ ): void {
41
+ if (obj == null || typeof obj !== 'object') {
42
+ callback(obj, (newValue: any) => {
43
+ if (parent !== null && key !== null) {
44
+ parent[key] = newValue;
45
+ }
46
+ });
47
+ return;
48
+ }
49
+
50
+ if (visited.has(obj)) {
51
+ return;
52
+ }
53
+ visited.add(obj);
54
+
55
+ if (Array.isArray(obj)) {
56
+ obj.forEach((item, index) => {
57
+ const update = (newValue: any) => {
58
+ obj[index] = newValue;
59
+ };
60
+ callback(item, update);
61
+ traverse(item, callback, obj, index, visited);
62
+ });
63
+ } else {
64
+ Object.entries(obj).forEach(([key, value]) => {
65
+ const update = (newValue: any) => {
66
+ obj[key] = newValue;
67
+ };
68
+ callback(value, update);
69
+ traverse(value, callback, obj, key, visited);
70
+ });
71
+ }
72
+ }