@developer_tribe/react-builder 0.1.25 → 0.1.27

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 (97) hide show
  1. package/dist/DeviceMockFrame.d.ts +22 -0
  2. package/dist/RenderPage.d.ts +6 -3
  3. package/dist/build-components/OnboardProvider/OnboardProviderProps.generated.d.ts +4 -1
  4. package/dist/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.d.ts +1 -0
  5. package/dist/build-components/index.d.ts +0 -1
  6. package/dist/build-components/useNode.d.ts +2 -0
  7. package/dist/index.cjs.js +1 -1
  8. package/dist/index.d.ts +2 -0
  9. package/dist/index.esm.js +1 -1
  10. package/dist/types/Device.d.ts +16 -0
  11. package/dist/types/Node.d.ts +2 -1
  12. package/dist/types/Project.d.ts +1 -1
  13. package/dist/utils/patterns.d.ts +4 -0
  14. package/package.json +1 -1
  15. package/scripts/prebuild/build-components.js +4 -4
  16. package/scripts/prebuild/utils/createBuildComponentsIndex.js +1 -4
  17. package/scripts/prebuild/utils/createBuildComponentsRootGetDefaults.js +2 -55
  18. package/scripts/prebuild/utils/createGetDefaultsPerComponent.js +2 -19
  19. package/scripts/prebuild/utils/createRootGetDefaults.js +2 -44
  20. package/scripts/prebuild/utils/validateAllComponentsOrThrow.js +3 -1
  21. package/src/AttributesEditor.tsx +19 -15
  22. package/src/DeviceMockFrame.tsx +119 -0
  23. package/src/RenderPage.tsx +19 -27
  24. package/src/assets/devices.json +364 -91
  25. package/src/assets/samples/getSamples.ts +5 -1
  26. package/src/build-components/Button/Button.tsx +2 -0
  27. package/src/build-components/Carousel/Carousel.tsx +2 -0
  28. package/src/build-components/CarouselButtons/CarouselButtons.tsx +2 -0
  29. package/src/build-components/CarouselDots/CarouselDots.tsx +2 -0
  30. package/src/build-components/CarouselItem/CarouselItem.tsx +2 -0
  31. package/src/build-components/CarouselProvider/CarouselProvider.tsx +2 -0
  32. package/src/build-components/Image/Image.tsx +2 -0
  33. package/src/build-components/Onboard/Onboard.tsx +2 -0
  34. package/src/build-components/OnboardButton/OnboardButton.tsx +2 -0
  35. package/src/build-components/OnboardButtons/OnboardButtons.tsx +2 -0
  36. package/src/build-components/OnboardDot/OnboardDot.tsx +2 -0
  37. package/src/build-components/OnboardFooter/OnboardFooter.tsx +2 -0
  38. package/src/build-components/OnboardImage/OnboardImage.tsx +2 -0
  39. package/src/build-components/OnboardItem/OnboardItem.tsx +2 -0
  40. package/src/build-components/OnboardProvider/OnboardProvider.tsx +13 -1
  41. package/src/build-components/OnboardProvider/OnboardProviderProps.generated.ts +4 -1
  42. package/src/build-components/OnboardProvider/pattern.json +4 -1
  43. package/src/build-components/OnboardSubtitle/OnboardSubtitle.tsx +2 -0
  44. package/src/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.ts +1 -0
  45. package/src/build-components/OnboardSubtitle/pattern.json +2 -1
  46. package/src/build-components/OnboardTitle/OnboardTitle.tsx +2 -0
  47. package/src/build-components/OnboardTitle/pattern.json +1 -1
  48. package/src/build-components/Text/Text.tsx +2 -0
  49. package/src/build-components/View/View.tsx +2 -0
  50. package/src/build-components/index.ts +0 -2
  51. package/src/build-components/useNode.ts +15 -0
  52. package/src/index.ts +2 -0
  53. package/src/types/Device.ts +21 -0
  54. package/src/types/Node.ts +2 -11
  55. package/src/types/Project.ts +1 -1
  56. package/src/utils/novaToJson.ts +166 -3
  57. package/src/utils/patterns.ts +11 -0
  58. package/dist/build-components/Button/getDefaults.d.ts +0 -3
  59. package/dist/build-components/Carousel/getDefaults.d.ts +0 -3
  60. package/dist/build-components/CarouselButtons/getDefaults.d.ts +0 -3
  61. package/dist/build-components/CarouselDots/getDefaults.d.ts +0 -3
  62. package/dist/build-components/CarouselItem/getDefaults.d.ts +0 -3
  63. package/dist/build-components/CarouselProvider/getDefaults.d.ts +0 -3
  64. package/dist/build-components/Image/getDefaults.d.ts +0 -3
  65. package/dist/build-components/Onboard/getDefaults.d.ts +0 -3
  66. package/dist/build-components/OnboardButton/getDefaults.d.ts +0 -3
  67. package/dist/build-components/OnboardButtons/getDefaults.d.ts +0 -3
  68. package/dist/build-components/OnboardDot/getDefaults.d.ts +0 -3
  69. package/dist/build-components/OnboardFooter/getDefaults.d.ts +0 -3
  70. package/dist/build-components/OnboardImage/getDefaults.d.ts +0 -3
  71. package/dist/build-components/OnboardItem/getDefaults.d.ts +0 -3
  72. package/dist/build-components/OnboardProvider/getDefaults.d.ts +0 -3
  73. package/dist/build-components/OnboardSubtitle/getDefaults.d.ts +0 -3
  74. package/dist/build-components/OnboardTitle/getDefaults.d.ts +0 -3
  75. package/dist/build-components/Text/getDefaults.d.ts +0 -3
  76. package/dist/build-components/View/getDefaults.d.ts +0 -3
  77. package/dist/build-components/getDefaults.d.ts +0 -25
  78. package/src/build-components/Button/getDefaults.ts +0 -11
  79. package/src/build-components/Carousel/getDefaults.ts +0 -11
  80. package/src/build-components/CarouselButtons/getDefaults.ts +0 -13
  81. package/src/build-components/CarouselDots/getDefaults.ts +0 -13
  82. package/src/build-components/CarouselItem/getDefaults.ts +0 -13
  83. package/src/build-components/CarouselProvider/getDefaults.ts +0 -13
  84. package/src/build-components/Image/getDefaults.ts +0 -11
  85. package/src/build-components/Onboard/getDefaults.ts +0 -11
  86. package/src/build-components/OnboardButton/getDefaults.ts +0 -13
  87. package/src/build-components/OnboardButtons/getDefaults.ts +0 -13
  88. package/src/build-components/OnboardDot/getDefaults.ts +0 -13
  89. package/src/build-components/OnboardFooter/getDefaults.ts +0 -13
  90. package/src/build-components/OnboardImage/getDefaults.ts +0 -13
  91. package/src/build-components/OnboardItem/getDefaults.ts +0 -13
  92. package/src/build-components/OnboardProvider/getDefaults.ts +0 -13
  93. package/src/build-components/OnboardSubtitle/getDefaults.ts +0 -13
  94. package/src/build-components/OnboardTitle/getDefaults.ts +0 -13
  95. package/src/build-components/Text/getDefaults.ts +0 -11
  96. package/src/build-components/View/getDefaults.ts +0 -11
  97. package/src/build-components/getDefaults.ts +0 -149
@@ -32,10 +32,14 @@ export function getBasicSamples(): Project[] {
32
32
  }
33
33
 
34
34
  export function getOnboardSamples(): Project[] {
35
- return [
35
+ const legacySamples = [
36
36
  vpnOnboard1 as Project,
37
37
  vpnOnboard2 as Project,
38
38
  vpnOnboard3 as Project,
39
39
  vpnOnboard4 as Project,
40
40
  ];
41
+ legacySamples.forEach((sample) => {
42
+ sample.data = novaToJson(sample);
43
+ });
44
+ return legacySamples;
41
45
  }
@@ -1,7 +1,9 @@
1
1
  import React from 'react';
2
2
  import type { ButtonComponentProps } from './ButtonProps.generated';
3
+ import useDefaults from '../useNode';
3
4
 
4
5
  function Button({ node }: ButtonComponentProps) {
6
+ node = useDefaults(node);
5
7
  return String(node?.type ?? 'button');
6
8
  }
7
9
 
@@ -2,8 +2,10 @@ import React from 'react';
2
2
  import type { CarouselComponentProps } from './CarouselProps.generated';
3
3
  import RenderNode from '../RenderNode.generated';
4
4
  import { isCarouselItem } from '../../utils/isCarousel';
5
+ import useDefaults from '../useNode';
5
6
 
6
7
  function Carousel({ node }: CarouselComponentProps) {
8
+ node = useDefaults(node);
7
9
  // Ensure children are carouselItems
8
10
  const renderChildren = () => {
9
11
  if (Array.isArray(node.children)) {
@@ -1,8 +1,10 @@
1
1
  import React, { useContext } from 'react';
2
2
  import type { CarouselButtonsComponentProps } from './CarouselButtonsProps.generated';
3
3
  import { carouselContext } from '../CarouselProvider/CarouselProvider';
4
+ import useDefaults from '../useNode';
4
5
 
5
6
  function CarouselButtons({ node }: CarouselButtonsComponentProps) {
7
+ node = useDefaults(node);
6
8
  const emblaApi = useContext(carouselContext);
7
9
  const buttonTypes = node.attributes?.buttonType || [
8
10
  'previous_button',
@@ -1,8 +1,10 @@
1
1
  import React, { useContext, useEffect, useState } from 'react';
2
2
  import type { CarouselDotsComponentProps } from './CarouselDotsProps.generated';
3
3
  import { carouselContext } from '../CarouselProvider/CarouselProvider';
4
+ import useDefaults from '../useNode';
4
5
 
5
6
  function CarouselDots({ node }: CarouselDotsComponentProps) {
7
+ node = useDefaults(node);
6
8
  const dotType = node.attributes?.dotType || 'normal_dot';
7
9
  const emblaApi = useContext(carouselContext);
8
10
  const [selectedIndex, setSelectedIndex] = useState(0);
@@ -1,8 +1,10 @@
1
1
  import React from 'react';
2
2
  import type { CarouselItemComponentProps } from './CarouselItemProps.generated';
3
3
  import { RenderNode } from '../..';
4
+ import useDefaults from '../useNode';
4
5
 
5
6
  export function CarouselItem({ node }: CarouselItemComponentProps) {
7
+ node = useDefaults(node);
6
8
  return (
7
9
  <div className="embla__slide" {...node.attributes}>
8
10
  {node.children && <RenderNode node={node.children} />}
@@ -2,9 +2,11 @@ import React, { createContext } from 'react';
2
2
  import type { CarouselProviderComponentProps } from './CarouselProviderProps.generated';
3
3
  import { RenderNode } from '../..';
4
4
  import useEmblaCarousel from 'embla-carousel-react';
5
+ import useDefaults from '../useNode';
5
6
 
6
7
  export const carouselContext = createContext<any>(undefined);
7
8
  function CarouselProvider({ node }: CarouselProviderComponentProps) {
9
+ node = useDefaults(node);
8
10
  const [emblaRef, emblaApi] = useEmblaCarousel(node.attributes as any);
9
11
  return (
10
12
  <carouselContext.Provider value={emblaApi}>
@@ -1,7 +1,9 @@
1
1
  import React from 'react';
2
2
  import type { ImageComponentProps } from './ImageProps.generated';
3
+ import useDefaults from '../useNode';
3
4
 
4
5
  function Image({ node }: ImageComponentProps) {
6
+ node = useDefaults(node);
5
7
  return (
6
8
  <img
7
9
  key={node.key}
@@ -1,8 +1,10 @@
1
1
  import React from 'react';
2
2
  import type { OnboardComponentProps } from './OnboardProps.generated';
3
3
  import Carousel from '../Carousel/Carousel';
4
+ import useDefaults from '../useNode';
4
5
 
5
6
  function Onboard({ node }: OnboardComponentProps) {
7
+ node = useDefaults(node);
6
8
  return <Carousel node={{ ...node, type: 'carousel' } as any} />;
7
9
  }
8
10
 
@@ -2,8 +2,10 @@ import React, { useContext } from 'react';
2
2
  import type { OnboardButtonComponentProps } from './OnboardButtonProps.generated';
3
3
  import { onboardContext } from '../OnboardProvider/OnboardProvider';
4
4
  import { mainNodeContext } from '../../RenderMainNode';
5
+ import useDefaults from '../useNode';
5
6
 
6
7
  function OnboardButton({ node }: OnboardButtonComponentProps) {
8
+ node = useDefaults(node);
7
9
  const { emblaApi } = useContext(onboardContext) ?? {};
8
10
  const { localication, defaultLanguage } = useContext(mainNodeContext) ?? {};
9
11
 
@@ -3,12 +3,14 @@ import type { Node } 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';
6
+ import useDefaults from '../useNode';
6
7
 
7
8
  const THEME_COLORS = {
8
9
  ONBOARD_SEPARATOR_COLOR: '#E5E7EB',
9
10
  };
10
11
 
11
12
  function OnboardButtons({ node }: OnboardButtonsComponentProps) {
13
+ node = useDefaults(node);
12
14
  const ctx = useContext(onboardContext) ?? {};
13
15
  const emblaApi = ctx.emblaApi;
14
16
  const [selectedIndex, setSelectedIndex] = useState(ctx.selectedIndex ?? 0);
@@ -1,8 +1,10 @@
1
1
  import React from 'react';
2
2
  import type { OnboardDotComponentProps } from './OnboardExpandingDotProps.generated';
3
3
  import CarouselDots from '../CarouselDots/CarouselDots';
4
+ import useDefaults from '../useNode';
4
5
 
5
6
  function OnboardDot({ node }: OnboardDotComponentProps) {
7
+ node = useDefaults(node);
6
8
  return <CarouselDots node={{ ...node, type: 'carouselDots' } as any} />;
7
9
  }
8
10
 
@@ -1,6 +1,7 @@
1
1
  import React, { useContext } from 'react';
2
2
  import type { OnboardFooterComponentProps } from './OnboardFooterProps.generated';
3
3
  import { mainNodeContext } from '../../RenderMainNode';
4
+ import useDefaults from '../useNode';
4
5
 
5
6
  type Segment =
6
7
  | { type: 'text'; value: string }
@@ -83,6 +84,7 @@ function buildSegments(
83
84
  }
84
85
 
85
86
  function OnboardFooter({ node }: OnboardFooterComponentProps) {
87
+ node = useDefaults(node);
86
88
  const { localication, defaultLanguage, setWarning } =
87
89
  useContext(mainNodeContext) ?? {};
88
90
  const t = (key?: string) =>
@@ -1,8 +1,10 @@
1
1
  import React from 'react';
2
2
  import type { OnboardImageComponentProps } from './OnboardImageProps.generated';
3
3
  import Image from '../Image/Image';
4
+ import useDefaults from '../useNode';
4
5
 
5
6
  function OnboardImage({ node }: OnboardImageComponentProps) {
7
+ node = useDefaults(node);
6
8
  return <Image node={{ ...node, type: 'image' } as any} />;
7
9
  }
8
10
 
@@ -3,8 +3,10 @@ import type { NodeData } from '../../types/Node';
3
3
  import type { OnboardItemComponentProps } from './OnboardItemProps.generated';
4
4
  import CarouselItem from '../CarouselItem/CarouselItem';
5
5
  import { isNodeArray } from '../../utils/analyseNode';
6
+ import useDefaults from '../useNode';
6
7
 
7
8
  function OnboardItem({ node }: OnboardItemComponentProps) {
9
+ node = useDefaults(node);
8
10
  const children = useMemo(() => {
9
11
  return (
10
12
  (
@@ -10,9 +10,13 @@ import type { OnboardProviderComponentProps } from './OnboardProviderProps.gener
10
10
  import { mainNodeContext } from '../../RenderMainNode';
11
11
  import useEmblaCarousel from 'embla-carousel-react';
12
12
  import RenderNode from '../RenderNode.generated';
13
+ import { renderPageContext } from '../../RenderPage';
14
+ import useDefaults from '../useNode';
13
15
 
14
16
  export const onboardContext = createContext<any>(undefined);
15
17
  function OnboardProvider({ node }: OnboardProviderComponentProps) {
18
+ node = useDefaults(node);
19
+ const { device } = useContext(renderPageContext) ?? {};
16
20
  const [emblaRef, emblaApi] = useEmblaCarousel(node.attributes as any);
17
21
  const { theme, setWarning } = useContext(mainNodeContext) ?? {};
18
22
  const [selectedIndex, setSelectedIndex] = useState(0);
@@ -49,7 +53,15 @@ function OnboardProvider({ node }: OnboardProviderComponentProps) {
49
53
 
50
54
  return (
51
55
  <onboardContext.Provider value={{ emblaApi, selectedIndex }}>
52
- <div className="carousel-provider">
56
+ <div
57
+ className="carousel-provider"
58
+ style={{
59
+ paddingTop: device?.insets?.[0],
60
+ paddingRight: device?.insets?.[1],
61
+ paddingBottom: device?.insets?.[2],
62
+ paddingLeft: device?.insets?.[3],
63
+ }}
64
+ >
53
65
  <div className="embla">
54
66
  <div className="embla__viewport" ref={emblaRef}>
55
67
  {node.children && <RenderNode node={children} />}
@@ -4,7 +4,10 @@ import type { NodeData } from '../../types/Node';
4
4
 
5
5
  export interface OnboardProviderPropsGenerated {
6
6
  child: string;
7
- attributes: {};
7
+ attributes: {
8
+ theme?: string;
9
+ use_safe_area_inset?: boolean;
10
+ };
8
11
  }
9
12
 
10
13
  export interface OnboardProviderComponentProps {
@@ -4,6 +4,9 @@
4
4
  "pattern": {
5
5
  "type": "OnboardProvider",
6
6
  "children": "node",
7
- "attributes": {}
7
+ "attributes": {
8
+ "theme": "string",
9
+ "use_safe_area_inset": "boolean"
10
+ }
8
11
  }
9
12
  }
@@ -1,8 +1,10 @@
1
1
  import React from 'react';
2
2
  import type { OnboardSubtitleComponentProps } from './OnboardSubtitleProps.generated';
3
3
  import Text from '../Text/Text';
4
+ import useDefaults from '../useNode';
4
5
 
5
6
  function OnboardSubtitle({ node }: OnboardSubtitleComponentProps) {
7
+ node = useDefaults(node);
6
8
  return (
7
9
  <Text
8
10
  node={
@@ -7,6 +7,7 @@ export interface OnboardSubtitlePropsGenerated {
7
7
  attributes: {
8
8
  color?: string;
9
9
  fontSize?: number;
10
+ textAlign?: 'left' | 'center' | 'right' | 'justify';
10
11
  fontWeight?:
11
12
  | 'normal'
12
13
  | 'bold'
@@ -7,6 +7,7 @@
7
7
  "attributes": {
8
8
  "color": "string",
9
9
  "fontSize": "number",
10
+ "textAlign": ["left", "center", "right", "justify"],
10
11
  "fontWeight": [
11
12
  "normal",
12
13
  "bold",
@@ -22,7 +23,7 @@
22
23
  ]
23
24
  }
24
25
  },
25
- "default": {
26
+ "defaults": {
26
27
  "fontSize": 14,
27
28
  "fontWeight": "600"
28
29
  }
@@ -1,8 +1,10 @@
1
1
  import React from 'react';
2
2
  import type { OnboardTitleComponentProps } from './OnboardTitleProps.generated';
3
3
  import Text from '../Text/Text';
4
+ import useDefaults from '../useNode';
4
5
 
5
6
  function OnboardTitle({ node }: OnboardTitleComponentProps) {
7
+ node = useDefaults(node);
6
8
  return (
7
9
  <Text
8
10
  node={
@@ -22,7 +22,7 @@
22
22
  ]
23
23
  }
24
24
  },
25
- "default": {
25
+ "defaults": {
26
26
  "fontSize": 24,
27
27
  "fontWeight": "700"
28
28
  }
@@ -1,8 +1,10 @@
1
1
  import React, { useContext } from 'react';
2
2
  import type { TextComponentProps } from './TextProps.generated';
3
3
  import { mainNodeContext } from '../../RenderMainNode';
4
+ import useDefaults from '../useNode';
4
5
 
5
6
  function Text({ node }: TextComponentProps) {
7
+ node = useDefaults(node);
6
8
  const { localication, defaultLanguage } = useContext(mainNodeContext) ?? {};
7
9
  const keyOrText: string = node.children as string;
8
10
  return (
@@ -5,6 +5,7 @@ import type {
5
5
  } from './ViewProps.generated';
6
6
  import RenderNode from '../RenderNode.generated';
7
7
  import { Node } from '../../types/Node';
8
+ import useDefaults from '../useNode';
8
9
 
9
10
  function mapAttributesToStyle(
10
11
  attributes: ViewPropsGenerated['attributes'],
@@ -48,6 +49,7 @@ function mapAttributesToStyle(
48
49
  }
49
50
 
50
51
  export function View({ node }: ViewComponentProps) {
52
+ node = useDefaults(node);
51
53
  return (
52
54
  <div
53
55
  style={mapAttributesToStyle(node.attributes ?? {})}
@@ -2,8 +2,6 @@
2
2
 
3
3
  export { default as RenderNode } from './RenderNode.generated';
4
4
 
5
- export { getDefaults } from './getDefaults';
6
-
7
5
  export type {
8
6
  ButtonPropsGenerated,
9
7
  ButtonComponentProps,
@@ -0,0 +1,15 @@
1
+ import { NodeData, NodeDefaultAttribute } from '../types/Node';
2
+ import { getDefaultsForType } from '../utils/patterns';
3
+
4
+ export default function useNode<
5
+ T extends NodeDefaultAttribute = NodeDefaultAttribute,
6
+ >(node: NodeData<T>): NodeData<T> {
7
+ const type = node?.type;
8
+ const defaults = getDefaultsForType(type) as Partial<T> | undefined;
9
+ if (!defaults) return node;
10
+ const mergedAttributes: T = {
11
+ ...(defaults as T),
12
+ ...((node.attributes as T) ?? ({} as T)),
13
+ };
14
+ return { ...node, attributes: mergedAttributes };
15
+ }
package/src/index.ts CHANGED
@@ -17,9 +17,11 @@ export { getOnboardSamples } from './assets/samples/getSamples';
17
17
  export { PreviewConfig } from './types/PreviewConfig';
18
18
  export { RenderPage } from './RenderPage';
19
19
  export { RenderMainNode } from './RenderMainNode';
20
+ export { DeviceMockFrame } from './DeviceMockFrame';
20
21
  export { novaToJson } from './utils/novaToJson';
21
22
  export type { Localication } from './types/PreviewConfig';
22
23
  export { getDevices } from './utils/getDevices';
23
24
  export type { Device } from './types/Device';
24
25
  export { AttributesEditor };
25
26
  export * from './build-components/index';
27
+ export { default as useNode } from './build-components/useNode';
@@ -4,6 +4,27 @@ export interface Device {
4
4
  width: number;
5
5
  height: number;
6
6
  type: 'phone' | 'tablet';
7
+ /**
8
+ * Optional physical corner radius of the device screen area in CSS pixels.
9
+ * Used only for visual preview rounding; has no effect on layout calculations.
10
+ */
11
+ radius?: number;
12
+ /**
13
+ * Insets in CSS pixels in the order: [top, right, bottom, left]
14
+ * Represents safe area or system UI overlaps (status bar, home indicator, etc.).
15
+ */
16
+ insets?: [number, number, number, number];
17
+ /**
18
+ * Navigation bar style depending on platform.
19
+ * - iOS: 'none' | 'homeIndicator' | 'tabBar' (visual reference only)
20
+ * - Android: 'threeButtons' | 'gesture' | 'none'
21
+ */
22
+ navigationBarType?:
23
+ | 'none'
24
+ | 'homeIndicator' // iOS
25
+ | 'tabBar' // iOS (for previewing tab bar overlap if desired)
26
+ | 'threeButtons' // Android classic nav bar
27
+ | 'gesture'; // Android gesture navigation
7
28
  /**
8
29
  * Relative importance for generic targeting and display ordering.
9
30
  * 1 = highest importance, 100 = lowest importance
package/src/types/Node.ts CHANGED
@@ -1,14 +1,4 @@
1
- export type NodeDefaultAttribute = Record<
2
- string,
3
- | boolean
4
- | string
5
- | number
6
- | null
7
- | undefined
8
- | string[]
9
- | Record<string, unknown>
10
- | Array<Record<string, unknown>>
11
- >;
1
+ export type NodeDefaultAttribute = Record<string, unknown>;
12
2
 
13
3
  export type Node<T = NodeDefaultAttribute> =
14
4
  | null
@@ -23,4 +13,5 @@ export interface NodeData<T = Record<string, unknown>> {
23
13
  children: Node<Record<string, unknown>>;
24
14
  attributes?: T;
25
15
  key?: string;
16
+ isMain?: boolean;
26
17
  }
@@ -5,7 +5,7 @@ export interface ProjectBase<T> {
5
5
  name: string;
6
6
  version: string;
7
7
  data: T;
8
- previewConfig: PreviewConfig;
8
+ previewConfig?: PreviewConfig;
9
9
  }
10
10
 
11
11
  export interface Project extends ProjectBase<Node> {}
@@ -26,6 +26,8 @@ function onboardNovaToJson(nova: ProjectBase<unknown>): Node {
26
26
 
27
27
  const providerNode: NodeData = {
28
28
  type: 'OnboardProvider',
29
+ isMain: true,
30
+ key: (nova?.data as any)?.data?.key,
29
31
  children: [
30
32
  carouselNode,
31
33
  // Place dots above buttons in visual order
@@ -35,6 +37,11 @@ function onboardNovaToJson(nova: ProjectBase<unknown>): Node {
35
37
  ],
36
38
  attributes: {
37
39
  theme: (nova?.data as any)?.theme,
40
+ // forward safe area inset preference; default to true when absent
41
+ use_safe_area_inset:
42
+ (attributes as any)?.use_safe_area_inset !== undefined
43
+ ? (attributes as any)?.use_safe_area_inset
44
+ : true,
38
45
  },
39
46
  };
40
47
 
@@ -88,17 +95,45 @@ function buildCarouselItem(
88
95
  if (comp?.layout === 'title-layout') {
89
96
  const title = comp?.attributes?.title_localization_key || '';
90
97
  const color = comp?.attributes?.title_color || undefined;
98
+ const { fontSize: titleFontSize, fontWeight: titleFontWeight } =
99
+ extractTextStyleAttributesFromComponent(comp);
91
100
  children.push({
92
101
  type: 'OnboardTitle',
93
- attributes: color ? { color } : undefined,
102
+ attributes:
103
+ color || titleFontSize || titleFontWeight
104
+ ? {
105
+ ...(color ? { color } : {}),
106
+ ...(typeof titleFontSize === 'number'
107
+ ? { fontSize: titleFontSize }
108
+ : {}),
109
+ ...(titleFontWeight ? { fontWeight: titleFontWeight } : {}),
110
+ }
111
+ : undefined,
94
112
  children: title,
95
113
  });
96
114
  } else if (comp?.layout === 'subtitle-layout') {
97
115
  const subtitle = comp?.attributes?.subtitle_localization_key || '';
98
116
  const color = comp?.attributes?.subtitle_color || undefined;
117
+ const {
118
+ fontSize: subtitleFontSize,
119
+ textAlign: subtitleTextAlign,
120
+ fontWeight: subtitleFontWeight,
121
+ } = extractTextStyleAttributesFromComponent(comp);
99
122
  children.push({
100
123
  type: 'OnboardSubtitle',
101
- attributes: color ? { color } : undefined,
124
+ attributes:
125
+ color || subtitleFontSize || subtitleTextAlign || subtitleFontWeight
126
+ ? {
127
+ ...(color ? { color } : {}),
128
+ ...(typeof subtitleFontSize === 'number'
129
+ ? { fontSize: subtitleFontSize }
130
+ : {}),
131
+ ...(subtitleTextAlign ? { textAlign: subtitleTextAlign } : {}),
132
+ ...(subtitleFontWeight
133
+ ? { fontWeight: subtitleFontWeight }
134
+ : {}),
135
+ }
136
+ : undefined,
102
137
  children: subtitle,
103
138
  });
104
139
  } else if (comp?.layout === 'image-layout') {
@@ -114,13 +149,20 @@ function buildCarouselItem(
114
149
  ? rawHeight
115
150
  : undefined;
116
151
 
152
+ // derive resizeMode from is_bg_image and try to extract borderRadius from styles
153
+ const isBgImage = Boolean(comp?.attributes?.is_bg_image);
154
+ const imageStyle = extractViewStyleAttributesFromComponent(comp);
155
+
117
156
  if (src) {
118
157
  children.push({
119
158
  type: 'OnboardImage',
120
159
  attributes: {
121
160
  src,
122
161
  ...(height ? { height } : {}),
123
- resizeMode: 'contain',
162
+ resizeMode: isBgImage ? 'cover' : 'contain',
163
+ ...(typeof imageStyle.borderRadius === 'number'
164
+ ? { borderRadius: imageStyle.borderRadius }
165
+ : {}),
124
166
  },
125
167
  children: undefined as unknown as Node,
126
168
  } as unknown as NodeData);
@@ -229,6 +271,127 @@ function buildCarouselItem(
229
271
  };
230
272
  }
231
273
 
274
+ //@eslint-disable-next-line @typescript-eslint/no-explicit-any
275
+ function extractTextStyleAttributesFromComponent(comp: any): {
276
+ fontSize?: number;
277
+ textAlign?: 'left' | 'center' | 'right' | 'justify';
278
+ fontWeight?:
279
+ | 'normal'
280
+ | 'bold'
281
+ | '100'
282
+ | '200'
283
+ | '300'
284
+ | '400'
285
+ | '500'
286
+ | '600'
287
+ | '700'
288
+ | '800'
289
+ | '900';
290
+ } {
291
+ const result: {
292
+ fontSize?: number;
293
+ textAlign?: 'left' | 'center' | 'right' | 'justify';
294
+ fontWeight?:
295
+ | 'normal'
296
+ | 'bold'
297
+ | '100'
298
+ | '200'
299
+ | '300'
300
+ | '400'
301
+ | '500'
302
+ | '600'
303
+ | '700'
304
+ | '800'
305
+ | '900';
306
+ } = {};
307
+
308
+ //@eslint-disable-next-line @typescript-eslint/no-explicit-any
309
+ const stylesRoot = (comp?.attributes?.styles || []) as any[];
310
+ if (!Array.isArray(stylesRoot) || stylesRoot.length === 0) return result;
311
+
312
+ const allowedAligns = new Set(['left', 'center', 'right', 'justify']);
313
+ const allowedWeights = new Set([
314
+ 'normal',
315
+ 'bold',
316
+ '100',
317
+ '200',
318
+ '300',
319
+ '400',
320
+ '500',
321
+ '600',
322
+ '700',
323
+ '800',
324
+ '900',
325
+ ]);
326
+
327
+ for (const s of stylesRoot) {
328
+ if (s?.layout !== 'style-layout') continue;
329
+ //@eslint-disable-next-line @typescript-eslint/no-explicit-any
330
+ const nested = (s?.attributes?.styles || []) as any[];
331
+ if (!Array.isArray(nested)) continue;
332
+ for (const st of nested) {
333
+ if (st?.layout !== 'Styles') continue;
334
+ const type = st?.attributes?.type;
335
+ if (type !== 'textStyle') continue;
336
+ const style = st?.attributes?.style || {};
337
+
338
+ const rawFontSize = style?.fontSize;
339
+ if (typeof rawFontSize === 'number' && Number.isFinite(rawFontSize)) {
340
+ result.fontSize = rawFontSize;
341
+ } else if (typeof rawFontSize === 'string') {
342
+ const n = Number.parseInt(rawFontSize, 10);
343
+ if (Number.isFinite(n)) result.fontSize = n;
344
+ }
345
+
346
+ const align = style?.textAlign;
347
+ if (typeof align === 'string' && allowedAligns.has(align)) {
348
+ result.textAlign = align as typeof result.textAlign;
349
+ }
350
+
351
+ const rawWeight = style?.fontWeight;
352
+ let normalizedWeight: string | undefined;
353
+ if (typeof rawWeight === 'number' && Number.isFinite(rawWeight)) {
354
+ normalizedWeight = String(rawWeight);
355
+ } else if (typeof rawWeight === 'string') {
356
+ normalizedWeight = rawWeight;
357
+ }
358
+ if (normalizedWeight && allowedWeights.has(normalizedWeight)) {
359
+ result.fontWeight = normalizedWeight as typeof result.fontWeight;
360
+ }
361
+ }
362
+ }
363
+
364
+ return result;
365
+ }
366
+
367
+ //@eslint-disable-next-line @typescript-eslint/no-explicit-any
368
+ function extractViewStyleAttributesFromComponent(comp: any): {
369
+ borderRadius?: number;
370
+ } {
371
+ const result: { borderRadius?: number } = {};
372
+ //@eslint-disable-next-line @typescript-eslint/no-explicit-any
373
+ const stylesRoot = (comp?.attributes?.styles || []) as any[];
374
+ if (!Array.isArray(stylesRoot) || stylesRoot.length === 0) return result;
375
+ for (const s of stylesRoot) {
376
+ if (s?.layout !== 'style-layout') continue;
377
+ //@eslint-disable-next-line @typescript-eslint/no-explicit-any
378
+ const nested = (s?.attributes?.styles || []) as any[];
379
+ if (!Array.isArray(nested)) continue;
380
+ for (const st of nested) {
381
+ if (st?.layout !== 'Styles') continue;
382
+ const style = st?.attributes?.style || {};
383
+ const rawBr = style?.borderRadius;
384
+ if (typeof rawBr === 'number' && Number.isFinite(rawBr)) {
385
+ result.borderRadius = rawBr;
386
+ } else if (typeof rawBr === 'string') {
387
+ const n = Number.parseInt(rawBr, 10);
388
+ if (Number.isFinite(n)) result.borderRadius = n;
389
+ }
390
+ }
391
+ }
392
+ return result;
393
+ }
394
+
232
395
  function mapDotsFromGeneralComponents(generalComponents: any[]): Node | null {
233
396
  const dots = generalComponents.find((gc) => gc?.layout === 'dots-layout');
234
397
  if (!dots) return null;