@developer_tribe/react-builder 1.2.22 → 1.2.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/dist/attribute-analyser/style/native/useExtractImageStyle.d.ts +6 -6
  2. package/dist/attribute-analyser/style/native/useExtractTextStyle.d.ts +6 -4
  3. package/dist/attribute-analyser/style/native/useExtractViewStyle.d.ts +5 -3
  4. package/dist/build-components/Image/ImageProps.generated.d.ts +2 -4
  5. package/dist/build-components/NavigationBarColor/NavigationBarColor.d.ts +5 -0
  6. package/dist/build-components/NavigationBarColor/NavigationBarColorProps.generated.d.ts +54 -0
  7. package/dist/build-components/OnboardImage/OnboardImageProps.generated.d.ts +1 -3
  8. package/dist/build-components/Separator/Separator.d.ts +5 -0
  9. package/dist/build-components/Separator/SeparatorProps.generated.d.ts +21 -0
  10. package/dist/build-components/StatusBarColor/StatusBarColor.d.ts +5 -0
  11. package/dist/build-components/StatusBarColor/StatusBarColorProps.generated.d.ts +54 -0
  12. package/dist/build-components/index.d.ts +4 -1
  13. package/dist/build-components/patterns.generated.d.ts +2111 -1251
  14. package/dist/components/AttributesEditorPanel.d.ts +1 -1
  15. package/dist/components/BuilderProvider.d.ts +1 -1
  16. package/dist/index.cjs.js +4 -4
  17. package/dist/index.cjs.js.map +1 -1
  18. package/dist/index.d.ts +2 -0
  19. package/dist/index.esm.js +4 -4
  20. package/dist/index.esm.js.map +1 -1
  21. package/dist/index.web.cjs.js +6 -6
  22. package/dist/index.web.cjs.js.map +1 -1
  23. package/dist/index.web.esm.js +4 -4
  24. package/dist/index.web.esm.js.map +1 -1
  25. package/dist/store.d.ts +4 -0
  26. package/dist/styles.css +1 -1
  27. package/dist/utils/attributeStyle.d.ts +21 -0
  28. package/dist/utils/extractImageStyle.d.ts +1 -1
  29. package/dist/utils/extractViewStyle/extractViewStyleNative.d.ts +1 -1
  30. package/package.json +7 -2
  31. package/src/DeviceMockFrame.tsx +8 -2
  32. package/src/assets/meta.json +1 -1
  33. package/src/assets/samples/paywall-1.json +44 -39
  34. package/src/assets/samples/paywall-2.json +44 -25
  35. package/src/assets/samples/paywall-app-delete-offer.json +40 -21
  36. package/src/assets/samples/paywall-app-open-offer.json +40 -21
  37. package/src/assets/samples/paywall-back-offer.json +40 -21
  38. package/src/assets/samples/paywall-notification-offer.json +40 -21
  39. package/src/assets/samples/vpn-onboard-1.json +84 -39
  40. package/src/assets/samples/vpn-onboard-2.json +85 -40
  41. package/src/assets/samples/vpn-onboard-3.json +84 -39
  42. package/src/assets/samples/vpn-onboard-4.json +84 -39
  43. package/src/assets/samples/vpn-onboard-5.json +102 -55
  44. package/src/assets/samples/vpn-onboard-6.json +87 -38
  45. package/src/attribute-analyser/style/native/useExtractImageStyle.ts +31 -25
  46. package/src/attribute-analyser/style/native/useExtractTextStyle.ts +26 -11
  47. package/src/attribute-analyser/style/native/useExtractViewStyle.ts +21 -11
  48. package/src/attributes-editor/useAttributesEditorModel.ts +23 -17
  49. package/src/build-components/BackgroundImage/pattern.json +9 -7
  50. package/src/build-components/CarouselDots/CarouselDots.tsx +12 -11
  51. package/src/build-components/CarouselProvider/CarouselProvider.tsx +3 -1
  52. package/src/build-components/Image/ImageProps.generated.ts +2 -4
  53. package/src/build-components/Image/pattern.json +15 -25
  54. package/src/build-components/NavigationBarColor/NavigationBarColor.tsx +39 -0
  55. package/src/build-components/NavigationBarColor/NavigationBarColorProps.generated.ts +71 -0
  56. package/src/build-components/NavigationBarColor/pattern.json +34 -0
  57. package/src/build-components/OnboardButton/OnboardButton.tsx +19 -5
  58. package/src/build-components/OnboardButtons/OnboardButtons.tsx +8 -10
  59. package/src/build-components/OnboardDot/OnboardDot.tsx +12 -10
  60. package/src/build-components/OnboardFooter/OnboardFooter.tsx +15 -4
  61. package/src/build-components/OnboardImage/OnboardImage.tsx +1 -1
  62. package/src/build-components/OnboardImage/OnboardImageProps.generated.ts +1 -3
  63. package/src/build-components/OnboardProvider/OnboardProvider.tsx +3 -1
  64. package/src/build-components/RenderNode.generated.tsx +15 -0
  65. package/src/build-components/Separator/Separator.tsx +41 -0
  66. package/src/build-components/Separator/SeparatorProps.generated.ts +26 -0
  67. package/src/build-components/Separator/pattern.json +59 -0
  68. package/src/build-components/StatusBarColor/StatusBarColor.tsx +39 -0
  69. package/src/build-components/StatusBarColor/StatusBarColorProps.generated.ts +71 -0
  70. package/src/build-components/StatusBarColor/pattern.json +34 -0
  71. package/src/build-components/Text/pattern.json +45 -38
  72. package/src/build-components/index.ts +15 -0
  73. package/src/build-components/patterns.generated.ts +2153 -1272
  74. package/src/build-components/useNode.ts +24 -25
  75. package/src/components/AttributesEditorPanel.tsx +4 -5
  76. package/src/components/Builder.tsx +1 -2
  77. package/src/components/BuilderProvider.tsx +43 -6
  78. package/src/components/JsonTextEditor.tsx +2 -2
  79. package/src/components/LoadingComponent.tsx +1 -1
  80. package/src/components/RenderErrorBoundary.tsx +1 -3
  81. package/src/index.ts +3 -0
  82. package/src/migrations/migrations/1.1.2_extract_component_attributes_from_style.ts +3 -3
  83. package/src/modals/BenefitPresetsModal.tsx +1 -1
  84. package/src/modals/ProductPresetsModal.tsx +1 -1
  85. package/src/pages/DebugJsonPage.tsx +7 -4
  86. package/src/pages/ProjectDebug.tsx +1 -1
  87. package/src/pages/ProjectPage.tsx +31 -32
  88. package/src/pages/ProjectValidationPage.tsx +2 -2
  89. package/src/store.ts +13 -0
  90. package/src/styles/layout/_builder.scss +6 -0
  91. package/src/utils/__special_exceptions.ts +5 -5
  92. package/src/utils/analyseNode.ts +2 -2
  93. package/src/utils/analyseNodeByPatterns.ts +10 -9
  94. package/src/utils/analyseNodeStructural.ts +1 -1
  95. package/src/utils/attributeStyle.ts +104 -0
  96. package/src/utils/extractImageStyle.ts +17 -13
  97. package/src/utils/extractTextStyle/extractTextStyle.ts +7 -7
  98. package/src/utils/extractTextStyle/extractTextStyleNative.ts +10 -10
  99. package/src/utils/extractViewStyle/extractViewStyle.ts +8 -11
  100. package/src/utils/extractViewStyle/extractViewStyleNative.ts +19 -19
  101. package/src/utils/loadFontFamily.ts +14 -19
  102. package/src/utils/logRenderStore.ts +5 -4
  103. package/src/utils/nodeTree.ts +1 -1
  104. package/src/utils/patterns.ts +26 -31
  105. package/src/utils/repairNodeKeys.ts +5 -7
  106. package/src/utils/wrapNodeInMain.ts +3 -3
@@ -70,7 +70,7 @@ function isNodeDataLike(
70
70
  value: unknown,
71
71
  ): value is NodeData<NodeDefaultAttribute> {
72
72
  if (!isPlainObject(value)) return false;
73
- const maybeType = (value as Record<string, unknown>).type;
73
+ const maybeType = value.type;
74
74
  // `children` is optional in persisted JSON; treat missing as `undefined`.
75
75
  // The structural validator handles overall shape; the pattern validator should
76
76
  // focus on types/attributes/children rules, not require the key to exist.
@@ -316,9 +316,10 @@ function validateCustomObjectValue(
316
316
  return fail(`Expected object for type "${typeName}"`, path);
317
317
  }
318
318
 
319
+ const valueRecord = value as Record<string, unknown>;
319
320
  for (const [fieldName, fieldSpec] of Object.entries(schema)) {
320
- if (!(fieldName in value)) continue;
321
- const fieldValue = (value as Record<string, unknown>)[fieldName];
321
+ if (!(fieldName in valueRecord)) continue;
322
+ const fieldValue = valueRecord[fieldName];
322
323
  const fieldPath = joinPath(path, fieldName);
323
324
  const res = validateAttributeValue(
324
325
  componentType,
@@ -387,8 +388,9 @@ function validateAttributesByPattern(
387
388
  {}) as AttributeSchema;
388
389
  const styleSchema = getStyleSubSchema(schema);
389
390
 
390
- const maybeStyle = (attrs as Record<string, unknown>).style;
391
- const maybeStyles = (attrs as Record<string, unknown>).styles;
391
+ const attrsRecord = attrs as Record<string, unknown>;
392
+ const maybeStyle = attrsRecord.style;
393
+ const maybeStyles = attrsRecord.styles;
392
394
 
393
395
  // schemaVersion=2 requires `attributes.styles` (plural), not `attributes.style` (singular)
394
396
  if (maybeStyle != null) {
@@ -403,9 +405,8 @@ function validateAttributesByPattern(
403
405
  if (!isPlainObject(maybeStyles)) {
404
406
  return fail(`styles must be an object`, joinPath(path, 'styles'));
405
407
  }
406
- for (const [styleKey, styleValue] of Object.entries(
407
- maybeStyles as Record<string, unknown>,
408
- )) {
408
+ const stylesRecord = maybeStyles as Record<string, unknown>;
409
+ for (const [styleKey, styleValue] of Object.entries(stylesRecord)) {
409
410
  const spec = (styleSchema?.[styleKey] ?? schema?.[styleKey]) as
410
411
  | AttributeTypeSpec
411
412
  | undefined;
@@ -510,7 +511,7 @@ function validateAnyNodeByPatterns(
510
511
  }
511
512
 
512
513
  if (isNodeArray(node)) {
513
- const arr = node as unknown as Node<NodeDefaultAttribute>[];
514
+ const arr = node as Node<NodeDefaultAttribute>[];
514
515
  for (let i = 0; i < arr.length; i++) {
515
516
  const res = validateAnyNodeByPatterns(arr[i], `${path}[${i}]`);
516
517
  if (!res.valid) return res;
@@ -28,7 +28,7 @@ export function analyseNodeStructural(
28
28
  }
29
29
 
30
30
  if (isNodeArray(node)) {
31
- const nodeArray = node as unknown as Node[];
31
+ const nodeArray = node as Node[];
32
32
  for (const value of nodeArray) {
33
33
  const res = analyseNodeStructural(value as Node<NodeDefaultAttribute>);
34
34
  if (!res.valid) return res;
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Shared utilities for attribute/style bag access.
3
+ * schemaVersion=2 uses `attributes.styles`, legacy uses `attributes.style`.
4
+ */
5
+
6
+ import type { NodeDefaultAttribute } from '../types/Node';
7
+
8
+ /** Returns styles or style bag from attributes (schemaVersion=2 uses styles, legacy uses style). */
9
+ export function getStyleBag(
10
+ attributes: NodeDefaultAttribute | undefined,
11
+ ): Record<string, unknown> | undefined {
12
+ const record = toAttributeRecord(attributes);
13
+ const bag = record.styles ?? record.style;
14
+ return isPlainObject(bag) ? (bag as Record<string, unknown>) : undefined;
15
+ }
16
+
17
+ /** Safe indexed access to attributes. Use for reading style/direct props. */
18
+ export function toAttributeRecord(
19
+ attributes: unknown,
20
+ ): Record<string, unknown> {
21
+ return (attributes ?? {}) as Record<string, unknown>;
22
+ }
23
+
24
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
25
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
26
+ }
27
+
28
+ // ---------------------------------------------------------------------------
29
+ // Style-key filtering – separates visual style keys from non-style attributes
30
+ // ---------------------------------------------------------------------------
31
+
32
+ /**
33
+ * All attribute keys that represent visual style properties.
34
+ * Used to separate style keys from non-style (behavioral/content) keys.
35
+ *
36
+ * Keep in sync with ViewStyleGenerated, TextStyleGenerated, and ImageStyleGenerated.
37
+ */
38
+ const STYLE_ATTR_KEYS_LIST = [
39
+ // Style bag containers
40
+ 'style',
41
+ 'styles',
42
+ // Layout
43
+ 'flexDirection',
44
+ 'flexWrap',
45
+ 'alignItems',
46
+ 'justifyContent',
47
+ 'gap',
48
+ // Padding
49
+ 'padding',
50
+ 'paddingHorizontal',
51
+ 'paddingVertical',
52
+ 'paddingTop',
53
+ 'paddingBottom',
54
+ 'paddingLeft',
55
+ 'paddingRight',
56
+ // Margin
57
+ 'margin',
58
+ 'marginHorizontal',
59
+ 'marginVertical',
60
+ 'marginTop',
61
+ 'marginBottom',
62
+ 'marginLeft',
63
+ 'marginRight',
64
+ // Background & border
65
+ 'backgroundColor',
66
+ 'borderRadius',
67
+ // Sizing
68
+ 'width',
69
+ 'minWidth',
70
+ 'maxWidth',
71
+ 'height',
72
+ 'minHeight',
73
+ 'maxHeight',
74
+ // Flex & position
75
+ 'flex',
76
+ 'position',
77
+ 'top',
78
+ 'bottom',
79
+ 'left',
80
+ 'right',
81
+ 'zIndex',
82
+ // Text
83
+ 'color',
84
+ 'fontSize',
85
+ 'fontFamily',
86
+ 'fontWeight',
87
+ 'textAlign',
88
+ // Image
89
+ 'resizeMode',
90
+ ] as const;
91
+
92
+ /** Type-level union of all style attribute keys. Use with `Omit<T, StyleAttrKey>`. */
93
+ export type StyleAttrKey = (typeof STYLE_ATTR_KEYS_LIST)[number];
94
+
95
+ const STYLE_ATTR_KEYS: ReadonlySet<string> = new Set(STYLE_ATTR_KEYS_LIST);
96
+
97
+ /** Strips all visual-style keys from an attributes record, returning only non-style keys. */
98
+ export function stripStyleKeys(
99
+ attrs: Record<string, unknown>,
100
+ ): Record<string, unknown> {
101
+ return Object.fromEntries(
102
+ Object.entries(attrs).filter(([key]) => !STYLE_ATTR_KEYS.has(key)),
103
+ );
104
+ }
@@ -1,26 +1,29 @@
1
- import { ImagePropsGenerated } from '../build-components/Image/ImageProps.generated';
1
+ import {
2
+ type ImagePropsGenerated,
3
+ type ResizeModeOptionType,
4
+ } from '../build-components/Image/ImageProps.generated';
2
5
  import type { NodeData } from '../types/Node';
3
6
  import {
4
7
  extractViewStyle,
5
8
  extractViewStyleNative,
6
9
  ExtractViewStyleOptions,
7
10
  } from './extractViewStyle';
11
+ import { getStyleBag } from './attributeStyle';
8
12
 
9
13
  export function extractImageStyle<T extends ImagePropsGenerated['attributes']>(
10
14
  node: NodeData<T>,
11
15
  options: ExtractViewStyleOptions = {},
12
16
  ) {
13
17
  const attributes = node.attributes;
14
- const styleBag = (attributes as any)?.style as
15
- | Record<string, unknown>
16
- | undefined;
17
18
  const style: React.CSSProperties = {};
18
19
 
19
20
  if (!attributes) return style;
20
21
 
21
- // Map resizeMode to CSS object-fit
22
- const resizeMode = ((attributes as any)?.resizeMode ??
23
- styleBag?.resizeMode) as any;
22
+ // Map resizeMode to CSS object-fit (schemaVersion=2 uses `styles`, fallback to legacy `style`)
23
+ const stylesBag = getStyleBag(attributes) as
24
+ | ImagePropsGenerated['attributes']['style']
25
+ | undefined;
26
+ const resizeMode: ResizeModeOptionType | undefined = stylesBag?.resizeMode;
24
27
  if (resizeMode === 'cover') style.objectFit = 'cover';
25
28
  else if (resizeMode === 'contain') style.objectFit = 'contain';
26
29
  else if (resizeMode === 'stretch') style.objectFit = 'fill';
@@ -33,18 +36,19 @@ export function extractImageStyleNative<
33
36
  T extends ImagePropsGenerated['attributes'],
34
37
  >(node: NodeData<T>, options: ExtractViewStyleOptions = {}) {
35
38
  const attributes = node.attributes;
36
- const styleBag = (attributes as any)?.style as
37
- | Record<string, unknown>
38
- | undefined;
39
39
  if (!attributes) return {};
40
40
 
41
- const resizeMode = ((attributes as any)?.resizeMode ??
42
- styleBag?.resizeMode) as any;
41
+ // schemaVersion=2 uses `styles`, fallback to legacy `style`
42
+ const nativeStylesBag = getStyleBag(attributes) as
43
+ | ImagePropsGenerated['attributes']['style']
44
+ | undefined;
45
+ const resizeMode: ResizeModeOptionType | undefined =
46
+ nativeStylesBag?.resizeMode;
43
47
 
44
48
  // In RN, resizeMode is usually an Image prop, but many codebases also accept it on ImageStyle.
45
49
  // We return it here so consumers can pass it through as they prefer.
46
50
  const nativeStyle: Record<string, unknown> = {};
47
51
  if (resizeMode) nativeStyle.resizeMode = resizeMode;
48
52
 
49
- return { ...extractViewStyleNative(node, options as any), ...nativeStyle };
53
+ return { ...extractViewStyleNative(node, options), ...nativeStyle };
50
54
  }
@@ -17,6 +17,7 @@ import {
17
17
  resolveClosestFontWeightKey,
18
18
  } from '../loadFontFamily';
19
19
  import { fontsDebug } from '../fontsDebug';
20
+ import { getStyleBag, toAttributeRecord } from '../attributeStyle';
20
21
 
21
22
  const inFlightFontLoads: Map<string, Promise<void>> = new Map<
22
23
  string,
@@ -97,11 +98,10 @@ export function extractTextStyle<T extends TextPropsGenerated['attributes']>(
97
98
  options: ExtractTextStyleOptions = {},
98
99
  ) {
99
100
  const attributes = node.attributes;
100
- const styleBag = (attributes as Record<string, unknown>)?.style as
101
- | TextStyleGenerated
102
- | undefined;
101
+ const attrRecord = toAttributeRecord(attributes);
102
+ const styleBag = getStyleBag(attributes) as TextStyleGenerated | undefined;
103
103
  const get = (key: string): unknown => {
104
- const direct = (attributes as Record<string, unknown>)?.[key];
104
+ const direct = attrRecord[key];
105
105
  if (direct !== undefined && direct !== null) return direct;
106
106
  return styleBag?.[key as keyof TextStyleGenerated];
107
107
  };
@@ -199,15 +199,15 @@ export function extractTextStyle<T extends TextPropsGenerated['attributes']>(
199
199
  'textTransform',
200
200
  ]);
201
201
 
202
- const textStyle: React.CSSProperties = {};
202
+ const textStyle: Record<string, unknown> = {};
203
203
  for (const key in fullStyle) {
204
204
  const typedKey = key as keyof React.CSSProperties;
205
205
  if (textStyleProperties.has(typedKey)) {
206
- (textStyle as Record<string, unknown>)[key] = fullStyle[typedKey];
206
+ textStyle[key] = fullStyle[typedKey];
207
207
  }
208
208
  }
209
209
 
210
- return textStyle;
210
+ return textStyle as React.CSSProperties;
211
211
  }
212
212
 
213
213
  return fullStyle;
@@ -12,6 +12,7 @@ import {
12
12
  findFontDefinition,
13
13
  resolveClosestFontWeightKey,
14
14
  } from '../loadFontFamily';
15
+ import { getStyleBag, toAttributeRecord } from '../attributeStyle';
15
16
 
16
17
  export type ExtractTextStyleNativeOptions = {
17
18
  appConfig?: AppConfig;
@@ -37,11 +38,10 @@ export function extractTextStyleNative<
37
38
  T extends TextPropsGenerated['attributes'],
38
39
  >(node: NodeData<T>, options: ExtractTextStyleNativeOptions = {}) {
39
40
  const attributes = node.attributes;
40
- const styleBag = (attributes as any)?.style as
41
- | Record<string, unknown>
42
- | undefined;
41
+ const attrRecord = toAttributeRecord(attributes);
42
+ const styleBag = getStyleBag(attributes);
43
43
  const get = (key: string): unknown => {
44
- const direct = (attributes as any)?.[key];
44
+ const direct = attrRecord[key];
45
45
  if (direct !== undefined && direct !== null) return direct;
46
46
  return styleBag?.[key];
47
47
  };
@@ -58,13 +58,13 @@ export function extractTextStyleNative<
58
58
  return style;
59
59
  }
60
60
 
61
- const rawFontSize = get('fontSize') as any;
61
+ const rawFontSize = get('fontSize') as string | number | undefined;
62
62
  const parsedFontSize = parseSize(rawFontSize);
63
63
  if (typeof parsedFontSize === 'number') style.fontSize = parsedFontSize;
64
64
  else style.fontSize = fs(14);
65
65
 
66
- const fontFamily = get('fontFamily') as any;
67
- const fontWeight = get('fontWeight') as any;
66
+ const fontFamily = get('fontFamily') as string | undefined;
67
+ const fontWeight = get('fontWeight') as string | number | undefined;
68
68
  const requestedWeight = weightToNumericKey(fontWeight);
69
69
  const normalizedFontFamily =
70
70
  typeof fontFamily === 'string' && fontFamily.trim().length > 0
@@ -89,14 +89,14 @@ export function extractTextStyleNative<
89
89
  if (!normalizedFontFamily && normalizedFontWeight)
90
90
  style.fontWeight = normalizedFontWeight;
91
91
 
92
- const resolvedTextColor = parseColor(get('color') as any, {
92
+ const resolvedTextColor = parseColor(get('color') as string | undefined, {
93
93
  projectColors: options.projectColors,
94
94
  theme,
95
95
  });
96
96
  style.color = resolvedTextColor ?? fallbackColor;
97
97
 
98
- const textAlign = get('textAlign');
99
- if (textAlign) style.textAlign = textAlign as any;
98
+ const textAlign = get('textAlign') as string | undefined;
99
+ if (textAlign) style.textAlign = textAlign;
100
100
 
101
101
  const viewStyle = extractViewStyleNative(node, {
102
102
  projectColors: options.projectColors,
@@ -6,6 +6,7 @@ import type { NodeData } from '../../types/Node';
6
6
  import type { ProjectColors } from '../../types/Project';
7
7
  import { parseSize } from '../../size-matters';
8
8
  import { parseColor } from '../parseColor';
9
+ import { getStyleBag, toAttributeRecord } from '../attributeStyle';
9
10
 
10
11
  export type ExtractViewStyleOptions = {
11
12
  projectColors?: ProjectColors;
@@ -17,11 +18,10 @@ export function extractViewStyle<T extends ViewPropsGenerated['attributes']>(
17
18
  options: ExtractViewStyleOptions = {},
18
19
  ) {
19
20
  const attributes = node.attributes;
20
- const styleBag = (attributes as Record<string, unknown>)?.style as
21
- | ViewStyleGenerated
22
- | undefined;
21
+ const attrRecord = toAttributeRecord(attributes);
22
+ const styleBag = getStyleBag(attributes) as ViewStyleGenerated | undefined;
23
23
  const get = (key: string): unknown => {
24
- const direct = (attributes as Record<string, unknown>)?.[key];
24
+ const direct = attrRecord[key];
25
25
  if (direct !== undefined && direct !== null) return direct;
26
26
  return styleBag?.[key as keyof ViewStyleGenerated];
27
27
  };
@@ -112,10 +112,7 @@ export function extractViewStyle<T extends ViewPropsGenerated['attributes']>(
112
112
  );
113
113
 
114
114
  const marginHorizontalRaw =
115
- ((attributes as Record<string, unknown>).marginHorizontal as
116
- | string
117
- | number
118
- | undefined) ??
115
+ (attrRecord.marginHorizontal as string | number | undefined) ??
119
116
  (styleBag?.marginHorizontal as string | number | undefined);
120
117
  if (!isEmptySizeValue(marginHorizontalRaw)) {
121
118
  const parsed = parseSize(marginHorizontalRaw);
@@ -209,13 +206,13 @@ export function extractViewStyle<T extends ViewPropsGenerated['attributes']>(
209
206
  'overflowY',
210
207
  ]);
211
208
 
212
- const filteredStyle: React.CSSProperties = {};
209
+ const filteredStyle: Record<string, unknown> = {};
213
210
  for (const key in style) {
214
211
  const typedKey = key as keyof React.CSSProperties;
215
212
  if (viewStyleProperties.has(typedKey)) {
216
- (filteredStyle as Record<string, unknown>)[key] = style[typedKey];
213
+ filteredStyle[key] = style[typedKey];
217
214
  }
218
215
  }
219
216
 
220
- return filteredStyle;
217
+ return filteredStyle as React.CSSProperties;
221
218
  }
@@ -1,8 +1,9 @@
1
- import { ViewPropsGenerated } from '../../build-components/View/ViewProps.generated';
1
+ import type { ViewPropsGenerated } from '../../build-components/View/ViewProps.generated';
2
2
  import type { NodeData } from '../../types/Node';
3
3
  import type { ProjectColors } from '../../types/Project';
4
4
  import { parseSize } from '../../size-matters';
5
5
  import { parseColor } from '../parseColor';
6
+ import { getStyleBag, toAttributeRecord } from '../attributeStyle';
6
7
 
7
8
  export type ExtractViewStyleNativeOptions = {
8
9
  projectColors?: ProjectColors;
@@ -17,11 +18,10 @@ export function extractViewStyleNative<
17
18
  T extends ViewPropsGenerated['attributes'],
18
19
  >(node: NodeData<T>, options: ExtractViewStyleNativeOptions = {}) {
19
20
  const attributes = node.attributes;
20
- const styleBag = (attributes as any)?.style as
21
- | Record<string, unknown>
22
- | undefined;
21
+ const attrRecord = toAttributeRecord(attributes);
22
+ const styleBag = getStyleBag(attributes);
23
23
  const get = (key: string): unknown => {
24
- const direct = (attributes as any)?.[key];
24
+ const direct = attrRecord[key];
25
25
  if (direct !== undefined && direct !== null) return direct;
26
26
  return styleBag?.[key];
27
27
  };
@@ -41,18 +41,18 @@ export function extractViewStyleNative<
41
41
  // We intentionally do not map it to overflowX/Y.
42
42
 
43
43
  const flexDirection = get('flexDirection');
44
- if (flexDirection) style.flexDirection = flexDirection as any;
44
+ if (flexDirection) style.flexDirection = String(flexDirection);
45
45
  const alignItems = get('alignItems');
46
- if (alignItems) style.alignItems = alignItems as any;
46
+ if (alignItems) style.alignItems = String(alignItems);
47
47
  const justifyContent = get('justifyContent');
48
- if (justifyContent) style.justifyContent = justifyContent as any;
48
+ if (justifyContent) style.justifyContent = String(justifyContent);
49
49
 
50
50
  const setParsedSize = (property: string, rawValue: unknown) => {
51
51
  if (isEmptySizeValue(rawValue)) return;
52
- const parsed = parseSize(rawValue as any);
52
+ const parsed = parseSize(rawValue as string | number);
53
53
  // RN generally expects numbers (dp). We allow percentages for width/height-like props.
54
54
  if (typeof parsed === 'number' || typeof parsed === 'string') {
55
- (style as any)[property] = parsed;
55
+ style[property] = parsed;
56
56
  }
57
57
  };
58
58
 
@@ -68,7 +68,7 @@ export function extractViewStyleNative<
68
68
  setParsedSize('margin', get('margin'));
69
69
  setParsedSize(
70
70
  'marginHorizontal',
71
- (attributes as any)?.marginHorizontal ?? styleBag?.marginHorizontal,
71
+ attrRecord.marginHorizontal ?? styleBag?.marginHorizontal,
72
72
  );
73
73
  setParsedSize('marginVertical', get('marginVertical'));
74
74
  setParsedSize('marginTop', get('marginTop'));
@@ -76,9 +76,9 @@ export function extractViewStyleNative<
76
76
  setParsedSize('marginLeft', get('marginLeft'));
77
77
  setParsedSize('marginRight', get('marginRight'));
78
78
 
79
- const backgroundColor = get('backgroundColor') as any;
79
+ const backgroundColor = get('backgroundColor') as string | undefined;
80
80
  if (backgroundColor) {
81
- (style as any).backgroundColor =
81
+ style.backgroundColor =
82
82
  parseColor(backgroundColor, {
83
83
  projectColors: options.projectColors,
84
84
  theme: options.theme,
@@ -93,19 +93,19 @@ export function extractViewStyleNative<
93
93
  setParsedSize('minHeight', get('minHeight'));
94
94
  setParsedSize('maxHeight', get('maxHeight'));
95
95
 
96
- const flex = get('flex') as any;
97
- if (flex !== undefined) (style as any).flex = flex;
96
+ const flex = get('flex') as number | undefined;
97
+ if (flex !== undefined) style.flex = flex;
98
98
 
99
- const position = get('position') as any;
100
- if (position) (style as any).position = position;
99
+ const position = get('position') as string | undefined;
100
+ if (position) style.position = position;
101
101
 
102
102
  setParsedSize('top', get('top'));
103
103
  setParsedSize('bottom', get('bottom'));
104
104
  setParsedSize('left', get('left'));
105
105
  setParsedSize('right', get('right'));
106
106
 
107
- const zIndex = get('zIndex') as any;
108
- if (zIndex !== undefined) (style as any).zIndex = zIndex;
107
+ const zIndex = get('zIndex') as number | undefined;
108
+ if (zIndex !== undefined) style.zIndex = zIndex;
109
109
 
110
110
  return style;
111
111
  }
@@ -124,25 +124,20 @@ export async function loadFontFamily(
124
124
  // Not a browser environment (SSR / RN): do nothing.
125
125
  if (typeof document === 'undefined') return;
126
126
 
127
- const fontsApi = (document as any).fonts as FontFaceSet | undefined;
128
- if (!fontsApi || typeof (globalThis as any).FontFace !== 'function') {
127
+ const fontsApi = document.fonts;
128
+ if (!fontsApi || typeof globalThis.FontFace !== 'function') {
129
129
  throw new Error('Font loading is not supported in this environment');
130
130
  }
131
- const safeFontsApi = fontsApi as FontFaceSet;
132
131
 
133
132
  function hasLoadedFace(familyName: string, weight: string): boolean {
134
133
  try {
135
- const set = safeFontsApi as unknown as Iterable<FontFace>;
136
- for (const face of set) {
137
- const f = face as unknown as {
138
- family?: string;
139
- weight?: string;
140
- status?: string;
141
- };
134
+ for (const face of fontsApi) {
142
135
  const fam =
143
- typeof f.family === 'string' ? f.family.replace(/['"]/g, '') : '';
144
- const w = typeof f.weight === 'string' ? f.weight.trim() : '';
145
- if (fam === familyName && w === weight && f.status === 'loaded') {
136
+ typeof face.family === 'string'
137
+ ? face.family.replace(/['"]/g, '')
138
+ : '';
139
+ const w = typeof face.weight === 'string' ? face.weight.trim() : '';
140
+ if (fam === familyName && w === weight && face.status === 'loaded') {
146
141
  return true;
147
142
  }
148
143
  }
@@ -251,17 +246,17 @@ export async function loadFontFamily(
251
246
  }
252
247
 
253
248
  async function loadAndAdd(src: string | ArrayBuffer, label: string) {
254
- const face = new (globalThis as any).FontFace(name, src as any, {
249
+ const face = new FontFace(name, src, {
255
250
  weight: preferWeight,
256
251
  display: 'swap',
257
- }) as FontFace;
252
+ });
258
253
  fontsDebug.info('loadFontFamily: FontFace.load() begin', {
259
254
  familyName: name,
260
255
  weight: preferWeight,
261
256
  source: label,
262
257
  });
263
258
  const loaded = await face.load();
264
- safeFontsApi.add(loaded);
259
+ fontsApi.add(loaded);
265
260
  fontsDebug.info('loadFontFamily: loaded + added to document.fonts', {
266
261
  familyName: name,
267
262
  weight: preferWeight,
@@ -296,18 +291,18 @@ export async function loadFontFamily(
296
291
 
297
292
  try {
298
293
  const afterQuery = `${preferWeight} 16px "${name}"`;
299
- const after = safeFontsApi.check(afterQuery);
294
+ const after = fontsApi.check(afterQuery);
300
295
  fontsDebug.info('loadFontFamily: document.fonts.check after add', {
301
296
  familyName: name,
302
297
  weight: preferWeight,
303
298
  result: after,
304
299
  });
305
300
  // Force the browser to "activate" the face for this query (and reveal errors if any).
306
- if (typeof (safeFontsApi as any).load === 'function') {
301
+ if (typeof fontsApi.load === 'function') {
307
302
  fontsDebug.info('loadFontFamily: document.fonts.load begin', {
308
303
  query: afterQuery,
309
304
  });
310
- await (safeFontsApi as any).load(afterQuery);
305
+ await fontsApi.load(afterQuery);
311
306
  fontsDebug.info('loadFontFamily: document.fonts.load done', {
312
307
  query: afterQuery,
313
308
  });
@@ -67,20 +67,21 @@ export function logRenderStore(options: LogRenderStoreOptions = {}): void {
67
67
  const { label, includeLocalStorage = true, extra } = options;
68
68
 
69
69
  const state = useRenderStore.getState();
70
- const keys = Object.keys(state as Record<string, unknown>).sort();
70
+ const stateRecord = state as Record<string, unknown>;
71
+ const keys = Object.keys(stateRecord).sort();
71
72
 
72
73
  const snapshot: Record<string, unknown> = {};
73
74
  const actions: string[] = [];
74
75
 
75
76
  for (const key of keys) {
76
- const v = (state as any)[key];
77
+ const v = stateRecord[key];
77
78
  if (isFn(v)) actions.push(key);
78
79
  else snapshot[key] = v;
79
80
  }
80
81
 
81
82
  const title = `[RB Debug] Render store${label ? ` (${label})` : ''}`;
82
- const groupCollapsed = (console as any).groupCollapsed as unknown;
83
- const groupEnd = (console as any).groupEnd as unknown;
83
+ const groupCollapsed = console.groupCollapsed as unknown;
84
+ const groupEnd = console.groupEnd as unknown;
84
85
 
85
86
  if (isFn(groupCollapsed)) groupCollapsed(title);
86
87
  else {
@@ -19,7 +19,7 @@ export function deleteNodeFromTree(root: Node, target: Node): Node {
19
19
  return changed ? nextChildren : root;
20
20
  }
21
21
 
22
- const data = root as any;
22
+ const data = root as NodeData;
23
23
  if ('children' in data) {
24
24
  const prev = data.children as Node;
25
25
  if (!prev) return root;