@developer_tribe/react-builder 1.2.22 → 1.2.24

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 (106) hide show
  1. package/dist/attribute-analyser/style/native/useExtractImageStyle.d.ts +6 -6
  2. package/dist/attribute-analyser/style/native/useExtractTextStyle.d.ts +6 -4
  3. package/dist/attribute-analyser/style/native/useExtractViewStyle.d.ts +5 -3
  4. package/dist/build-components/Image/ImageProps.generated.d.ts +2 -4
  5. package/dist/build-components/NavigationBarColor/NavigationBarColor.d.ts +5 -0
  6. package/dist/build-components/NavigationBarColor/NavigationBarColorProps.generated.d.ts +54 -0
  7. package/dist/build-components/OnboardImage/OnboardImageProps.generated.d.ts +1 -3
  8. package/dist/build-components/Separator/Separator.d.ts +5 -0
  9. package/dist/build-components/Separator/SeparatorProps.generated.d.ts +21 -0
  10. package/dist/build-components/StatusBarColor/StatusBarColor.d.ts +5 -0
  11. package/dist/build-components/StatusBarColor/StatusBarColorProps.generated.d.ts +54 -0
  12. package/dist/build-components/index.d.ts +4 -1
  13. package/dist/build-components/patterns.generated.d.ts +2111 -1251
  14. package/dist/components/AttributesEditorPanel.d.ts +1 -1
  15. package/dist/components/BuilderProvider.d.ts +1 -1
  16. package/dist/index.cjs.js +4 -4
  17. package/dist/index.cjs.js.map +1 -1
  18. package/dist/index.d.ts +2 -0
  19. package/dist/index.esm.js +4 -4
  20. package/dist/index.esm.js.map +1 -1
  21. package/dist/index.web.cjs.js +6 -6
  22. package/dist/index.web.cjs.js.map +1 -1
  23. package/dist/index.web.esm.js +4 -4
  24. package/dist/index.web.esm.js.map +1 -1
  25. package/dist/store.d.ts +4 -0
  26. package/dist/styles.css +1 -1
  27. package/dist/utils/attributeStyle.d.ts +21 -0
  28. package/dist/utils/extractImageStyle.d.ts +1 -1
  29. package/dist/utils/extractViewStyle/extractViewStyleNative.d.ts +1 -1
  30. package/package.json +7 -2
  31. package/src/DeviceMockFrame.tsx +8 -2
  32. package/src/assets/meta.json +1 -1
  33. package/src/assets/samples/paywall-1.json +44 -39
  34. package/src/assets/samples/paywall-2.json +44 -25
  35. package/src/assets/samples/paywall-app-delete-offer.json +40 -21
  36. package/src/assets/samples/paywall-app-open-offer.json +40 -21
  37. package/src/assets/samples/paywall-back-offer.json +40 -21
  38. package/src/assets/samples/paywall-notification-offer.json +40 -21
  39. package/src/assets/samples/vpn-onboard-1.json +84 -39
  40. package/src/assets/samples/vpn-onboard-2.json +85 -40
  41. package/src/assets/samples/vpn-onboard-3.json +84 -39
  42. package/src/assets/samples/vpn-onboard-4.json +84 -39
  43. package/src/assets/samples/vpn-onboard-5.json +102 -55
  44. package/src/assets/samples/vpn-onboard-6.json +87 -38
  45. package/src/attribute-analyser/style/native/useExtractImageStyle.ts +31 -25
  46. package/src/attribute-analyser/style/native/useExtractTextStyle.ts +26 -11
  47. package/src/attribute-analyser/style/native/useExtractViewStyle.ts +21 -11
  48. package/src/attributes-editor/useAttributesEditorModel.ts +23 -17
  49. package/src/build-components/BackgroundImage/pattern.json +9 -7
  50. package/src/build-components/CarouselDots/CarouselDots.tsx +12 -11
  51. package/src/build-components/CarouselProvider/CarouselProvider.tsx +3 -1
  52. package/src/build-components/Image/ImageProps.generated.ts +2 -4
  53. package/src/build-components/Image/pattern.json +15 -25
  54. package/src/build-components/NavigationBarColor/NavigationBarColor.tsx +39 -0
  55. package/src/build-components/NavigationBarColor/NavigationBarColorProps.generated.ts +71 -0
  56. package/src/build-components/NavigationBarColor/pattern.json +34 -0
  57. package/src/build-components/OnboardButton/OnboardButton.tsx +19 -5
  58. package/src/build-components/OnboardButtons/OnboardButtons.tsx +8 -10
  59. package/src/build-components/OnboardDot/OnboardDot.tsx +12 -10
  60. package/src/build-components/OnboardFooter/OnboardFooter.tsx +15 -4
  61. package/src/build-components/OnboardImage/OnboardImage.tsx +1 -1
  62. package/src/build-components/OnboardImage/OnboardImageProps.generated.ts +1 -3
  63. package/src/build-components/OnboardProvider/OnboardProvider.tsx +3 -1
  64. package/src/build-components/RenderNode.generated.tsx +15 -0
  65. package/src/build-components/Separator/Separator.tsx +41 -0
  66. package/src/build-components/Separator/SeparatorProps.generated.ts +26 -0
  67. package/src/build-components/Separator/pattern.json +59 -0
  68. package/src/build-components/StatusBarColor/StatusBarColor.tsx +39 -0
  69. package/src/build-components/StatusBarColor/StatusBarColorProps.generated.ts +71 -0
  70. package/src/build-components/StatusBarColor/pattern.json +34 -0
  71. package/src/build-components/Text/pattern.json +45 -38
  72. package/src/build-components/index.ts +15 -0
  73. package/src/build-components/patterns.generated.ts +2153 -1272
  74. package/src/build-components/useNode.ts +24 -25
  75. package/src/components/AttributesEditorPanel.tsx +4 -5
  76. package/src/components/Builder.tsx +1 -2
  77. package/src/components/BuilderProvider.tsx +43 -6
  78. package/src/components/JsonTextEditor.tsx +2 -2
  79. package/src/components/LoadingComponent.tsx +1 -1
  80. package/src/components/RenderErrorBoundary.tsx +1 -3
  81. package/src/index.ts +3 -0
  82. package/src/migrations/migrations/1.1.2_extract_component_attributes_from_style.ts +3 -3
  83. package/src/modals/BenefitPresetsModal.tsx +1 -1
  84. package/src/modals/ProductPresetsModal.tsx +1 -1
  85. package/src/pages/DebugJsonPage.tsx +7 -4
  86. package/src/pages/ProjectDebug.tsx +1 -1
  87. package/src/pages/ProjectPage.tsx +31 -32
  88. package/src/pages/ProjectValidationPage.tsx +2 -2
  89. package/src/store.ts +13 -0
  90. package/src/styles/layout/_builder.scss +6 -0
  91. package/src/utils/__special_exceptions.ts +5 -5
  92. package/src/utils/analyseNode.ts +2 -2
  93. package/src/utils/analyseNodeByPatterns.ts +10 -9
  94. package/src/utils/analyseNodeStructural.ts +1 -1
  95. package/src/utils/attributeStyle.ts +104 -0
  96. package/src/utils/extractImageStyle.ts +17 -13
  97. package/src/utils/extractTextStyle/extractTextStyle.ts +7 -7
  98. package/src/utils/extractTextStyle/extractTextStyleNative.ts +10 -10
  99. package/src/utils/extractViewStyle/extractViewStyle.ts +8 -11
  100. package/src/utils/extractViewStyle/extractViewStyleNative.ts +19 -19
  101. package/src/utils/loadFontFamily.ts +14 -19
  102. package/src/utils/logRenderStore.ts +5 -4
  103. package/src/utils/nodeTree.ts +1 -1
  104. package/src/utils/patterns.ts +26 -31
  105. package/src/utils/repairNodeKeys.ts +5 -7
  106. package/src/utils/wrapNodeInMain.ts +3 -3
@@ -9,7 +9,9 @@ import { useExtractViewStyle } from '../../attribute-analyser/style/web/useExtra
9
9
  export const carouselContext = createContext<any>(undefined);
10
10
  function CarouselProvider({ node }: CarouselProviderComponentProps) {
11
11
  node = useNode(node);
12
- const [emblaRef, emblaApi] = useEmblaCarousel(node.attributes as any);
12
+ const [emblaRef, emblaApi] = useEmblaCarousel(
13
+ node.attributes as Parameters<typeof useEmblaCarousel>[0],
14
+ );
13
15
  const generatedId = useId();
14
16
  const attributeName = node.sourceType ?? node.type ?? 'carouselProvider';
15
17
  const attributeKey = node.key ?? generatedId;
@@ -2,7 +2,6 @@
2
2
 
3
3
  import type { NodeData } from '../../types/Node';
4
4
 
5
- export type ResizeModeOptionType = 'cover' | 'contain' | 'stretch' | 'center';
6
5
  export type FlexDirectionOptionType = 'row' | 'column';
7
6
  export type FlexWrapOptionType = 'nowrap' | 'wrap' | 'wrap-reverse';
8
7
  export type AlignItemsOptionType =
@@ -19,6 +18,7 @@ export type JustifyContentOptionType =
19
18
  | 'space-around'
20
19
  | 'space-evenly';
21
20
  export type PositionOptionType = 'relative' | 'absolute';
21
+ export type ResizeModeOptionType = 'cover' | 'contain' | 'stretch' | 'center';
22
22
 
23
23
  export interface ImageStyleGenerated {
24
24
  flexDirection?: FlexDirectionOptionType;
@@ -55,6 +55,7 @@ export interface ImageStyleGenerated {
55
55
  left?: string;
56
56
  right?: string;
57
57
  zIndex?: number;
58
+ resizeMode?: ResizeModeOptionType;
58
59
  }
59
60
 
60
61
  export interface ImagePropsGenerated {
@@ -65,9 +66,6 @@ export interface ImagePropsGenerated {
65
66
  title?: string;
66
67
  description?: string;
67
68
  src?: string;
68
- width?: string;
69
- height?: string;
70
- resizeMode?: ResizeModeOptionType;
71
69
  };
72
70
  }
73
71
 
@@ -8,16 +8,27 @@
8
8
  "title": "title",
9
9
  "description": "description",
10
10
  "src": "string",
11
- "width": "size",
12
- "height": "size",
13
- "resizeMode": ["cover", "contain", "stretch", "center"]
11
+ "style": {
12
+ "resizeMode": ["cover", "contain", "stretch", "center"]
13
+ }
14
+ },
15
+ "defaults": {
16
+ "resizeMode": "contain"
14
17
  }
15
18
  },
16
19
  "meta": {
17
20
  "desiredParent": ["all"],
18
21
  "label": "Image",
19
22
  "description": "Shows an image or graphic.",
20
- "styles": {},
23
+ "styles": {
24
+ "resizeMode": {
25
+ "label": "Resize Mode",
26
+ "description": "How the image fits the frame.",
27
+ "category": "style",
28
+ "specialCategory": null,
29
+ "sort": 4
30
+ }
31
+ },
21
32
  "attributes": {
22
33
  "src": {
23
34
  "label": "Src",
@@ -25,27 +36,6 @@
25
36
  "category": "other",
26
37
  "specialCategory": null,
27
38
  "sort": 1
28
- },
29
- "width": {
30
- "label": "Width",
31
- "description": "Image width.",
32
- "category": "container",
33
- "specialCategory": null,
34
- "sort": 2
35
- },
36
- "height": {
37
- "label": "Height",
38
- "description": "Image height.",
39
- "category": "container",
40
- "specialCategory": null,
41
- "sort": 3
42
- },
43
- "resizeMode": {
44
- "label": "Resize Mode",
45
- "description": "How the image fits the frame.",
46
- "category": "other",
47
- "specialCategory": null,
48
- "sort": 4
49
39
  }
50
40
  }
51
41
  }
@@ -0,0 +1,39 @@
1
+ import React, { useEffect } from 'react';
2
+ import type { NavigationBarColorComponentProps } from './NavigationBarColorProps.generated';
3
+ import useNode from '../useNode';
4
+ import { useBuilderParams } from '../../components/BuilderProvider';
5
+ import { parseColor } from '../../utils/parseColor';
6
+ import { getStyleBag } from '../../utils/attributeStyle';
7
+ import { useLogRender } from '../../utils/useLogRender';
8
+ import { useRenderStore } from '../../store';
9
+
10
+ function NavigationBarColor({ node }: NavigationBarColorComponentProps) {
11
+ useLogRender('NavigationBarColor');
12
+ node = useNode(node);
13
+
14
+ const { appConfig, projectColors } = useBuilderParams();
15
+ const setNavBarOverrideColor = useRenderStore(
16
+ (s) => s.setNavBarOverrideColor,
17
+ );
18
+
19
+ const rawBg = getStyleBag(node.attributes)?.backgroundColor as
20
+ | string
21
+ | undefined;
22
+ const resolvedColor = parseColor(rawBg, {
23
+ projectColors,
24
+ theme: appConfig?.theme,
25
+ });
26
+
27
+ useEffect(() => {
28
+ if (resolvedColor) {
29
+ setNavBarOverrideColor(resolvedColor);
30
+ }
31
+ return () => {
32
+ setNavBarOverrideColor(null);
33
+ };
34
+ }, [resolvedColor, setNavBarOverrideColor]);
35
+
36
+ return null;
37
+ }
38
+
39
+ export default React.memo(NavigationBarColor);
@@ -0,0 +1,71 @@
1
+ /* AUTO-GENERATED FILE - DO NOT EDIT */
2
+
3
+ import type { NodeData } from '../../types/Node';
4
+
5
+ export type FlexDirectionOptionType = 'row' | 'column';
6
+ export type FlexWrapOptionType = 'nowrap' | 'wrap' | 'wrap-reverse';
7
+ export type AlignItemsOptionType =
8
+ | 'flex-start'
9
+ | 'center'
10
+ | 'flex-end'
11
+ | 'stretch'
12
+ | 'baseline';
13
+ export type JustifyContentOptionType =
14
+ | 'flex-start'
15
+ | 'center'
16
+ | 'flex-end'
17
+ | 'space-between'
18
+ | 'space-around'
19
+ | 'space-evenly';
20
+ export type PositionOptionType = 'relative' | 'absolute';
21
+
22
+ export interface NavigationBarColorStyleGenerated {
23
+ flexDirection?: FlexDirectionOptionType;
24
+ flexWrap?: FlexWrapOptionType;
25
+ alignItems?: AlignItemsOptionType;
26
+ justifyContent?: JustifyContentOptionType;
27
+ gap?: string;
28
+ padding?: string;
29
+ paddingHorizontal?: string;
30
+ paddingVertical?: string;
31
+ paddingTop?: string;
32
+ paddingBottom?: string;
33
+ paddingLeft?: string;
34
+ paddingRight?: string;
35
+ margin?: string;
36
+ marginHorizontal?: string;
37
+ marginVertical?: string;
38
+ marginTop?: string;
39
+ marginBottom?: string;
40
+ marginLeft?: string;
41
+ marginRight?: string;
42
+ backgroundColor?: string;
43
+ borderRadius?: string;
44
+ width?: string;
45
+ minWidth?: string;
46
+ maxWidth?: string;
47
+ height?: string;
48
+ minHeight?: string;
49
+ maxHeight?: string;
50
+ flex?: number;
51
+ position?: PositionOptionType;
52
+ top?: string;
53
+ bottom?: string;
54
+ left?: string;
55
+ right?: string;
56
+ zIndex?: number;
57
+ }
58
+
59
+ export interface NavigationBarColorPropsGenerated {
60
+ child: string;
61
+ attributes: {
62
+ style?: NavigationBarColorStyleGenerated;
63
+ scrollable?: boolean;
64
+ title?: string;
65
+ description?: string;
66
+ };
67
+ }
68
+
69
+ export interface NavigationBarColorComponentProps {
70
+ node: NodeData<NavigationBarColorPropsGenerated['attributes']>;
71
+ }
@@ -0,0 +1,34 @@
1
+ {
2
+ "schemaVersion": 2,
3
+ "pattern": {
4
+ "type": "NavigationBarColor",
5
+ "children": "never",
6
+ "extends": "View",
7
+ "attributes": {
8
+ "title": "title",
9
+ "description": "description",
10
+ "style": {
11
+ "backgroundColor": "color"
12
+ }
13
+ }
14
+ },
15
+ "meta": {
16
+ "desiredParent": ["all"],
17
+ "label": "Navigation Bar Color",
18
+ "description": "Sets the OS navigation bar background color.",
19
+ "styles": {
20
+ "backgroundColor": {
21
+ "label": "Background Color",
22
+ "description": "Navigation bar background color.",
23
+ "category": "style",
24
+ "specialCategory": null,
25
+ "sort": 1
26
+ }
27
+ }
28
+ },
29
+ "defaults": {
30
+ "style": {
31
+ "backgroundColor": "THEME_COLORS.BACKGROUND"
32
+ }
33
+ }
34
+ }
@@ -10,13 +10,16 @@ import { useLogRender } from '../../utils/useLogRender';
10
10
  import { useExtractViewStyle } from '../../attribute-analyser/style/web/useExtractViewStyle';
11
11
  import { useMockOSContext, useMockPermission } from '../../mockOS';
12
12
  import { useLocalize } from '../../hooks/useLocalize';
13
+ import { parseColor } from '../../utils/parseColor';
14
+ import { getStyleBag, toAttributeRecord } from '../../utils/attributeStyle';
13
15
 
14
16
  function OnboardButton({ node }: OnboardButtonComponentProps) {
15
17
  useLogRender('OnboardButton');
16
18
  node = useNode(node);
17
19
  const attributeName = node.sourceType ?? node.type ?? 'OnboardButton';
18
20
  const { emblaApi } = useContext(onboardContext) ?? {};
19
- const { appConfig } = useBuilderParams();
21
+ const { appConfig, projectColors } = useBuilderParams();
22
+ const theme = appConfig?.theme;
20
23
 
21
24
  const context = useMockOSContext();
22
25
  const mockPermissionManager = useMockPermission(context);
@@ -25,14 +28,25 @@ function OnboardButton({ node }: OnboardButtonComponentProps) {
25
28
  const attributeKey = node.key ?? generatedId;
26
29
 
27
30
  const attrs = node.attributes;
28
- const styleBag = attrs?.style;
31
+ const attrRecord = toAttributeRecord(attrs);
32
+ const styleBag = getStyleBag(attrs);
29
33
  const labelRaw = attrs?.labelKey ?? '';
30
34
  const localize = useLocalize({ appConfig });
31
35
  const label = localize(labelRaw);
32
36
 
33
- const flex = styleBag?.flex ?? 1;
34
- const textColor = attrs?.button_text_color ?? '#FFFFFF';
35
- const backgroundColor = attrs?.button_background_color ?? '#0066FF';
37
+ const flex = (attrRecord.flex ?? styleBag?.flex ?? 1) as number;
38
+
39
+ // The editor saves color attrs inside `styles` (meta category === 'style'),
40
+ // but legacy JSON may have them at the top level.
41
+ const rawTextColor = (attrRecord.button_text_color ??
42
+ styleBag?.button_text_color) as string | undefined;
43
+ const rawBgColor = (attrRecord.button_background_color ??
44
+ styleBag?.button_background_color) as string | undefined;
45
+
46
+ const textColor =
47
+ parseColor(rawTextColor, { projectColors, theme }) ?? '#FFFFFF';
48
+ const backgroundColor =
49
+ parseColor(rawBgColor, { projectColors, theme }) ?? '#0066FF';
36
50
  const viewStyle = useExtractViewStyle(node);
37
51
 
38
52
  const handleClick = () => {
@@ -1,5 +1,5 @@
1
1
  import React, { useContext, useEffect, useId, useMemo, useState } from 'react';
2
- import type { Node } from '../../types/Node';
2
+ import type { Node, NodeData } from '../../types/Node';
3
3
  import type { OnboardButtonsComponentProps } from './OnboardButtonsProps.generated';
4
4
  import { onboardContext } from '../OnboardProvider/OnboardProvider';
5
5
  import RenderNode from '../RenderNode.generated';
@@ -9,6 +9,7 @@ import { useLogRender } from '../../utils/useLogRender';
9
9
  import { useExtractViewStyle } from '../../attribute-analyser/style/web/useExtractViewStyle';
10
10
  import { isNodeSelected, SELECTED_OUTLINE_STYLE } from '../../utils/selection';
11
11
  import { useMergedStyle } from '../../utils/useMergedStyle';
12
+ import { getStyleBag, toAttributeRecord } from '../../utils/attributeStyle';
12
13
 
13
14
  function OnboardButtons({ node }: OnboardButtonsComponentProps) {
14
15
  useLogRender('OnboardButtons');
@@ -28,13 +29,10 @@ function OnboardButtons({ node }: OnboardButtonsComponentProps) {
28
29
  }
29
30
  }, [ctx.selectedIndex]);
30
31
 
31
- const attributes = node.attributes as any;
32
- const styleBag = attributes?.style as Record<string, unknown> | undefined as
33
- | Record<string, unknown>
34
- | undefined;
32
+ const attrRecord = toAttributeRecord(node.attributes);
33
+ const styleBag = getStyleBag(node.attributes);
35
34
  const direction =
36
- (attributes?.buttons_direction ?? (styleBag as any)?.buttons_direction) ===
37
- 'column'
35
+ (attrRecord.buttons_direction ?? styleBag?.buttons_direction) === 'column'
38
36
  ? 'column'
39
37
  : 'row';
40
38
 
@@ -62,9 +60,9 @@ function OnboardButtons({ node }: OnboardButtonsComponentProps) {
62
60
  const generatedId = useId();
63
61
 
64
62
  // New condition logic: hide when condition is carousel-index and does not match
65
- const condition = attributes?.condition ?? (styleBag as any)?.condition;
63
+ const condition = attrRecord.condition ?? styleBag?.condition;
66
64
  const conditionVariable =
67
- attributes?.conditionVariable ?? (styleBag as any)?.conditionVariable;
65
+ attrRecord.conditionVariable ?? styleBag?.conditionVariable;
68
66
  const shouldHide =
69
67
  condition === 'carousel-index' &&
70
68
  typeof conditionVariable === 'number' &&
@@ -81,7 +79,7 @@ function OnboardButtons({ node }: OnboardButtonsComponentProps) {
81
79
  style={{ ...viewStyle, boxSizing: 'border-box' }}
82
80
  >
83
81
  {children.map((child, idx) => (
84
- <RenderNode key={(child as any)?.key ?? idx} node={child} />
82
+ <RenderNode key={(child as NodeData)?.key ?? idx} node={child} />
85
83
  ))}
86
84
  </div>
87
85
  );
@@ -10,6 +10,7 @@ import { useMergedStyle } from '../../utils/useMergedStyle';
10
10
  import { parseColor } from '../../utils/parseColor';
11
11
  import { parseSize } from '../../size-matters';
12
12
  import { defaultAppConfig } from '../../types/PreviewConfig';
13
+ import { getStyleBag, toAttributeRecord } from '../../utils/attributeStyle';
13
14
 
14
15
  function OnboardDot({ node }: OnboardDotComponentProps) {
15
16
  useLogRender('OnboardDot');
@@ -20,12 +21,12 @@ function OnboardDot({ node }: OnboardDotComponentProps) {
20
21
  const attributeName = node.type ?? 'OnboardDot';
21
22
  const attributeKey = node.key ?? generatedId;
22
23
  const attrs = node.attributes;
23
- const stylesBag =
24
- ((attrs as any)?.styles as Record<string, unknown> | undefined) ??
25
- ((attrs as any)?.style as Record<string, unknown> | undefined) ??
26
- undefined;
24
+ const attrRecord = toAttributeRecord(attrs);
25
+ const stylesBag = getStyleBag(attrs);
27
26
  const dotType =
28
- (stylesBag?.dotType as any) ?? (attrs as any)?.dotType ?? 'normal_dot';
27
+ (stylesBag?.dotType as string | undefined) ??
28
+ (attrRecord.dotType as string | undefined) ??
29
+ 'normal_dot';
29
30
  const GHOST_DOT_DARK_COLOR = '#E4E5E7';
30
31
  const GHOST_DOT_LIGHT_COLOR = '#F7F7F9';
31
32
  const {
@@ -43,14 +44,14 @@ function OnboardDot({ node }: OnboardDotComponentProps) {
43
44
  : GHOST_DOT_LIGHT_COLOR;
44
45
  const inactiveDotOpacity =
45
46
  (stylesBag?.inactive_dot_opacity as number | undefined) ??
46
- (attrs as any)?.inactive_dot_opacity ??
47
+ (attrRecord.inactive_dot_opacity as number | undefined) ??
47
48
  0.3;
48
49
  const inactiveDotColorOverride =
49
50
  (stylesBag?.inactive_dot_color as string | undefined) ??
50
- (attrs as any)?.inactive_dot_color;
51
+ (attrRecord.inactive_dot_color as string | undefined);
51
52
  const activeDotColor =
52
53
  (stylesBag?.active_dot_color as string | undefined) ??
53
- (attrs as any)?.active_dot_color;
54
+ (attrRecord.active_dot_color as string | undefined);
54
55
  const resolvedActiveDotColor = useMemo(
55
56
  () => parseColor(activeDotColor, { theme: appConfig.theme, projectColors }),
56
57
  [activeDotColor, appConfig.theme, projectColors],
@@ -83,7 +84,8 @@ function OnboardDot({ node }: OnboardDotComponentProps) {
83
84
  isSelected ? SELECTED_OUTLINE_STYLE : undefined,
84
85
  );
85
86
  const dotThicknessRaw =
86
- (stylesBag?.dot_thickness as any) ?? (attrs as any)?.dot_thickness;
87
+ (stylesBag?.dot_thickness as string | number | undefined) ??
88
+ (attrRecord.dot_thickness as string | number | undefined);
87
89
  const dotSizeCss = useMemo((): string => {
88
90
  const parsed = parseSize(dotThicknessRaw);
89
91
  if (parsed === undefined) return '10px';
@@ -100,7 +102,7 @@ function OnboardDot({ node }: OnboardDotComponentProps) {
100
102
  const n = Number.isFinite(px) ? px : 10;
101
103
  return `${Math.max(0, n / 3)}px`;
102
104
  }, [dotSizeCss]);
103
- const gapValue = (style as any)?.gap ?? dotGapCss;
105
+ const gapValue = style.gap ?? dotGapCss;
104
106
  const containerStyle = useMergedStyle(style, {
105
107
  display: 'flex',
106
108
  flexWrap: 'wrap',
@@ -9,6 +9,7 @@ import { isNodeSelected, SELECTED_OUTLINE_STYLE } from '../../utils/selection';
9
9
  import { useMergedStyle } from '../../utils/useMergedStyle';
10
10
  import { defaultAppConfig } from '../../types/PreviewConfig';
11
11
  import { parseColor } from '../../utils/parseColor';
12
+ import { getStyleBag, toAttributeRecord } from '../../utils/attributeStyle';
12
13
 
13
14
  type Segment =
14
15
  | { type: 'text'; value: string }
@@ -107,26 +108,36 @@ function OnboardFooter({ node }: OnboardFooterComponentProps) {
107
108
  key ? (localication?.[defaultLanguage ?? 'en']?.[key] ?? key) : '';
108
109
 
109
110
  const attrs = node?.attributes;
111
+ const attrRecord = toAttributeRecord(attrs);
112
+ const styleBag = getStyleBag(attrs);
110
113
  const text = t(attrs?.textLocalizationKey);
111
114
  const textStyle = useExtractTextStyle(node, true);
112
115
  const viewStyle = useExtractViewStyle(node);
113
116
 
117
+ // Read linked-word colors from both top-level attributes and the styles bag.
118
+ // The editor saves them inside `styles` (meta category === 'style'),
119
+ // but legacy JSON may have them at the top level.
120
+ const rawFirstColor = (attrRecord.linkedWordFirstColor ??
121
+ styleBag?.linkedWordFirstColor) as string | undefined;
122
+ const rawSecondColor = (attrRecord.linkedWordSecondColor ??
123
+ styleBag?.linkedWordSecondColor) as string | undefined;
124
+
114
125
  // Parse colors for linked words
115
126
  const parsedFirstColor = useMemo(
116
127
  () =>
117
- parseColor(attrs?.linkedWordFirstColor, {
128
+ parseColor(rawFirstColor, {
118
129
  projectColors,
119
130
  theme,
120
131
  }),
121
- [attrs?.linkedWordFirstColor, projectColors, theme],
132
+ [rawFirstColor, projectColors, theme],
122
133
  );
123
134
  const parsedSecondColor = useMemo(
124
135
  () =>
125
- parseColor(attrs?.linkedWordSecondColor, {
136
+ parseColor(rawSecondColor, {
126
137
  projectColors,
127
138
  theme,
128
139
  }),
129
- [attrs?.linkedWordSecondColor, projectColors, theme],
140
+ [rawSecondColor, projectColors, theme],
130
141
  );
131
142
 
132
143
  const mergedTextStyle = useMergedStyle(textStyle, {
@@ -70,7 +70,7 @@ function OnboardImage({ node }: OnboardImageComponentProps) {
70
70
  ...node,
71
71
  type: 'image',
72
72
  sourceType: attributeName,
73
- } as unknown as ImageComponentProps['node']
73
+ } as ImageComponentProps['node']
74
74
  }
75
75
  />
76
76
  );
@@ -21,6 +21,7 @@ export type JustifyContentOptionType =
21
21
  export type PositionOptionType = 'relative' | 'absolute';
22
22
 
23
23
  export interface OnboardImageStyleGenerated {
24
+ resizeMode?: ResizeModeOptionType;
24
25
  flexDirection?: FlexDirectionOptionType;
25
26
  flexWrap?: FlexWrapOptionType;
26
27
  alignItems?: AlignItemsOptionType;
@@ -64,9 +65,6 @@ export interface OnboardImagePropsGenerated {
64
65
  title?: string;
65
66
  description?: string;
66
67
  src?: string;
67
- width?: string;
68
- height?: string;
69
- resizeMode?: ResizeModeOptionType;
70
68
  scrollable?: boolean;
71
69
  video_url?: string;
72
70
  lottie?: string;
@@ -20,7 +20,9 @@ function OnboardProvider({ node }: OnboardProviderComponentProps) {
20
20
  node = useNode(node);
21
21
  const generatedId = useId();
22
22
  const attributeName = node.sourceType ?? node.type ?? 'OnboardProvider';
23
- const [emblaRef, emblaApi] = useEmblaCarousel(node.attributes as any);
23
+ const [emblaRef, emblaApi] = useEmblaCarousel(
24
+ node.attributes as Parameters<typeof useEmblaCarousel>[0],
25
+ );
24
26
  const [selectedIndex, setSelectedIndex] = useState(0);
25
27
  const attributeKey = node.key ?? generatedId;
26
28
 
@@ -25,6 +25,7 @@ import type { CountDownComponentProps } from './CountDown/CountDownProps.generat
25
25
  import type { CounterComponentProps } from './Counter/CounterProps.generated';
26
26
  import type { ImageComponentProps } from './Image/ImageProps.generated';
27
27
  import type { MainComponentProps } from './Main/MainProps.generated';
28
+ import type { NavigationBarColorComponentProps } from './NavigationBarColor/NavigationBarColorProps.generated';
28
29
  import type { OnboardComponentProps } from './Onboard/OnboardProps.generated';
29
30
  import type { OnboardButtonComponentProps } from './OnboardButton/OnboardButtonProps.generated';
30
31
  import type { OnboardButtonsComponentProps } from './OnboardButtons/OnboardButtonsProps.generated';
@@ -42,6 +43,8 @@ import type { PaywallOptionsComponentProps } from './PaywallOptions/PaywallOptio
42
43
  import type { PaywallProviderComponentProps } from './PaywallProvider/PaywallProviderProps.generated';
43
44
  import type { PaywallSubscribeButtonComponentProps } from './PaywallSubscribeButton/PaywallSubscribeButtonProps.generated';
44
45
  import type { RadioButtonComponentProps } from './RadioButton/RadioButtonProps.generated';
46
+ import type { SeparatorComponentProps } from './Separator/SeparatorProps.generated';
47
+ import type { StatusBarColorComponentProps } from './StatusBarColor/StatusBarColorProps.generated';
45
48
  import type { TextComponentProps } from './Text/TextProps.generated';
46
49
  import type { ViewComponentProps } from './View/ViewProps.generated';
47
50
  import BIcon from './BIcon/BIcon';
@@ -56,6 +59,7 @@ import { CountDown } from './CountDown/CountDown';
56
59
  import { Counter } from './Counter/Counter';
57
60
  import Image from './Image/Image';
58
61
  import Main from './Main/Main';
62
+ import NavigationBarColor from './NavigationBarColor/NavigationBarColor';
59
63
  import Onboard from './Onboard/Onboard';
60
64
  import OnboardButton from './OnboardButton/OnboardButton';
61
65
  import OnboardButtons from './OnboardButtons/OnboardButtons';
@@ -73,6 +77,8 @@ import PaywallOptions from './PaywallOptions/PaywallOptions';
73
77
  import PaywallProvider from './PaywallProvider/PaywallProvider';
74
78
  import PaywallSubscribeButton from './PaywallSubscribeButton/PaywallSubscribeButton';
75
79
  import RadioButton from './RadioButton/RadioButton';
80
+ import Separator from './Separator/Separator';
81
+ import StatusBarColor from './StatusBarColor/StatusBarColor';
76
82
  import Text from './Text/Text';
77
83
  import View from './View/View';
78
84
 
@@ -89,6 +95,7 @@ type BuilderNode =
89
95
  | (CounterComponentProps['node'] & { type: 'Counter' })
90
96
  | (ImageComponentProps['node'] & { type: 'Image' })
91
97
  | (MainComponentProps['node'] & { type: 'Main' })
98
+ | (NavigationBarColorComponentProps['node'] & { type: 'NavigationBarColor' })
92
99
  | (OnboardComponentProps['node'] & { type: 'Onboard' })
93
100
  | (OnboardButtonComponentProps['node'] & { type: 'OnboardButton' })
94
101
  | (OnboardButtonsComponentProps['node'] & { type: 'OnboardButtons' })
@@ -108,6 +115,8 @@ type BuilderNode =
108
115
  type: 'PaywallSubscribeButton';
109
116
  })
110
117
  | (RadioButtonComponentProps['node'] & { type: 'RadioButton' })
118
+ | (SeparatorComponentProps['node'] & { type: 'Separator' })
119
+ | (StatusBarColorComponentProps['node'] & { type: 'StatusBarColor' })
111
120
  | (TextComponentProps['node'] & { type: 'Text' })
112
121
  | (ViewComponentProps['node'] & { type: 'View' });
113
122
 
@@ -165,6 +174,8 @@ function RenderNode({ node }: { node: Node }) {
165
174
  return <Image node={normalizedNode} />;
166
175
  case 'Main':
167
176
  return <Main node={normalizedNode} />;
177
+ case 'NavigationBarColor':
178
+ return <NavigationBarColor node={normalizedNode} />;
168
179
  case 'Onboard':
169
180
  return <Onboard node={normalizedNode} />;
170
181
  case 'OnboardButton':
@@ -199,6 +210,10 @@ function RenderNode({ node }: { node: Node }) {
199
210
  return <PaywallSubscribeButton node={normalizedNode} />;
200
211
  case 'RadioButton':
201
212
  return <RadioButton node={normalizedNode} />;
213
+ case 'Separator':
214
+ return <Separator node={normalizedNode} />;
215
+ case 'StatusBarColor':
216
+ return <StatusBarColor node={normalizedNode} />;
202
217
  case 'Text':
203
218
  return <Text node={normalizedNode} />;
204
219
  case 'View':
@@ -0,0 +1,41 @@
1
+ import React, { useId } from 'react';
2
+ import type { SeparatorComponentProps } from './SeparatorProps.generated';
3
+ import useNode from '../useNode';
4
+ import { useBuilderParams } from '../../components/BuilderProvider';
5
+ import { useExtractViewStyle } from '../../attribute-analyser/style/web/useExtractViewStyle';
6
+ import { useLogRender } from '../../utils/useLogRender';
7
+ import { isNodeSelected, SELECTED_OUTLINE_STYLE } from '../../utils/selection';
8
+ import { useMergedStyle } from '../../utils/useMergedStyle';
9
+
10
+ function Separator({ node }: SeparatorComponentProps) {
11
+ useLogRender('Separator');
12
+ node = useNode(node);
13
+
14
+ const generatedId = useId();
15
+ const attributeName = node.sourceType ?? node.type ?? 'Separator';
16
+ const attributeKey = node.key ?? generatedId;
17
+
18
+ const { previewMode, selectedKey } = useBuilderParams();
19
+
20
+ const baseStyle = useExtractViewStyle(node);
21
+
22
+ const isSelected = isNodeSelected({
23
+ previewMode: !!previewMode,
24
+ current: selectedKey ? { key: selectedKey } : undefined,
25
+ node,
26
+ });
27
+ const style = useMergedStyle(
28
+ baseStyle,
29
+ isSelected ? SELECTED_OUTLINE_STYLE : undefined,
30
+ );
31
+
32
+ return (
33
+ <div
34
+ attribute-name={attributeName}
35
+ attribute-key={attributeKey}
36
+ style={style}
37
+ />
38
+ );
39
+ }
40
+
41
+ export default React.memo(Separator);
@@ -0,0 +1,26 @@
1
+ /* AUTO-GENERATED FILE - DO NOT EDIT */
2
+
3
+ import type { NodeData } from '../../types/Node';
4
+
5
+ export interface SeparatorStyleGenerated {
6
+ backgroundColor?: string;
7
+ width?: string;
8
+ height?: string;
9
+ marginHorizontal?: string;
10
+ marginVertical?: string;
11
+ marginTop?: string;
12
+ marginBottom?: string;
13
+ }
14
+
15
+ export interface SeparatorPropsGenerated {
16
+ child: string;
17
+ attributes: {
18
+ style?: SeparatorStyleGenerated;
19
+ title?: string;
20
+ description?: string;
21
+ };
22
+ }
23
+
24
+ export interface SeparatorComponentProps {
25
+ node: NodeData<SeparatorPropsGenerated['attributes']>;
26
+ }