Skip to content

Commit 975b6e2

Browse files
committed
docs-begin
1 parent 6d1c245 commit 975b6e2

File tree

8 files changed

+221
-1
lines changed

8 files changed

+221
-1
lines changed

assets/bottom_baseline.png

15.1 KB
Loading

assets/bottom_baseline_issue.png

21.9 KB
Loading

assets/dex.png

545 KB
Loading

assets/fold.png

1.66 MB
Loading

assets/top_baseline.png

22.8 KB
Loading

assets/top_baseline_issue.png

9.17 KB
Loading

docs/HOWTO.md

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
# Адаптивная верстка на React Native
2+
3+
> Описание проблем, возникших при портировании классического React приложения в React Native можно прочитать в этой статье. Речь идет о поддержке различных форм факторов устройств, в том числе, Galaxy Fold
4+
5+
## Проблема
6+
7+
Некоторое время назад начал разработку мобильного приложения на React Native. Проблема в том, что требованием к приложению является Galaxy Fold и Samsung DeX
8+
9+
![fold](../assets/fold.png)
10+
11+
Как следствие, встал вопрос реализации адаптивной верстки форм.Одно и тоже приложение должно рисовать формы в одну колонку в режиме телефона и две колонки в режиме планшета. Если писать разный код компоновок, придется дублировать логику форм, как минимум, новое поле добавляется два раза.
12+
13+
![dex](../assets/dex.png)
14+
15+
Yoga Layout, движок верстки из React Native, поддерживает только flexbox. В результате, грамотно реализовать верстку, избегая излишней вложенности View, является нетривиальной задачей: вложенные группы должны отображаться от верхнего края родителя, поля ввода должны равняться на нижний край строки отображения (baseline)
16+
17+
![top_baseline](../assets/top_baseline.png)
18+
19+
Если принебречь правилом привязки групп полей к верхнему краю, получится уродство
20+
21+
![bottom_baseline_issue](../assets/bottom_baseline_issue.png)
22+
23+
Поля внутри группы должны быть привязаны к нижнему краю строки
24+
25+
![bottom_baseline](../assets/bottom_baseline.png)
26+
27+
Уродство при верхнем baseline для полей не очевидно с первого взгляда, но очень бросается при использование standard полей из Material 1 на Android 4
28+
29+
![top_baseline_issue](../assets/top_baseline_issue.png)
30+
31+
## Решение проблемы
32+
33+
Для того, чтобы делегировать сложную задачу грамотной компоновки форм более дешевому специалисту, был разработан шаблонизатор, генерирующий верстку по указанным выше правилам из JSON шаблона. Пример шаблона в блоке кода ниже
34+
35+
```tsx
36+
import { One, FieldType, TypedField } from 'rn-declarative';
37+
38+
import { Text } from '@ui-kitten/components';
39+
import { ScrollView } from 'react-native';
40+
41+
const fields: TypedField[] = [
42+
{
43+
type: FieldType.Component,
44+
style: {
45+
justifyContent: 'center',
46+
width: '100%',
47+
height: 125,
48+
},
49+
element: () => (
50+
<Text category='h4'>
51+
Adaptive columns
52+
</Text>
53+
),
54+
},
55+
{
56+
type: FieldType.Group,
57+
style: {
58+
width: '100%',
59+
},
60+
fields: [
61+
{
62+
type: FieldType.Group,
63+
phoneStyle: {
64+
width: '100%',
65+
},
66+
tabletStyle: {
67+
width: '50%',
68+
},
69+
desktopStyle: {
70+
width: '25%',
71+
},
72+
fields: [
73+
{
74+
type: FieldType.Component,
75+
style: {
76+
width: '100%',
77+
},
78+
element: () => (
79+
<Text category='h6'>
80+
FieldType.Text
81+
</Text>
82+
),
83+
},
84+
{
85+
type: FieldType.Text,
86+
style: {
87+
width: '100%',
88+
},
89+
name: 'text',
90+
title: 'Text',
91+
description: 'Single line',
92+
},
93+
{
94+
type: FieldType.Text,
95+
style: {
96+
width: '100%',
97+
},
98+
validation: {
99+
required: true,
100+
},
101+
dirty: true,
102+
name: 'text_invalid',
103+
title: 'Text',
104+
description: 'Invalid',
105+
},
106+
{
107+
type: FieldType.Text,
108+
style: {
109+
width: '100%',
110+
},
111+
inputMultiline: true,
112+
name: 'text',
113+
title: 'Text',
114+
description: 'Multi line',
115+
},
116+
],
117+
},
118+
119+
...
120+
121+
];
122+
123+
export const MainPage = () => {
124+
return (
125+
<ScrollView>
126+
<One fields={fields} onChange={console.log} />
127+
</ScrollView>
128+
);
129+
};
130+
131+
export default MainPage;
132+
```
133+
134+
Библиотека разделена на два модуля: [rn-declarative](https://www.npmjs.com/package/rn-declarative) и [rn-declarative-eva](https://www.npmjs.com/package/rn-declarative-eva). Первая содержит базовую логику и не зависит от UI kit: может быть установлена в любом проекте не зависимо от версии `react-native` или фреймворка (поддерживается как `Expo`, так и starter kit от `react-native-community`). Кроме `react` и `react-native` других зависимостей нет.
135+
136+
```tsx
137+
import { useMediaContext } from 'rn-declarative'
138+
139+
...
140+
141+
const { isPhone, isTablet, isDesktop } = useMediaContext();
142+
```
143+
144+
Настройка ширины компоновок и полей осуществляется через свойства объектов `phoneStyle`, `tabletStyle` и `desktopStyle`. Если не хотим менять стиль исходя из форм-фактора устройства, можно написать просто `style`. Подключение UI Kit осуществляется через контекст с слотами `<OneSlotFactory />` для подключения реализации `FieldType`.
145+
146+
```tsx
147+
import { Toggle } from '@ui-kitten/components';
148+
import { OneSlotFactory, ISwitchSlot } from 'rn-declarative';
149+
150+
export const Switch = ({
151+
disabled,
152+
value,
153+
onChange,
154+
onFocus,
155+
onBlur,
156+
title,
157+
}: ISwitchSlot) => {
158+
return (
159+
<Toggle
160+
checked={Boolean(value)}
161+
disabled={disabled}
162+
onChange={() => onChange(!value)}
163+
onFocus={onFocus}
164+
onBlur={onBlur}
165+
>
166+
{title}
167+
</Toggle>
168+
);
169+
};
170+
171+
...
172+
173+
const defaultSlots = {
174+
CheckBox,
175+
Combo,
176+
Items,
177+
Radio,
178+
Button,
179+
Text,
180+
Switch,
181+
YesNo,
182+
};
183+
184+
...
185+
186+
<OneSlotFactory
187+
{...defaultSlots}
188+
>
189+
{children}
190+
</OneSlotFactory>
191+
```
192+
193+
P.S. Любой другой компонент или пользовательскую верстку можно прозрачно интегрировать через `FieldType.Component` (есть `onChange` и `value`) или `FieldType.Layout`
194+
195+
```tsx
196+
{
197+
type: FieldType.Component,
198+
element: () => (
199+
<Text category='h4'>
200+
Sample component
201+
</Text>
202+
),
203+
},
204+
205+
...
206+
207+
{
208+
type: FieldType.Layout,
209+
customLayout: ({ children }) => (
210+
<ScrollView>
211+
{children}
212+
</ScrollView>
213+
),
214+
},
215+
```
216+
217+
Код компонента опубликован на Github, посмотреть можно по ссылке:
218+
[https://github.com/react-declarative/rn-declarative/](https://github.com/react-declarative/rn-declarative/)
219+
220+
Спасибо за внимание!

packages/rn-declarative/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ⚛️ rn-declarative
22

3-
> Responsive layout for the `react-native`
3+
> Responsive layout for the `react-native`. Documentation published in [this article](https://github.com/react-declarative/rn-declarative/blob/master/docs/HOWTO.md)
44
55
![screencast](https://github.com/react-declarative/rn-declarative/blob/HEAD/assets/screencast.gif)
66

0 commit comments

Comments
 (0)