@developer_tribe/react-builder 0.1.30 → 0.1.32

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 (59) hide show
  1. package/dist/build-components/Button/ButtonProps.generated.d.ts +2 -1
  2. package/dist/build-components/CarouselButtons/CarouselButtonsProps.generated.d.ts +2 -1
  3. package/dist/build-components/CarouselDots/CarouselDotsProps.generated.d.ts +2 -1
  4. package/dist/build-components/Image/ImageProps.generated.d.ts +2 -1
  5. package/dist/build-components/OnboardButton/OnboardButtonProps.generated.d.ts +8 -5
  6. package/dist/build-components/OnboardButtons/OnboardButtonsProps.generated.d.ts +6 -3
  7. package/dist/build-components/OnboardDot/OnboardDotProps.generated.d.ts +2 -1
  8. package/dist/build-components/OnboardFooter/OnboardFooterProps.generated.d.ts +10 -4
  9. package/dist/build-components/OnboardImage/OnboardImageProps.generated.d.ts +4 -1
  10. package/dist/build-components/OnboardItem/OnboardItemProps.generated.d.ts +4 -2
  11. package/dist/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.d.ts +10 -4
  12. package/dist/build-components/OnboardTitle/OnboardTitleProps.generated.d.ts +10 -4
  13. package/dist/build-components/Text/TextProps.generated.d.ts +10 -4
  14. package/dist/build-components/View/ViewProps.generated.d.ts +6 -3
  15. package/dist/build-components/patterns.generated.d.ts +11 -7
  16. package/dist/index.cjs.js +6 -4
  17. package/dist/index.d.ts +3 -0
  18. package/dist/index.esm.js +6 -4
  19. package/dist/utils/extractImageStyle.d.ts +3 -0
  20. package/dist/utils/extractTextStyle.d.ts +3 -0
  21. package/dist/utils/extractViewStyle.d.ts +3 -0
  22. package/package.json +2 -1
  23. package/scripts/prebuild/prebuild.js +1 -1
  24. package/scripts/prebuild/utils/createGeneratedProps.js +64 -5
  25. package/src/assets/samples/getSamples.ts +6 -0
  26. package/src/assets/samples/vpn-onboard-5.json +1025 -0
  27. package/src/assets/samples/vpn-onboard-6.json +709 -0
  28. package/src/build-components/Button/ButtonProps.generated.ts +14 -12
  29. package/src/build-components/CarouselButtons/CarouselButtonsProps.generated.ts +6 -1
  30. package/src/build-components/CarouselDots/CarouselDotsProps.generated.ts +9 -7
  31. package/src/build-components/Image/Image.tsx +2 -13
  32. package/src/build-components/Image/ImageProps.generated.ts +3 -1
  33. package/src/build-components/OnboardButton/OnboardButton.tsx +4 -5
  34. package/src/build-components/OnboardButton/OnboardButtonProps.generated.ts +14 -10
  35. package/src/build-components/OnboardButton/pattern.json +3 -3
  36. package/src/build-components/OnboardButtons/OnboardButtonsProps.generated.ts +10 -3
  37. package/src/build-components/OnboardDot/OnboardDotProps.generated.ts +9 -7
  38. package/src/build-components/OnboardFooter/OnboardFooter.tsx +2 -29
  39. package/src/build-components/OnboardFooter/OnboardFooterProps.generated.ts +33 -21
  40. package/src/build-components/OnboardImage/OnboardImage.tsx +24 -1
  41. package/src/build-components/OnboardImage/OnboardImageProps.generated.ts +5 -1
  42. package/src/build-components/OnboardImage/pattern.json +3 -5
  43. package/src/build-components/OnboardItem/OnboardItemProps.generated.ts +5 -2
  44. package/src/build-components/OnboardProvider/OnboardProvider.tsx +0 -1
  45. package/src/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.ts +33 -21
  46. package/src/build-components/OnboardTitle/OnboardTitle.tsx +0 -1
  47. package/src/build-components/OnboardTitle/OnboardTitleProps.generated.ts +33 -21
  48. package/src/build-components/Text/Text.tsx +3 -12
  49. package/src/build-components/Text/TextProps.generated.ts +33 -21
  50. package/src/build-components/Text/pattern.json +3 -3
  51. package/src/build-components/View/View.tsx +2 -45
  52. package/src/build-components/View/ViewProps.generated.ts +18 -9
  53. package/src/build-components/patterns.generated.ts +11 -7
  54. package/src/build-components/useNode.ts +0 -1
  55. package/src/index.ts +3 -0
  56. package/src/utils/extractImageStyle.ts +24 -0
  57. package/src/utils/extractTextStyle.ts +109 -0
  58. package/src/utils/extractViewStyle.ts +44 -0
  59. package/src/utils/novaToJson.ts +10 -2
@@ -2,19 +2,41 @@
2
2
 
3
3
  import type { NodeData } from '../../types/Node';
4
4
 
5
+ export type FlexDirectionOptionType = 'row' | 'column';
6
+ export type AlignItemsOptionType =
7
+ | 'flex-start'
8
+ | 'center'
9
+ | 'flex-end'
10
+ | 'stretch'
11
+ | 'baseline';
12
+ export type JustifyContentOptionType =
13
+ | 'flex-start'
14
+ | 'center'
15
+ | 'flex-end'
16
+ | 'space-between'
17
+ | 'space-around'
18
+ | 'space-evenly';
19
+ export type FontWeightOptionType =
20
+ | 'normal'
21
+ | 'bold'
22
+ | '100'
23
+ | '200'
24
+ | '300'
25
+ | '400'
26
+ | '500'
27
+ | '600'
28
+ | '700'
29
+ | '800'
30
+ | '900';
31
+ export type TextAlignOptionType = 'left' | 'center' | 'right' | 'justify';
32
+
5
33
  export interface TextPropsGenerated {
6
34
  child: string;
7
35
  attributes: {
8
36
  scrollable?: boolean;
9
- flexDirection?: 'row' | 'column';
10
- alignItems?: 'flex-start' | 'center' | 'flex-end' | 'stretch' | 'baseline';
11
- justifyContent?:
12
- | 'flex-start'
13
- | 'center'
14
- | 'flex-end'
15
- | 'space-between'
16
- | 'space-around'
17
- | 'space-evenly';
37
+ flexDirection?: FlexDirectionOptionType;
38
+ alignItems?: AlignItemsOptionType;
39
+ justifyContent?: JustifyContentOptionType;
18
40
  gap?: string;
19
41
  padding?: number;
20
42
  paddingHorizontal?: string;
@@ -36,18 +58,8 @@ export interface TextPropsGenerated {
36
58
  height?: number;
37
59
  color?: string;
38
60
  fontSize?: number;
39
- fontWeight?:
40
- | 'normal'
41
- | 'bold'
42
- | '100'
43
- | '200'
44
- | '300'
45
- | '400'
46
- | '500'
47
- | '600'
48
- | '700'
49
- | '800'
50
- | '900';
61
+ fontWeight?: FontWeightOptionType;
62
+ textAlign?: TextAlignOptionType;
51
63
  };
52
64
  }
53
65
 
@@ -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
  );
@@ -2,19 +2,28 @@
2
2
 
3
3
  import type { NodeData } from '../../types/Node';
4
4
 
5
+ export type FlexDirectionOptionType = 'row' | 'column';
6
+ export type AlignItemsOptionType =
7
+ | 'flex-start'
8
+ | 'center'
9
+ | 'flex-end'
10
+ | 'stretch'
11
+ | 'baseline';
12
+ export type JustifyContentOptionType =
13
+ | 'flex-start'
14
+ | 'center'
15
+ | 'flex-end'
16
+ | 'space-between'
17
+ | 'space-around'
18
+ | 'space-evenly';
19
+
5
20
  export interface ViewPropsGenerated {
6
21
  child: string;
7
22
  attributes: {
8
23
  scrollable?: boolean;
9
- flexDirection?: 'row' | 'column';
10
- alignItems?: 'flex-start' | 'center' | 'flex-end' | 'stretch' | 'baseline';
11
- justifyContent?:
12
- | 'flex-start'
13
- | 'center'
14
- | 'flex-end'
15
- | 'space-between'
16
- | 'space-around'
17
- | 'space-evenly';
24
+ flexDirection?: FlexDirectionOptionType;
25
+ alignItems?: AlignItemsOptionType;
26
+ justifyContent?: JustifyContentOptionType;
18
27
  gap?: string;
19
28
  padding?: number;
20
29
  paddingHorizontal?: string;
@@ -111,15 +111,15 @@ 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
  },
118
117
  types: {
119
118
  EventObject: {
120
119
  type: ['Permission', 'Navigate'],
121
- permission: ['att', 'notification', 'rating', 'null'],
122
- navigate_to: ['string', 'null'],
120
+ permission: ['att', 'notification', 'rating', 'GDPR', 'null'],
121
+ navigate_to: 'string',
122
+ targetIndex: 'number',
123
123
  },
124
124
  },
125
125
  },
@@ -185,6 +185,7 @@ export const patterns = [
185
185
  '800',
186
186
  '900',
187
187
  ],
188
+ textAlign: ['left', 'center', 'right', 'justify'],
188
189
  scrollable: 'boolean',
189
190
  flexDirection: ['row', 'column'],
190
191
  alignItems: ['flex-start', 'center', 'flex-end', 'stretch', 'baseline'],
@@ -223,7 +224,6 @@ export const patterns = [
223
224
  linkedWordSecondColor: 'string',
224
225
  linkedWordSecondPage: 'string',
225
226
  },
226
- textAlign: ['left', 'center', 'right', 'justify'],
227
227
  },
228
228
  defaults: { paddingHorizontal: '24@s' },
229
229
  types: {},
@@ -240,8 +240,12 @@ export const patterns = [
240
240
  height: 'number',
241
241
  resizeMode: ['cover', 'contain', 'stretch', 'center'],
242
242
  borderRadius: 'number',
243
+ video_url: 'string',
244
+ lottie: 'string',
243
245
  },
244
246
  },
247
+ types: {},
248
+ defaults: {},
245
249
  },
246
250
  {
247
251
  schemaVersion: 1,
@@ -307,6 +311,7 @@ export const patterns = [
307
311
  '800',
308
312
  '900',
309
313
  ],
314
+ textAlign: ['left', 'center', 'right', 'justify'],
310
315
  scrollable: 'boolean',
311
316
  flexDirection: ['row', 'column'],
312
317
  alignItems: ['flex-start', 'center', 'flex-end', 'stretch', 'baseline'],
@@ -338,7 +343,6 @@ export const patterns = [
338
343
  width: 'number',
339
344
  height: 'number',
340
345
  },
341
- textAlign: ['left', 'center', 'right', 'justify'],
342
346
  },
343
347
  defaults: { fontSize: '14@fs', fontWeight: '600' },
344
348
  types: {},
@@ -365,6 +369,7 @@ export const patterns = [
365
369
  '800',
366
370
  '900',
367
371
  ],
372
+ textAlign: ['left', 'center', 'right', 'justify'],
368
373
  scrollable: 'boolean',
369
374
  flexDirection: ['row', 'column'],
370
375
  alignItems: ['flex-start', 'center', 'flex-end', 'stretch', 'baseline'],
@@ -396,7 +401,6 @@ export const patterns = [
396
401
  width: 'number',
397
402
  height: 'number',
398
403
  },
399
- textAlign: ['left', 'center', 'right', 'justify'],
400
404
  },
401
405
  defaults: { fontSize: '24@fs', fontWeight: '700', textAlign: 'center' },
402
406
  types: {},
@@ -453,8 +457,8 @@ export const patterns = [
453
457
  '800',
454
458
  '900',
455
459
  ],
460
+ textAlign: ['left', 'center', 'right', 'justify'],
456
461
  },
457
- textAlign: ['left', 'center', 'right', 'justify'],
458
462
  },
459
463
  types: {},
460
464
  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
+ }
@@ -172,6 +172,9 @@ function buildCarouselItem(
172
172
  // derive resizeMode from is_bg_image and try to extract borderRadius from styles
173
173
  const isBgImage = Boolean(comp?.attributes?.is_bg_image);
174
174
  const imageStyle = extractViewStyleAttributesFromComponent(comp);
175
+ // optional rich media
176
+ const lottie = comp?.attributes?.lottie as string | undefined;
177
+ const videoUrl = comp?.attributes?.video_url as string | undefined;
175
178
 
176
179
  if (src) {
177
180
  children.push({
@@ -183,6 +186,8 @@ function buildCarouselItem(
183
186
  ...(typeof imageStyle.borderRadius === 'number'
184
187
  ? { borderRadius: imageStyle.borderRadius }
185
188
  : {}),
189
+ ...(typeof lottie === 'string' ? { lottie } : {}),
190
+ ...(typeof videoUrl === 'string' ? { video_url: videoUrl } : {}),
186
191
  },
187
192
  children: undefined as unknown as Node,
188
193
  } as unknown as NodeData);
@@ -208,7 +213,7 @@ function buildCarouselItem(
208
213
  const animationColor = attrs?.animation_color as string | undefined;
209
214
  const flex = attrs?.flex ? Number(attrs.flex) : undefined;
210
215
 
211
- // Find first Navigate event and map to target index
216
+ // Normalize events and compute Navigate target indices when resolvable
212
217
  let targetIndex: number | undefined = undefined;
213
218
  //@eslint-disable-next-line @typescript-eslint/no-explicit-any
214
219
  const actions = (attrs?.actions || []) as any[];
@@ -216,6 +221,7 @@ function buildCarouselItem(
216
221
  type: 'Permission' | 'Navigate';
217
222
  permission?: string;
218
223
  navigate_to?: string | null;
224
+ targetIndex?: number;
219
225
  }[] = [];
220
226
  for (const action of actions) {
221
227
  //@eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -241,6 +247,9 @@ function buildCarouselItem(
241
247
  normalizedEvents.push({
242
248
  type: 'Navigate',
243
249
  navigate_to,
250
+ ...(hasTarget
251
+ ? { targetIndex: pageKeyToIndex.get(nextKey!)! }
252
+ : {}),
244
253
  });
245
254
  if (hasTarget) {
246
255
  targetIndex = pageKeyToIndex.get(nextKey!)!;
@@ -264,7 +273,6 @@ function buildCarouselItem(
264
273
  ? { button_background_color: buttonBackgroundColor }
265
274
  : {}),
266
275
  ...(typeof flex === 'number' ? { flex } : {}),
267
- ...(typeof targetIndex === 'number' ? { targetIndex } : {}),
268
276
  ...(normalizedEvents.length ? { events: normalizedEvents } : {}),
269
277
  },
270
278
  children: undefined as unknown as Node,