react-native-divkit 0.1.0-alpha.2 → 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.
- package/README.md +112 -111
- package/dist/components/utilities/Background.d.ts +11 -0
- package/dist/components/utilities/Background.d.ts.map +1 -0
- package/dist/components/utilities/Background.js +73 -0
- package/dist/components/utilities/Background.js.map +1 -0
- package/dist/components/utilities/Outer.d.ts.map +1 -1
- package/dist/components/utilities/Outer.js +22 -9
- package/dist/components/utilities/Outer.js.map +1 -1
- package/package.json +7 -6
- package/src/components/utilities/Background.tsx +120 -0
- package/src/components/utilities/Outer.tsx +21 -10
- package/src/components/utilities/README.md +4 -3
package/README.md
CHANGED
|
@@ -1,51 +1,60 @@
|
|
|
1
1
|
# react-native-divkit
|
|
2
2
|
|
|
3
|
-
DivKit
|
|
3
|
+
Рендерер DivKit для React Native — фреймворк для Server-Driven UI.
|
|
4
4
|
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
[](https://www.npmjs.com/package/react-native-divkit)
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## Обзор
|
|
9
9
|
|
|
10
|
-
DivKit
|
|
10
|
+
DivKit — это фреймворк для построения UI на основе данных с сервера (Server-Driven UI), который позволяет описывать макеты в формате JSON и рендерить их нативно. Данная реализация для React Native основана на веб-версии DivKit, переиспользуя движок выражений и адаптируя компоненты под React Native.
|
|
11
11
|
|
|
12
|
-
##
|
|
12
|
+
## Статус
|
|
13
13
|
|
|
14
|
-
**MVP
|
|
14
|
+
**MVP Версия 0.1.0-alpha**
|
|
15
15
|
|
|
16
|
-
|
|
|
16
|
+
| Функция | Статус |
|
|
17
17
|
| ------------------- | ----------- |
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
|
|
|
21
|
-
|
|
|
22
|
-
|
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
27
|
-
##
|
|
18
|
+
| Текстовый компонент | ✅ Готово |
|
|
19
|
+
| Компонент контейнера| ✅ Готово |
|
|
20
|
+
| Компонент изображения| ✅ Готово |
|
|
21
|
+
| Компонент состояния | ✅ Готово |
|
|
22
|
+
| Система переменных | ✅ Готово |
|
|
23
|
+
| Движок выражений | ✅ Готово |
|
|
24
|
+
| Обработчики действий| ✅ Готово |
|
|
25
|
+
| Подстановка шаблонов| ✅ Готово |
|
|
26
|
+
|
|
27
|
+
## Скриншоты
|
|
28
|
+
|
|
29
|
+
<p>
|
|
30
|
+
<img src="screenshots/image1.png" width="200" alt="" />
|
|
31
|
+
<img src="screenshots/image3.png" width="200" alt="" />
|
|
32
|
+
<img src="screenshots/image4.png" width="200" alt="" />
|
|
33
|
+
<img src="screenshots/image8.png" width="200" alt="" />
|
|
34
|
+
</p>
|
|
35
|
+
|
|
36
|
+
## Установка
|
|
28
37
|
|
|
29
38
|
```bash
|
|
30
39
|
npm install react-native-divkit
|
|
31
40
|
```
|
|
32
41
|
|
|
33
|
-
###
|
|
42
|
+
### Опциональные зависимости
|
|
34
43
|
|
|
35
|
-
|
|
44
|
+
Для расширенной функциональности установите следующие пакеты:
|
|
36
45
|
|
|
37
46
|
```bash
|
|
38
|
-
#
|
|
47
|
+
# Оптимизированная загрузка изображений с кешированием
|
|
39
48
|
npm install react-native-fast-image
|
|
40
49
|
|
|
41
|
-
#
|
|
50
|
+
# Поддержка градиентов (фоны)
|
|
42
51
|
npm install react-native-linear-gradient
|
|
43
52
|
|
|
44
|
-
#
|
|
53
|
+
# Поддержка буфера обмена
|
|
45
54
|
npm install @react-native-clipboard/clipboard
|
|
46
55
|
```
|
|
47
56
|
|
|
48
|
-
##
|
|
57
|
+
## Быстрый старт
|
|
49
58
|
|
|
50
59
|
```tsx
|
|
51
60
|
import { DivKit } from 'react-native-divkit';
|
|
@@ -58,7 +67,7 @@ const divKitJson = {
|
|
|
58
67
|
state_id: 0,
|
|
59
68
|
div: {
|
|
60
69
|
type: 'text',
|
|
61
|
-
text: '
|
|
70
|
+
text: 'Привет, @{name}!',
|
|
62
71
|
font_size: 24,
|
|
63
72
|
text_color: '#000000',
|
|
64
73
|
text_alignment_horizontal: 'center'
|
|
@@ -69,7 +78,7 @@ const divKitJson = {
|
|
|
69
78
|
{
|
|
70
79
|
type: 'string',
|
|
71
80
|
name: 'name',
|
|
72
|
-
value: '
|
|
81
|
+
value: 'Мир'
|
|
73
82
|
}
|
|
74
83
|
]
|
|
75
84
|
}
|
|
@@ -79,22 +88,22 @@ export default function App() {
|
|
|
79
88
|
return (
|
|
80
89
|
<DivKit
|
|
81
90
|
data={divKitJson}
|
|
82
|
-
onStat={stat => console.log('
|
|
83
|
-
onCustomAction={action => console.log('
|
|
84
|
-
onError={error => console.error('
|
|
91
|
+
onStat={stat => console.log('Статистика:', stat.type, stat.action.log_id)}
|
|
92
|
+
onCustomAction={action => console.log('Кастомное действие:', action.url)}
|
|
93
|
+
onError={error => console.error('Ошибка:', error.message)}
|
|
85
94
|
/>
|
|
86
95
|
);
|
|
87
96
|
}
|
|
88
97
|
```
|
|
89
98
|
|
|
90
|
-
##
|
|
99
|
+
## Компоненты
|
|
91
100
|
|
|
92
|
-
### Text
|
|
101
|
+
### Text (Текст)
|
|
93
102
|
|
|
94
103
|
```json
|
|
95
104
|
{
|
|
96
105
|
"type": "text",
|
|
97
|
-
"text": "
|
|
106
|
+
"text": "Привет, мир",
|
|
98
107
|
"font_size": 16,
|
|
99
108
|
"font_weight": "bold",
|
|
100
109
|
"text_color": "#000000",
|
|
@@ -103,21 +112,21 @@ export default function App() {
|
|
|
103
112
|
}
|
|
104
113
|
```
|
|
105
114
|
|
|
106
|
-
### Container
|
|
115
|
+
### Container (Контейнер)
|
|
107
116
|
|
|
108
117
|
```json
|
|
109
118
|
{
|
|
110
119
|
"type": "container",
|
|
111
120
|
"orientation": "vertical",
|
|
112
121
|
"items": [
|
|
113
|
-
{ "type": "text", "text": "
|
|
114
|
-
{ "type": "text", "text": "
|
|
122
|
+
{ "type": "text", "text": "Элемент 1" },
|
|
123
|
+
{ "type": "text", "text": "Элемент 2" }
|
|
115
124
|
],
|
|
116
125
|
"content_alignment_horizontal": "center"
|
|
117
126
|
}
|
|
118
127
|
```
|
|
119
128
|
|
|
120
|
-
### Image
|
|
129
|
+
### Image (Изображение)
|
|
121
130
|
|
|
122
131
|
```json
|
|
123
132
|
{
|
|
@@ -129,7 +138,7 @@ export default function App() {
|
|
|
129
138
|
}
|
|
130
139
|
```
|
|
131
140
|
|
|
132
|
-
### State
|
|
141
|
+
### State (Состояние)
|
|
133
142
|
|
|
134
143
|
```json
|
|
135
144
|
{
|
|
@@ -139,25 +148,25 @@ export default function App() {
|
|
|
139
148
|
"states": [
|
|
140
149
|
{
|
|
141
150
|
"state_id": "state1",
|
|
142
|
-
"div": { "type": "text", "text": "
|
|
151
|
+
"div": { "type": "text", "text": "Состояние 1" }
|
|
143
152
|
},
|
|
144
153
|
{
|
|
145
154
|
"state_id": "state2",
|
|
146
|
-
"div": { "type": "text", "text": "
|
|
155
|
+
"div": { "type": "text", "text": "Состояние 2" }
|
|
147
156
|
}
|
|
148
157
|
]
|
|
149
158
|
}
|
|
150
159
|
```
|
|
151
160
|
|
|
152
|
-
##
|
|
161
|
+
## Переменные
|
|
153
162
|
|
|
154
|
-
|
|
163
|
+
Объявление переменных в JSON:
|
|
155
164
|
|
|
156
165
|
```json
|
|
157
166
|
{
|
|
158
167
|
"card": {
|
|
159
168
|
"variables": [
|
|
160
|
-
{ "type": "string", "name": "userName", "value": "
|
|
169
|
+
{ "type": "string", "name": "userName", "value": "Мир" },
|
|
161
170
|
{ "type": "integer", "name": "counter", "value": 0 },
|
|
162
171
|
{ "type": "color", "name": "textColor", "value": "#FF0000" },
|
|
163
172
|
{ "type": "boolean", "name": "isActive", "value": true }
|
|
@@ -166,37 +175,37 @@ Define variables in your JSON:
|
|
|
166
175
|
}
|
|
167
176
|
```
|
|
168
177
|
|
|
169
|
-
|
|
178
|
+
Использование переменных в выражениях:
|
|
170
179
|
|
|
171
180
|
```json
|
|
172
181
|
{
|
|
173
182
|
"type": "text",
|
|
174
|
-
"text": "
|
|
183
|
+
"text": "Привет, @{userName}!",
|
|
175
184
|
"text_color": "@{textColor}"
|
|
176
185
|
}
|
|
177
186
|
```
|
|
178
187
|
|
|
179
|
-
###
|
|
188
|
+
### Типы переменных
|
|
180
189
|
|
|
181
|
-
|
|
|
190
|
+
| Тип | Описание | Пример |
|
|
182
191
|
| --------- | ---------------- | ------------------ |
|
|
183
|
-
| `string` |
|
|
184
|
-
| `integer` |
|
|
185
|
-
| `number` |
|
|
186
|
-
| `boolean` |
|
|
187
|
-
| `color` |
|
|
188
|
-
| `url` | URL
|
|
189
|
-
| `dict` |
|
|
190
|
-
| `array` |
|
|
192
|
+
| `string` | Текстовая строка | `"Hello"` |
|
|
193
|
+
| `integer` | Целое число | `42` |
|
|
194
|
+
| `number` | Дробное число | `3.14` |
|
|
195
|
+
| `boolean` | Логическое | `true` |
|
|
196
|
+
| `color` | Цвет | `"#FF5500"` |
|
|
197
|
+
| `url` | URL | `"https://..."` |
|
|
198
|
+
| `dict` | Словарь (объект) | `{"key": "value"}` |
|
|
199
|
+
| `array` | Список (массив) | `[1, 2, 3]` |
|
|
191
200
|
|
|
192
|
-
## Actions
|
|
201
|
+
## Действия (Actions)
|
|
193
202
|
|
|
194
|
-
|
|
203
|
+
Действия вызываются при взаимодействии с пользователем:
|
|
195
204
|
|
|
196
205
|
```json
|
|
197
206
|
{
|
|
198
207
|
"type": "text",
|
|
199
|
-
"text": "
|
|
208
|
+
"text": "Нажми меня",
|
|
200
209
|
"actions": [
|
|
201
210
|
{
|
|
202
211
|
"log_id": "button_tap",
|
|
@@ -206,9 +215,9 @@ Actions are triggered by user interaction:
|
|
|
206
215
|
}
|
|
207
216
|
```
|
|
208
217
|
|
|
209
|
-
###
|
|
218
|
+
### Типизированные действия
|
|
210
219
|
|
|
211
|
-
#### set_variable
|
|
220
|
+
#### set_variable (установка переменной)
|
|
212
221
|
|
|
213
222
|
```json
|
|
214
223
|
{
|
|
@@ -220,7 +229,7 @@ Actions are triggered by user interaction:
|
|
|
220
229
|
}
|
|
221
230
|
```
|
|
222
231
|
|
|
223
|
-
#### set_state
|
|
232
|
+
#### set_state (смена состояния)
|
|
224
233
|
|
|
225
234
|
```json
|
|
226
235
|
{
|
|
@@ -232,21 +241,21 @@ Actions are triggered by user interaction:
|
|
|
232
241
|
}
|
|
233
242
|
```
|
|
234
243
|
|
|
235
|
-
## Props
|
|
244
|
+
## Свойства (Props)
|
|
236
245
|
|
|
237
|
-
|
|
|
238
|
-
| ---------------- | ---------------------- |
|
|
239
|
-
| `data` | `DivJson` |
|
|
240
|
-
| `onStat` | `(stat) => void` |
|
|
241
|
-
| `onCustomAction` | `(action) => void` |
|
|
242
|
-
| `onError` | `(error) => void` |
|
|
243
|
-
| `direction` | `'ltr' \| 'rtl'` |
|
|
244
|
-
| `platform` | `'desktop' \| 'touch'` |
|
|
245
|
-
| `style` | `ViewStyle` |
|
|
246
|
+
| Свойство | Тип | Обязательно | Описание |
|
|
247
|
+
| ---------------- | ---------------------- | ----------- | ---------------------------------- |
|
|
248
|
+
| `data` | `DivJson` | Да | JSON-данные DivKit |
|
|
249
|
+
| `onStat` | `(stat) => void` | Нет | Колбэк статистики |
|
|
250
|
+
| `onCustomAction` | `(action) => void` | Нет | Обработчик кастомных действий |
|
|
251
|
+
| `onError` | `(error) => void` | Нет | Обработчик ошибок |
|
|
252
|
+
| `direction` | `'ltr' \| 'rtl'` | Нет | Направление текста (по умолч.: `'ltr'`) |
|
|
253
|
+
| `platform` | `'desktop' \| 'touch'` | Нет | Тип платформы (по умолч.: `'touch'`) |
|
|
254
|
+
| `style` | `ViewStyle` | Нет | Стили контейнера |
|
|
246
255
|
|
|
247
|
-
##
|
|
256
|
+
## Хуки
|
|
248
257
|
|
|
249
|
-
|
|
258
|
+
Для продвинутого использования вы можете использовать хуки напрямую:
|
|
250
259
|
|
|
251
260
|
```tsx
|
|
252
261
|
import { useDivKitContext, useVariable, useVariableState, useAction } from 'react-native-divkit';
|
|
@@ -257,82 +266,74 @@ function MyComponent() {
|
|
|
257
266
|
|
|
258
267
|
return (
|
|
259
268
|
<View>
|
|
260
|
-
<Text
|
|
261
|
-
<Button onPress={() => setVariable('counter', counter + 1)} title="
|
|
269
|
+
<Text>Счетчик: {counter}</Text>
|
|
270
|
+
<Button onPress={() => setVariable('counter', counter + 1)} title="Увеличить" />
|
|
262
271
|
</View>
|
|
263
272
|
);
|
|
264
273
|
}
|
|
265
274
|
```
|
|
266
275
|
|
|
267
|
-
##
|
|
276
|
+
## Примеры
|
|
268
277
|
|
|
269
|
-
|
|
278
|
+
Смотрите директорию [examples/BasicExample](examples/BasicExample/) для готового React Native приложения, демонстрирующего все возможности.
|
|
270
279
|
|
|
271
280
|
```bash
|
|
272
281
|
cd examples/BasicExample
|
|
273
282
|
npm install
|
|
274
|
-
npm run ios #
|
|
283
|
+
npm run ios # или npm run android
|
|
275
284
|
```
|
|
276
285
|
|
|
277
|
-
##
|
|
286
|
+
## Документация
|
|
278
287
|
|
|
279
|
-
- [API
|
|
280
|
-
- [
|
|
281
|
-
- [
|
|
288
|
+
- [Справочник API](docs/API.md) - Полная документация API
|
|
289
|
+
- [Руководство по миграции](docs/MIGRATION.md) - Миграция с веб-версии
|
|
290
|
+
- [Архитектура](docs/ARCHITECTURE.md) - Внутренняя архитектура
|
|
282
291
|
|
|
283
|
-
##
|
|
292
|
+
## Не включено в MVP
|
|
284
293
|
|
|
285
|
-
|
|
294
|
+
Следующие функции запланированы для будущих версий:
|
|
286
295
|
|
|
287
|
-
- Gallery, Pager, Slider, Tabs
|
|
288
|
-
- Input, Select, Switch
|
|
289
|
-
-
|
|
290
|
-
-
|
|
291
|
-
-
|
|
292
|
-
-
|
|
296
|
+
- Gallery (Галерея), Pager (Пейджер), Slider (Слайдер), Tabs (Вкладки)
|
|
297
|
+
- Input (Ввод), Select (Выбор), Switch (Переключатель)
|
|
298
|
+
- Видео, Lottie-анимации
|
|
299
|
+
- Диапазоны текста, сложные градиенты
|
|
300
|
+
- Продвинутые переходы и анимации
|
|
301
|
+
- API пользовательских компонентов
|
|
293
302
|
|
|
294
|
-
##
|
|
303
|
+
## Архитектура
|
|
295
304
|
|
|
296
|
-
|
|
305
|
+
Библиотека основана на DivKit Web (TypeScript + Svelte):
|
|
297
306
|
|
|
298
|
-
|
|
|
307
|
+
| Компонент | Переиспользование |
|
|
299
308
|
| ----------------- | ----------------------- |
|
|
300
|
-
|
|
|
301
|
-
|
|
|
302
|
-
|
|
|
303
|
-
|
|
|
304
|
-
|
|
|
309
|
+
| Движок выражений | 100% скопировано |
|
|
310
|
+
| Определения типов | 100% скопировано |
|
|
311
|
+
| Утилиты | ~90% адаптировано |
|
|
312
|
+
| Компоненты | ~20% (переписано под RN)|
|
|
313
|
+
| Система контекстов| Новая (специфично для React)|
|
|
305
314
|
|
|
306
|
-
##
|
|
315
|
+
## Разработка
|
|
307
316
|
|
|
308
317
|
```bash
|
|
309
|
-
#
|
|
318
|
+
# Установка зависимостей
|
|
310
319
|
npm install
|
|
311
320
|
|
|
312
|
-
#
|
|
321
|
+
# Сборка парсера PEG
|
|
313
322
|
npm run build:peggy
|
|
314
323
|
|
|
315
|
-
#
|
|
324
|
+
# Проверка типов
|
|
316
325
|
npm run typecheck
|
|
317
326
|
|
|
318
|
-
#
|
|
327
|
+
# Линтинг
|
|
319
328
|
npm run lint
|
|
320
329
|
|
|
321
|
-
#
|
|
330
|
+
# Сборка
|
|
322
331
|
npm run build
|
|
323
332
|
|
|
324
|
-
#
|
|
333
|
+
# Тесты
|
|
325
334
|
npm test
|
|
326
335
|
```
|
|
327
336
|
|
|
328
|
-
##
|
|
329
|
-
|
|
330
|
-
1. Fork the repository
|
|
331
|
-
2. Create a feature branch
|
|
332
|
-
3. Make your changes
|
|
333
|
-
4. Add tests
|
|
334
|
-
5. Submit a pull request
|
|
335
|
-
|
|
336
|
-
## License
|
|
337
|
+
## Лицензия
|
|
337
338
|
|
|
338
339
|
Apache 2.0
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ViewStyle } from 'react-native';
|
|
3
|
+
import type { Background as BackgroundType } from '../../types/background';
|
|
4
|
+
export interface BackgroundProps {
|
|
5
|
+
layers?: BackgroundType[];
|
|
6
|
+
style?: ViewStyle;
|
|
7
|
+
width?: number;
|
|
8
|
+
height?: number;
|
|
9
|
+
}
|
|
10
|
+
export declare const Background: ({ layers, style }: BackgroundProps) => React.JSX.Element | null;
|
|
11
|
+
//# sourceMappingURL=Background.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Background.d.ts","sourceRoot":"","sources":["../../../src/components/utilities/Background.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAoB,MAAM,wBAAwB,CAAC;AAE7F,MAAM,WAAW,eAAe;IAC5B,MAAM,CAAC,EAAE,cAAc,EAAE,CAAC;IAC1B,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAoFD,eAAO,MAAM,UAAU,GAAI,mBAAmB,eAAe,6BAyB5D,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { StyleSheet, View } from 'react-native';
|
|
3
|
+
import Svg, { Defs, RadialGradient, Stop, Rect } from 'react-native-svg';
|
|
4
|
+
const RadialGradientLayer = ({ layer }) => {
|
|
5
|
+
// Default to 50% 50% if not specified
|
|
6
|
+
let cx = '50%';
|
|
7
|
+
let cy = '50%';
|
|
8
|
+
if (layer.center_x) {
|
|
9
|
+
if (layer.center_x.type === 'fixed') {
|
|
10
|
+
cx = `${layer.center_x.value}`;
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
cx = `${layer.center_x.value * 100}%`;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
if (layer.center_y) {
|
|
17
|
+
if (layer.center_y.type === 'fixed') {
|
|
18
|
+
cy = `${layer.center_y.value}`;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
cy = `${layer.center_y.value * 100}%`;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// Colors
|
|
25
|
+
// If colors array provided, distribute evenly
|
|
26
|
+
// If color_map provided, use it
|
|
27
|
+
let stops = [];
|
|
28
|
+
if (layer.colors) {
|
|
29
|
+
stops = layer.colors.map((color, index) => (<Stop key={index} offset={index / (layer.colors.length - 1)} stopColor={color} stopOpacity={1}/>));
|
|
30
|
+
}
|
|
31
|
+
else if (layer.color_map) {
|
|
32
|
+
stops = layer.color_map.map((point, index) => (<Stop key={index} offset={point.position} stopColor={point.color} stopOpacity={1}/>));
|
|
33
|
+
}
|
|
34
|
+
// Radius
|
|
35
|
+
// DivKit defaults: farthest_corner
|
|
36
|
+
// SVG RadialGradient rx ry defaults to 50%
|
|
37
|
+
// To support "farthest_corner" accurately we might need complex math or just use a large radius like 100%?
|
|
38
|
+
// For now, let's stick to 50% (containing circle) or 100%?
|
|
39
|
+
// A safe default for radial gradients covering a view is often 50% rx/ry if the center is center.
|
|
40
|
+
// If the center is custom, we might need adjustments.
|
|
41
|
+
// Let's use '50%' for rx/ry which is standard for "closest-side" sort of.
|
|
42
|
+
// Specifying units="userSpaceOnUse" allows pixel values.
|
|
43
|
+
// Specifying units="objectBoundingBox" (default) allows percentages.
|
|
44
|
+
// We'll use objectBoundingBox
|
|
45
|
+
const rx = '50%';
|
|
46
|
+
const ry = '50%';
|
|
47
|
+
return (<Svg height="100%" width="100%" style={StyleSheet.absoluteFill}>
|
|
48
|
+
<Defs>
|
|
49
|
+
<RadialGradient id="grad" cx={cx} cy={cy} rx={rx} ry={ry} fx={cx} fy={cy} gradientUnits="objectBoundingBox">
|
|
50
|
+
{stops}
|
|
51
|
+
</RadialGradient>
|
|
52
|
+
</Defs>
|
|
53
|
+
<Rect x="0" y="0" width="100%" height="100%" fill="url(#grad)"/>
|
|
54
|
+
</Svg>);
|
|
55
|
+
};
|
|
56
|
+
export const Background = ({ layers, style }) => {
|
|
57
|
+
if (!layers || layers.length === 0)
|
|
58
|
+
return null;
|
|
59
|
+
return (<View style={[StyleSheet.absoluteFill, style, { zIndex: -1, overflow: 'hidden' }]} pointerEvents="none">
|
|
60
|
+
{layers.map((layer, index) => {
|
|
61
|
+
if (layer.type === 'solid') {
|
|
62
|
+
return (<View key={index} style={[StyleSheet.absoluteFill, { backgroundColor: layer.color }]}/>);
|
|
63
|
+
}
|
|
64
|
+
if (layer.type === 'radial_gradient') {
|
|
65
|
+
return (<View key={index} style={StyleSheet.absoluteFill}>
|
|
66
|
+
<RadialGradientLayer layer={layer}/>
|
|
67
|
+
</View>);
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
})}
|
|
71
|
+
</View>);
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=Background.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Background.js","sourceRoot":"","sources":["../../../src/components/utilities/Background.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Outer.d.ts","sourceRoot":"","sources":["../../../src/components/utilities/Outer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAW,MAAM,OAAO,CAAC;AAClD,OAAO,EAAmB,SAAS,EAAc,MAAM,cAAc,CAAC;AACtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"Outer.d.ts","sourceRoot":"","sources":["../../../src/components/utilities/Outer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAW,MAAM,OAAO,CAAC;AAClD,OAAO,EAAmB,SAAS,EAAc,MAAM,cAAc,CAAC;AACtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AASpD,MAAM,WAAW,UAAU,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW;IAC3D,gBAAgB,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC;IACtC,QAAQ,EAAE,SAAS,CAAC;IACpB,KAAK,CAAC,EAAE,SAAS,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAAE,EACvD,gBAAgB,EAChB,QAAQ,EACR,KAAK,EAAE,WAAW,EACrB,EAAE,UAAU,CAAC,CAAC,CAAC,4BAmNf"}
|
|
@@ -3,6 +3,7 @@ import { View, Pressable, StyleSheet } from 'react-native';
|
|
|
3
3
|
import { useDerivedFromVarsSimple } from '../../hooks/useDerivedFromVars';
|
|
4
4
|
import { useActionHandler, useHasActions } from '../../hooks/useAction';
|
|
5
5
|
import { useDivKitContext } from '../../context/DivKitContext';
|
|
6
|
+
import { Background } from './Background';
|
|
6
7
|
/**
|
|
7
8
|
* Outer component - base wrapper for all DivKit components
|
|
8
9
|
* Handles visibility, sizing, padding, margins, background, borders, and actions
|
|
@@ -133,14 +134,7 @@ export function Outer({ componentContext, children, style: customStyle }) {
|
|
|
133
134
|
styles.marginRight = m.right;
|
|
134
135
|
}
|
|
135
136
|
}
|
|
136
|
-
// Background
|
|
137
|
-
if (background && Array.isArray(background)) {
|
|
138
|
-
const bg = background;
|
|
139
|
-
const solidBg = bg.find((b) => b?.type === 'solid');
|
|
140
|
-
if (solidBg && solidBg.color) {
|
|
141
|
-
styles.backgroundColor = solidBg.color;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
137
|
+
// Background handled by Background component
|
|
144
138
|
// Border
|
|
145
139
|
if (border) {
|
|
146
140
|
const b = border;
|
|
@@ -200,12 +194,31 @@ export function Outer({ componentContext, children, style: customStyle }) {
|
|
|
200
194
|
const finalStyle = useMemo(() => {
|
|
201
195
|
return StyleSheet.flatten([containerStyle, customStyle]);
|
|
202
196
|
}, [containerStyle, customStyle]);
|
|
197
|
+
const borderStyle = useMemo(() => {
|
|
198
|
+
const s = finalStyle || {};
|
|
199
|
+
const res = {};
|
|
200
|
+
if (s.borderRadius)
|
|
201
|
+
res.borderRadius = s.borderRadius;
|
|
202
|
+
if (s.borderTopLeftRadius)
|
|
203
|
+
res.borderTopLeftRadius = s.borderTopLeftRadius;
|
|
204
|
+
if (s.borderTopRightRadius)
|
|
205
|
+
res.borderTopRightRadius = s.borderTopRightRadius;
|
|
206
|
+
if (s.borderBottomLeftRadius)
|
|
207
|
+
res.borderBottomLeftRadius = s.borderBottomLeftRadius;
|
|
208
|
+
if (s.borderBottomRightRadius)
|
|
209
|
+
res.borderBottomRightRadius = s.borderBottomRightRadius;
|
|
210
|
+
return res;
|
|
211
|
+
}, [finalStyle]);
|
|
203
212
|
// Render with or without Pressable based on actions
|
|
204
213
|
if (hasActions) {
|
|
205
214
|
return (<Pressable onPress={handlePress} style={finalStyle}>
|
|
215
|
+
<Background layers={background} style={borderStyle}/>
|
|
206
216
|
{children}
|
|
207
217
|
</Pressable>);
|
|
208
218
|
}
|
|
209
|
-
return <View style={finalStyle}>
|
|
219
|
+
return (<View style={finalStyle}>
|
|
220
|
+
<Background layers={background} style={borderStyle}/>
|
|
221
|
+
{children}
|
|
222
|
+
</View>);
|
|
210
223
|
}
|
|
211
224
|
//# sourceMappingURL=Outer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Outer.js","sourceRoot":"","sources":["../../../src/components/utilities/Outer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAa,OAAO,EAAE,MAAM,OAAO,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAa,UAAU,EAAE,MAAM,cAAc,CAAC;AAMtE,OAAO,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"Outer.js","sourceRoot":"","sources":["../../../src/components/utilities/Outer.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-divkit",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.3",
|
|
4
4
|
"description": "DivKit renderer for React Native - Server-driven UI framework",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -34,22 +34,23 @@
|
|
|
34
34
|
"peggy": "^3.0.2"
|
|
35
35
|
},
|
|
36
36
|
"optionalDependencies": {
|
|
37
|
+
"@react-native-clipboard/clipboard": "^1.13.0",
|
|
37
38
|
"react-native-fast-image": "^8.6.3",
|
|
38
39
|
"react-native-linear-gradient": "^2.8.3",
|
|
39
|
-
"
|
|
40
|
+
"react-native-svg": "^15.15.2"
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|
|
43
|
+
"@testing-library/react-native": "^12.0.0",
|
|
44
|
+
"@types/jest": "^29.0.0",
|
|
42
45
|
"@types/react": "^18.2.0",
|
|
43
46
|
"@types/react-native": "^0.72.0",
|
|
44
47
|
"@typescript-eslint/eslint-plugin": "^5.59.0",
|
|
45
48
|
"@typescript-eslint/parser": "^5.59.0",
|
|
46
49
|
"eslint": "^8.47.0",
|
|
47
|
-
"typescript": "^5.7.0",
|
|
48
50
|
"jest": "^29.0.0",
|
|
51
|
+
"react-test-renderer": "^18.2.0",
|
|
49
52
|
"ts-jest": "^29.0.0",
|
|
50
|
-
"
|
|
51
|
-
"@types/jest": "^29.0.0",
|
|
52
|
-
"react-test-renderer": "^18.2.0"
|
|
53
|
+
"typescript": "^5.7.0"
|
|
53
54
|
},
|
|
54
55
|
"repository": {
|
|
55
56
|
"type": "git",
|
|
@@ -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>;
|
|
@@ -145,15 +146,8 @@ export function Outer<T extends DivBaseData = DivBaseData>({
|
|
|
145
146
|
}
|
|
146
147
|
}
|
|
147
148
|
|
|
148
|
-
// Background
|
|
149
|
-
|
|
150
|
-
const bg = background as any[];
|
|
151
|
-
const solidBg = bg.find((b: any) => b?.type === 'solid');
|
|
152
|
-
if (solidBg && solidBg.color) {
|
|
153
|
-
styles.backgroundColor = solidBg.color;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
149
|
+
// Background handled by Background component
|
|
150
|
+
|
|
157
151
|
// Border
|
|
158
152
|
if (border) {
|
|
159
153
|
const b = border as any;
|
|
@@ -216,14 +210,31 @@ export function Outer<T extends DivBaseData = DivBaseData>({
|
|
|
216
210
|
return StyleSheet.flatten([containerStyle, customStyle]);
|
|
217
211
|
}, [containerStyle, customStyle]);
|
|
218
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
|
+
|
|
219
224
|
// Render with or without Pressable based on actions
|
|
220
225
|
if (hasActions) {
|
|
221
226
|
return (
|
|
222
227
|
<Pressable onPress={handlePress} style={finalStyle}>
|
|
228
|
+
<Background layers={background as any} style={borderStyle} />
|
|
223
229
|
{children}
|
|
224
230
|
</Pressable>
|
|
225
231
|
);
|
|
226
232
|
}
|
|
227
233
|
|
|
228
|
-
return
|
|
234
|
+
return (
|
|
235
|
+
<View style={finalStyle}>
|
|
236
|
+
<Background layers={background as any} style={borderStyle} />
|
|
237
|
+
{children}
|
|
238
|
+
</View>
|
|
239
|
+
);
|
|
229
240
|
}
|
|
@@ -14,7 +14,7 @@ Base wrapper component for all DivKit components. Handles common properties like
|
|
|
14
14
|
- ✅ Width/Height sizing (`fixed`, `match_parent`, `wrap_content`)
|
|
15
15
|
- ✅ Padding and margins (with RTL support)
|
|
16
16
|
- ✅ Opacity/alpha
|
|
17
|
-
- ✅ Background (solid colors
|
|
17
|
+
- ✅ Background (solid colors, radial gradients using `react-native-svg`)
|
|
18
18
|
- ✅ Borders (width, color, style, radius)
|
|
19
19
|
- ✅ Shadows
|
|
20
20
|
- ✅ Actions (tap handlers)
|
|
@@ -114,7 +114,7 @@ The current implementation is MVP-focused and includes only essential features:
|
|
|
114
114
|
|
|
115
115
|
**Deferred (Post-MVP):**
|
|
116
116
|
|
|
117
|
-
- Complex backgrounds (images, gradients)
|
|
117
|
+
- Complex backgrounds (images, linear gradients, nine-patch)
|
|
118
118
|
- Advanced animations and transitions
|
|
119
119
|
- Extensions
|
|
120
120
|
- Custom focus handling
|
|
@@ -130,7 +130,7 @@ The current implementation is MVP-focused and includes only essential features:
|
|
|
130
130
|
|
|
131
131
|
### Future Enhancements
|
|
132
132
|
|
|
133
|
-
1. **Advanced Backgrounds** - Add support for images, gradients, nine-patch
|
|
133
|
+
1. **Advanced Backgrounds** - Add support for images, linear gradients, nine-patch
|
|
134
134
|
2. **Animations** - Implement action animations and transitions
|
|
135
135
|
3. **Focus** - Add keyboard focus and custom focus styles
|
|
136
136
|
4. **Accessibility** - Enhanced accessibility support
|
|
@@ -142,6 +142,7 @@ The current implementation is MVP-focused and includes only essential features:
|
|
|
142
142
|
## Related Files
|
|
143
143
|
|
|
144
144
|
- [Outer.tsx](./Outer.tsx) - Main wrapper component
|
|
145
|
+
- [Background.tsx](./Background.tsx) - Background rendering component (solid, gradients)
|
|
145
146
|
- [Unknown.tsx](./Unknown.tsx) - Fallback component
|
|
146
147
|
- [Web Outer.svelte](../../../web/divkit/src/components/utilities/Outer.svelte) - Reference implementation
|
|
147
148
|
|