react-native-divkit 0.1.0-alpha.1 → 0.1.0-alpha.3

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 (150) hide show
  1. package/README.md +200 -201
  2. package/dist/DivKit.d.ts.map +1 -1
  3. package/dist/DivKit.js +24 -7
  4. package/dist/DivKit.js.map +1 -1
  5. package/dist/actions/array.d.ts.map +1 -1
  6. package/dist/actions/array.js +1 -1
  7. package/dist/actions/array.js.map +1 -1
  8. package/dist/actions/copyToClipboard.d.ts.map +1 -1
  9. package/dist/actions/copyToClipboard.js +2 -1
  10. package/dist/actions/copyToClipboard.js.map +1 -1
  11. package/dist/actions/dict.d.ts.map +1 -1
  12. package/dist/actions/dict.js.map +1 -1
  13. package/dist/actions/updateStructure.d.ts.map +1 -1
  14. package/dist/actions/updateStructure.js.map +1 -1
  15. package/dist/components/container/DivContainer.d.ts.map +1 -1
  16. package/dist/components/container/DivContainer.js +3 -5
  17. package/dist/components/container/DivContainer.js.map +1 -1
  18. package/dist/components/image/DivImage.d.ts.map +1 -1
  19. package/dist/components/image/DivImage.js +1 -6
  20. package/dist/components/image/DivImage.js.map +1 -1
  21. package/dist/components/state/DivState.d.ts.map +1 -1
  22. package/dist/components/state/DivState.js +3 -5
  23. package/dist/components/state/DivState.js.map +1 -1
  24. package/dist/components/text/DivText.d.ts.map +1 -1
  25. package/dist/components/text/DivText.js +4 -4
  26. package/dist/components/text/DivText.js.map +1 -1
  27. package/dist/components/utilities/Background.d.ts +11 -0
  28. package/dist/components/utilities/Background.d.ts.map +1 -0
  29. package/dist/components/utilities/Background.js +73 -0
  30. package/dist/components/utilities/Background.js.map +1 -0
  31. package/dist/components/utilities/Outer.d.ts.map +1 -1
  32. package/dist/components/utilities/Outer.js +24 -10
  33. package/dist/components/utilities/Outer.js.map +1 -1
  34. package/dist/context/index.d.ts.map +1 -1
  35. package/dist/context/index.js.map +1 -1
  36. package/dist/expressions/eval.d.ts.map +1 -1
  37. package/dist/expressions/eval.js +19 -11
  38. package/dist/expressions/eval.js.map +1 -1
  39. package/dist/expressions/funcs/array.d.ts.map +1 -1
  40. package/dist/expressions/funcs/array.js +72 -168
  41. package/dist/expressions/funcs/array.js.map +1 -1
  42. package/dist/expressions/funcs/colors.d.ts.map +1 -1
  43. package/dist/expressions/funcs/colors.js.map +1 -1
  44. package/dist/expressions/funcs/customFuncs.d.ts.map +1 -1
  45. package/dist/expressions/funcs/customFuncs.js +6 -4
  46. package/dist/expressions/funcs/customFuncs.js.map +1 -1
  47. package/dist/expressions/funcs/datetime.d.ts.map +1 -1
  48. package/dist/expressions/funcs/datetime.js +1 -1
  49. package/dist/expressions/funcs/datetime.js.map +1 -1
  50. package/dist/expressions/funcs/dict.d.ts.map +1 -1
  51. package/dist/expressions/funcs/dict.js.map +1 -1
  52. package/dist/expressions/funcs/funcs.d.ts.map +1 -1
  53. package/dist/expressions/funcs/funcs.js +21 -13
  54. package/dist/expressions/funcs/funcs.js.map +1 -1
  55. package/dist/expressions/funcs/math.d.ts.map +1 -1
  56. package/dist/expressions/funcs/math.js +40 -20
  57. package/dist/expressions/funcs/math.js.map +1 -1
  58. package/dist/expressions/funcs/std.d.ts.map +1 -1
  59. package/dist/expressions/funcs/std.js +4 -4
  60. package/dist/expressions/funcs/std.js.map +1 -1
  61. package/dist/expressions/funcs/strings.d.ts.map +1 -1
  62. package/dist/expressions/funcs/strings.js +1 -2
  63. package/dist/expressions/funcs/strings.js.map +1 -1
  64. package/dist/expressions/funcs/trigonometry.js +2 -2
  65. package/dist/expressions/funcs/trigonometry.js.map +1 -1
  66. package/dist/expressions/json.d.ts +2 -2
  67. package/dist/expressions/json.d.ts.map +1 -1
  68. package/dist/expressions/json.js +6 -4
  69. package/dist/expressions/json.js.map +1 -1
  70. package/dist/expressions/utils.d.ts.map +1 -1
  71. package/dist/expressions/utils.js +9 -10
  72. package/dist/expressions/utils.js.map +1 -1
  73. package/dist/expressions/variable.d.ts.map +1 -1
  74. package/dist/expressions/variable.js +3 -7
  75. package/dist/expressions/variable.js.map +1 -1
  76. package/dist/expressions/walk.d.ts.map +1 -1
  77. package/dist/expressions/walk.js.map +1 -1
  78. package/dist/hooks/index.d.ts.map +1 -1
  79. package/dist/hooks/index.js.map +1 -1
  80. package/dist/hooks/useAction.d.ts.map +1 -1
  81. package/dist/hooks/useAction.js.map +1 -1
  82. package/dist/hooks/useDerivedFromVars.d.ts.map +1 -1
  83. package/dist/hooks/useDerivedFromVars.js.map +1 -1
  84. package/dist/hooks/useVariable.d.ts.map +1 -1
  85. package/dist/hooks/useVariable.js +4 -4
  86. package/dist/hooks/useVariable.js.map +1 -1
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/index.js.map +1 -1
  89. package/dist/stores/createObservable.d.ts.map +1 -1
  90. package/dist/stores/createObservable.js.map +1 -1
  91. package/dist/utils/applyTemplate.d.ts +2 -2
  92. package/dist/utils/applyTemplate.d.ts.map +1 -1
  93. package/dist/utils/applyTemplate.js +13 -13
  94. package/dist/utils/applyTemplate.js.map +1 -1
  95. package/dist/utils/correctColor.d.ts.map +1 -1
  96. package/dist/utils/correctColor.js +8 -6
  97. package/dist/utils/correctColor.js.map +1 -1
  98. package/dist/utils/formatDate.d.ts.map +1 -1
  99. package/dist/utils/formatDate.js +7 -10
  100. package/dist/utils/formatDate.js.map +1 -1
  101. package/dist/utils/wrapError.d.ts.map +1 -1
  102. package/dist/utils/wrapError.js.map +1 -1
  103. package/package.json +8 -7
  104. package/src/DivKit.tsx +258 -220
  105. package/src/actions/array.ts +91 -64
  106. package/src/actions/copyToClipboard.ts +28 -19
  107. package/src/actions/dict.ts +36 -26
  108. package/src/actions/updateStructure.ts +86 -61
  109. package/src/components/README.md +38 -18
  110. package/src/components/container/DivContainer.tsx +4 -14
  111. package/src/components/image/DivImage.tsx +1 -11
  112. package/src/components/state/DivState.tsx +3 -9
  113. package/src/components/text/DivText.tsx +8 -20
  114. package/src/components/utilities/Background.tsx +120 -0
  115. package/src/components/utilities/Outer.tsx +24 -23
  116. package/src/components/utilities/README.md +37 -32
  117. package/src/context/index.ts +2 -11
  118. package/src/expressions/ast.d.ts +16 -9
  119. package/src/expressions/eval.ts +82 -37
  120. package/src/expressions/funcs/array.ts +129 -209
  121. package/src/expressions/funcs/colors.ts +1 -3
  122. package/src/expressions/funcs/customFuncs.ts +6 -4
  123. package/src/expressions/funcs/datetime.ts +10 -3
  124. package/src/expressions/funcs/dict.ts +16 -2
  125. package/src/expressions/funcs/funcs.ts +75 -89
  126. package/src/expressions/funcs/math.ts +103 -43
  127. package/src/expressions/funcs/std.ts +4 -7
  128. package/src/expressions/funcs/strings.ts +9 -25
  129. package/src/expressions/funcs/trigonometry.ts +2 -2
  130. package/src/expressions/json.ts +60 -53
  131. package/src/expressions/utils.ts +24 -22
  132. package/src/expressions/variable.ts +5 -21
  133. package/src/expressions/walk.ts +6 -3
  134. package/src/hooks/README.md +61 -53
  135. package/src/hooks/index.ts +3 -18
  136. package/src/hooks/useAction.ts +1 -3
  137. package/src/hooks/useDerivedFromVars.ts +3 -13
  138. package/src/hooks/useVariable.ts +7 -17
  139. package/src/index.ts +10 -48
  140. package/src/stores/createObservable.ts +35 -35
  141. package/src/types/alignment.d.ts +15 -6
  142. package/src/types/background.d.ts +6 -2
  143. package/src/types/base.d.ts +41 -9
  144. package/src/types/componentContext.d.ts +27 -22
  145. package/src/types/container.d.ts +1 -4
  146. package/src/types/text.d.ts +1 -1
  147. package/src/utils/applyTemplate.ts +103 -109
  148. package/src/utils/correctColor.ts +9 -8
  149. package/src/utils/formatDate.ts +175 -86
  150. package/src/utils/wrapError.ts +7 -4
@@ -3,9 +3,11 @@
3
3
  ## MVP Components Implemented
4
4
 
5
5
  ### 1. DivText (`text/DivText.tsx`)
6
+
6
7
  Text rendering component with variable substitution and styling.
7
8
 
8
9
  **Features:**
10
+
9
11
  - ✅ Text rendering with variable substitution
10
12
  - ✅ Font styling (size, weight, color, family)
11
13
  - ✅ Text alignment (horizontal, RTL support)
@@ -15,6 +17,7 @@ Text rendering component with variable substitution and styling.
15
17
  - ✅ Font feature settings (basic)
16
18
 
17
19
  **Deferred:**
20
+
18
21
  - Text ranges (nested styling)
19
22
  - Text images
20
23
  - Text gradients
@@ -23,9 +26,11 @@ Text rendering component with variable substitution and styling.
23
26
  - Selectable text with custom actions
24
27
 
25
28
  ### 2. DivContainer (`container/DivContainer.tsx`)
29
+
26
30
  Flex container component for layout management.
27
31
 
28
32
  **Features:**
33
+
29
34
  - ✅ Vertical/horizontal/overlap orientation
30
35
  - ✅ Content alignment (horizontal & vertical)
31
36
  - ✅ Item spacing (gap)
@@ -33,6 +38,7 @@ Flex container component for layout management.
33
38
  - ✅ RTL support
34
39
 
35
40
  **Deferred:**
41
+
36
42
  - Wrap layout mode
37
43
  - Separators (visual dividers)
38
44
  - Line separators (for wrap mode)
@@ -41,9 +47,11 @@ Flex container component for layout management.
41
47
  - Clip to bounds
42
48
 
43
49
  ### 3. DivImage (`image/DivImage.tsx`)
50
+
44
51
  Image component with network loading and scaling.
45
52
 
46
53
  **Features:**
54
+
47
55
  - ✅ Network image loading
48
56
  - ✅ Scaling modes (fill, fit, stretch, no_scale)
49
57
  - ✅ Placeholder color while loading
@@ -52,6 +60,7 @@ Image component with network loading and scaling.
52
60
  - ✅ Aspect ratio
53
61
 
54
62
  **Deferred:**
63
+
55
64
  - GIF support (requires react-native-gif)
56
65
  - Image preview (blur-up technique)
57
66
  - Tint color and tint modes
@@ -61,9 +70,11 @@ Image component with network loading and scaling.
61
70
  - react-native-fast-image integration
62
71
 
63
72
  ### 4. DivState (`state/DivState.tsx`)
73
+
64
74
  State management component for switching between different UI states.
65
75
 
66
76
  **Features:**
77
+
67
78
  - ✅ State selection by state_id
68
79
  - ✅ Default state
69
80
  - ✅ State switching via actions (set_state)
@@ -72,6 +83,7 @@ State management component for switching between different UI states.
72
83
  - ✅ Two-way binding with variables
73
84
 
74
85
  **Deferred:**
86
+
75
87
  - Transition animations (in/out/change)
76
88
  - Animation timing and interpolation
77
89
  - Clip to bounds
@@ -79,15 +91,18 @@ State management component for switching between different UI states.
79
91
  - Multiple concurrent state transitions
80
92
 
81
93
  ### 5. DivComponent (`DivComponent.tsx`)
94
+
82
95
  Universal component router that dispatches to appropriate component based on type.
83
96
 
84
97
  **Supported Types:**
98
+
85
99
  - `text` → DivText
86
100
  - `container` → DivContainer
87
101
  - `image` / `gif` → DivImage
88
102
  - `state` → DivState
89
103
 
90
104
  **Deferred Types:**
105
+
91
106
  - `gallery`, `pager`, `tabs`
92
107
  - `slider`, `indicator`
93
108
  - `input`, `select`, `switch`
@@ -97,9 +112,11 @@ Universal component router that dispatches to appropriate component based on typ
97
112
  ## Utility Components
98
113
 
99
114
  ### Outer (`utilities/Outer.tsx`)
115
+
100
116
  Base wrapper component providing common functionality for all components.
101
117
 
102
118
  **Features:**
119
+
103
120
  - Visibility handling (visible/invisible/gone)
104
121
  - Sizing (width/height with fixed/match_parent/wrap_content)
105
122
  - Padding and margins
@@ -110,6 +127,7 @@ Base wrapper component providing common functionality for all components.
110
127
  - Action handling (onPress)
111
128
 
112
129
  ### Unknown (`utilities/Unknown.tsx`)
130
+
113
131
  Fallback component for unsupported types.
114
132
 
115
133
  ## Architecture
@@ -131,6 +149,7 @@ DivComponent (router)
131
149
  ## Integration with Context System
132
150
 
133
151
  All components integrate with:
152
+
134
153
  - **DivKitContext**: Access to variables, actions, configuration
135
154
  - **StateContext**: State management and transitions
136
155
  - **ActionContext**: Action execution
@@ -139,6 +158,7 @@ All components integrate with:
139
158
  ## Integration with Hooks
140
159
 
141
160
  All components use reactive hooks:
161
+
142
162
  - `useDerivedFromVarsSimple`: Variable substitution
143
163
  - `useAction`, `useActionHandler`: Action execution
144
164
  - `useDivKitContext`, `useStateContext`: Context access
@@ -149,29 +169,29 @@ All components use reactive hooks:
149
169
  import { DivComponent } from 'react-native-divkit';
150
170
 
151
171
  const json = {
152
- type: 'container',
153
- orientation: 'vertical',
154
- items: [
155
- {
156
- type: 'text',
157
- text: 'Hello @{userName}!',
158
- font_size: 24
159
- },
160
- {
161
- type: 'image',
162
- image_url: 'https://example.com/image.png',
163
- scale: 'fit'
164
- }
165
- ]
172
+ type: 'container',
173
+ orientation: 'vertical',
174
+ items: [
175
+ {
176
+ type: 'text',
177
+ text: 'Hello @{userName}!',
178
+ font_size: 24
179
+ },
180
+ {
181
+ type: 'image',
182
+ image_url: 'https://example.com/image.png',
183
+ scale: 'fit'
184
+ }
185
+ ]
166
186
  };
167
187
 
168
188
  const componentContext = {
169
- json,
170
- variables: variablesMap,
171
- // ... other context props
189
+ json,
190
+ variables: variablesMap
191
+ // ... other context props
172
192
  };
173
193
 
174
- <DivComponent componentContext={componentContext} />
194
+ <DivComponent componentContext={componentContext} />;
175
195
  ```
176
196
 
177
197
  ## Next Steps (Phase 6)
@@ -50,10 +50,7 @@ export function DivContainer({ componentContext }: DivContainerProps) {
50
50
  variables || new Map()
51
51
  );
52
52
 
53
- const itemSpacing = useDerivedFromVarsSimple<number>(
54
- json.item_spacing || 0,
55
- variables || new Map()
56
- );
53
+ const itemSpacing = useDerivedFromVarsSimple<number>(json.item_spacing || 0, variables || new Map());
57
54
 
58
55
  // Build container style
59
56
  const containerStyle = useMemo((): ViewStyle => {
@@ -120,19 +117,14 @@ export function DivContainer({ componentContext }: DivContainerProps) {
120
117
 
121
118
  return json.items.map((item, index) => {
122
119
  const childContext = componentContext.produceChildContext(item, {
123
- path: index,
120
+ path: index
124
121
  });
125
122
 
126
123
  if (!childContext) {
127
124
  return null;
128
125
  }
129
126
 
130
- const child = (
131
- <DivComponent
132
- key={item.id || `item-${index}`}
133
- componentContext={childContext}
134
- />
135
- );
127
+ const child = <DivComponent key={item.id || `item-${index}`} componentContext={childContext} />;
136
128
 
137
129
  // Wrap in positioned View for overlap mode
138
130
  if (orientation === 'overlap' && childWrapperStyle) {
@@ -149,9 +141,7 @@ export function DivContainer({ componentContext }: DivContainerProps) {
149
141
 
150
142
  return (
151
143
  <Outer componentContext={componentContext}>
152
- <View style={containerStyle}>
153
- {renderChildren()}
154
- </View>
144
+ <View style={containerStyle}>{renderChildren()}</View>
155
145
  </Outer>
156
146
  );
157
147
  }
@@ -43,10 +43,7 @@ export function DivImage({ componentContext }: DivImageProps) {
43
43
  variables || new Map()
44
44
  );
45
45
 
46
- const scale = useDerivedFromVarsSimple<ImageScale>(
47
- json.scale || 'fill',
48
- variables || new Map()
49
- );
46
+ const scale = useDerivedFromVarsSimple<ImageScale>(json.scale || 'fill', variables || new Map());
50
47
 
51
48
  const placeholderColor = useDerivedFromVarsSimple<string | undefined>(
52
49
  json.placeholder_color,
@@ -103,12 +100,6 @@ export function DivImage({ componentContext }: DivImageProps) {
103
100
  return style;
104
101
  }, [json.aspect]);
105
102
 
106
- // Handle image load events
107
- const handleLoadStart = () => {
108
- setLoading(true);
109
- setError(false);
110
- };
111
-
112
103
  const handleLoadEnd = () => {
113
104
  setLoading(false);
114
105
  };
@@ -146,7 +137,6 @@ export function DivImage({ componentContext }: DivImageProps) {
146
137
  source={{ uri: imageUrl }}
147
138
  style={imageStyle}
148
139
  resizeMode={resizeMode}
149
- onLoadStart={handleLoadStart}
150
140
  onLoadEnd={handleLoadEnd}
151
141
  onError={handleError}
152
142
  />
@@ -116,7 +116,7 @@ export function DivState({ componentContext }: DivStateProps) {
116
116
  if (!currentState?.div) return undefined;
117
117
 
118
118
  return componentContext.produceChildContext(currentState.div, {
119
- path: currentStateId,
119
+ path: currentStateId
120
120
  });
121
121
  }, [currentState, currentStateId, componentContext]);
122
122
 
@@ -129,18 +129,12 @@ export function DivState({ componentContext }: DivStateProps) {
129
129
  // Import DivComponent dynamically to avoid circular dependency
130
130
  const DivComponent = require('../DivComponent').DivComponent;
131
131
 
132
- return (
133
- <DivComponent
134
- componentContext={childContext}
135
- />
136
- );
132
+ return <DivComponent componentContext={childContext} />;
137
133
  };
138
134
 
139
135
  return (
140
136
  <Outer componentContext={componentContext}>
141
- <View>
142
- {renderContent()}
143
- </View>
137
+ <View>{renderContent()}</View>
144
138
  </Outer>
145
139
  );
146
140
  }
@@ -35,30 +35,18 @@ export function DivText({ componentContext }: DivTextProps) {
35
35
  const { json, variables } = componentContext;
36
36
 
37
37
  // Reactive properties - use hooks for properties that may contain variables
38
- const text = useDerivedFromVarsSimple<string>(
39
- json.text || '',
40
- variables || new Map()
41
- );
38
+ const text = useDerivedFromVarsSimple<string>(json.text || '', variables || new Map());
42
39
 
43
- const fontSize = useDerivedFromVarsSimple<number>(
44
- json.font_size || 12,
45
- variables || new Map()
46
- );
40
+ const fontSize = useDerivedFromVarsSimple<number>(json.font_size || 12, variables || new Map());
47
41
 
48
- const textColor = useDerivedFromVarsSimple<string>(
49
- json.text_color || '#000000',
50
- variables || new Map()
51
- );
42
+ const textColor = useDerivedFromVarsSimple<string>(json.text_color || '#000000', variables || new Map());
52
43
 
53
44
  const textAlignmentHorizontal = useDerivedFromVarsSimple(
54
45
  json.text_alignment_horizontal || 'start',
55
46
  variables || new Map()
56
47
  );
57
48
 
58
- const maxLines = useDerivedFromVarsSimple<number | undefined>(
59
- json.max_lines,
60
- variables || new Map()
61
- );
49
+ const maxLines = useDerivedFromVarsSimple<number | undefined>(json.max_lines, variables || new Map());
62
50
 
63
51
  // Build text style
64
52
  const textStyle = useMemo((): TextStyle => {
@@ -77,10 +65,10 @@ export function DivText({ componentContext }: DivTextProps) {
77
65
  // Font weight
78
66
  if (json.font_weight) {
79
67
  const weightMap: Record<FontWeight, TextStyle['fontWeight']> = {
80
- 'light': '300',
81
- 'regular': '400',
82
- 'medium': '500',
83
- 'bold': '700'
68
+ light: '300',
69
+ regular: '400',
70
+ medium: '500',
71
+ bold: '700'
84
72
  };
85
73
  style.fontWeight = weightMap[json.font_weight] || '400';
86
74
  } else if (json.font_weight_value) {
@@ -0,0 +1,120 @@
1
+ import React from 'react';
2
+ import { StyleSheet, View, ViewStyle } from 'react-native';
3
+ import Svg, { Defs, RadialGradient, Stop, Rect } from 'react-native-svg';
4
+ import type { Background as BackgroundType, RadialBackground } from '../../types/background';
5
+
6
+ export interface BackgroundProps {
7
+ layers?: BackgroundType[];
8
+ style?: ViewStyle;
9
+ width?: number;
10
+ height?: number;
11
+ }
12
+
13
+ const RadialGradientLayer = ({ layer }: { layer: RadialBackground }) => {
14
+ // Default to 50% 50% if not specified
15
+ let cx = '50%';
16
+ let cy = '50%';
17
+
18
+ if (layer.center_x) {
19
+ if (layer.center_x.type === 'fixed') {
20
+ cx = `${layer.center_x.value}`;
21
+ } else {
22
+ cx = `${layer.center_x.value * 100}%`;
23
+ }
24
+ }
25
+
26
+ if (layer.center_y) {
27
+ if (layer.center_y.type === 'fixed') {
28
+ cy = `${layer.center_y.value}`;
29
+ } else {
30
+ cy = `${layer.center_y.value * 100}%`;
31
+ }
32
+ }
33
+
34
+ // Colors
35
+ // If colors array provided, distribute evenly
36
+ // If color_map provided, use it
37
+ let stops: JSX.Element[] = [];
38
+
39
+ if (layer.colors) {
40
+ stops = layer.colors.map((color, index) => (
41
+ <Stop
42
+ key={index}
43
+ offset={index / (layer.colors!.length - 1)}
44
+ stopColor={color}
45
+ stopOpacity={1}
46
+ />
47
+ ));
48
+ } else if (layer.color_map) {
49
+ stops = layer.color_map.map((point, index) => (
50
+ <Stop
51
+ key={index}
52
+ offset={point.position}
53
+ stopColor={point.color}
54
+ stopOpacity={1}
55
+ />
56
+ ));
57
+ }
58
+
59
+ // Radius
60
+ // DivKit defaults: farthest_corner
61
+ // SVG RadialGradient rx ry defaults to 50%
62
+ // To support "farthest_corner" accurately we might need complex math or just use a large radius like 100%?
63
+ // For now, let's stick to 50% (containing circle) or 100%?
64
+ // A safe default for radial gradients covering a view is often 50% rx/ry if the center is center.
65
+ // If the center is custom, we might need adjustments.
66
+ // Let's use '50%' for rx/ry which is standard for "closest-side" sort of.
67
+ // Specifying units="userSpaceOnUse" allows pixel values.
68
+ // Specifying units="objectBoundingBox" (default) allows percentages.
69
+
70
+ // We'll use objectBoundingBox
71
+ const rx = '50%';
72
+ const ry = '50%';
73
+
74
+ return (
75
+ <Svg height="100%" width="100%" style={StyleSheet.absoluteFill}>
76
+ <Defs>
77
+ <RadialGradient
78
+ id="grad"
79
+ cx={cx}
80
+ cy={cy}
81
+ rx={rx}
82
+ ry={ry}
83
+ fx={cx}
84
+ fy={cy}
85
+ gradientUnits="objectBoundingBox"
86
+ >
87
+ {stops}
88
+ </RadialGradient>
89
+ </Defs>
90
+ <Rect x="0" y="0" width="100%" height="100%" fill="url(#grad)" />
91
+ </Svg>
92
+ );
93
+ };
94
+
95
+ export const Background = ({ layers, style }: BackgroundProps) => {
96
+ if (!layers || layers.length === 0) return null;
97
+
98
+ return (
99
+ <View style={[StyleSheet.absoluteFill, style, { zIndex: -1, overflow: 'hidden' }]} pointerEvents="none">
100
+ {layers.map((layer, index) => {
101
+ if (layer.type === 'solid') {
102
+ return (
103
+ <View
104
+ key={index}
105
+ style={[StyleSheet.absoluteFill, { backgroundColor: layer.color }]}
106
+ />
107
+ );
108
+ }
109
+ if (layer.type === 'radial_gradient') {
110
+ return (
111
+ <View key={index} style={StyleSheet.absoluteFill}>
112
+ <RadialGradientLayer layer={layer} />
113
+ </View>
114
+ );
115
+ }
116
+ return null;
117
+ })}
118
+ </View>
119
+ );
120
+ };
@@ -8,6 +8,7 @@ import type { MaybeMissing } from '../../expressions/json';
8
8
  import { useDerivedFromVarsSimple } from '../../hooks/useDerivedFromVars';
9
9
  import { useActionHandler, useHasActions } from '../../hooks/useAction';
10
10
  import { useDivKitContext } from '../../context/DivKitContext';
11
+ import { Background } from './Background';
11
12
 
12
13
  export interface OuterProps<T extends DivBaseData = DivBaseData> {
13
14
  componentContext: ComponentContext<T>;
@@ -31,14 +32,8 @@ export function Outer<T extends DivBaseData = DivBaseData>({
31
32
 
32
33
  // Only use reactive hooks for truly dynamic properties (visibility, alpha)
33
34
  // For MVP, other properties are read directly from JSON (can be enhanced later)
34
- const visibility = useDerivedFromVarsSimple<Visibility>(
35
- json.visibility || 'visible',
36
- variables || new Map()
37
- );
38
- const alpha = useDerivedFromVarsSimple<number>(
39
- json.alpha !== undefined ? json.alpha : 1,
40
- variables || new Map()
41
- );
35
+ const visibility = useDerivedFromVarsSimple<Visibility>(json.visibility || 'visible', variables || new Map());
36
+ const alpha = useDerivedFromVarsSimple<number>(json.alpha !== undefined ? json.alpha : 1, variables || new Map());
42
37
 
43
38
  // Extract properties directly from JSON for MVP (non-reactive)
44
39
  const paddings = json.paddings;
@@ -76,7 +71,10 @@ export function Outer<T extends DivBaseData = DivBaseData>({
76
71
  if (widthVal.type === 'fixed') {
77
72
  styles.width = (widthVal as FixedSize).value;
78
73
  } else if (widthVal.type === 'match_parent') {
79
- styles.width = '100%';
74
+ // Use alignSelf: 'stretch' instead of width: '100%' so that
75
+ // margins are subtracted from the available space rather than
76
+ // added on top of 100%, which would cause overflow.
77
+ styles.alignSelf = 'stretch';
80
78
  styles.flexGrow = (widthVal as MatchParentSize).weight || 1;
81
79
  } else if (widthVal.type === 'wrap_content') {
82
80
  styles.alignSelf = 'flex-start';
@@ -84,7 +82,7 @@ export function Outer<T extends DivBaseData = DivBaseData>({
84
82
  }
85
83
  } else {
86
84
  // Default: match_parent
87
- styles.width = '100%';
85
+ styles.alignSelf = 'stretch';
88
86
  styles.flexGrow = 1;
89
87
  }
90
88
 
@@ -148,15 +146,8 @@ export function Outer<T extends DivBaseData = DivBaseData>({
148
146
  }
149
147
  }
150
148
 
151
- // Background (MVP: only solid colors)
152
- if (background && Array.isArray(background)) {
153
- const bg = background as any[];
154
- const solidBg = bg.find((b: any) => b?.type === 'solid');
155
- if (solidBg && solidBg.color) {
156
- styles.backgroundColor = solidBg.color;
157
- }
158
- }
159
-
149
+ // Background handled by Background component
150
+
160
151
  // Border
161
152
  if (border) {
162
153
  const b = border as any;
@@ -219,13 +210,22 @@ export function Outer<T extends DivBaseData = DivBaseData>({
219
210
  return StyleSheet.flatten([containerStyle, customStyle]);
220
211
  }, [containerStyle, customStyle]);
221
212
 
213
+ const borderStyle = useMemo(() => {
214
+ const s = finalStyle || {};
215
+ const res: ViewStyle = {};
216
+ if (s.borderRadius) res.borderRadius = s.borderRadius;
217
+ if (s.borderTopLeftRadius) res.borderTopLeftRadius = s.borderTopLeftRadius;
218
+ if (s.borderTopRightRadius) res.borderTopRightRadius = s.borderTopRightRadius;
219
+ if (s.borderBottomLeftRadius) res.borderBottomLeftRadius = s.borderBottomLeftRadius;
220
+ if (s.borderBottomRightRadius) res.borderBottomRightRadius = s.borderBottomRightRadius;
221
+ return res;
222
+ }, [finalStyle]);
223
+
222
224
  // Render with or without Pressable based on actions
223
225
  if (hasActions) {
224
226
  return (
225
- <Pressable
226
- onPress={handlePress}
227
- style={finalStyle}
228
- >
227
+ <Pressable onPress={handlePress} style={finalStyle}>
228
+ <Background layers={background as any} style={borderStyle} />
229
229
  {children}
230
230
  </Pressable>
231
231
  );
@@ -233,6 +233,7 @@ export function Outer<T extends DivBaseData = DivBaseData>({
233
233
 
234
234
  return (
235
235
  <View style={finalStyle}>
236
+ <Background layers={background as any} style={borderStyle} />
236
237
  {children}
237
238
  </View>
238
239
  );