@developer_tribe/react-builder 1.2.22 → 1.2.23

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 (100) hide show
  1. package/dist/attribute-analyser/style/native/useExtractImageStyle.d.ts +2 -2
  2. package/dist/build-components/Image/ImageProps.generated.d.ts +2 -4
  3. package/dist/build-components/NavigationBarColor/NavigationBarColor.d.ts +5 -0
  4. package/dist/build-components/NavigationBarColor/NavigationBarColorProps.generated.d.ts +54 -0
  5. package/dist/build-components/OnboardImage/OnboardImageProps.generated.d.ts +1 -3
  6. package/dist/build-components/Separator/Separator.d.ts +5 -0
  7. package/dist/build-components/Separator/SeparatorProps.generated.d.ts +21 -0
  8. package/dist/build-components/StatusBarColor/StatusBarColor.d.ts +5 -0
  9. package/dist/build-components/StatusBarColor/StatusBarColorProps.generated.d.ts +54 -0
  10. package/dist/build-components/index.d.ts +4 -1
  11. package/dist/build-components/patterns.generated.d.ts +2105 -1253
  12. package/dist/components/AttributesEditorPanel.d.ts +1 -1
  13. package/dist/components/BuilderProvider.d.ts +1 -1
  14. package/dist/index.cjs.js +4 -4
  15. package/dist/index.cjs.js.map +1 -1
  16. package/dist/index.esm.js +4 -4
  17. package/dist/index.esm.js.map +1 -1
  18. package/dist/index.web.cjs.js +6 -6
  19. package/dist/index.web.cjs.js.map +1 -1
  20. package/dist/index.web.esm.js +4 -4
  21. package/dist/index.web.esm.js.map +1 -1
  22. package/dist/store.d.ts +4 -0
  23. package/dist/styles.css +1 -1
  24. package/dist/utils/attributeStyle.d.ts +9 -0
  25. package/dist/utils/extractImageStyle.d.ts +1 -1
  26. package/dist/utils/extractViewStyle/extractViewStyleNative.d.ts +1 -1
  27. package/package.json +2 -2
  28. package/src/DeviceMockFrame.tsx +8 -2
  29. package/src/assets/meta.json +1 -1
  30. package/src/assets/samples/paywall-1.json +39 -34
  31. package/src/assets/samples/paywall-2.json +39 -20
  32. package/src/assets/samples/paywall-app-delete-offer.json +40 -21
  33. package/src/assets/samples/paywall-app-open-offer.json +40 -21
  34. package/src/assets/samples/paywall-back-offer.json +40 -21
  35. package/src/assets/samples/paywall-notification-offer.json +40 -21
  36. package/src/assets/samples/vpn-onboard-1.json +84 -39
  37. package/src/assets/samples/vpn-onboard-2.json +85 -40
  38. package/src/assets/samples/vpn-onboard-3.json +84 -39
  39. package/src/assets/samples/vpn-onboard-4.json +84 -39
  40. package/src/assets/samples/vpn-onboard-5.json +102 -55
  41. package/src/assets/samples/vpn-onboard-6.json +87 -38
  42. package/src/attribute-analyser/style/native/useExtractImageStyle.ts +24 -22
  43. package/src/attribute-analyser/style/native/useExtractTextStyle.ts +9 -4
  44. package/src/attribute-analyser/style/native/useExtractViewStyle.ts +19 -7
  45. package/src/attributes-editor/useAttributesEditorModel.ts +23 -17
  46. package/src/build-components/BackgroundImage/pattern.json +9 -7
  47. package/src/build-components/CarouselDots/CarouselDots.tsx +12 -11
  48. package/src/build-components/CarouselProvider/CarouselProvider.tsx +3 -1
  49. package/src/build-components/Image/ImageProps.generated.ts +2 -4
  50. package/src/build-components/Image/pattern.json +12 -25
  51. package/src/build-components/NavigationBarColor/NavigationBarColor.tsx +39 -0
  52. package/src/build-components/NavigationBarColor/NavigationBarColorProps.generated.ts +71 -0
  53. package/src/build-components/NavigationBarColor/pattern.json +34 -0
  54. package/src/build-components/OnboardButtons/OnboardButtons.tsx +8 -10
  55. package/src/build-components/OnboardDot/OnboardDot.tsx +12 -10
  56. package/src/build-components/OnboardImage/OnboardImage.tsx +1 -1
  57. package/src/build-components/OnboardImage/OnboardImageProps.generated.ts +1 -3
  58. package/src/build-components/OnboardProvider/OnboardProvider.tsx +3 -1
  59. package/src/build-components/RenderNode.generated.tsx +15 -0
  60. package/src/build-components/Separator/Separator.tsx +41 -0
  61. package/src/build-components/Separator/SeparatorProps.generated.ts +26 -0
  62. package/src/build-components/Separator/pattern.json +59 -0
  63. package/src/build-components/StatusBarColor/StatusBarColor.tsx +39 -0
  64. package/src/build-components/StatusBarColor/StatusBarColorProps.generated.ts +71 -0
  65. package/src/build-components/StatusBarColor/pattern.json +34 -0
  66. package/src/build-components/Text/pattern.json +45 -38
  67. package/src/build-components/index.ts +15 -0
  68. package/src/build-components/patterns.generated.ts +2149 -1272
  69. package/src/build-components/useNode.ts +24 -25
  70. package/src/components/AttributesEditorPanel.tsx +4 -5
  71. package/src/components/Builder.tsx +1 -2
  72. package/src/components/BuilderProvider.tsx +40 -3
  73. package/src/components/JsonTextEditor.tsx +2 -2
  74. package/src/components/LoadingComponent.tsx +1 -1
  75. package/src/components/RenderErrorBoundary.tsx +1 -3
  76. package/src/migrations/migrations/1.1.2_extract_component_attributes_from_style.ts +3 -3
  77. package/src/modals/BenefitPresetsModal.tsx +1 -1
  78. package/src/modals/ProductPresetsModal.tsx +1 -1
  79. package/src/pages/DebugJsonPage.tsx +7 -4
  80. package/src/pages/ProjectDebug.tsx +1 -1
  81. package/src/pages/ProjectPage.tsx +31 -32
  82. package/src/pages/ProjectValidationPage.tsx +2 -2
  83. package/src/store.ts +13 -0
  84. package/src/styles/layout/_builder.scss +6 -0
  85. package/src/utils/__special_exceptions.ts +5 -5
  86. package/src/utils/analyseNode.ts +2 -2
  87. package/src/utils/analyseNodeByPatterns.ts +10 -9
  88. package/src/utils/analyseNodeStructural.ts +1 -1
  89. package/src/utils/attributeStyle.ts +26 -0
  90. package/src/utils/extractImageStyle.ts +17 -13
  91. package/src/utils/extractTextStyle/extractTextStyle.ts +7 -7
  92. package/src/utils/extractTextStyle/extractTextStyleNative.ts +10 -10
  93. package/src/utils/extractViewStyle/extractViewStyle.ts +8 -11
  94. package/src/utils/extractViewStyle/extractViewStyleNative.ts +19 -19
  95. package/src/utils/loadFontFamily.ts +14 -19
  96. package/src/utils/logRenderStore.ts +5 -4
  97. package/src/utils/nodeTree.ts +1 -1
  98. package/src/utils/patterns.ts +26 -31
  99. package/src/utils/repairNodeKeys.ts +5 -7
  100. package/src/utils/wrapNodeInMain.ts +3 -3
@@ -82,9 +82,34 @@
82
82
  "attributes": {
83
83
  "useSafeAreaView": true,
84
84
  "description": "Ekranın ana kapsayıcısı. (#1)",
85
- "title": "Main 1"
85
+ "title": "Main 1",
86
+ "styles": {
87
+ "paddingBottom": 16
88
+ }
86
89
  },
87
90
  "children": [
91
+ {
92
+ "type": "StatusBarColor",
93
+ "attributes": {
94
+ "title": "Status Bar Color",
95
+ "description": "Status bar background color.",
96
+ "styles": {
97
+ "backgroundColor": "THEME_COLORS.BACKGROUND"
98
+ }
99
+ },
100
+ "children": null
101
+ },
102
+ {
103
+ "type": "NavigationBarColor",
104
+ "attributes": {
105
+ "title": "Navigation Bar Color",
106
+ "description": "Navigation bar background color.",
107
+ "styles": {
108
+ "backgroundColor": "THEME_COLORS.BACKGROUND"
109
+ }
110
+ },
111
+ "children": null
112
+ },
88
113
  {
89
114
  "type": "OnboardProvider",
90
115
  "children": [
@@ -111,7 +136,7 @@
111
136
  "description": "Sayfa başlığı. (#1)",
112
137
  "title": "OnboardTitle 1",
113
138
  "styles": {
114
- "color": "#F4F5FF"
139
+ "color": "THEME_COLORS.ONBOARD_TITLE"
115
140
  }
116
141
  },
117
142
  "children": "onboard.title.one-page"
@@ -122,7 +147,7 @@
122
147
  "description": "Sayfa alt başlığı. (#1)",
123
148
  "title": "OnboardSubtitle 1",
124
149
  "styles": {
125
- "color": "#DBDDEB"
150
+ "color": "THEME_COLORS.ONBOARD_SUBTITLE"
126
151
  }
127
152
  },
128
153
  "children": "onboard.subtitle.one-page"
@@ -153,7 +178,7 @@
153
178
  "description": "Sayfa başlığı. (#2)",
154
179
  "title": "OnboardTitle 2",
155
180
  "styles": {
156
- "color": "#F4F5FF"
181
+ "color": "THEME_COLORS.ONBOARD_TITLE"
157
182
  }
158
183
  },
159
184
  "children": "onboard.title.two-page"
@@ -164,7 +189,7 @@
164
189
  "description": "Sayfa alt başlığı. (#2)",
165
190
  "title": "OnboardSubtitle 2",
166
191
  "styles": {
167
- "color": "#DBDDEB"
192
+ "color": "THEME_COLORS.ONBOARD_SUBTITLE"
168
193
  }
169
194
  },
170
195
  "children": "onboard.subtitle.two-page"
@@ -195,7 +220,7 @@
195
220
  "description": "Sayfa başlığı. (#3)",
196
221
  "title": "OnboardTitle 3",
197
222
  "styles": {
198
- "color": "#F4F5FF"
223
+ "color": "THEME_COLORS.ONBOARD_TITLE"
199
224
  }
200
225
  },
201
226
  "children": "onboard.title.three-page"
@@ -206,7 +231,7 @@
206
231
  "description": "Sayfa alt başlığı. (#3)",
207
232
  "title": "OnboardSubtitle 3",
208
233
  "styles": {
209
- "color": "#DBDDEB"
234
+ "color": "THEME_COLORS.ONBOARD_SUBTITLE"
210
235
  }
211
236
  },
212
237
  "children": "onboard.subtitle.three-page"
@@ -237,7 +262,7 @@
237
262
  "description": "Sayfa başlığı. (#4)",
238
263
  "title": "OnboardTitle 4",
239
264
  "styles": {
240
- "color": "#F4F5FF"
265
+ "color": "THEME_COLORS.ONBOARD_TITLE"
241
266
  }
242
267
  },
243
268
  "children": "onboard.title.four-page"
@@ -248,7 +273,7 @@
248
273
  "description": "Sayfa alt başlığı. (#4)",
249
274
  "title": "OnboardSubtitle 4",
250
275
  "styles": {
251
- "color": "#DBDDEB"
276
+ "color": "THEME_COLORS.ONBOARD_SUBTITLE"
252
277
  }
253
278
  },
254
279
  "children": "onboard.subtitle.four-page"
@@ -262,7 +287,10 @@
262
287
  ],
263
288
  "attributes": {
264
289
  "description": "Onboarding ana yapısı. (#1)",
265
- "title": "Onboard 1"
290
+ "title": "Onboard 1",
291
+ "styles": {
292
+ "flex": 1
293
+ }
266
294
  }
267
295
  },
268
296
  {
@@ -271,10 +299,22 @@
271
299
  "dotType": "expanding_dot",
272
300
  "dot_thickness": 20,
273
301
  "inactive_dot_opacity": 0.3,
274
- "inactive_dot_color": "#E4E5E7",
275
- "active_dot_color": "#007AFF",
302
+ "inactive_dot_color": "STATIC_COLORS.ONBOARD_DOT_INACTIVE",
303
+ "active_dot_color": "STATIC_COLORS.ONBOARD_DOT_ACTIVE",
276
304
  "styles": {
277
- "flex": 2
305
+ "paddingVertical": 12
306
+ }
307
+ }
308
+ },
309
+ {
310
+ "type": "Separator",
311
+ "attributes": {
312
+ "title": "Separator",
313
+ "description": "Horizontal separator line",
314
+ "styles": {
315
+ "width": "100%",
316
+ "height": 2,
317
+ "backgroundColor": "STATIC_COLORS.SEPARATOR_COLOR"
278
318
  }
279
319
  }
280
320
  },
@@ -287,8 +327,9 @@
287
327
  "description": "Sayfa buton grubu. (#1)",
288
328
  "title": "OnboardButtons 1",
289
329
  "styles": {
290
- "flex": 2,
291
- "justifyContent": "flex-end"
330
+ "height": 40,
331
+ "marginVertical": 12,
332
+ "flexShrink": 0
292
333
  }
293
334
  },
294
335
  "children": [
@@ -296,8 +337,8 @@
296
337
  "type": "OnboardButton",
297
338
  "attributes": {
298
339
  "labelKey": "onboard.skip.one-page",
299
- "button_text_color": "#0450E2",
300
- "button_background_color": "#161827",
340
+ "button_text_color": "THEME_COLORS.BUTTON_SECONDARY_TEXT",
341
+ "button_background_color": "THEME_COLORS.TEXT",
301
342
  "events": [
302
343
  {
303
344
  "type": "Navigate",
@@ -314,8 +355,8 @@
314
355
  "type": "OnboardButton",
315
356
  "attributes": {
316
357
  "labelKey": "onboard.next.one-page",
317
- "button_text_color": "#FFFFFF",
318
- "button_background_color": "#0450E2",
358
+ "button_text_color": "STATIC_COLORS.BUTTON_PRIMARY_TEXT",
359
+ "button_background_color": "STATIC_COLORS.BUTTON_PRIMARY_BACKGROUND",
319
360
  "events": [
320
361
  {
321
362
  "type": "Navigate",
@@ -339,8 +380,9 @@
339
380
  "description": "Sayfa buton grubu. (#2)",
340
381
  "title": "OnboardButtons 2",
341
382
  "styles": {
342
- "flex": 2,
343
- "justifyContent": "flex-end"
383
+ "height": 40,
384
+ "marginVertical": 12,
385
+ "flexShrink": 0
344
386
  }
345
387
  },
346
388
  "children": [
@@ -348,8 +390,8 @@
348
390
  "type": "OnboardButton",
349
391
  "attributes": {
350
392
  "labelKey": "onboard.skip.two-page",
351
- "button_text_color": "#0450E2",
352
- "button_background_color": "#161827",
393
+ "button_text_color": "THEME_COLORS.BUTTON_SECONDARY_TEXT",
394
+ "button_background_color": "THEME_COLORS.TEXT",
353
395
  "events": [
354
396
  {
355
397
  "type": "Navigate",
@@ -366,8 +408,8 @@
366
408
  "type": "OnboardButton",
367
409
  "attributes": {
368
410
  "labelKey": "onboard.next.two-page",
369
- "button_text_color": "#FFFFFF",
370
- "button_background_color": "#0450E2",
411
+ "button_text_color": "STATIC_COLORS.BUTTON_PRIMARY_TEXT",
412
+ "button_background_color": "STATIC_COLORS.BUTTON_PRIMARY_BACKGROUND",
371
413
  "events": [
372
414
  {
373
415
  "type": "Navigate",
@@ -391,8 +433,9 @@
391
433
  "description": "Sayfa buton grubu. (#3)",
392
434
  "title": "OnboardButtons 3",
393
435
  "styles": {
394
- "flex": 2,
395
- "justifyContent": "flex-end"
436
+ "height": 40,
437
+ "marginVertical": 12,
438
+ "flexShrink": 0
396
439
  }
397
440
  },
398
441
  "children": [
@@ -400,8 +443,8 @@
400
443
  "type": "OnboardButton",
401
444
  "attributes": {
402
445
  "labelKey": "onboard.skip.three-page",
403
- "button_text_color": "#0450E2",
404
- "button_background_color": "#161827",
446
+ "button_text_color": "THEME_COLORS.BUTTON_SECONDARY_TEXT",
447
+ "button_background_color": "THEME_COLORS.TEXT",
405
448
  "events": [
406
449
  {
407
450
  "type": "Navigate",
@@ -418,8 +461,8 @@
418
461
  "type": "OnboardButton",
419
462
  "attributes": {
420
463
  "labelKey": "onboard.next.three-page",
421
- "button_text_color": "#FFFFFF",
422
- "button_background_color": "#0450E2",
464
+ "button_text_color": "STATIC_COLORS.BUTTON_PRIMARY_TEXT",
465
+ "button_background_color": "STATIC_COLORS.BUTTON_PRIMARY_BACKGROUND",
423
466
  "events": [
424
467
  {
425
468
  "type": "Permission",
@@ -445,15 +488,20 @@
445
488
  "condition": "carousel-index",
446
489
  "conditionVariable": 3,
447
490
  "description": "Sayfa buton grubu. (#4)",
448
- "title": "OnboardButtons 4"
491
+ "title": "OnboardButtons 4",
492
+ "styles": {
493
+ "height": 40,
494
+ "marginVertical": 12,
495
+ "flexShrink": 0
496
+ }
449
497
  },
450
498
  "children": [
451
499
  {
452
500
  "type": "OnboardButton",
453
501
  "attributes": {
454
502
  "labelKey": "onboard.allow.four-page",
455
- "button_text_color": "#FFFFFF",
456
- "button_background_color": "#0450E2",
503
+ "button_text_color": "STATIC_COLORS.BUTTON_PRIMARY_TEXT",
504
+ "button_background_color": "STATIC_COLORS.BUTTON_PRIMARY_BACKGROUND",
457
505
  "events": [
458
506
  {
459
507
  "type": "Permission",
@@ -477,7 +525,8 @@
477
525
  "title": "Onboard Footer Wrap",
478
526
  "description": "Wrapper for OnboardFooter component",
479
527
  "styles": {
480
- "flex": 2
528
+ "marginHorizontal": 25,
529
+ "flexShrink": 0
481
530
  }
482
531
  },
483
532
  "children": [
@@ -486,14 +535,14 @@
486
535
  "attributes": {
487
536
  "textLocalizationKey": "view.onboarding.footer.description",
488
537
  "linkedWordFirstLocalizationKey": "view.onboarding.btnPrivacy",
489
- "linkedWordFirstColor": "#0450E2",
538
+ "linkedWordFirstColor": "STATIC_COLORS.LINK_COLOR",
490
539
  "linkedWordFirstPage": "privacy",
491
540
  "linkedWordSecondLocalizationKey": "view.onboarding.btnTerms",
492
- "linkedWordSecondColor": "#0450E2",
541
+ "linkedWordSecondColor": "STATIC_COLORS.LINK_COLOR",
493
542
  "linkedWordSecondPage": "terms",
494
543
  "styles": {
495
544
  "gap": 8,
496
- "color": "#81838F"
545
+ "color": "THEME_COLORS.FOOTER_TEXT"
497
546
  }
498
547
  }
499
548
  }
@@ -1,9 +1,13 @@
1
1
  import { useMemo } from 'react';
2
2
  import type { NodeData } from '../../../types/Node';
3
- import type { ImagePropsGenerated } from '../../../build-components/Image/ImageProps.generated';
3
+ import type {
4
+ ImagePropsGenerated,
5
+ ResizeModeOptionType,
6
+ } from '../../../build-components/Image/ImageProps.generated';
4
7
  import { useBuilderParams } from '../../../components/BuilderProvider';
5
8
  import { extractImageStyleNative } from '../../../utils/extractImageStyle';
6
9
  import { defaultAppConfig } from '../../../types/PreviewConfig';
10
+ import { getStyleBag } from '../../../utils/attributeStyle';
7
11
 
8
12
  export function useExtractImageStyle<
9
13
  T extends ImagePropsGenerated['attributes'],
@@ -13,33 +17,31 @@ export function useExtractImageStyle<
13
17
  const projectColors = builderProjectColors;
14
18
 
15
19
  return useMemo(() => {
16
- // extractImageStyleNative includes `resizeMode` in the returned object, but in RN
17
- // resizeMode is typically passed as an Image prop. We normalize to `{ style, other }`.
18
- const styleWithResizeMode = extractImageStyleNative(node, {
19
- theme,
20
- projectColors,
21
- });
20
+ // extractImageStyleNative returns Record<string, unknown> that may include resizeMode.
21
+ // In RN, resizeMode is typically an Image prop, not a style property.
22
22
  const { resizeMode: resizeModeFromStyle, ...style } =
23
- (styleWithResizeMode as unknown as Record<string, unknown>) ?? {};
23
+ extractImageStyleNative(node, { theme, projectColors });
24
24
 
25
- const attrs = node.attributes as unknown as Record<string, unknown>;
26
- const styleBag =
27
- (attrs?.style as Record<string, unknown> | undefined) ?? undefined;
28
- const { style: _style, ...rest } = attrs ?? {};
29
- void _style;
25
+ const attrs = node.attributes;
26
+
27
+ // Prefer the typed resizeMode from attributes, fall back to extracted style value.
28
+ const imgStylesBag = getStyleBag(attrs);
29
+ const resizeMode = ((imgStylesBag?.resizeMode as string | undefined) ??
30
+ resizeModeFromStyle) as ResizeModeOptionType | undefined;
31
+
32
+ // Forward all non-style attributes; style bag is already consumed above.
33
+ //Optimzation trade off by readability: fromEntries+filter avoids generic-to-Record double assertion.
34
+ const forwardedAttrs = Object.fromEntries(
35
+ Object.entries(attrs ?? {}).filter(
36
+ ([key]) => key !== 'style' && key !== 'styles',
37
+ ),
38
+ );
30
39
 
31
40
  return {
32
41
  style,
33
42
  other: {
34
- ...rest,
35
- // In RN, resizeMode is commonly an Image prop (not style). We expose it as "other".
36
- resizeMode: (resizeModeFromStyle ??
37
- (styleBag?.resizeMode as unknown)) as
38
- | 'cover'
39
- | 'contain'
40
- | 'stretch'
41
- | 'center'
42
- | undefined,
43
+ ...forwardedAttrs,
44
+ resizeMode,
43
45
  },
44
46
  };
45
47
  }, [node, theme, projectColors]);
@@ -4,6 +4,7 @@ import type { TextPropsGenerated } from '../../../build-components/Text/TextProp
4
4
  import { defaultAppConfig } from '../../../types/PreviewConfig';
5
5
  import { useBuilderParams } from '../../../components/BuilderProvider';
6
6
  import { extractTextStyleNative } from '../../../utils/extractTextStyle';
7
+ import { getStyleBag } from '../../../utils/attributeStyle';
7
8
 
8
9
  export function useExtractTextStyle<T extends TextPropsGenerated['attributes']>(
9
10
  node: NodeData<T>,
@@ -26,11 +27,15 @@ export function useExtractTextStyle<T extends TextPropsGenerated['attributes']>(
26
27
  fonts,
27
28
  });
28
29
 
29
- const attrs = node.attributes as unknown as Record<string, unknown>;
30
- const styleBag =
31
- (attrs?.style as Record<string, unknown> | undefined) ?? undefined;
32
- const { style: _style, ...rest } = attrs ?? {};
30
+ const attrs = node.attributes ?? {};
31
+ const styleBag = getStyleBag(attrs);
32
+ const {
33
+ style: _style,
34
+ styles: _styles,
35
+ ...rest
36
+ } = attrs as Record<string, unknown>;
33
37
  void _style;
38
+ void _styles;
34
39
 
35
40
  return {
36
41
  style,
@@ -4,6 +4,7 @@ import type { ViewPropsGenerated } from '../../../build-components/View/ViewProp
4
4
  import { useBuilderParams } from '../../../components/BuilderProvider';
5
5
  import { extractViewStyleNative } from '../../../utils/extractViewStyle';
6
6
  import { defaultAppConfig } from '../../../types/PreviewConfig';
7
+ import { getStyleBag, toAttributeRecord } from '../../../utils/attributeStyle';
7
8
 
8
9
  export function useExtractViewStyle<T extends ViewPropsGenerated['attributes']>(
9
10
  node: NodeData<T>,
@@ -15,17 +16,28 @@ export function useExtractViewStyle<T extends ViewPropsGenerated['attributes']>(
15
16
  return useMemo(() => {
16
17
  const style = extractViewStyleNative(node, { theme, projectColors });
17
18
 
18
- const attrs = node.attributes as unknown as Record<string, unknown>;
19
- const styleBag =
20
- (attrs?.style as Record<string, unknown> | undefined) ?? undefined;
21
- const { style: _style, ...rest } = attrs ?? {};
22
- void _style;
19
+ const attrs = node.attributes;
20
+
21
+ // Forward all non-style attributes; style bag is already consumed by extractViewStyleNative.
22
+ //Optimzation trade off by readability: fromEntries+filter avoids generic-to-Record double assertion.
23
+ const forwardedAttrs = Object.fromEntries(
24
+ Object.entries(attrs ?? {}).filter(
25
+ ([key]) => key !== 'style' && key !== 'styles',
26
+ ),
27
+ );
28
+
29
+ // scrollable may reside inside the styles bag at runtime (not on ViewStyleGenerated).
30
+ const attrRecord = toAttributeRecord(attrs);
31
+ const styleBag = getStyleBag(attrs);
32
+ const scrollable = (attrRecord.scrollable ?? styleBag?.scrollable) as
33
+ | boolean
34
+ | undefined;
23
35
 
24
36
  return {
25
37
  style,
26
38
  other: {
27
- ...rest,
28
- scrollable: styleBag?.scrollable as boolean | undefined,
39
+ ...forwardedAttrs,
40
+ scrollable,
29
41
  },
30
42
  };
31
43
  }, [node, theme, projectColors]);
@@ -32,7 +32,9 @@ function isPlainObject(value: unknown): value is Record<string, unknown> {
32
32
  return typeof value === 'object' && value !== null && !Array.isArray(value);
33
33
  }
34
34
 
35
- function buildProjectColorsFallback(appConfig: any): ProjectColors | undefined {
35
+ function buildProjectColorsFallback(
36
+ appConfig: Record<string, unknown> | undefined | null,
37
+ ): ProjectColors | undefined {
36
38
  const addColor = (collection: Set<string>, value?: string) => {
37
39
  if (typeof value !== 'string') return;
38
40
  const trimmed = value.trim();
@@ -41,10 +43,17 @@ function buildProjectColorsFallback(appConfig: any): ProjectColors | undefined {
41
43
  };
42
44
 
43
45
  const fallback = new Set<string>();
44
- const styles = [
45
- appConfig?.screenStyle?.light,
46
- appConfig?.screenStyle?.dark,
47
- ].filter(Boolean) as Array<{
46
+ const screenStyle = (
47
+ appConfig as {
48
+ screenStyle?: {
49
+ light?: Record<string, string>;
50
+ dark?: Record<string, string>;
51
+ };
52
+ }
53
+ )?.screenStyle;
54
+ const styles = [screenStyle?.light, screenStyle?.dark].filter(
55
+ Boolean,
56
+ ) as Array<{
48
57
  backgroundColor?: string;
49
58
  color?: string;
50
59
  seperatorColor?: string;
@@ -74,12 +83,8 @@ export function useAttributesEditorModel({
74
83
  projectColors,
75
84
  }: AttributesEditorProps): AttributesEditorModel {
76
85
  const isInvalidNode = !node || isNodeString(node);
77
- const baseData = isInvalidNode
78
- ? ({
79
- type: 'View',
80
- children: null,
81
- attributes: {},
82
- } as unknown as NodeData<NodeDefaultAttribute>)
86
+ const baseData: NodeData<NodeDefaultAttribute> = isInvalidNode
87
+ ? { type: 'View', children: null, attributes: {} }
83
88
  : (node as NodeData<NodeDefaultAttribute>);
84
89
 
85
90
  const data = useNode(baseData);
@@ -152,7 +157,7 @@ export function useAttributesEditorModel({
152
157
  'Legacy/unmigrated attributes detected. This editor expects schemaVersion=2 (style nested under attributes.styles). Please run migrations.',
153
158
  {
154
159
  componentType: data?.type,
155
- nodeKey: (baseData as any)?.key,
160
+ nodeKey: baseData.key,
156
161
  legacyFlatStyleKeys,
157
162
  hasInvalidStyleBag,
158
163
  styleBagSource: styleBagInfo.source,
@@ -178,7 +183,9 @@ export function useAttributesEditorModel({
178
183
 
179
184
  const projectColorsForPicker = useMemo<ProjectColors | undefined>(() => {
180
185
  if (projectColors) return projectColors;
181
- return buildProjectColorsFallback(appConfig);
186
+ return buildProjectColorsFallback(
187
+ appConfig as unknown as Record<string, unknown>,
188
+ );
182
189
  }, [appConfig, projectColors]);
183
190
 
184
191
  const viewAttributes = useMemo<
@@ -400,11 +407,10 @@ export function useAttributesEditorModel({
400
407
  : {}
401
408
  ) as Record<string, unknown>;
402
409
  const nextStyle = { ...prevStyle, [name]: val };
403
- // Keep both keys in sync:
404
- // - `styles` is the canonical store for the editor (latest migration).
405
- // - `style` is kept for back-compat because build-components currently read `attributes.style`.
410
+ // `styles` is the canonical store (schemaVersion=2).
406
411
  nextAttrs.styles = nextStyle;
407
- nextAttrs.style = nextStyle;
412
+ // Remove legacy `style` key if present so the validator doesn't reject it.
413
+ delete nextAttrs.style;
408
414
  if (name in nextAttrs) delete nextAttrs[name];
409
415
  } else {
410
416
  nextAttrs[name] = val;
@@ -27,13 +27,6 @@
27
27
  "desiredParent": ["all", "background"],
28
28
  "label": "Background Image",
29
29
  "description": "Background image.",
30
- "src": {
31
- "label": "Src",
32
- "description": "Image source URL.",
33
- "category": "other",
34
- "specialCategory": null,
35
- "sort": 1
36
- },
37
30
  "styles": {
38
31
  "resizeMode": {
39
32
  "label": "Resize Mode",
@@ -42,6 +35,15 @@
42
35
  "specialCategory": null,
43
36
  "sort": 4
44
37
  }
38
+ },
39
+ "attributes": {
40
+ "src": {
41
+ "label": "Src",
42
+ "description": "Image source URL.",
43
+ "category": "other",
44
+ "specialCategory": null,
45
+ "sort": 1
46
+ }
45
47
  }
46
48
  },
47
49
  "defaults": {
@@ -10,6 +10,7 @@ import { parseColor } from '../../utils/parseColor';
10
10
  import { parseSize } from '../../size-matters';
11
11
  import { useBuilderParams } from '../../components/BuilderProvider';
12
12
  import { defaultAppConfig } from '../../types/PreviewConfig';
13
+ import { getStyleBag, toAttributeRecord } from '../../utils/attributeStyle';
13
14
 
14
15
  function CarouselDots({ node }: CarouselDotsComponentProps) {
15
16
  useLogRender('CarouselDots');
@@ -20,13 +21,12 @@ function CarouselDots({ node }: CarouselDotsComponentProps) {
20
21
  const attributeName = node.sourceType ?? node.type ?? 'carouselDots';
21
22
  const attributeKey = node.key ?? generatedId;
22
23
 
23
- const attrsAny = node.attributes as any;
24
- const stylesBag =
25
- (attrsAny?.styles as Record<string, unknown> | undefined) ??
26
- (attrsAny?.style as Record<string, unknown> | undefined) ??
27
- undefined;
24
+ const attrRecord = toAttributeRecord(node.attributes);
25
+ const stylesBag = getStyleBag(node.attributes);
28
26
  const dotType =
29
- (stylesBag?.dotType as any) ?? (attrsAny?.dotType as any) ?? 'normal_dot';
27
+ (stylesBag?.dotType as string | undefined) ??
28
+ (attrRecord.dotType as string | undefined) ??
29
+ 'normal_dot';
30
30
  const style = useExtractViewStyle(node);
31
31
 
32
32
  // Use the appropriate context based on sourceType
@@ -40,16 +40,17 @@ function CarouselDots({ node }: CarouselDotsComponentProps) {
40
40
  const GHOST_DOT_LIGHT_COLOR = '#F7F7F9';
41
41
  const inactiveDotOpacity =
42
42
  (stylesBag?.inactive_dot_opacity as number | undefined) ??
43
- attrsAny?.inactive_dot_opacity ??
43
+ (attrRecord.inactive_dot_opacity as number | undefined) ??
44
44
  0.3;
45
45
  const inactiveDotColorOverride =
46
46
  (stylesBag?.inactive_dot_color as string | undefined) ??
47
- attrsAny?.inactive_dot_color;
47
+ (attrRecord.inactive_dot_color as string | undefined);
48
48
  const activeDotColor =
49
49
  (stylesBag?.active_dot_color as string | undefined) ??
50
- attrsAny?.active_dot_color;
50
+ (attrRecord.active_dot_color as string | undefined);
51
51
  const dotThicknessRaw =
52
- (stylesBag?.dot_thickness as any) ?? attrsAny?.dot_thickness;
52
+ (stylesBag?.dot_thickness as string | number | undefined) ??
53
+ (attrRecord.dot_thickness as string | number | undefined);
53
54
 
54
55
  const { appConfig: builderAppConfig, projectColors } = useBuilderParams();
55
56
  const appConfig = builderAppConfig ?? defaultAppConfig;
@@ -92,7 +93,7 @@ function CarouselDots({ node }: CarouselDotsComponentProps) {
92
93
  return `${Math.max(0, n / 3)}px`;
93
94
  }, [dotSizeCss]);
94
95
 
95
- const gapValue = (style as any)?.gap ?? dotGapCss;
96
+ const gapValue = style.gap ?? dotGapCss;
96
97
  const containerStyle = useMergedStyle(style, {
97
98
  display: 'flex',
98
99
  flexWrap: 'wrap',
@@ -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