react-native-divkit 0.1.0-alpha.1

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 (322) hide show
  1. package/LICENSE +176 -0
  2. package/README.md +340 -0
  3. package/dist/DivKit.d.ts +68 -0
  4. package/dist/DivKit.d.ts.map +1 -0
  5. package/dist/DivKit.js +400 -0
  6. package/dist/DivKit.js.map +1 -0
  7. package/dist/actions/array.d.ts +8 -0
  8. package/dist/actions/array.d.ts.map +1 -0
  9. package/dist/actions/array.js +139 -0
  10. package/dist/actions/array.js.map +1 -0
  11. package/dist/actions/copyToClipboard.d.ts +22 -0
  12. package/dist/actions/copyToClipboard.d.ts.map +1 -0
  13. package/dist/actions/copyToClipboard.js +63 -0
  14. package/dist/actions/copyToClipboard.js.map +1 -0
  15. package/dist/actions/dict.d.ts +6 -0
  16. package/dist/actions/dict.d.ts.map +1 -0
  17. package/dist/actions/dict.js +58 -0
  18. package/dist/actions/dict.js.map +1 -0
  19. package/dist/actions/index.d.ts +11 -0
  20. package/dist/actions/index.d.ts.map +1 -0
  21. package/dist/actions/index.js +11 -0
  22. package/dist/actions/index.js.map +1 -0
  23. package/dist/actions/updateStructure.d.ts +6 -0
  24. package/dist/actions/updateStructure.d.ts.map +1 -0
  25. package/dist/actions/updateStructure.js +116 -0
  26. package/dist/actions/updateStructure.js.map +1 -0
  27. package/dist/components/DivComponent.d.ts +29 -0
  28. package/dist/components/DivComponent.d.ts.map +1 -0
  29. package/dist/components/DivComponent.js +62 -0
  30. package/dist/components/DivComponent.js.map +1 -0
  31. package/dist/components/container/DivContainer.d.ts +26 -0
  32. package/dist/components/container/DivContainer.d.ts.map +1 -0
  33. package/dist/components/container/DivContainer.js +172 -0
  34. package/dist/components/container/DivContainer.js.map +1 -0
  35. package/dist/components/container/index.d.ts +3 -0
  36. package/dist/components/container/index.d.ts.map +1 -0
  37. package/dist/components/container/index.js +2 -0
  38. package/dist/components/container/index.js.map +1 -0
  39. package/dist/components/image/DivImage.d.ts +29 -0
  40. package/dist/components/image/DivImage.d.ts.map +1 -0
  41. package/dist/components/image/DivImage.js +122 -0
  42. package/dist/components/image/DivImage.js.map +1 -0
  43. package/dist/components/image/index.d.ts +3 -0
  44. package/dist/components/image/index.d.ts.map +1 -0
  45. package/dist/components/image/index.js +2 -0
  46. package/dist/components/image/index.js.map +1 -0
  47. package/dist/components/index.d.ts +14 -0
  48. package/dist/components/index.d.ts.map +1 -0
  49. package/dist/components/index.js +11 -0
  50. package/dist/components/index.js.map +1 -0
  51. package/dist/components/state/DivState.d.ts +26 -0
  52. package/dist/components/state/DivState.d.ts.map +1 -0
  53. package/dist/components/state/DivState.js +121 -0
  54. package/dist/components/state/DivState.js.map +1 -0
  55. package/dist/components/state/index.d.ts +3 -0
  56. package/dist/components/state/index.d.ts.map +1 -0
  57. package/dist/components/state/index.js +2 -0
  58. package/dist/components/state/index.js.map +1 -0
  59. package/dist/components/text/DivText.d.ts +28 -0
  60. package/dist/components/text/DivText.d.ts.map +1 -0
  61. package/dist/components/text/DivText.js +143 -0
  62. package/dist/components/text/DivText.js.map +1 -0
  63. package/dist/components/text/index.d.ts +3 -0
  64. package/dist/components/text/index.d.ts.map +1 -0
  65. package/dist/components/text/index.js +2 -0
  66. package/dist/components/text/index.js.map +1 -0
  67. package/dist/components/utilities/Outer.d.ts +17 -0
  68. package/dist/components/utilities/Outer.d.ts.map +1 -0
  69. package/dist/components/utilities/Outer.js +210 -0
  70. package/dist/components/utilities/Outer.js.map +1 -0
  71. package/dist/components/utilities/Unknown.d.ts +11 -0
  72. package/dist/components/utilities/Unknown.d.ts.map +1 -0
  73. package/dist/components/utilities/Unknown.js +50 -0
  74. package/dist/components/utilities/Unknown.js.map +1 -0
  75. package/dist/components/utilities/index.d.ts +5 -0
  76. package/dist/components/utilities/index.d.ts.map +1 -0
  77. package/dist/components/utilities/index.js +3 -0
  78. package/dist/components/utilities/index.js.map +1 -0
  79. package/dist/context/ActionContext.d.ts +25 -0
  80. package/dist/context/ActionContext.d.ts.map +1 -0
  81. package/dist/context/ActionContext.js +20 -0
  82. package/dist/context/ActionContext.js.map +1 -0
  83. package/dist/context/DivKitContext.d.ts +33 -0
  84. package/dist/context/DivKitContext.d.ts.map +1 -0
  85. package/dist/context/DivKitContext.js +14 -0
  86. package/dist/context/DivKitContext.js.map +1 -0
  87. package/dist/context/EnabledContext.d.ts +31 -0
  88. package/dist/context/EnabledContext.d.ts.map +1 -0
  89. package/dist/context/EnabledContext.js +31 -0
  90. package/dist/context/EnabledContext.js.map +1 -0
  91. package/dist/context/StateContext.d.ts +57 -0
  92. package/dist/context/StateContext.d.ts.map +1 -0
  93. package/dist/context/StateContext.js +20 -0
  94. package/dist/context/StateContext.js.map +1 -0
  95. package/dist/context/index.d.ts +9 -0
  96. package/dist/context/index.d.ts.map +1 -0
  97. package/dist/context/index.js +9 -0
  98. package/dist/context/index.js.map +1 -0
  99. package/dist/expressions/bigint.d.ts +8 -0
  100. package/dist/expressions/bigint.d.ts.map +1 -0
  101. package/dist/expressions/bigint.js +31 -0
  102. package/dist/expressions/bigint.js.map +1 -0
  103. package/dist/expressions/const.d.ts +15 -0
  104. package/dist/expressions/const.d.ts.map +1 -0
  105. package/dist/expressions/const.js +15 -0
  106. package/dist/expressions/const.js.map +1 -0
  107. package/dist/expressions/eval.d.ts +77 -0
  108. package/dist/expressions/eval.d.ts.map +1 -0
  109. package/dist/expressions/eval.js +459 -0
  110. package/dist/expressions/eval.js.map +1 -0
  111. package/dist/expressions/expressions.d.ts +7 -0
  112. package/dist/expressions/expressions.d.ts.map +1 -0
  113. package/dist/expressions/expressions.js +3191 -0
  114. package/dist/expressions/expressions.js.map +1 -0
  115. package/dist/expressions/funcs/array.d.ts +2 -0
  116. package/dist/expressions/funcs/array.d.ts.map +1 -0
  117. package/dist/expressions/funcs/array.js +381 -0
  118. package/dist/expressions/funcs/array.js.map +1 -0
  119. package/dist/expressions/funcs/colors.d.ts +2 -0
  120. package/dist/expressions/funcs/colors.d.ts.map +1 -0
  121. package/dist/expressions/funcs/colors.js +75 -0
  122. package/dist/expressions/funcs/colors.js.map +1 -0
  123. package/dist/expressions/funcs/customFuncs.d.ts +8 -0
  124. package/dist/expressions/funcs/customFuncs.d.ts.map +1 -0
  125. package/dist/expressions/funcs/customFuncs.js +114 -0
  126. package/dist/expressions/funcs/customFuncs.js.map +1 -0
  127. package/dist/expressions/funcs/datetime.d.ts +2 -0
  128. package/dist/expressions/funcs/datetime.d.ts.map +1 -0
  129. package/dist/expressions/funcs/datetime.js +182 -0
  130. package/dist/expressions/funcs/datetime.js.map +1 -0
  131. package/dist/expressions/funcs/dict.d.ts +2 -0
  132. package/dist/expressions/funcs/dict.d.ts.map +1 -0
  133. package/dist/expressions/funcs/dict.js +170 -0
  134. package/dist/expressions/funcs/dict.js.map +1 -0
  135. package/dist/expressions/funcs/funcs.d.ts +80 -0
  136. package/dist/expressions/funcs/funcs.d.ts.map +1 -0
  137. package/dist/expressions/funcs/funcs.js +146 -0
  138. package/dist/expressions/funcs/funcs.js.map +1 -0
  139. package/dist/expressions/funcs/index.d.ts +2 -0
  140. package/dist/expressions/funcs/index.d.ts.map +1 -0
  141. package/dist/expressions/funcs/index.js +23 -0
  142. package/dist/expressions/funcs/index.js.map +1 -0
  143. package/dist/expressions/funcs/interval.d.ts +2 -0
  144. package/dist/expressions/funcs/interval.d.ts.map +1 -0
  145. package/dist/expressions/funcs/interval.js +61 -0
  146. package/dist/expressions/funcs/interval.js.map +1 -0
  147. package/dist/expressions/funcs/math.d.ts +2 -0
  148. package/dist/expressions/funcs/math.d.ts.map +1 -0
  149. package/dist/expressions/funcs/math.js +324 -0
  150. package/dist/expressions/funcs/math.js.map +1 -0
  151. package/dist/expressions/funcs/std.d.ts +2 -0
  152. package/dist/expressions/funcs/std.d.ts.map +1 -0
  153. package/dist/expressions/funcs/std.js +293 -0
  154. package/dist/expressions/funcs/std.js.map +1 -0
  155. package/dist/expressions/funcs/stored.d.ts +4 -0
  156. package/dist/expressions/funcs/stored.d.ts.map +1 -0
  157. package/dist/expressions/funcs/stored.js +62 -0
  158. package/dist/expressions/funcs/stored.js.map +1 -0
  159. package/dist/expressions/funcs/strings.d.ts +2 -0
  160. package/dist/expressions/funcs/strings.d.ts.map +1 -0
  161. package/dist/expressions/funcs/strings.js +158 -0
  162. package/dist/expressions/funcs/strings.js.map +1 -0
  163. package/dist/expressions/funcs/trigonometry.d.ts +2 -0
  164. package/dist/expressions/funcs/trigonometry.d.ts.map +1 -0
  165. package/dist/expressions/funcs/trigonometry.js +92 -0
  166. package/dist/expressions/funcs/trigonometry.js.map +1 -0
  167. package/dist/expressions/json.d.ts +18 -0
  168. package/dist/expressions/json.d.ts.map +1 -0
  169. package/dist/expressions/json.js +271 -0
  170. package/dist/expressions/json.js.map +1 -0
  171. package/dist/expressions/parserCache.d.ts +4 -0
  172. package/dist/expressions/parserCache.d.ts.map +1 -0
  173. package/dist/expressions/parserCache.js +23 -0
  174. package/dist/expressions/parserCache.js.map +1 -0
  175. package/dist/expressions/simpleUnescapeString.d.ts +2 -0
  176. package/dist/expressions/simpleUnescapeString.d.ts.map +1 -0
  177. package/dist/expressions/simpleUnescapeString.js +61 -0
  178. package/dist/expressions/simpleUnescapeString.js.map +1 -0
  179. package/dist/expressions/utils.d.ts +29 -0
  180. package/dist/expressions/utils.d.ts.map +1 -0
  181. package/dist/expressions/utils.js +236 -0
  182. package/dist/expressions/utils.js.map +1 -0
  183. package/dist/expressions/variable.d.ts +82 -0
  184. package/dist/expressions/variable.d.ts.map +1 -0
  185. package/dist/expressions/variable.js +337 -0
  186. package/dist/expressions/variable.js.map +1 -0
  187. package/dist/expressions/walk.d.ts +7 -0
  188. package/dist/expressions/walk.d.ts.map +1 -0
  189. package/dist/expressions/walk.js +39 -0
  190. package/dist/expressions/walk.js.map +1 -0
  191. package/dist/hooks/index.d.ts +8 -0
  192. package/dist/hooks/index.d.ts.map +1 -0
  193. package/dist/hooks/index.js +11 -0
  194. package/dist/hooks/index.js.map +1 -0
  195. package/dist/hooks/useAction.d.ts +102 -0
  196. package/dist/hooks/useAction.d.ts.map +1 -0
  197. package/dist/hooks/useAction.js +116 -0
  198. package/dist/hooks/useAction.js.map +1 -0
  199. package/dist/hooks/useDerivedFromVars.d.ts +72 -0
  200. package/dist/hooks/useDerivedFromVars.d.ts.map +1 -0
  201. package/dist/hooks/useDerivedFromVars.js +100 -0
  202. package/dist/hooks/useDerivedFromVars.js.map +1 -0
  203. package/dist/hooks/useVariable.d.ts +86 -0
  204. package/dist/hooks/useVariable.d.ts.map +1 -0
  205. package/dist/hooks/useVariable.js +130 -0
  206. package/dist/hooks/useVariable.js.map +1 -0
  207. package/dist/index.d.ts +30 -0
  208. package/dist/index.d.ts.map +1 -0
  209. package/dist/index.js +28 -0
  210. package/dist/index.js.map +1 -0
  211. package/dist/stores/createObservable.d.ts +38 -0
  212. package/dist/stores/createObservable.d.ts.map +1 -0
  213. package/dist/stores/createObservable.js +49 -0
  214. package/dist/stores/createObservable.js.map +1 -0
  215. package/dist/utils/applyTemplate.d.ts +8 -0
  216. package/dist/utils/applyTemplate.d.ts.map +1 -0
  217. package/dist/utils/applyTemplate.js +94 -0
  218. package/dist/utils/applyTemplate.js.map +1 -0
  219. package/dist/utils/correctColor.d.ts +18 -0
  220. package/dist/utils/correctColor.d.ts.map +1 -0
  221. package/dist/utils/correctColor.js +79 -0
  222. package/dist/utils/correctColor.js.map +1 -0
  223. package/dist/utils/escapeRegExp.d.ts +2 -0
  224. package/dist/utils/escapeRegExp.d.ts.map +1 -0
  225. package/dist/utils/escapeRegExp.js +4 -0
  226. package/dist/utils/escapeRegExp.js.map +1 -0
  227. package/dist/utils/formatDate.d.ts +6 -0
  228. package/dist/utils/formatDate.d.ts.map +1 -0
  229. package/dist/utils/formatDate.js +325 -0
  230. package/dist/utils/formatDate.js.map +1 -0
  231. package/dist/utils/padLeft.d.ts +2 -0
  232. package/dist/utils/padLeft.d.ts.map +1 -0
  233. package/dist/utils/padLeft.js +7 -0
  234. package/dist/utils/padLeft.js.map +1 -0
  235. package/dist/utils/uniq.d.ts +2 -0
  236. package/dist/utils/uniq.d.ts.map +1 -0
  237. package/dist/utils/uniq.js +4 -0
  238. package/dist/utils/uniq.js.map +1 -0
  239. package/dist/utils/wrapError.d.ts +10 -0
  240. package/dist/utils/wrapError.d.ts.map +1 -0
  241. package/dist/utils/wrapError.js +9 -0
  242. package/dist/utils/wrapError.js.map +1 -0
  243. package/package.json +58 -0
  244. package/src/DivKit.tsx +542 -0
  245. package/src/actions/array.ts +170 -0
  246. package/src/actions/copyToClipboard.ts +82 -0
  247. package/src/actions/dict.ts +71 -0
  248. package/src/actions/index.ts +11 -0
  249. package/src/actions/updateStructure.ts +134 -0
  250. package/src/components/DivComponent.tsx +75 -0
  251. package/src/components/README.md +230 -0
  252. package/src/components/container/DivContainer.tsx +222 -0
  253. package/src/components/container/index.ts +2 -0
  254. package/src/components/image/DivImage.tsx +172 -0
  255. package/src/components/image/index.ts +2 -0
  256. package/src/components/index.ts +20 -0
  257. package/src/components/state/DivState.tsx +146 -0
  258. package/src/components/state/index.ts +2 -0
  259. package/src/components/text/DivText.tsx +186 -0
  260. package/src/components/text/index.ts +2 -0
  261. package/src/components/utilities/Outer.tsx +239 -0
  262. package/src/components/utilities/README.md +175 -0
  263. package/src/components/utilities/Unknown.tsx +60 -0
  264. package/src/components/utilities/index.ts +4 -0
  265. package/src/context/ActionContext.tsx +37 -0
  266. package/src/context/DivKitContext.tsx +54 -0
  267. package/src/context/EnabledContext.tsx +50 -0
  268. package/src/context/StateContext.tsx +75 -0
  269. package/src/context/index.ts +33 -0
  270. package/src/expressions/ast.d.ts +101 -0
  271. package/src/expressions/bigint.ts +38 -0
  272. package/src/expressions/const.ts +16 -0
  273. package/src/expressions/eval.ts +669 -0
  274. package/src/expressions/expressions.peggy +235 -0
  275. package/src/expressions/expressions.ts +2854 -0
  276. package/src/expressions/funcs/array.ts +412 -0
  277. package/src/expressions/funcs/colors.ts +100 -0
  278. package/src/expressions/funcs/customFuncs.ts +139 -0
  279. package/src/expressions/funcs/datetime.ts +232 -0
  280. package/src/expressions/funcs/dict.ts +207 -0
  281. package/src/expressions/funcs/funcs.ts +323 -0
  282. package/src/expressions/funcs/index.ts +23 -0
  283. package/src/expressions/funcs/interval.ts +76 -0
  284. package/src/expressions/funcs/math.ts +395 -0
  285. package/src/expressions/funcs/std.ts +392 -0
  286. package/src/expressions/funcs/stored.ts +62 -0
  287. package/src/expressions/funcs/strings.ts +200 -0
  288. package/src/expressions/funcs/trigonometry.ts +108 -0
  289. package/src/expressions/json.ts +367 -0
  290. package/src/expressions/parserCache.ts +32 -0
  291. package/src/expressions/simpleUnescapeString.ts +57 -0
  292. package/src/expressions/utils.ts +271 -0
  293. package/src/expressions/variable.ts +429 -0
  294. package/src/expressions/walk.ts +43 -0
  295. package/src/hooks/README.md +265 -0
  296. package/src/hooks/index.ts +28 -0
  297. package/src/hooks/useAction.ts +152 -0
  298. package/src/hooks/useDerivedFromVars.ts +187 -0
  299. package/src/hooks/useVariable.ts +157 -0
  300. package/src/index.ts +97 -0
  301. package/src/stores/createObservable.ts +64 -0
  302. package/src/types/alignment.d.ts +13 -0
  303. package/src/types/background.d.ts +71 -0
  304. package/src/types/base.d.ts +224 -0
  305. package/src/types/border.d.ts +46 -0
  306. package/src/types/componentContext.d.ts +98 -0
  307. package/src/types/container.d.ts +40 -0
  308. package/src/types/edgeInserts.d.ts +9 -0
  309. package/src/types/general.d.ts +3 -0
  310. package/src/types/image.d.ts +33 -0
  311. package/src/types/imageScale.d.ts +1 -0
  312. package/src/types/layoutParams.d.ts +27 -0
  313. package/src/types/sizes.d.ts +37 -0
  314. package/src/types/state.d.ts +19 -0
  315. package/src/types/text.d.ts +126 -0
  316. package/src/utils/applyTemplate.ts +145 -0
  317. package/src/utils/correctColor.ts +102 -0
  318. package/src/utils/escapeRegExp.ts +3 -0
  319. package/src/utils/formatDate.ts +385 -0
  320. package/src/utils/padLeft.ts +6 -0
  321. package/src/utils/uniq.ts +3 -0
  322. package/src/utils/wrapError.ts +21 -0
@@ -0,0 +1,43 @@
1
+ import type { Node } from './ast';
2
+
3
+ export function walk(ast: Node, visitors: {
4
+ [Type in Node['type']]?: (node: Extract<Node, { type: Type }>) => void;
5
+ }): void {
6
+ visitors[ast.type]?.(ast as any);
7
+
8
+ switch (ast.type) {
9
+ case 'TemplateLiteral':
10
+ ast.expressions.forEach(item => {
11
+ walk(item, visitors);
12
+ });
13
+ break;
14
+ case 'BinaryExpression':
15
+ case 'LogicalExpression':
16
+ walk(ast.left, visitors);
17
+ walk(ast.right, visitors);
18
+ break;
19
+ case 'UnaryExpression':
20
+ walk(ast.argument, visitors);
21
+ break;
22
+ case 'ConditionalExpression':
23
+ walk(ast.test, visitors);
24
+ walk(ast.consequent, visitors);
25
+ walk(ast.alternate, visitors);
26
+ break;
27
+ case 'TryExpression':
28
+ walk(ast.test, visitors);
29
+ walk(ast.alternate, visitors);
30
+ break;
31
+ case 'CallExpression':
32
+ ast.arguments.forEach(item => {
33
+ walk(item, visitors);
34
+ });
35
+ break;
36
+ case 'MethodExpression':
37
+ walk(ast.object, visitors);
38
+ ast.arguments.forEach(item => {
39
+ walk(item, visitors);
40
+ });
41
+ break;
42
+ }
43
+ }
@@ -0,0 +1,265 @@
1
+ # DivKit React Hooks
2
+
3
+ Custom React hooks для работы с DivKit в React Native приложениях.
4
+
5
+ ## Hooks Overview
6
+
7
+ ### 1. `useDerivedFromVars` - Реактивность переменных
8
+
9
+ Главный hook для подстановки переменных в JSON props с автоматическим обновлением при изменении переменных.
10
+
11
+ **Использование:**
12
+ ```tsx
13
+ import { useDerivedFromVars } from './hooks';
14
+
15
+ function DivText({ json, variables }: Props) {
16
+ // json.text = "Hello @{userName}!"
17
+ const text = useDerivedFromVars(json.text, { variables });
18
+ // text = "Hello Alice!" - обновляется при изменении userName
19
+
20
+ return <Text>{text}</Text>;
21
+ }
22
+ ```
23
+
24
+ **Опции:**
25
+ - `variables` - Map переменных из DivKitContext
26
+ - `additionalVars` - дополнительные локальные переменные
27
+ - `keepComplex` - сохранять dict/array как объекты (не stringify)
28
+ - `maxDepth` - максимальная глубина обработки (default: 10)
29
+ - `customFunctions` - кастомные функции для expressions
30
+ - `weekStartDay` - день начала недели (0 = воскресенье)
31
+
32
+ **Simplified version:**
33
+ ```tsx
34
+ const text = useDerivedFromVarsSimple(json.text, variables);
35
+ ```
36
+
37
+ ---
38
+
39
+ ### 2. `useVariable` - Подписка на переменную
40
+
41
+ Подписывается на конкретную переменную по имени и обновляет компонент при её изменении.
42
+
43
+ **Использование:**
44
+ ```tsx
45
+ import { useVariable } from './hooks';
46
+
47
+ function UserGreeting() {
48
+ const userName = useVariable('userName');
49
+
50
+ return <Text>Hello {userName}</Text>;
51
+ }
52
+ ```
53
+
54
+ **Related hooks:**
55
+ - `useVariableInstance(variable)` - подписка на Variable инстанс
56
+ - `useVariableSetter(name)` - получить setter функцию
57
+ - `useVariableState(name)` - получить [value, setter] tuple (как useState)
58
+
59
+ **Example with setter:**
60
+ ```tsx
61
+ function Counter() {
62
+ const [count, setCount] = useVariableState('counter');
63
+
64
+ return (
65
+ <View>
66
+ <Text>Count: {count}</Text>
67
+ <Button title="+" onPress={() => setCount((count || 0) + 1)} />
68
+ </View>
69
+ );
70
+ }
71
+ ```
72
+
73
+ ---
74
+
75
+ ### 3. `useAction` - Выполнение actions
76
+
77
+ Hooks для выполнения DivKit actions (click, visibility, etc.)
78
+
79
+ **Использование:**
80
+ ```tsx
81
+ import { useActionHandler } from './hooks';
82
+
83
+ function DivText({ json }: { json: DivTextJson }) {
84
+ const onPress = useActionHandler(json.actions);
85
+
86
+ return (
87
+ <Pressable onPress={onPress}>
88
+ <Text>{json.text}</Text>
89
+ </Pressable>
90
+ );
91
+ }
92
+ ```
93
+
94
+ **Available hooks:**
95
+ - `useAction(options)` - выполнить одиночный action
96
+ - `useActions(options)` - выполнить массив actions
97
+ - `useActionHandler(actions, options)` - получить onPress handler
98
+ - `useHasActions(actions)` - проверить наличие actions
99
+
100
+ **Example with useAction:**
101
+ ```tsx
102
+ function MyButton() {
103
+ const execAction = useAction({ processUrls: true });
104
+
105
+ const handlePress = () => {
106
+ execAction({
107
+ log_id: 'button_click',
108
+ typed: { type: 'set_variable', variable_name: 'counter', value: 42 }
109
+ });
110
+ };
111
+
112
+ return <Button title="Click" onPress={handlePress} />;
113
+ }
114
+ ```
115
+
116
+ ---
117
+
118
+ ## Architecture
119
+
120
+ ### Паттерн реализации
121
+
122
+ Все hooks следуют единому паттерну:
123
+
124
+ 1. **Получение контекста** - через `useDivKitContext()`
125
+ 2. **Подписка на изменения** - через `useEffect` + Variable.subscribe()
126
+ 3. **Автоматический cleanup** - unsubscribe в return useEffect
127
+
128
+ ### Зависимости
129
+
130
+ - `DivKitContext` - главный контекст с переменными и actions
131
+ - `Observable` - реализация реактивности (замена Svelte stores)
132
+ - `Variable` - классы переменных с методом subscribe()
133
+ - `prepareVars` / `applyVars` - обработка expressions в JSON
134
+
135
+ ### Связь с Web реализацией
136
+
137
+ Эти hooks заменяют Svelte реактивные конструкции:
138
+
139
+ | Svelte (Web) | React Native (Hooks) |
140
+ |--------------|----------------------|
141
+ | `$: derived = getDerivedFromVars(json)` | `useDerivedFromVars(json, { variables })` |
142
+ | `$variable` (auto-subscribe) | `useVariable('variable')` |
143
+ | `execAnyActions(actions)` | `useActions(options)` |
144
+
145
+ ---
146
+
147
+ ## Best Practices
148
+
149
+ ### 1. Оптимизация производительности
150
+
151
+ ```tsx
152
+ // ❌ Плохо - создается новый объект options каждый рендер
153
+ const text = useDerivedFromVars(json.text, { variables, keepComplex: true });
154
+
155
+ // ✅ Хорошо - используем useMemo для стабильного объекта
156
+ const options = useMemo(() => ({ variables, keepComplex: true }), [variables]);
157
+ const text = useDerivedFromVars(json.text, options);
158
+
159
+ // ✅ Или используйте simplified version для базовых случаев
160
+ const text = useDerivedFromVarsSimple(json.text, variables);
161
+ ```
162
+
163
+ ### 2. Условная подписка
164
+
165
+ ```tsx
166
+ // useVariable автоматически обрабатывает отсутствие переменной
167
+ const userName = useVariable('userName'); // undefined если не найдена
168
+
169
+ // Для проверки наличия:
170
+ if (userName !== undefined) {
171
+ // переменная существует
172
+ }
173
+ ```
174
+
175
+ ### 3. Action handling
176
+
177
+ ```tsx
178
+ // ✅ Простой случай - используйте useActionHandler
179
+ const onPress = useActionHandler(json.actions);
180
+
181
+ // ✅ Сложный случай - используйте useActions для контроля
182
+ const execActions = useActions({ componentContext, processUrls: false });
183
+ const handlePress = async () => {
184
+ await execActions(json.actions);
185
+ console.log('Actions completed');
186
+ };
187
+ ```
188
+
189
+ ---
190
+
191
+ ## Testing
192
+
193
+ ### Unit тесты
194
+
195
+ ```tsx
196
+ import { renderHook, act } from '@testing-library/react-hooks';
197
+ import { useVariable } from './hooks';
198
+ import { createVariable } from '../expressions/variable';
199
+
200
+ test('useVariable subscribes and updates', () => {
201
+ const variable = createVariable('test', 'string', 'initial');
202
+ const variables = new Map([['test', variable]]);
203
+
204
+ const { result } = renderHook(() => useVariable('test'), {
205
+ wrapper: ({ children }) => (
206
+ <DivKitContext.Provider value={{ variables, /* ... */ }}>
207
+ {children}
208
+ </DivKitContext.Provider>
209
+ )
210
+ });
211
+
212
+ expect(result.current).toBe('initial');
213
+
214
+ act(() => {
215
+ variable.setValue('updated');
216
+ });
217
+
218
+ expect(result.current).toBe('updated');
219
+ });
220
+ ```
221
+
222
+ ---
223
+
224
+ ## Migration from Web
225
+
226
+ При портировании компонентов из Web версии:
227
+
228
+ 1. Замените `$: derived = getDerivedFromVars(...)` на `useDerivedFromVars(...)`
229
+ 2. Замените `$variable` на `useVariable('variable')`
230
+ 3. Замените прямые вызовы `execAnyActions` на `useActions()` hook
231
+ 4. Удалите `onMount` и замените на `useEffect`
232
+
233
+ **Пример миграции:**
234
+
235
+ ```svelte
236
+ <!-- Web (Svelte) -->
237
+ <script>
238
+ import { getContext } from 'svelte';
239
+ const { variables } = getContext(ROOT_CTX);
240
+
241
+ $: text = getDerivedFromVars(json.text);
242
+ $: color = getDerivedFromVars(json.color);
243
+ </script>
244
+ ```
245
+
246
+ ```tsx
247
+ // React Native
248
+ function DivText({ json }: Props) {
249
+ const { variables } = useDivKitContext();
250
+
251
+ const text = useDerivedFromVarsSimple(json.text, variables);
252
+ const color = useDerivedFromVarsSimple(json.color, variables);
253
+
254
+ return <Text style={{ color }}>{text}</Text>;
255
+ }
256
+ ```
257
+
258
+ ---
259
+
260
+ ## Next Steps
261
+
262
+ После реализации hooks, следующие фазы:
263
+ - **Phase 4:** Base Component Wrapper (Outer.tsx)
264
+ - **Phase 5:** MVP Components (Text, Container, Image, State)
265
+ - **Phase 6:** Main DivKit Component integration
@@ -0,0 +1,28 @@
1
+ /**
2
+ * DivKit React Hooks
3
+ * Export all custom hooks for use in React Native components
4
+ */
5
+
6
+ // Main reactivity hook - variable substitution in JSON
7
+ export {
8
+ useDerivedFromVars,
9
+ useDerivedFromVarsSimple,
10
+ type UseDerivedFromVarsOptions
11
+ } from './useDerivedFromVars';
12
+
13
+ // Variable subscription hooks
14
+ export {
15
+ useVariable,
16
+ useVariableInstance,
17
+ useVariableSetter,
18
+ useVariableState
19
+ } from './useVariable';
20
+
21
+ // Action execution hooks
22
+ export {
23
+ useAction,
24
+ useActions,
25
+ useActionHandler,
26
+ useHasActions,
27
+ type UseActionOptions
28
+ } from './useAction';
@@ -0,0 +1,152 @@
1
+ /**
2
+ * React hooks for executing DivKit actions
3
+ * Handles click actions, visibility actions, and other user interactions
4
+ */
5
+
6
+ import { useCallback } from 'react';
7
+ import type { Action } from '../../typings/common';
8
+ import type { MaybeMissing } from '../expressions/json';
9
+ import { useDivKitContext } from '../context/DivKitContext';
10
+ import type { ComponentContext } from '../types/componentContext';
11
+
12
+ export interface UseActionOptions {
13
+ /**
14
+ * Component context (for action execution context)
15
+ */
16
+ componentContext?: ComponentContext;
17
+
18
+ /**
19
+ * Whether to process URLs (open external links)
20
+ * Default: true
21
+ */
22
+ processUrls?: boolean;
23
+ }
24
+
25
+ /**
26
+ * Hook to execute a single action
27
+ *
28
+ * @param options Action execution options
29
+ * @returns Function to execute an action
30
+ *
31
+ * @example
32
+ * ```tsx
33
+ * function MyButton({ action }: { action: Action }) {
34
+ * const execAction = useAction();
35
+ *
36
+ * return (
37
+ * <Pressable onPress={() => execAction(action)}>
38
+ * <Text>Click me</Text>
39
+ * </Pressable>
40
+ * );
41
+ * }
42
+ * ```
43
+ */
44
+ export function useAction(options?: UseActionOptions) {
45
+ const { execAnyActions } = useDivKitContext();
46
+
47
+ return useCallback(
48
+ async (action: MaybeMissing<Action> | undefined) => {
49
+ if (!action) {
50
+ return;
51
+ }
52
+
53
+ await execAnyActions([action], options);
54
+ },
55
+ [execAnyActions, options]
56
+ );
57
+ }
58
+
59
+ /**
60
+ * Hook to execute multiple actions
61
+ *
62
+ * @param options Action execution options
63
+ * @returns Function to execute an array of actions
64
+ *
65
+ * @example
66
+ * ```tsx
67
+ * function MyButton({ actions }: { actions: Action[] }) {
68
+ * const execActions = useActions();
69
+ *
70
+ * return (
71
+ * <Pressable onPress={() => execActions(actions)}>
72
+ * <Text>Click me</Text>
73
+ * </Pressable>
74
+ * );
75
+ * }
76
+ * ```
77
+ */
78
+ export function useActions(options?: UseActionOptions) {
79
+ const { execAnyActions } = useDivKitContext();
80
+
81
+ return useCallback(
82
+ async (actions: MaybeMissing<Action[]> | undefined) => {
83
+ if (!actions || actions.length === 0) {
84
+ return;
85
+ }
86
+
87
+ await execAnyActions(actions, options);
88
+ },
89
+ [execAnyActions, options]
90
+ );
91
+ }
92
+
93
+ /**
94
+ * Hook to create an onPress handler from actions
95
+ * Convenience hook for Pressable/TouchableOpacity components
96
+ *
97
+ * @param actions Actions to execute on press
98
+ * @param options Action execution options
99
+ * @returns onPress handler function, or undefined if no actions
100
+ *
101
+ * @example
102
+ * ```tsx
103
+ * function DivText({ json }: { json: DivTextJson }) {
104
+ * const onPress = useActionHandler(json.actions);
105
+ *
106
+ * return (
107
+ * <Pressable onPress={onPress}>
108
+ * <Text>{json.text}</Text>
109
+ * </Pressable>
110
+ * );
111
+ * }
112
+ * ```
113
+ */
114
+ export function useActionHandler(
115
+ actions: MaybeMissing<Action[]> | undefined,
116
+ options?: UseActionOptions
117
+ ): (() => Promise<void>) | undefined {
118
+ const execActions = useActions(options);
119
+
120
+ return useCallback(() => {
121
+ if (!actions || actions.length === 0) {
122
+ return Promise.resolve();
123
+ }
124
+ return execActions(actions);
125
+ }, [actions, execActions]);
126
+ }
127
+
128
+ /**
129
+ * Hook to check if actions array is not empty
130
+ * Useful for conditional rendering or styling
131
+ *
132
+ * @param actions Actions to check
133
+ * @returns true if actions exist and are not empty
134
+ *
135
+ * @example
136
+ * ```tsx
137
+ * function DivText({ json }: { json: DivTextJson }) {
138
+ * const hasActions = useHasActions(json.actions);
139
+ *
140
+ * return (
141
+ * <Text style={{ cursor: hasActions ? 'pointer' : 'default' }}>
142
+ * {json.text}
143
+ * </Text>
144
+ * );
145
+ * }
146
+ * ```
147
+ */
148
+ export function useHasActions(
149
+ actions: MaybeMissing<Action[]> | undefined
150
+ ): boolean {
151
+ return Boolean(actions && actions.length > 0);
152
+ }
@@ -0,0 +1,187 @@
1
+ /**
2
+ * React hook for reactive variable substitution in JSON props
3
+ * Replaces Svelte's $: reactive statements with React hooks
4
+ *
5
+ * Based on getDerivedFromVars pattern from Web Root.svelte
6
+ */
7
+
8
+ import { useEffect, useState, useMemo, useRef } from 'react';
9
+ import type { MaybeMissing } from '../expressions/json';
10
+ import { prepareVars } from '../expressions/json';
11
+ import type { Variable } from '../expressions/variable';
12
+ import type { VariablesMap } from '../expressions/eval';
13
+ import type { Store } from '../../typings/store';
14
+ import type { CustomFunctions } from '../expressions/funcs/customFuncs';
15
+ import type { Unsubscriber } from '../stores/createObservable';
16
+
17
+ export interface UseDerivedFromVarsOptions {
18
+ /**
19
+ * Variables map (from DivKitContext)
20
+ */
21
+ variables: VariablesMap;
22
+
23
+ /**
24
+ * Additional variables (optional, for local context)
25
+ */
26
+ additionalVars?: Map<string, Variable>;
27
+
28
+ /**
29
+ * Keep complex types (dict, array) as objects instead of stringifying
30
+ * Default: false
31
+ */
32
+ keepComplex?: boolean;
33
+
34
+ /**
35
+ * Maximum depth for expression processing
36
+ * Default: 10
37
+ */
38
+ maxDepth?: number;
39
+
40
+ /**
41
+ * Custom functions for expressions
42
+ */
43
+ customFunctions?: CustomFunctions;
44
+
45
+ /**
46
+ * Store context (for advanced features)
47
+ */
48
+ store?: Store;
49
+
50
+ /**
51
+ * Week start day (0 = Sunday, 1 = Monday)
52
+ * Default: 0
53
+ */
54
+ weekStartDay?: number;
55
+
56
+ /**
57
+ * Error logger
58
+ */
59
+ logError?: (error: Error) => void;
60
+ }
61
+
62
+ /**
63
+ * Hook that subscribes to variables used in JSON expressions and re-evaluates when they change
64
+ *
65
+ * @example
66
+ * ```tsx
67
+ * const text = useDerivedFromVars(
68
+ * json.text, // "Hello @{userName}"
69
+ * { variables }
70
+ * );
71
+ * // text = "Hello Alice"
72
+ * // When userName variable changes, text automatically updates
73
+ * ```
74
+ */
75
+ export function useDerivedFromVars<T>(
76
+ jsonProp: T,
77
+ options: UseDerivedFromVarsOptions
78
+ ): MaybeMissing<T> {
79
+ const {
80
+ variables,
81
+ additionalVars,
82
+ keepComplex = false,
83
+ maxDepth = 10,
84
+ customFunctions,
85
+ store,
86
+ weekStartDay = 0,
87
+ logError = console.error
88
+ } = options;
89
+
90
+ // Merge variables maps
91
+ const allVariables = useMemo(() => {
92
+ if (!additionalVars || additionalVars.size === 0) {
93
+ return variables;
94
+ }
95
+ const merged = new Map(variables);
96
+ additionalVars.forEach((value, key) => {
97
+ merged.set(key, value);
98
+ });
99
+ return merged;
100
+ }, [variables, additionalVars]);
101
+
102
+ // Prepare expressions once (parse and analyze JSON for @{} expressions)
103
+ const prepared = useMemo(() => {
104
+ return prepareVars(jsonProp, logError, store, weekStartDay, maxDepth);
105
+ }, [jsonProp, logError, store, weekStartDay, maxDepth]);
106
+
107
+ // Apply variables and get initial value
108
+ const getComputedValue = () => {
109
+ const { result, usedVars } = prepared.applyVars(
110
+ allVariables,
111
+ customFunctions,
112
+ keepComplex
113
+ );
114
+ return { result, usedVars };
115
+ };
116
+
117
+ // State for the computed value
118
+ const [value, setValue] = useState<MaybeMissing<T>>(() => getComputedValue().result);
119
+
120
+ // Track used variables for subscription
121
+ const usedVarsRef = useRef<Set<Variable>>();
122
+
123
+ // Subscribe to used variables and update when they change
124
+ useEffect(() => {
125
+ // If no expressions, no need to subscribe
126
+ if (!prepared.hasExpression) {
127
+ return;
128
+ }
129
+
130
+ // Compute initial value and get used variables
131
+ const { result, usedVars } = getComputedValue();
132
+ setValue(result);
133
+ usedVarsRef.current = usedVars;
134
+
135
+ // Subscribe to all used variables
136
+ const unsubscribers: Unsubscriber[] = [];
137
+
138
+ if (usedVars) {
139
+ usedVars.forEach(variable => {
140
+ const unsubscribe = variable.subscribe(() => {
141
+ // Variable changed - recompute value
142
+ const { result: newResult, usedVars: newUsedVars } = getComputedValue();
143
+ setValue(newResult);
144
+
145
+ // If used variables changed, we need to re-subscribe
146
+ // This happens if expression result depends on conditional logic
147
+ if (newUsedVars && newUsedVars !== usedVarsRef.current) {
148
+ const hasChanged =
149
+ newUsedVars.size !== usedVarsRef.current?.size ||
150
+ Array.from(newUsedVars).some(v => !usedVarsRef.current?.has(v));
151
+
152
+ if (hasChanged) {
153
+ // usedVars changed - trigger re-render to re-subscribe
154
+ usedVarsRef.current = newUsedVars;
155
+ }
156
+ }
157
+ });
158
+ unsubscribers.push(unsubscribe);
159
+ });
160
+ }
161
+
162
+ return () => {
163
+ unsubscribers.forEach(unsub => unsub());
164
+ };
165
+ }, [prepared, allVariables, customFunctions, keepComplex]);
166
+
167
+ if (!prepared.hasExpression) {
168
+ return getComputedValue().result;
169
+ }
170
+
171
+ return value;
172
+ }
173
+
174
+ /**
175
+ * Simplified version that only takes variables map (most common use case)
176
+ *
177
+ * @example
178
+ * ```tsx
179
+ * const text = useDerivedFromVarsSimple(json.text, variables);
180
+ * ```
181
+ */
182
+ export function useDerivedFromVarsSimple<T>(
183
+ jsonProp: T,
184
+ variables: VariablesMap
185
+ ): MaybeMissing<T> {
186
+ return useDerivedFromVars(jsonProp, { variables });
187
+ }