@developer_tribe/react-builder 1.2.28 → 1.2.30

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 (83) hide show
  1. package/dist/RenderPage.d.ts +7 -2
  2. package/dist/attributes-editor/attributesEditorModelTypes.d.ts +0 -1
  3. package/dist/build-components/index.generated.d.ts +38 -0
  4. package/dist/components/BuilderProvider.d.ts +9 -15
  5. package/dist/hooks/useLocalize.d.ts +3 -2
  6. package/dist/hooks/usePreviewSelection.d.ts +12 -0
  7. package/dist/index.cjs.js +1 -28
  8. package/dist/index.cjs.js.map +1 -1
  9. package/dist/index.d.ts +4 -2
  10. package/dist/index.esm.js +1 -28
  11. package/dist/index.esm.js.map +1 -1
  12. package/dist/index.web.cjs.js +4 -4
  13. package/dist/index.web.cjs.js.map +1 -1
  14. package/dist/index.web.esm.js +4 -4
  15. package/dist/index.web.esm.js.map +1 -1
  16. package/dist/logger.d.ts +3 -6
  17. package/dist/modals/IconPickerModal.d.ts +1 -1
  18. package/dist/pages/DebugJsonPage.d.ts +1 -4
  19. package/dist/product-base/index.d.ts +24 -0
  20. package/dist/size-matters/index.d.ts +15 -6
  21. package/dist/store.d.ts +5 -3
  22. package/dist/types/Icons.generated.d.ts +2 -0
  23. package/dist/types/PreviewConfig.d.ts +6 -8
  24. package/dist/types/Project.d.ts +4 -3
  25. package/dist/utils/extractTextStyle/extractTextStyle.d.ts +2 -0
  26. package/dist/utils/extractTextStyle/extractTextStyleNative.d.ts +2 -0
  27. package/dist/utils/extractViewStyle/extractViewStyle.d.ts +2 -0
  28. package/dist/utils/extractViewStyle/extractViewStyleNative.d.ts +2 -0
  29. package/package.json +1 -1
  30. package/scripts/prebuild/utils/validateAllComponentsOrThrow.js +19 -9
  31. package/src/RenderPage.tsx +66 -57
  32. package/src/assets/.DS_Store +0 -0
  33. package/src/assets/meta.json +1 -1
  34. package/src/assets/samples/carousel-sample.json +2 -6
  35. package/src/assets/samples/getSamples.ts +14 -4
  36. package/src/attribute-analyser/style/native/useExtractImageStyle.ts +3 -3
  37. package/src/attribute-analyser/style/native/useExtractTextStyle.ts +8 -2
  38. package/src/attribute-analyser/style/native/useExtractViewStyle.ts +7 -3
  39. package/src/attribute-analyser/style/web/useExtractImageStyle.ts +3 -3
  40. package/src/attribute-analyser/style/web/useExtractTextStyle.ts +8 -2
  41. package/src/attribute-analyser/style/web/useExtractViewStyle.ts +3 -3
  42. package/src/attributes-editor/AttributesEditorFields.tsx +1 -1
  43. package/src/attributes-editor/attributesEditorModelTypes.ts +0 -3
  44. package/src/attributes-editor/useAttributesEditorModel.ts +0 -3
  45. package/src/build-components/BIcon/BIcon.tsx +1 -1
  46. package/src/build-components/Button/Button.tsx +2 -2
  47. package/src/build-components/CarouselDots/CarouselDots.tsx +3 -3
  48. package/src/build-components/OnboardButton/OnboardButton.tsx +2 -2
  49. package/src/build-components/OnboardDot/OnboardDot.tsx +9 -3
  50. package/src/build-components/OnboardFooter/OnboardFooter.tsx +4 -5
  51. package/src/build-components/PaywallCloseButton/PaywallCloseButton.tsx +1 -1
  52. package/src/build-components/PaywallSubscribeButton/PaywallSubscribeButton.tsx +2 -2
  53. package/src/build-components/Text/Text.tsx +2 -2
  54. package/src/build-components/index.generated.ts +184 -0
  55. package/src/components/BottomBar.tsx +7 -9
  56. package/src/components/BuilderProvider.tsx +47 -84
  57. package/src/components/EditorHeader.tsx +6 -3
  58. package/src/hooks/useLocalize.ts +14 -10
  59. package/src/hooks/usePreviewSelection.ts +66 -0
  60. package/src/index.ts +2 -2
  61. package/src/logger.ts +4 -20
  62. package/src/modals/IconPickerModal.tsx +1 -1
  63. package/src/modals/InspectModal.tsx +6 -7
  64. package/src/modals/ProductPresetsModal.tsx +2 -2
  65. package/src/pages/DebugJsonPage.tsx +0 -6
  66. package/src/pages/ProjectPage.tsx +12 -57
  67. package/src/pages/tabs/SideTool.tsx +7 -7
  68. package/src/product-base/extractAndroidParams.ts +4 -11
  69. package/src/product-base/extractIOSParams.ts +4 -10
  70. package/src/product-base/index.ts +36 -0
  71. package/src/size-matters/index.ts +44 -31
  72. package/src/store.ts +12 -6
  73. package/src/styles/modals/_inspect-modal.scss +7 -3
  74. package/src/types/Icons.generated.ts +244 -0
  75. package/src/types/PreviewConfig.ts +5 -9
  76. package/src/types/Project.ts +4 -3
  77. package/src/utils/extractImageStyle.ts +4 -2
  78. package/src/utils/extractTextStyle/extractTextStyle.ts +6 -1
  79. package/src/utils/extractTextStyle/extractTextStyleNative.ts +4 -1
  80. package/src/utils/extractViewStyle/extractViewStyle.ts +7 -5
  81. package/src/utils/extractViewStyle/extractViewStyleNative.ts +3 -1
  82. package/src/utils/getDefaultProject.ts +0 -1
  83. package/src/utils/replaceLocalizationParams.ts +1 -1
package/dist/logger.d.ts CHANGED
@@ -5,14 +5,11 @@
5
5
  * This minimal version keeps product-base compilable in the builder context.
6
6
  */
7
7
  type LogPayload = Record<string, unknown>;
8
- type LogOptions = {
9
- remote?: boolean;
10
- };
11
8
  declare function noop(): void;
12
9
  export declare const iapLogger: {
13
- error(_tags: string[], message: string, payload?: LogPayload, _opts?: LogOptions): void;
14
- warn(_tags: string[], message: string, payload?: LogPayload, _opts?: LogOptions): void;
15
- info(_tags: string[], message: string, payload?: LogPayload, _opts?: LogOptions): void;
10
+ error(_tags: string[], message: string, payload?: LogPayload): void;
11
+ warn(_tags: string[], message: string, payload?: LogPayload): void;
12
+ info(_tags: string[], message: string, payload?: LogPayload): void;
16
13
  debug: typeof noop;
17
14
  };
18
15
  export {};
@@ -1,4 +1,4 @@
1
- import { type IconsType } from '../types/Icons';
1
+ import { type IconsType } from '../types/Icons.generated';
2
2
  type IconPickerModalProps = {
3
3
  value?: IconsType;
4
4
  onSelect: (icon: IconsType) => void;
@@ -1,6 +1,5 @@
1
1
  import React from 'react';
2
2
  import type { Node } from '../types/Node';
3
- import type { AppConfig } from '../types/PreviewConfig';
4
3
  export type DebugJsonPageProps = {
5
4
  data: Node | null | undefined;
6
5
  setData: React.Dispatch<React.SetStateAction<Node>>;
@@ -10,8 +9,6 @@ export type DebugJsonPageProps = {
10
9
  description?: string;
11
10
  previewMode?: boolean;
12
11
  setPreviewMode?: (next: boolean) => void;
13
- appConfig?: AppConfig;
14
- setAppConfig?: (next: AppConfig) => void;
15
12
  logLabel?: string;
16
13
  };
17
- export declare function DebugJsonPage({ data, setData, project, onClose, title, description, previewMode, setPreviewMode, appConfig, setAppConfig, logLabel, }: DebugJsonPageProps): import("react/jsx-runtime").JSX.Element;
14
+ export declare function DebugJsonPage({ data, setData, project, onClose, title, description, previewMode, setPreviewMode, logLabel, }: DebugJsonPageProps): import("react/jsx-runtime").JSX.Element;
@@ -25,3 +25,27 @@ export * from './extractAndroidParams';
25
25
  export * from './extractIOSParams';
26
26
  export * from './buildPaywallLocalizationParams';
27
27
  export * from './usePaywallLocalizationParams';
28
+ import type { Product } from './types';
29
+ /** Preset map: preset key → product array. */
30
+ export type MockProductPresets = Record<string, Product[]>;
31
+ /**
32
+ * Returns a deep-copy of all mock product presets.
33
+ *
34
+ * Use this instead of importing `mockProducts.json` directly so that:
35
+ * 1. Consumers don't couple to a JSON file path.
36
+ * 2. Each call returns a fresh copy (safe to mutate).
37
+ *
38
+ * @example
39
+ * import { getMockProducts } from '@developer_tribe/react-builder';
40
+ * const presets = getMockProducts(); // { 'preset-1': [...], ... }
41
+ */
42
+ export declare function getMockProducts(): MockProductPresets;
43
+ /**
44
+ * Returns the product list for a specific preset key (deep-copied).
45
+ * Returns an empty array if the key doesn't exist.
46
+ *
47
+ * @example
48
+ * import { getMockProductsByPreset } from '@developer_tribe/react-builder';
49
+ * const products = getMockProductsByPreset('preset-1');
50
+ */
51
+ export declare function getMockProductsByPreset(presetKey: string): Product[];
@@ -1,6 +1,15 @@
1
- export declare function scale(size: number): number;
2
- export declare function verticalScale(size: number): number;
3
- export declare const s: typeof scale;
4
- export declare const vs: typeof verticalScale;
5
- export declare const fs: typeof verticalScale;
6
- export declare function parseSize(value?: string | number): string | number | undefined;
1
+ import { type BaseSize } from '../types/PreviewConfig';
2
+ import type { Device } from '../types/Device';
3
+ /**
4
+ * Calculates scaling factors based on baseSize and current device dimensions.
5
+ */
6
+ export declare function getScalers(customBaseSize?: BaseSize, customDevice?: Device): {
7
+ scale: (size: number) => number;
8
+ verticalScale: (size: number) => number;
9
+ moderateScale: (size: number, factor?: number) => number;
10
+ };
11
+ export declare const s: (size: number) => number;
12
+ export declare const vs: (size: number) => number;
13
+ export declare const fs: (size: number) => number;
14
+ export declare const ms: (size: number, factor?: number) => number;
15
+ export declare function parseSize(value: string | number | undefined, baseSize?: BaseSize, device?: Device): string | number | undefined;
package/dist/store.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Device } from './types/Device';
2
- import { type AppConfig, type Theme } from './types/PreviewConfig';
2
+ import { type BaseSize, type Localication, type Theme } from './types/PreviewConfig';
3
3
  import { Node } from './types/Node';
4
4
  import type { LogEntry, LogLevel, ProjectColors } from './types/Project';
5
5
  import type { Product } from './paywall/types/paywall-types';
@@ -16,14 +16,16 @@ type RenderStore = {
16
16
  incForceRender: () => void;
17
17
  device: Device;
18
18
  setDevice: (device: Device) => void;
19
- appConfig: AppConfig;
20
- setAppConfig: (appConfig: AppConfig) => void;
19
+ baseSize: BaseSize;
20
+ setBaseSize: (baseSize: BaseSize) => void;
21
21
  projectColors?: ProjectColors;
22
22
  setProjectColors: (projectColors?: ProjectColors) => void;
23
23
  theme: Theme;
24
24
  setTheme: (theme: Theme) => void;
25
25
  defaultLanguage: string;
26
26
  setDefaultLanguage: (lang: string) => void;
27
+ localization: Localication;
28
+ setLocalization: (localization: Localication) => void;
27
29
  isRtl: boolean;
28
30
  setIsRtl: (isRtl: boolean) => void;
29
31
  previewMode: boolean;
@@ -0,0 +1,2 @@
1
+ export declare const Icons: readonly ["activity", "activity-heart", "alert-circle", "alert-triangle", "anchor", "archive", "arrow-down", "arrow-left", "arrow-narrow-down-left", "arrow-narrow-up-right", "arrow-right", "arrow-right-smooth", "asterisk-01", "asterisk-02", "at-sign", "award", "battery-charging", "bell-01", "bell-02", "bell-ringing-02", "bookmark", "bookmark-add", "bookmark-check", "bookmark-minus", "bookmark-x", "bubble", "building-01", "building-02", "building-03", "building-04", "building-05", "building-06", "building-07", "building-08", "building-09", "camera", "camera-01", "camera-steel", "check", "check-circle", "check-circle-bold", "check-circle-broken", "check-done-01", "check-done-02", "check-heart", "check-square", "check-square-broken", "check-verified-01", "check-verified-02", "check-verified-03", "checkbox", "checkv", "chevron-down", "chevron-down2", "chevron-left", "chevron-left-2", "chevron-right", "chevron-right-empty", "chevron-right-smooth", "chevron-up", "circle", "clock", "clock-fast-forward", "close", "close-circle", "cloud-01", "cloud-blank-01", "cloud-blank-02", "coin", "coins-02", "colors", "copy-01", "copy-02", "copy-03", "copy-04", "copy-05", "copy-06", "copy-07", "corner-down-right", "crypto-bold", "delete-icon", "diamond", "dice-3", "divide-01", "divide-02", "divide-03", "document-check-bold", "dots-circle", "dots-grid", "dots-horizontal", "dots-vertical", "download-01", "download-02", "download-03", "edit-03", "edit-04", "edit-05", "element-3", "ellipse-127", "exclaimation-circle", "eye-off-line", "face-smile", "file-04", "file-05", "file-check-02", "file-plus-01", "file-shield-02", "filter-funnel-01", "flag-03", "flash", "folder", "folder-plus", "gallery", "globe-01", "globe-04", "globe-bold", "guard", "headphones-01", "headphones-02", "headset-bold", "heart", "heart-bold", "help-circle", "home-2", "home-line", "hourglass-02", "image", "image-01", "image-03", "inbox-01", "inbox-arrow-down", "info-circle", "keyboard-line", "lamp-charge", "layer", "light", "like-dislike", "lock-03", "logout", "magicpen", "mail", "mail-01", "marker", "medal-star", "menu", "menu-04", "message-circle-01", "message-plus-circle", "message-question-circle", "message-text-circle-01", "message-text-square-02", "message-x-square", "microphone-02", "microphone-slash", "mirror", "moon-01", "moon-bold", "mouse-circle", "move", "notification", "notification-fill", "notification-text", "pdf-01", "pencil-01", "phone", "phone-01", "phone-arrow-down-left", "phone-arrow-up-right", "phone-hang-up", "phone-hangup2", "phone-incoming-01", "phone-outgoing-01", "phone-plus", "phone-x", "plus", "plus-circle", "printer", "question-mark-circle", "refresh-ccw-01", "refresh-cw-01", "refresh-cw-04", "refresh-right-square-bold", "remove-circle", "repeat-04", "repeat-bold", "ruler-pen", "search", "search-lg", "search-md", "search-refraction", "send-01", "send-02", "send-diagonal", "setting-2", "settings", "settings-02", "settings-04", "settings-2", "settings-cog", "share-01", "share-03", "share-04", "share-05", "share-06", "share-bold", "shield-01", "shield-bold", "solar-check", "speaker", "speaker-wave", "speedometer-03", "star", "star-rounded", "sun", "target-03", "text-input", "translate", "trash", "trash-02", "trash-03", "trash-04", "trush-square-bold", "unlimited", "user-circle", "user-jogging", "user-plus-01", "user-square", "user-x-01", "user-x-02", "user2", "users-02", "users-speaker", "verify", "voice-cricle", "x-circle", "x-close", "x-sm", "zap"];
2
+ export type IconsType = (typeof Icons)[number];
@@ -1,11 +1,4 @@
1
1
  export type Theme = 'light' | 'dark';
2
- export interface AppConfig {
3
- localication: Localication;
4
- baseSize: {
5
- width: number;
6
- height: number;
7
- };
8
- }
9
2
  export type LocalizationKey = 'base.builder.paywall.period.monthly' | 'base.builder.paywall.period.annual' | 'base.builder.paywall.period.weekly' | 'base.builder.paywall.period.daily' | 'base.builder.paywall.period.monthlyPromo' | 'base.builder.paywall.period.annualPromo' | 'base.builder.paywall.pricing.default.text' | 'base.builder.paywall.pricing.freeTrial.text' | 'base.builder.paywall.pricing.regular.text' | 'base.builder.paywall.promo.default.text' | 'base.builder.paywall.promo.freeTrial.text' | 'base.builder.paywall.promo.regular.text' | 'onboard.title.one-page' | 'onboard.title.two-page' | 'onboard.title.three-page' | 'onboard.title.four-page' | 'onboard.title.one-page2' | 'onboard.title.two-page2' | 'onboard.title.three-page2' | 'onboard.title.four-page2' | 'onboard.subtitle.one-page' | 'onboard.subtitle.two-page' | 'onboard.subtitle.three-page' | 'onboard.subtitle.four-page' | 'onboard.subtitle.one-page2' | 'onboard.subtitle.two-page2' | 'onboard.subtitle.three-page2' | 'onboard.subtitle.four-page2' | 'onboard.next.one-page' | 'onboard.next.two-page' | 'onboard.next.three-page' | 'onboard.skip.one-page' | 'onboard.skip.two-page' | 'onboard.skip.three-page' | 'onboard.allow.four-page' | 'view.onboarding.footer.description' | 'view.onboarding.btnPrivacy' | 'view.onboarding.btnTerms' | (string & {});
10
3
  export type Localication = Record<string, Partial<Record<LocalizationKey, string>>>;
11
4
  export declare const defaultLocalization: Localication;
@@ -15,4 +8,9 @@ export declare const defaultLocalization: Localication;
15
8
  */
16
9
  export declare function mergeLocalization(base: Localication, custom: Localication): Localication;
17
10
  export declare const defaultTheme: Theme;
18
- export declare const defaultAppConfig: AppConfig;
11
+ export type BaseSize = {
12
+ width: number;
13
+ height: number;
14
+ };
15
+ export declare const defaultBaseSize: BaseSize;
16
+ export declare const defaultLanguage = "en";
@@ -1,5 +1,5 @@
1
1
  import { Node } from '../types/Node';
2
- import { AppConfig } from './PreviewConfig';
2
+ import { BaseSize, Localication } from './PreviewConfig';
3
3
  export type ProjectColorTokenMap = Record<string, string>;
4
4
  export type ProjectThemeColors = {
5
5
  light?: ProjectColorTokenMap;
@@ -19,7 +19,8 @@ export interface ProjectBase<T> {
19
19
  */
20
20
  type?: 'paywall' | 'onboard' | 'other';
21
21
  data: T;
22
- appConfig?: AppConfig;
22
+ baseSize?: BaseSize;
23
+ localization?: Localication;
23
24
  projectColors?: ProjectColors;
24
25
  }
25
26
  export interface Project extends ProjectBase<Node> {
@@ -27,7 +28,7 @@ export interface Project extends ProjectBase<Node> {
27
28
  /**
28
29
  * Lightweight subset of Project containing only the essential metadata
29
30
  * needed for persistence (name, version, type, data).
30
- * Excludes runtime/editor-only fields like appConfig and projectColors.
31
+ * Excludes runtime/editor-only fields like projectColors.
31
32
  */
32
33
  export type ProjectMeta = Pick<Project, 'name' | 'version' | 'type' | 'data'>;
33
34
  export type LogLevel = 'NONE' | 'ERROR' | 'WARN' | 'INFO' | 'VERBOSE';
@@ -3,6 +3,7 @@ import type { TextPropsGenerated } from '../../build-components/Text/TextProps.g
3
3
  import type { Theme } from '../../types/PreviewConfig';
4
4
  import type { ProjectColors } from '../../types/Project';
5
5
  import type { Fonts } from '../../types/Fonts';
6
+ import type { BaseSize } from '../../types/PreviewConfig';
6
7
  export type ExtractTextStyleOptions = {
7
8
  theme?: Theme;
8
9
  projectColors?: ProjectColors;
@@ -10,5 +11,6 @@ export type ExtractTextStyleOptions = {
10
11
  onFontLoaded?: (fontFamily: string) => void;
11
12
  onError?: (error: string) => void;
12
13
  directlyTextStyle?: boolean;
14
+ baseSize?: BaseSize;
13
15
  };
14
16
  export declare function extractTextStyle<T extends TextPropsGenerated['attributes']>(node: NodeData<T>, options?: ExtractTextStyleOptions): import("react").CSSProperties;
@@ -3,10 +3,12 @@ import type { TextPropsGenerated } from '../../build-components/Text/TextProps.g
3
3
  import type { Theme } from '../../types/PreviewConfig';
4
4
  import type { ProjectColors } from '../../types/Project';
5
5
  import type { Fonts } from '../../types/Fonts';
6
+ import type { BaseSize } from '../../types/PreviewConfig';
6
7
  export type ExtractTextStyleNativeOptions = {
7
8
  theme?: Theme;
8
9
  projectColors?: ProjectColors;
9
10
  fonts?: Fonts;
11
+ baseSize?: BaseSize;
10
12
  };
11
13
  /**
12
14
  * React Native-friendly text style extraction.
@@ -1,8 +1,10 @@
1
1
  import { ViewPropsGenerated } from '../../build-components/View/ViewProps.generated';
2
2
  import type { NodeData } from '../../types/Node';
3
3
  import type { ProjectColors } from '../../types/Project';
4
+ import type { BaseSize } from '../../types/PreviewConfig';
4
5
  export type ExtractViewStyleOptions = {
5
6
  projectColors?: ProjectColors;
6
7
  theme?: string;
8
+ baseSize?: BaseSize;
7
9
  };
8
10
  export declare function extractViewStyle<T extends ViewPropsGenerated['attributes']>(node: NodeData<T>, options?: ExtractViewStyleOptions): import("react").CSSProperties;
@@ -1,9 +1,11 @@
1
1
  import type { ViewPropsGenerated } from '../../build-components/View/ViewProps.generated';
2
2
  import type { NodeData } from '../../types/Node';
3
3
  import type { ProjectColors } from '../../types/Project';
4
+ import type { BaseSize } from '../../types/PreviewConfig';
4
5
  export type ExtractViewStyleNativeOptions = {
5
6
  projectColors?: ProjectColors;
6
7
  theme?: string;
8
+ baseSize?: BaseSize;
7
9
  };
8
10
  /**
9
11
  * Extracts a React Native-friendly style object from node attributes.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@developer_tribe/react-builder",
3
- "version": "1.2.28",
3
+ "version": "1.2.30",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "restricted": true,
@@ -25,6 +25,7 @@ async function getAllEntriesInComponentsRoot(paths) {
25
25
  d.name !== 'getDefaults.ts' &&
26
26
  d.name !== 'useDefaults.ts' &&
27
27
  d.name !== 'useNode.ts' &&
28
+ d.name !== 'index.generated.ts' &&
28
29
  d.name !== 'patterns.generated.ts'
29
30
  );
30
31
  });
@@ -174,15 +175,17 @@ async function validatePatternJson(componentDir, componentName) {
174
175
  const patternDefaults = isPlainObject(source?.pattern?.defaults)
175
176
  ? source.pattern.defaults
176
177
  : {};
177
- const directDefaults = isPlainObject(source?.defaults) ? source.defaults : {};
178
+ const directDefaults = isPlainObject(source?.defaults)
179
+ ? source.defaults
180
+ : {};
178
181
  const combined = { ...patternDefaults, ...directDefaults };
179
-
182
+
180
183
  // Convert legacy `style` to `styles` (schemaVersion 2)
181
184
  if (isPlainObject(combined.style)) {
182
185
  combined.styles = { ...(combined.styles || {}), ...combined.style };
183
186
  delete combined.style;
184
187
  }
185
-
188
+
186
189
  return combined;
187
190
  };
188
191
 
@@ -358,17 +361,24 @@ async function validatePatternJson(componentDir, componentName) {
358
361
  const mergedTypes = { ...(parentData.types || {}), ...(data.types || {}) };
359
362
  const parentDefaults = buildDefaultsBlock(parentData);
360
363
  const childDefaults = buildDefaultsBlock(data);
361
-
364
+
362
365
  // Deep-merge `styles` from both parent and child (already converted from legacy `style` by buildDefaultsBlock)
363
- const parentDefaultStyles = isPlainObject(parentDefaults?.styles) ? parentDefaults.styles : {};
364
- const childDefaultStyles = isPlainObject(childDefaults?.styles) ? childDefaults.styles : {};
365
- const mergedDefaultStyles = { ...parentDefaultStyles, ...childDefaultStyles };
366
-
366
+ const parentDefaultStyles = isPlainObject(parentDefaults?.styles)
367
+ ? parentDefaults.styles
368
+ : {};
369
+ const childDefaultStyles = isPlainObject(childDefaults?.styles)
370
+ ? childDefaults.styles
371
+ : {};
372
+ const mergedDefaultStyles = {
373
+ ...parentDefaultStyles,
374
+ ...childDefaultStyles,
375
+ };
376
+
367
377
  const mergedDefaults = {
368
378
  ...parentDefaults,
369
379
  ...childDefaults,
370
380
  };
371
-
381
+
372
382
  // Set merged styles if there are any
373
383
  if (Object.keys(mergedDefaultStyles).length > 0) {
374
384
  mergedDefaults.styles = mergedDefaultStyles;
@@ -1,15 +1,15 @@
1
- import { useEffect, useRef } from 'react';
1
+ import { useRef } from 'react';
2
2
  import { DeviceMockFrame } from './DeviceMockFrame';
3
3
  import { Node } from './types/Node';
4
4
  import { RenderNode } from './build-components';
5
5
  import { useRenderStore } from './store';
6
6
  import { useLogRender } from './utils/useLogRender';
7
- import { findNodeByKeyNested } from './utils/findNodeByKeyNested';
8
7
  import {
9
8
  BuilderProvider,
10
- type BuilderProviderParams,
9
+ BuilderProviderParams,
11
10
  } from './components/BuilderProvider';
12
11
  import { RenderErrorBoundary } from './components/RenderErrorBoundary';
12
+ import { usePreviewSelection } from './hooks/usePreviewSelection';
13
13
  export type ScreenStyle = {
14
14
  light: { backgroundColor: string; color: string; seperatorColor?: string };
15
15
  dark: { backgroundColor: string; color: string; seperatorColor?: string };
@@ -18,7 +18,12 @@ interface RenderPageProps {
18
18
  data: Node;
19
19
  name: string;
20
20
  onSelectNode?: (node: Node | null) => void;
21
- params: BuilderProviderParams;
21
+ /**
22
+ * Optional overrides for the builder provider.
23
+ * Useful for debug views or isolated rendering where we want to Inject
24
+ * specific mocks (products, benefits) instead of reading from the global store.
25
+ */
26
+ params?: Partial<BuilderProviderParams>;
22
27
  }
23
28
 
24
29
  function isNullish(value: unknown): value is null | undefined {
@@ -32,65 +37,69 @@ export function RenderPage({
32
37
  params,
33
38
  }: RenderPageProps) {
34
39
  useLogRender('RenderPage');
35
- const { previewMode, forceRender, setCurrent, appFont } = useRenderStore(
36
- (s) => ({
37
- previewMode: s.previewMode,
38
- forceRender: s.forceRender,
39
- setCurrent: s.setCurrent,
40
- appFont: s.appFont,
41
- }),
42
- );
40
+ const {
41
+ products,
42
+ benefits,
43
+ mockTheme,
44
+ mockDefaultLanguage,
45
+ overrideLocalization,
46
+ overrideBaseSize,
47
+ overrideProjectColors,
48
+ overrideFonts,
49
+ appFont,
50
+ previewMode,
51
+ current,
52
+ forceRender,
53
+ setCurrent,
54
+ } = useRenderStore((s) => ({
55
+ products: s.products,
56
+ benefits: s.benefits,
57
+ mockTheme: s.theme,
58
+ mockDefaultLanguage: s.defaultLanguage,
59
+ overrideLocalization: s.localization,
60
+ overrideBaseSize: s.baseSize,
61
+ overrideProjectColors: s.projectColors,
62
+ overrideFonts: s.fonts,
63
+ appFont: s.appFont,
64
+ previewMode: s.previewMode,
65
+ current: s.current,
66
+ forceRender: s.forceRender,
67
+ setCurrent: s.setCurrent,
68
+ }));
43
69
  const previewRootRef = useRef<HTMLDivElement | null>(null);
44
70
  const showEmptyState = isNullish(data);
45
71
 
46
- useEffect(() => {
47
- if (!previewMode) {
48
- return;
49
- }
50
-
51
- const root = previewRootRef.current;
52
- if (!root) {
53
- return;
54
- }
55
-
56
- const handleClick = (event: MouseEvent) => {
57
- const target = event.target as HTMLElement | null;
58
-
59
- if (!target) return;
60
-
61
- // Ignore clicks on carousel dots to avoid interfering with navigation
62
- if (target.closest('.embla__dot')) {
63
- return;
64
- }
65
-
66
- // Some build-components may attach synthetic keys (e.g. React `useId()`),
67
- // which are not present in the persisted node tree. Walk up until we find
68
- // an attribute-key that resolves to a real node.
69
- let element = target.closest('[attribute-key]') as HTMLElement | null;
70
- while (element) {
71
- const key = element.getAttribute('attribute-key');
72
- if (key) {
73
- const node = findNodeByKeyNested(data, key);
74
- if (node) {
75
- setCurrent(node);
76
- onSelectNode?.(node);
77
- return;
78
- }
79
- }
80
- element = element.parentElement?.closest('[attribute-key]') ?? null;
81
- }
82
- };
83
-
84
- root.addEventListener('click', handleClick);
85
-
86
- return () => {
87
- root.removeEventListener('click', handleClick);
88
- };
89
- }, [previewMode, data, onSelectNode, setCurrent, forceRender]); // forceRender: retrigger effect when we want to force a refresh (e.g. route change)
72
+ usePreviewSelection({
73
+ previewMode: !!previewMode,
74
+ data,
75
+ rootRef: previewRootRef,
76
+ onSelectNode,
77
+ setCurrent,
78
+ forceRender,
79
+ });
90
80
 
91
81
  return (
92
82
  <RenderErrorBoundary subtitle="caught by RenderPage">
93
- <BuilderProvider params={params}>
83
+ <BuilderProvider
84
+ params={{
85
+ // Store defaults
86
+ mockTheme: mockTheme,
87
+ mockDefaultLanguage: mockDefaultLanguage,
88
+ localization: overrideLocalization,
89
+ baseSize: overrideBaseSize,
90
+ mockProducts: products,
91
+ mockBenefits: benefits as any,
92
+ projectColors: overrideProjectColors ?? undefined,
93
+ fonts: overrideFonts,
94
+ previewMode: previewMode,
95
+ selectedKey:
96
+ current && typeof current === 'object' && 'key' in current
97
+ ? ((current as any).key as string)
98
+ : undefined,
99
+ // Explicit overrides take precedence
100
+ ...params,
101
+ }}
102
+ >
94
103
  <DeviceMockFrame appName={name}>
95
104
  <div
96
105
  className="screen-preview"
Binary file
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "supportedProjectVersion": "1.1.2",
3
- "reactBuilderVersion": "1.2.27"
3
+ "reactBuilderVersion": "1.2.29"
4
4
  }
@@ -280,9 +280,7 @@
280
280
  {
281
281
  "type": "carouselButtons",
282
282
  "attributes": {
283
- "buttonType": [
284
- "previous_button"
285
- ],
283
+ "buttonType": ["previous_button"],
286
284
  "styles": {
287
285
  "backgroundColor": "#6366f1",
288
286
  "borderRadius": 12,
@@ -295,9 +293,7 @@
295
293
  {
296
294
  "type": "carouselButtons",
297
295
  "attributes": {
298
- "buttonType": [
299
- "next_button"
300
- ],
296
+ "buttonType": ["next_button"],
301
297
  "styles": {
302
298
  "backgroundColor": "#8b5cf6",
303
299
  "borderRadius": 12,
@@ -20,13 +20,23 @@ import vpnOnboard4 from './vpn-onboard-4.json';
20
20
  import vpnOnboard5 from './vpn-onboard-5.json';
21
21
  import vpnOnboard6 from './vpn-onboard-6.json';
22
22
  import vpnOnboard7 from './vpn-onboard-7.json';
23
- import { defaultAppConfig } from '../../types/PreviewConfig';
23
+ import {
24
+ defaultBaseSize,
25
+ defaultLocalization,
26
+ } from '../../types/PreviewConfig';
24
27
 
25
28
  function normalizeSample(sample: any): Project {
26
- return {
29
+ // Legacy migration: extract baseSize/localization from appConfig if present
30
+ const legacyConfig = sample.appConfig || {};
31
+ const project = {
27
32
  ...sample,
28
- appConfig: { ...defaultAppConfig, ...(sample as any).appConfig },
29
- } as Project;
33
+ baseSize: legacyConfig.baseSize || sample.baseSize || defaultBaseSize,
34
+ localization:
35
+ legacyConfig.localization || sample.localization || defaultLocalization,
36
+ };
37
+ // Ensure appConfig is removed if it existed
38
+ delete project.appConfig;
39
+ return project as Project;
30
40
  }
31
41
 
32
42
  export function getSamples(): Project[] {
@@ -22,13 +22,13 @@ export function useExtractImageStyle<
22
22
  style: ImageStyle;
23
23
  other: Omit<T, StyleAttrKey> & { resizeMode?: ResizeModeOptionType };
24
24
  } {
25
- const { theme, projectColors } = useBuilderParams();
25
+ const { mockTheme: theme, projectColors, baseSize } = useBuilderParams();
26
26
 
27
27
  return useMemo(() => {
28
28
  // extractImageStyleNative returns Record<string, unknown> that may include resizeMode.
29
29
  // In RN, resizeMode is typically an Image prop, not a style property.
30
30
  const { resizeMode: resizeModeFromStyle, ...style } =
31
- extractImageStyleNative(node, { theme, projectColors });
31
+ extractImageStyleNative(node, { theme, projectColors, baseSize });
32
32
 
33
33
  const attrs = node.attributes;
34
34
  const stripped = stripStyleKeys(toAttributeRecord(attrs));
@@ -45,5 +45,5 @@ export function useExtractImageStyle<
45
45
  resizeMode,
46
46
  } as Omit<T, StyleAttrKey> & { resizeMode?: ResizeModeOptionType },
47
47
  };
48
- }, [node, theme, projectColors]);
48
+ }, [node, theme, projectColors, baseSize]);
49
49
  }
@@ -20,7 +20,12 @@ export function useExtractTextStyle<T extends TextPropsGenerated['attributes']>(
20
20
  showEllipsis?: boolean;
21
21
  };
22
22
  } {
23
- const { theme, projectColors, fonts } = useBuilderParams();
23
+ const {
24
+ mockTheme: theme,
25
+ projectColors,
26
+ fonts,
27
+ baseSize,
28
+ } = useBuilderParams();
24
29
 
25
30
  return useMemo(
26
31
  () => {
@@ -28,6 +33,7 @@ export function useExtractTextStyle<T extends TextPropsGenerated['attributes']>(
28
33
  theme,
29
34
  projectColors,
30
35
  fonts,
36
+ baseSize,
31
37
  });
32
38
 
33
39
  const attrs = node.attributes;
@@ -51,6 +57,6 @@ export function useExtractTextStyle<T extends TextPropsGenerated['attributes']>(
51
57
  };
52
58
  },
53
59
  // fonts is intentionally included: extractTextStyleNative resolves weights via font definitions.
54
- [node, theme, projectColors, fonts],
60
+ [node, theme, projectColors, fonts, baseSize],
55
61
  );
56
62
  }
@@ -17,10 +17,14 @@ export function useExtractViewStyle<T extends ViewPropsGenerated['attributes']>(
17
17
  style: ViewStyle;
18
18
  other: Omit<T, StyleAttrKey> & { scrollable?: boolean };
19
19
  } {
20
- const { theme, projectColors } = useBuilderParams();
20
+ const { mockTheme: theme, projectColors, baseSize } = useBuilderParams();
21
21
 
22
22
  return useMemo(() => {
23
- const style = extractViewStyleNative(node, { theme, projectColors });
23
+ const style = extractViewStyleNative(node, {
24
+ theme,
25
+ projectColors,
26
+ baseSize,
27
+ });
24
28
  const attrs = node.attributes;
25
29
  const stripped = stripStyleKeys(toAttributeRecord(attrs));
26
30
  const styleBag = getStyleBag(attrs);
@@ -35,5 +39,5 @@ export function useExtractViewStyle<T extends ViewPropsGenerated['attributes']>(
35
39
  | undefined,
36
40
  } as Omit<T, StyleAttrKey> & { scrollable?: boolean },
37
41
  };
38
- }, [node, theme, projectColors]);
42
+ }, [node, theme, projectColors, baseSize]);
39
43
  }
@@ -8,10 +8,10 @@ import { extractImageStyle } from '../../../utils/extractImageStyle';
8
8
  export function useExtractImageStyle<
9
9
  T extends ImagePropsGenerated['attributes'],
10
10
  >(node: NodeData<T>): CSSProperties {
11
- const { theme, projectColors } = useBuilderParams();
11
+ const { mockTheme: theme, projectColors, baseSize } = useBuilderParams();
12
12
 
13
13
  return useMemo(
14
- () => extractImageStyle(node, { theme, projectColors }),
15
- [node, theme, projectColors],
14
+ () => extractImageStyle(node, { theme, projectColors, baseSize }),
15
+ [node, theme, projectColors, baseSize],
16
16
  );
17
17
  }
@@ -9,7 +9,12 @@ export function useExtractTextStyle<T extends TextPropsGenerated['attributes']>(
9
9
  node: NodeData<T>,
10
10
  directlyTextStyle = false,
11
11
  ): CSSProperties {
12
- const { theme, projectColors, fonts } = useBuilderParams();
12
+ const {
13
+ mockTheme: theme,
14
+ projectColors,
15
+ fonts,
16
+ baseSize,
17
+ } = useBuilderParams();
13
18
 
14
19
  return useMemo(
15
20
  () =>
@@ -18,8 +23,9 @@ export function useExtractTextStyle<T extends TextPropsGenerated['attributes']>(
18
23
  projectColors,
19
24
  fonts,
20
25
  directlyTextStyle,
26
+ baseSize,
21
27
  }),
22
28
  // fonts is intentionally included: extractTextStyle resolves weights via font definitions.
23
- [node, theme, projectColors, fonts, directlyTextStyle],
29
+ [node, theme, projectColors, fonts, directlyTextStyle, baseSize],
24
30
  );
25
31
  }