@developer_tribe/react-builder 0.1.30 → 0.1.31

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 (35) hide show
  1. package/dist/build-components/OnboardButton/OnboardButtonProps.generated.d.ts +0 -1
  2. package/dist/build-components/OnboardFooter/OnboardFooterProps.generated.d.ts +1 -0
  3. package/dist/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.d.ts +1 -0
  4. package/dist/build-components/OnboardTitle/OnboardTitleProps.generated.d.ts +1 -0
  5. package/dist/build-components/Text/TextProps.generated.d.ts +1 -0
  6. package/dist/build-components/patterns.generated.d.ts +4 -5
  7. package/dist/index.cjs.js +4 -4
  8. package/dist/index.d.ts +3 -0
  9. package/dist/index.esm.js +4 -4
  10. package/dist/utils/extractImageStyle.d.ts +3 -0
  11. package/dist/utils/extractTextStyle.d.ts +3 -0
  12. package/dist/utils/extractViewStyle.d.ts +3 -0
  13. package/package.json +1 -1
  14. package/scripts/prebuild/prebuild.js +1 -1
  15. package/src/build-components/Image/Image.tsx +2 -13
  16. package/src/build-components/OnboardButton/OnboardButton.tsx +4 -5
  17. package/src/build-components/OnboardButton/OnboardButtonProps.generated.ts +0 -1
  18. package/src/build-components/OnboardButton/pattern.json +0 -1
  19. package/src/build-components/OnboardFooter/OnboardFooter.tsx +2 -29
  20. package/src/build-components/OnboardFooter/OnboardFooterProps.generated.ts +1 -0
  21. package/src/build-components/OnboardProvider/OnboardProvider.tsx +0 -1
  22. package/src/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.ts +1 -0
  23. package/src/build-components/OnboardTitle/OnboardTitle.tsx +0 -1
  24. package/src/build-components/OnboardTitle/OnboardTitleProps.generated.ts +1 -0
  25. package/src/build-components/Text/Text.tsx +3 -12
  26. package/src/build-components/Text/TextProps.generated.ts +1 -0
  27. package/src/build-components/Text/pattern.json +3 -3
  28. package/src/build-components/View/View.tsx +2 -45
  29. package/src/build-components/patterns.generated.ts +4 -5
  30. package/src/build-components/useNode.ts +0 -1
  31. package/src/index.ts +3 -0
  32. package/src/utils/extractImageStyle.ts +24 -0
  33. package/src/utils/extractTextStyle.ts +109 -0
  34. package/src/utils/extractViewStyle.ts +44 -0
  35. package/src/utils/novaToJson.ts +5 -2
@@ -0,0 +1,3 @@
1
+ import { ImagePropsGenerated } from '../build-components/Image/ImageProps.generated';
2
+ import type { NodeData } from '../types/Node';
3
+ export declare function extractImageStyle<T extends ImagePropsGenerated['attributes']>(node: NodeData<T>): import("react").CSSProperties;
@@ -0,0 +1,3 @@
1
+ import type { NodeData } from '../types/Node';
2
+ import type { TextPropsGenerated } from '../build-components/Text/TextProps.generated';
3
+ export declare function extractTextStyle<T extends TextPropsGenerated['attributes']>(node: NodeData<T>): import("react").CSSProperties;
@@ -0,0 +1,3 @@
1
+ import { ViewPropsGenerated } from '../build-components/View/ViewProps.generated';
2
+ import type { NodeData } from '../types/Node';
3
+ export declare function extractViewStyle<T extends ViewPropsGenerated['attributes']>(node: NodeData<T>): import("react").CSSProperties;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@developer_tribe/react-builder",
3
- "version": "0.1.30",
3
+ "version": "0.1.31",
4
4
  "type": "module",
5
5
  "restricted": true,
6
6
  "main": "dist/index.cjs.js",
@@ -2,7 +2,7 @@
2
2
 
3
3
  import run from './build-components.js';
4
4
 
5
- console.log('Building components...');
5
+ console.info('Building components...');
6
6
  try {
7
7
  await run();
8
8
  } catch (err) {
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import type { ImageComponentProps } from './ImageProps.generated';
3
3
  import useNode from '../useNode';
4
+ import { extractImageStyle } from '../../utils/extractImageStyle';
4
5
 
5
6
  function Image({ node }: ImageComponentProps) {
6
7
  node = useNode(node);
@@ -10,19 +11,7 @@ function Image({ node }: ImageComponentProps) {
10
11
  src={node.attributes?.src}
11
12
  width={node.attributes?.width}
12
13
  height={node.attributes?.height}
13
- style={{
14
- width: node.attributes?.width,
15
- height: node.attributes?.height,
16
- borderRadius: node.attributes?.borderRadius,
17
- objectFit:
18
- node.attributes?.resizeMode === 'cover'
19
- ? 'cover'
20
- : node.attributes?.resizeMode === 'contain'
21
- ? 'contain'
22
- : node.attributes?.resizeMode === 'stretch'
23
- ? 'fill'
24
- : undefined,
25
- }}
14
+ style={extractImageStyle(node)}
26
15
  alt=""
27
16
  />
28
17
  );
@@ -27,15 +27,14 @@ function OnboardButton({ node }: OnboardButtonComponentProps) {
27
27
  if (e.type === 'Permission') {
28
28
  alert(`Permission requested: ${e.permission ?? 'unknown'}`);
29
29
  } else if (e.type === 'Navigate') {
30
- if (typeof node.attributes?.targetIndex === 'number') {
31
- emblaApi?.scrollTo(node.attributes.targetIndex);
30
+ const eventTargetIndex = (e as any)?.targetIndex;
31
+ if (typeof eventTargetIndex === 'number') {
32
+ emblaApi?.scrollTo(eventTargetIndex);
32
33
  navigateHandled = true;
33
34
  }
34
35
  }
35
36
  }
36
- if (!navigateHandled && typeof node.attributes?.targetIndex === 'number') {
37
- emblaApi?.scrollTo(node.attributes.targetIndex);
38
- }
37
+ // Fallback: do nothing when there is no Navigate event
39
38
  };
40
39
 
41
40
  return (
@@ -22,7 +22,6 @@ export interface OnboardButtonPropsGenerated {
22
22
  animation_color?: string;
23
23
  button_background_color?: string;
24
24
  flex?: number;
25
- targetIndex?: number;
26
25
  events?: EventObjectGenerated[];
27
26
  };
28
27
  }
@@ -17,7 +17,6 @@
17
17
  "animation_color": "string",
18
18
  "button_background_color": "string",
19
19
  "flex": "number",
20
- "targetIndex": "number",
21
20
  "events": "EventObject[]"
22
21
  }
23
22
  },
@@ -3,6 +3,7 @@ import type { OnboardFooterComponentProps } from './OnboardFooterProps.generated
3
3
  import useNode from '../useNode';
4
4
  import { useRenderStore } from '../../store';
5
5
  import { parseSize } from '../../size-matters';
6
+ import { extractTextStyle } from '../../utils/extractTextStyle';
6
7
 
7
8
  type Segment =
8
9
  | { type: 'text'; value: string }
@@ -94,34 +95,7 @@ function OnboardFooter({ node }: OnboardFooterComponentProps) {
94
95
  key ? (localication?.[defaultLanguage ?? 'en']?.[key] ?? key) : '';
95
96
 
96
97
  const text = t(node?.attributes?.textLocalizationKey);
97
- const style: React.CSSProperties = {
98
- display: 'flex',
99
- flexDirection: (node?.attributes?.flexDirection as any) ?? 'column',
100
- gap: typeof node?.attributes?.gap === 'number' ? node.attributes.gap : 0,
101
- padding:
102
- typeof node?.attributes?.padding === 'number'
103
- ? node.attributes.padding
104
- : undefined,
105
- margin:
106
- typeof node?.attributes?.margin === 'number'
107
- ? node.attributes.margin
108
- : undefined,
109
- backgroundColor: node?.attributes?.backgroundColor,
110
- borderRadius:
111
- typeof node?.attributes?.borderRadius === 'number'
112
- ? node.attributes.borderRadius
113
- : undefined,
114
- width:
115
- typeof node?.attributes?.width === 'number'
116
- ? node.attributes.width
117
- : undefined,
118
- height:
119
- typeof node?.attributes?.height === 'number'
120
- ? node.attributes.height
121
- : undefined,
122
- alignItems: node?.attributes?.alignItems as any,
123
- justifyContent: node?.attributes?.justifyContent as any,
124
- };
98
+ const style: React.CSSProperties = extractTextStyle(node);
125
99
 
126
100
  const linkStyle = (color?: string): React.CSSProperties => ({
127
101
  color,
@@ -129,7 +103,6 @@ function OnboardFooter({ node }: OnboardFooterComponentProps) {
129
103
  });
130
104
 
131
105
  const paddingHorizontal = parseSize(node?.attributes?.paddingHorizontal);
132
- console.log('----', node?.attributes);
133
106
  return (
134
107
  <div
135
108
  style={{
@@ -19,6 +19,7 @@ export interface OnboardFooterPropsGenerated {
19
19
  | '700'
20
20
  | '800'
21
21
  | '900';
22
+ textAlign?: 'left' | 'center' | 'right' | 'justify';
22
23
  scrollable?: boolean;
23
24
  flexDirection?: 'row' | 'column';
24
25
  alignItems?: 'flex-start' | 'center' | 'flex-end' | 'stretch' | 'baseline';
@@ -15,7 +15,6 @@ import useNode from '../useNode';
15
15
  export const onboardContext = createContext<any>(undefined);
16
16
  function OnboardProvider({ node }: OnboardProviderComponentProps) {
17
17
  node = useNode(node);
18
- console.log('node', node);
19
18
  const device = useRenderStore((s) => s.device);
20
19
  const [emblaRef, emblaApi] = useEmblaCarousel(node.attributes as any);
21
20
  const [selectedIndex, setSelectedIndex] = useState(0);
@@ -19,6 +19,7 @@ export interface OnboardSubtitlePropsGenerated {
19
19
  | '700'
20
20
  | '800'
21
21
  | '900';
22
+ textAlign?: 'left' | 'center' | 'right' | 'justify';
22
23
  scrollable?: boolean;
23
24
  flexDirection?: 'row' | 'column';
24
25
  alignItems?: 'flex-start' | 'center' | 'flex-end' | 'stretch' | 'baseline';
@@ -5,7 +5,6 @@ import useNode from '../useNode';
5
5
 
6
6
  function OnboardTitle({ node }: OnboardTitleComponentProps) {
7
7
  node = useNode(node);
8
- console.log('node', node);
9
8
  return <Text node={node} />;
10
9
  }
11
10
 
@@ -19,6 +19,7 @@ export interface OnboardTitlePropsGenerated {
19
19
  | '700'
20
20
  | '800'
21
21
  | '900';
22
+ textAlign?: 'left' | 'center' | 'right' | 'justify';
22
23
  scrollable?: boolean;
23
24
  flexDirection?: 'row' | 'column';
24
25
  alignItems?: 'flex-start' | 'center' | 'flex-end' | 'stretch' | 'baseline';
@@ -2,28 +2,19 @@ import React from 'react';
2
2
  import type { TextComponentProps } from './TextProps.generated';
3
3
  import useNode from '../useNode';
4
4
  import { useRenderStore } from '../../store';
5
- import { fs, parseSize } from '../../size-matters';
5
+ import { extractTextStyle } from '../../utils/extractTextStyle';
6
6
 
7
7
  function Text({ node }: TextComponentProps) {
8
8
  node = useNode(node);
9
- const { screenStyle, theme } = useRenderStore((s) => ({
10
- screenStyle: s.screenStyle,
11
- theme: s.theme,
12
- }));
13
- const color =
14
- theme === 'light' ? screenStyle.light.color : screenStyle.dark.color;
15
9
  const { defaultLanguage, localication } = useRenderStore((s) => ({
16
10
  defaultLanguage: s.defaultLanguage,
17
11
  localication: s.localication,
18
12
  }));
19
13
  const keyOrText: string = node.children as string;
20
- const style = (node.attributes as React.CSSProperties) ?? {};
21
- const fontSize = node.attributes?.fontSize
22
- ? parseSize(node.attributes.fontSize)
23
- : fs(14);
14
+ const style = extractTextStyle(node);
24
15
 
25
16
  return (
26
- <p style={{ ...style, fontSize, color: style.color ?? color }}>
17
+ <p style={style}>
27
18
  {localication?.[defaultLanguage ?? 'en']?.[keyOrText] ?? keyOrText}
28
19
  </p>
29
20
  );
@@ -48,6 +48,7 @@ export interface TextPropsGenerated {
48
48
  | '700'
49
49
  | '800'
50
50
  | '900';
51
+ textAlign?: 'left' | 'center' | 'right' | 'justify';
51
52
  };
52
53
  }
53
54
 
@@ -20,8 +20,8 @@
20
20
  "700",
21
21
  "800",
22
22
  "900"
23
- ]
24
- },
25
- "textAlign": ["left", "center", "right", "justify"]
23
+ ],
24
+ "textAlign": ["left", "center", "right", "justify"]
25
+ }
26
26
  }
27
27
  }
@@ -6,55 +6,12 @@ import type {
6
6
  import RenderNode from '../RenderNode.generated';
7
7
  import { Node } from '../../types/Node';
8
8
  import useNode from '../useNode';
9
-
10
- function mapAttributesToStyle(
11
- attributes: ViewPropsGenerated['attributes'],
12
- ): React.CSSProperties {
13
- const scrollable = attributes?.scrollable ?? false;
14
- const style: React.CSSProperties = {
15
- display: 'flex',
16
- flexDirection: 'column',
17
- };
18
- if (!attributes) return style;
19
- if (scrollable) {
20
- if (attributes.flexDirection === 'row') {
21
- style.overflowX = 'auto';
22
- style.overflowY = 'hidden';
23
- style.maxWidth = '100%';
24
- style.maxHeight = '100%';
25
- } else {
26
- style.overflowY = 'auto';
27
- style.overflowX = 'hidden';
28
- style.maxHeight = '100%';
29
- style.maxWidth = '100%';
30
- }
31
- }
32
- if (attributes.flexDirection) style.flexDirection = attributes.flexDirection;
33
- if (attributes.alignItems)
34
- style.alignItems =
35
- attributes.alignItems as React.CSSProperties['alignItems'];
36
- if (attributes.justifyContent)
37
- style.justifyContent =
38
- attributes.justifyContent as React.CSSProperties['justifyContent'];
39
- if (attributes.gap !== undefined) style.gap = attributes.gap;
40
- if (attributes.padding !== undefined) style.padding = attributes.padding;
41
- if (attributes.margin !== undefined) style.margin = attributes.margin;
42
- if (attributes.backgroundColor)
43
- style.backgroundColor = attributes.backgroundColor;
44
- if (attributes.borderRadius !== undefined)
45
- style.borderRadius = attributes.borderRadius;
46
- if (attributes.width !== undefined) style.width = attributes.width;
47
- if (attributes.height !== undefined) style.height = attributes.height;
48
- return style;
49
- }
9
+ import { extractViewStyle } from '../../utils/extractViewStyle';
50
10
 
51
11
  export function View({ node }: ViewComponentProps) {
52
12
  node = useNode(node);
53
13
  return (
54
- <div
55
- style={mapAttributesToStyle(node.attributes ?? {})}
56
- className="scroll-container"
57
- >
14
+ <div style={extractViewStyle(node)} className="scroll-container">
58
15
  <RenderNode node={node.children as Node} />
59
16
  </div>
60
17
  );
@@ -111,7 +111,6 @@ export const patterns = [
111
111
  animation_color: 'string',
112
112
  button_background_color: 'string',
113
113
  flex: 'number',
114
- targetIndex: 'number',
115
114
  events: 'EventObject[]',
116
115
  },
117
116
  },
@@ -185,6 +184,7 @@ export const patterns = [
185
184
  '800',
186
185
  '900',
187
186
  ],
187
+ textAlign: ['left', 'center', 'right', 'justify'],
188
188
  scrollable: 'boolean',
189
189
  flexDirection: ['row', 'column'],
190
190
  alignItems: ['flex-start', 'center', 'flex-end', 'stretch', 'baseline'],
@@ -223,7 +223,6 @@ export const patterns = [
223
223
  linkedWordSecondColor: 'string',
224
224
  linkedWordSecondPage: 'string',
225
225
  },
226
- textAlign: ['left', 'center', 'right', 'justify'],
227
226
  },
228
227
  defaults: { paddingHorizontal: '24@s' },
229
228
  types: {},
@@ -307,6 +306,7 @@ export const patterns = [
307
306
  '800',
308
307
  '900',
309
308
  ],
309
+ textAlign: ['left', 'center', 'right', 'justify'],
310
310
  scrollable: 'boolean',
311
311
  flexDirection: ['row', 'column'],
312
312
  alignItems: ['flex-start', 'center', 'flex-end', 'stretch', 'baseline'],
@@ -338,7 +338,6 @@ export const patterns = [
338
338
  width: 'number',
339
339
  height: 'number',
340
340
  },
341
- textAlign: ['left', 'center', 'right', 'justify'],
342
341
  },
343
342
  defaults: { fontSize: '14@fs', fontWeight: '600' },
344
343
  types: {},
@@ -365,6 +364,7 @@ export const patterns = [
365
364
  '800',
366
365
  '900',
367
366
  ],
367
+ textAlign: ['left', 'center', 'right', 'justify'],
368
368
  scrollable: 'boolean',
369
369
  flexDirection: ['row', 'column'],
370
370
  alignItems: ['flex-start', 'center', 'flex-end', 'stretch', 'baseline'],
@@ -396,7 +396,6 @@ export const patterns = [
396
396
  width: 'number',
397
397
  height: 'number',
398
398
  },
399
- textAlign: ['left', 'center', 'right', 'justify'],
400
399
  },
401
400
  defaults: { fontSize: '24@fs', fontWeight: '700', textAlign: 'center' },
402
401
  types: {},
@@ -453,8 +452,8 @@ export const patterns = [
453
452
  '800',
454
453
  '900',
455
454
  ],
455
+ textAlign: ['left', 'center', 'right', 'justify'],
456
456
  },
457
- textAlign: ['left', 'center', 'right', 'justify'],
458
457
  },
459
458
  types: {},
460
459
  defaults: {},
@@ -6,7 +6,6 @@ export default function useNode<
6
6
  >(node: NodeData<T>): NodeData<T> {
7
7
  const type = node?.type;
8
8
  const defaults = getDefaultsForType(type) as Partial<T> | undefined;
9
- console.log('defaults', type, defaults);
10
9
  if (!defaults) return node;
11
10
  const mergedAttributes: T = {
12
11
  ...(defaults as T),
package/src/index.ts CHANGED
@@ -25,3 +25,6 @@ export { AttributesEditor };
25
25
  export * from './build-components/index';
26
26
  export { default as useNode } from './build-components/useNode';
27
27
  export { querySelector } from './utils/querySelector';
28
+ export { extractViewStyle } from './utils/extractViewStyle';
29
+ export { extractImageStyle } from './utils/extractImageStyle';
30
+ export { extractTextStyle } from './utils/extractTextStyle';
@@ -0,0 +1,24 @@
1
+ import { ImagePropsGenerated } from '../build-components/Image/ImageProps.generated';
2
+ import type { NodeData } from '../types/Node';
3
+
4
+ export function extractImageStyle<T extends ImagePropsGenerated['attributes']>(
5
+ node: NodeData<T>,
6
+ ) {
7
+ const attributes = node.attributes;
8
+ const style: React.CSSProperties = {};
9
+
10
+ if (!attributes) return style;
11
+
12
+ if (attributes.width !== undefined) style.width = attributes.width;
13
+ if (attributes.height !== undefined) style.height = attributes.height;
14
+ if (attributes.borderRadius !== undefined)
15
+ style.borderRadius = attributes.borderRadius;
16
+
17
+ // Map resizeMode to CSS object-fit
18
+ if (attributes.resizeMode === 'cover') style.objectFit = 'cover';
19
+ else if (attributes.resizeMode === 'contain') style.objectFit = 'contain';
20
+ else if (attributes.resizeMode === 'stretch') style.objectFit = 'fill';
21
+ else if (attributes.resizeMode === 'center') style.objectFit = 'none';
22
+
23
+ return style;
24
+ }
@@ -0,0 +1,109 @@
1
+ import type { NodeData } from '../types/Node';
2
+ import type { TextPropsGenerated } from '../build-components/Text/TextProps.generated';
3
+ import { fs, parseSize } from '../size-matters';
4
+ import { useRenderStore } from '../store';
5
+
6
+ export function extractTextStyle<T extends TextPropsGenerated['attributes']>(
7
+ node: NodeData<T>,
8
+ ) {
9
+ const attributes = node.attributes;
10
+
11
+ const { screenStyle, theme } = useRenderStore.getState();
12
+ const fallbackColor =
13
+ theme === 'light' ? screenStyle.light.color : screenStyle.dark.color;
14
+
15
+ const style: React.CSSProperties = {};
16
+
17
+ if (!attributes) {
18
+ style.fontSize = fs(14);
19
+ style.color = fallbackColor;
20
+ return style;
21
+ }
22
+
23
+ // Typography
24
+ if (attributes.fontSize !== undefined) {
25
+ const parsed = parseSize(attributes.fontSize);
26
+ style.fontSize = parsed as React.CSSProperties['fontSize'];
27
+ } else {
28
+ style.fontSize = fs(14);
29
+ }
30
+ if (attributes.fontWeight) style.fontWeight = attributes.fontWeight;
31
+ style.color = attributes.color ?? fallbackColor;
32
+ if (attributes.textAlign)
33
+ style.textAlign = attributes.textAlign as React.CSSProperties['textAlign'];
34
+
35
+ // Spacing
36
+ // Shorthand
37
+ if (attributes.padding !== undefined) style.padding = attributes.padding;
38
+ if (attributes.margin !== undefined) style.margin = attributes.margin;
39
+
40
+ // Axis shorthands
41
+ if (attributes.paddingHorizontal !== undefined) {
42
+ const v = parseSize(attributes.paddingHorizontal);
43
+ style.paddingLeft = v as React.CSSProperties['paddingLeft'];
44
+ style.paddingRight = v as React.CSSProperties['paddingRight'];
45
+ }
46
+ if (attributes.paddingVertical !== undefined) {
47
+ const v = parseSize(attributes.paddingVertical);
48
+ style.paddingTop = v as React.CSSProperties['paddingTop'];
49
+ style.paddingBottom = v as React.CSSProperties['paddingBottom'];
50
+ }
51
+ if (attributes.marginHorizontal !== undefined) {
52
+ const v = parseSize(attributes.marginHorizontal);
53
+ style.marginLeft = v as React.CSSProperties['marginLeft'];
54
+ style.marginRight = v as React.CSSProperties['marginRight'];
55
+ }
56
+ if (attributes.marginVertical !== undefined) {
57
+ const v = parseSize(attributes.marginVertical);
58
+ style.marginTop = v as React.CSSProperties['marginTop'];
59
+ style.marginBottom = v as React.CSSProperties['marginBottom'];
60
+ }
61
+
62
+ // Edges
63
+ if (attributes.paddingTop !== undefined)
64
+ style.paddingTop = parseSize(
65
+ attributes.paddingTop,
66
+ ) as React.CSSProperties['paddingTop'];
67
+ if (attributes.paddingBottom !== undefined)
68
+ style.paddingBottom = parseSize(
69
+ attributes.paddingBottom,
70
+ ) as React.CSSProperties['paddingBottom'];
71
+ if (attributes.paddingLeft !== undefined)
72
+ style.paddingLeft = parseSize(
73
+ attributes.paddingLeft,
74
+ ) as React.CSSProperties['paddingLeft'];
75
+ if (attributes.paddingRight !== undefined)
76
+ style.paddingRight = parseSize(
77
+ attributes.paddingRight,
78
+ ) as React.CSSProperties['paddingRight'];
79
+
80
+ if (attributes.marginTop !== undefined)
81
+ style.marginTop = parseSize(
82
+ attributes.marginTop,
83
+ ) as React.CSSProperties['marginTop'];
84
+ if (attributes.marginBottom !== undefined)
85
+ style.marginBottom = parseSize(
86
+ attributes.marginBottom,
87
+ ) as React.CSSProperties['marginBottom'];
88
+ if (attributes.marginLeft !== undefined)
89
+ style.marginLeft = parseSize(
90
+ attributes.marginLeft,
91
+ ) as React.CSSProperties['marginLeft'];
92
+ if (attributes.marginRight !== undefined)
93
+ style.marginRight = parseSize(
94
+ attributes.marginRight,
95
+ ) as React.CSSProperties['marginRight'];
96
+
97
+ // Decor
98
+ if (attributes.backgroundColor)
99
+ style.backgroundColor = attributes.backgroundColor;
100
+ if (attributes.borderRadius !== undefined)
101
+ style.borderRadius =
102
+ attributes.borderRadius as React.CSSProperties['borderRadius'];
103
+
104
+ // Dimensions (rare for text but supported by schema)
105
+ if (attributes.width !== undefined) style.width = attributes.width;
106
+ if (attributes.height !== undefined) style.height = attributes.height;
107
+
108
+ return style;
109
+ }
@@ -0,0 +1,44 @@
1
+ import { ViewPropsGenerated } from '../build-components/View/ViewProps.generated';
2
+ import type { NodeData } from '../types/Node';
3
+
4
+ export function extractViewStyle<T extends ViewPropsGenerated['attributes']>(
5
+ node: NodeData<T>,
6
+ ) {
7
+ const attributes = node.attributes;
8
+ const scrollable = attributes?.scrollable ?? false;
9
+ const style: React.CSSProperties = {
10
+ display: 'flex',
11
+ flexDirection: 'column',
12
+ };
13
+ if (!attributes) return style;
14
+ if (scrollable) {
15
+ if (attributes.flexDirection === 'row') {
16
+ style.overflowX = 'auto';
17
+ style.overflowY = 'hidden';
18
+ style.maxWidth = '100%';
19
+ style.maxHeight = '100%';
20
+ } else {
21
+ style.overflowY = 'auto';
22
+ style.overflowX = 'hidden';
23
+ style.maxHeight = '100%';
24
+ style.maxWidth = '100%';
25
+ }
26
+ }
27
+ if (attributes.flexDirection) style.flexDirection = attributes.flexDirection;
28
+ if (attributes.alignItems)
29
+ style.alignItems =
30
+ attributes.alignItems as React.CSSProperties['alignItems'];
31
+ if (attributes.justifyContent)
32
+ style.justifyContent =
33
+ attributes.justifyContent as React.CSSProperties['justifyContent'];
34
+ if (attributes.gap !== undefined) style.gap = attributes.gap;
35
+ if (attributes.padding !== undefined) style.padding = attributes.padding;
36
+ if (attributes.margin !== undefined) style.margin = attributes.margin;
37
+ if (attributes.backgroundColor)
38
+ style.backgroundColor = attributes.backgroundColor;
39
+ if (attributes.borderRadius !== undefined)
40
+ style.borderRadius = attributes.borderRadius;
41
+ if (attributes.width !== undefined) style.width = attributes.width;
42
+ if (attributes.height !== undefined) style.height = attributes.height;
43
+ return style;
44
+ }
@@ -208,7 +208,7 @@ function buildCarouselItem(
208
208
  const animationColor = attrs?.animation_color as string | undefined;
209
209
  const flex = attrs?.flex ? Number(attrs.flex) : undefined;
210
210
 
211
- // Find first Navigate event and map to target index
211
+ // Normalize events and compute Navigate target indices when resolvable
212
212
  let targetIndex: number | undefined = undefined;
213
213
  //@eslint-disable-next-line @typescript-eslint/no-explicit-any
214
214
  const actions = (attrs?.actions || []) as any[];
@@ -216,6 +216,7 @@ function buildCarouselItem(
216
216
  type: 'Permission' | 'Navigate';
217
217
  permission?: string;
218
218
  navigate_to?: string | null;
219
+ targetIndex?: number;
219
220
  }[] = [];
220
221
  for (const action of actions) {
221
222
  //@eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -241,6 +242,9 @@ function buildCarouselItem(
241
242
  normalizedEvents.push({
242
243
  type: 'Navigate',
243
244
  navigate_to,
245
+ ...(hasTarget
246
+ ? { targetIndex: pageKeyToIndex.get(nextKey!)! }
247
+ : {}),
244
248
  });
245
249
  if (hasTarget) {
246
250
  targetIndex = pageKeyToIndex.get(nextKey!)!;
@@ -264,7 +268,6 @@ function buildCarouselItem(
264
268
  ? { button_background_color: buttonBackgroundColor }
265
269
  : {}),
266
270
  ...(typeof flex === 'number' ? { flex } : {}),
267
- ...(typeof targetIndex === 'number' ? { targetIndex } : {}),
268
271
  ...(normalizedEvents.length ? { events: normalizedEvents } : {}),
269
272
  },
270
273
  children: undefined as unknown as Node,