npm package: @schema-gui/rn
Description: React Native renderer implementation for shema-gui core library
Author: YarikM
Latest version: 0.1.0

# @schema-gui/rn

React Native рендерер для декларативного рендерингу UI на основі JSON схем

---

## 📋 Зміст

1. [Вступ](#вступ)
2. [Встановлення](#встановлення)
3. [Швидкий старт](#швидкий-старт)
4. [API](#api)
5. [Підтримувані компоненти](#підтримувані-компоненти)
6. [Відмінності від Web рендерера](#відмінності-від-web-рендерера)
7. [Приклади](#приклади)
8. [Стилізація](#стилізація)
9. [Події](#події)
10. [Детальна документація](#детальна-документація)

---

## Вступ

`@schema-gui/rn` - це React Native реалізація `UIRendererEngine` для бібліотеки `@schema-gui/core`. Вона дозволяє використовувати ті самі JSON схеми для створення UI в React Native додатках.

### Основні можливості

- **Кросплатформенність** - одна схема для iOS та Android
- **Нативні компоненти** - використання React Native компонентів замість HTML
- **Повна сумісність** - підтримка всіх можливостей core бібліотеки
- **Type-safe** - повна типізація TypeScript

### Архітектура

```
JSON Schema → UIRenderer → UIRendererEngineReactNative → React Native Components
```

---

## Встановлення

```bash
npm install @schema-gui/rn @schema-gui/core
```

або

```bash
yarn add @schema-gui/rn @schema-gui/core
```

### Залежності

- React Native 0.81+
- React 19+
- TypeScript 5+
- `@schema-gui/core`

---

## Швидкий старт

### Базовий приклад

```typescript
import { UIRenderer } from '@schema-gui/core';
import { UIRendererEngineReactNative } from '@schema-gui/rn';
import { useState } from 'react';
import { SafeAreaView } from 'react-native';

const schema = {
  type: "view",
  style: {
    padding: 20,
    backgroundColor: "#1E1E1E"
  },
  children: [
    {
      type: "text",
      text: "Hello, {name}!",
      style: {
        fontSize: 24,
        color: "#FFFFFF",
        fontWeight: "600"
      }
    },
    {
      type: "text",
      text: "Age: {age}",
      style: {
        fontSize: 16,
        color: "#CCCCCC",
        marginTop: 10
      }
    }
  ]
};

const data = {
  name: "John",
  age: 30
};

function App() {
  const [state, setState] = useState({});
  const setStateValue = (variable: string, value: unknown) => {
    setState({ ...state, [variable]: value });
  };

  const renderEngine = new UIRendererEngineReactNative();

  return (
    <SafeAreaView style={{ flex: 1 }}>
      <UIRenderer
        schema={schema}
        data={data}
        renderEngine={renderEngine}
        state={state}
        setStateValue={setStateValue}
      />
    </SafeAreaView>
  );
}
```

---

## API

### UIRendererEngineReactNative

React Native реалізація рендерера.

```typescript
import { UIRendererEngineReactNative } from '@schema-gui/rn';

const renderEngine = new UIRendererEngineReactNative();
```

### Використання з UIRenderer

```typescript
import { UIRenderer } from '@schema-gui/core';
import { UIRendererEngineReactNative } from '@schema-gui/rn';

<UIRenderer
  schema={schema}
  data={data}
  renderEngine={new UIRendererEngineReactNative()}
  state={state}
  setStateValue={setStateValue}
/>
```

---

## Підтримувані компоненти

### Базові компоненти

| Тип в схемі | React Native компонент | Опис |
|------------|------------------------|------|
| `container` | `View` | Контейнер для групування елементів |
| `view` | `View` | Основний контейнер |
| `text` | `Text` | Текстовий елемент |
| `image` / `img` | `Image` | Зображення |
| `button` | `Button` | Кнопка |
| `text_input` | `TextInput` | Поле вводу тексту |
| `switch` | `Switch` | Перемикач |
| `activity_indicator` | `ActivityIndicator` | Індикатор завантаження |
| `scroll_view` | `ScrollView` | Прокручуваний контейнер |
| `modal` | `Modal` | Модальне вікно |
| `keyboard_avoiding_view` | `KeyboardAvoidingView` | Контейнер з обробкою клавіатури |
| `image_background` | `ImageBackground` | Фонове зображення |
| `status_bar` | `StatusBar` | Статус бар |

### Touchable компоненти

| Тип в схемі | React Native компонент | Опис |
|------------|------------------------|------|
| `pressable` | `Pressable` | Натискається елемент (рекомендовано) |
| `touchable_opacity` | `TouchableOpacity` | Натискається з ефектом прозорості |
| `touchable_highlight` | `TouchableHighlight` | Натискається з підсвічуванням |
| `touchable_without_feedback` | `TouchableWithoutFeedback` | Натискається без візуального ефекту |

### Приклад використання

```typescript
const schema = {
  type: "view",
  children: [
    {
      type: "text",
      text: "Hello World"
    },
    {
      type: "image",
      src: "{imageUrl}",
      style: {
        width: 100,
        height: 100
      }
    },
    {
      type: "pressable",
      events: {
        onPress: {
          actionType: "setState",
          variable: "$state.pressed",
          value: "true"
        }
      },
      children: [
        {
          type: "text",
          text: "Press me"
        }
      ]
    }
  ]
};
```

---

## Відмінності від Web рендерера

### 1. Компоненти

**Web:** HTML елементи (`div`, `span`, `button`, тощо)  
**React Native:** Нативні компоненти (`View`, `Text`, `Pressable`, тощо)

### 2. Стилі

**Web:** CSS властивості (camelCase)  
**React Native:** React Native StyleSheet (camelCase, але з обмеженнями)

**Відмінності:**
- React Native не підтримує всі CSS властивості
- Використовується `StyleSheet.create()` для оптимізації
- Деякі властивості мають інші назви (наприклад, `flexDirection` замість `flex-direction`)

### 3. Події

**Web:** `onClick`, `onChange`, тощо  
**React Native:** `onPress`, `onChangeText`, тощо

**Відображення:**
- `onClick` → `onPress` (для touchable компонентів)
- `onChange` → `onChangeText` (для TextInput)

### 4. Зображення

**Web:** `<img src="url" />`  
**React Native:** `<Image source={{ uri: "url" }} />`

Рендерер автоматично конвертує `src` в `source: { uri: src }`.

### 5. className

React Native рендерер підтримує `className` для інтеграції з NativeWind або іншими CSS-in-JS рішеннями. Анімаційні класи (як `animate-*`, `transition-*`, `duration-*`) автоматично фільтруються.

---

## Приклади

### Приклад 1: Простий список

```typescript
const schema = {
  type: "scroll_view",
  style: {
    flex: 1
  },
  children: [
    {
      type: "view",
      iterate: {
        source: "items",
        limit: 10
      },
      style: {
        padding: 15,
        borderBottomWidth: 1,
        borderBottomColor: "#333"
      },
      children: [
        {
          type: "text",
          text: "{$item.name}",
          style: {
            fontSize: 16,
            color: "#000000"
          }
        }
      ]
    }
  ]
};

const data = {
  items: [
    { name: "Item 1" },
    { name: "Item 2" },
    { name: "Item 3" }
  ]
};
```

### Приклад 2: Форма з вводом

```typescript
const schema = {
  type: "view",
  style: {
    padding: 20
  },
  children: [
    {
      type: "text",
      text: "Name:",
      style: {
        fontSize: 16,
        marginBottom: 5
      }
    },
    {
      type: "text_input",
      value: "{$state.name}",
      placeholder: "Enter your name",
      events: {
        onChangeText: {
          actionType: "setState",
          variable: "$state.name",
          value: "value"
        }
      },
      style: {
        borderWidth: 1,
        borderColor: "#CCCCCC",
        borderRadius: 8,
        padding: 10,
        marginBottom: 20
      }
    },
    {
      type: "text",
      text: "Hello, {$state.name}!",
      condition: {
        key: "$state.name",
        operator: "!==",
        value: ""
      },
      style: {
        fontSize: 18,
        color: "#1E1E1E"
      }
    }
  ]
};
```

### Приклад 3: Модальне вікно

```typescript
const schema = {
  type: "view",
  children: [
    {
      type: "pressable",
      events: {
        onPress: {
          actionType: "setState",
          variable: "$state.showModal",
          value: "true"
        }
      },
      style: {
        padding: 15,
        backgroundColor: "#1E1E1E",
        borderRadius: 8
      },
      children: [
        {
          type: "text",
          text: "Open Modal",
          style: {
            color: "#FFFFFF",
            fontSize: 16
          }
        }
      ]
    },
    {
      type: "modal",
      visible: "{$state.showModal}",
      transparent: true,
      animationType: "fade",
      events: {
        onRequestClose: {
          actionType: "setState",
          variable: "$state.showModal",
          value: "false"
        }
      },
      children: [
        {
          type: "view",
          style: {
            flex: 1,
            justifyContent: "center",
            alignItems: "center",
            backgroundColor: "rgba(0, 0, 0, 0.5)"
          },
          children: [
            {
              type: "view",
              style: {
                backgroundColor: "#FFFFFF",
                padding: 30,
                borderRadius: 12,
                minWidth: 300
              },
              children: [
                {
                  type: "text",
                  text: "Modal Title",
                  style: {
                    fontSize: 24,
                    fontWeight: "600",
                    marginBottom: 15
                  }
                },
                {
                  type: "text",
                  text: "Modal content goes here",
                  style: {
                    fontSize: 16,
                    marginBottom: 20
                  }
                },
                {
                  type: "pressable",
                  events: {
                    onPress: {
                      actionType: "setState",
                      variable: "$state.showModal",
                      value: "false"
                    }
                  },
                  style: {
                    padding: 12,
                    backgroundColor: "#1E1E1E",
                    borderRadius: 8,
                    alignItems: "center"
                  },
                  children: [
                    {
                      type: "text",
                      text: "Close",
                      style: {
                        color: "#FFFFFF",
                        fontSize: 16
                      }
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  ]
};
```

### Приклад 4: Список з кнопками

```typescript
const schema = {
  type: "scroll_view",
  style: {
    flex: 1
  },
  children: [
    {
      type: "view",
      iterate: {
        source: "todos"
      },
      style: {
        flexDirection: "row",
        padding: 15,
        borderBottomWidth: 1,
        borderBottomColor: "#E0E0E0",
        alignItems: "center"
      },
      children: [
        {
          type: "text",
          text: "{$item.title}",
          style: {
            flex: 1,
            fontSize: 16
          }
        },
        {
          type: "switch",
          value: "{$item.completed}",
          events: {
            onValueChange: {
              actionType: "code",
              code: `
                const updatedTodos = data.todos.map(todo => 
                  todo.id === item.id 
                    ? { ...todo, completed: value }
                    : todo
                );
                setStateValue('todos', updatedTodos);
              `
            }
          }
        }
      ]
    }
  ]
};
```

### Приклад 5: Зображення

```typescript
const schema = {
  type: "view",
  children: [
    {
      type: "image",
      src: "{imageUrl}",
      style: {
        width: 200,
        height: 200,
        borderRadius: 10
      }
    }
  ]
};
```

**Примітка:** React Native автоматично конвертує `src` в `source: { uri: src }`.

---

## Стилізація

### React Native StyleSheet

Стилі в React Native використовують об'єктний синтаксис, сумісний з `React.CSSProperties`, але з обмеженнями:

```typescript
{
  type: "view",
  style: {
    flex: 1,
    padding: 20,
    backgroundColor: "#1E1E1E",
    borderRadius: 8,
    flexDirection: "column",
    alignItems: "center",
    justifyContent: "center"
  }
}
```

### Умовні стилі

Підтримуються умовні стилі як у web версії:

```typescript
{
  type: "text",
  text: "Status",
  style: {
    color: {
      condition: {
        key: "status",
        operator: "===",
        value: "active"
      },
      true: "#00C853",
      false: "#D50000"
    }
  }
}
```

### className для NativeWind

Якщо ви використовуєте NativeWind, можете використовувати `className`:

```typescript
{
  type: "view",
  className: "flex-1 bg-gray-100 p-4"
}
```

**Примітка:** Анімаційні класи (як `animate-*`, `transition-*`) автоматично фільтруються.

---

## Події

### Підтримувані події

React Native рендерер підтримує стандартні React Native події:

| Подія | Компоненти | Опис |
|------|------------|------|
| `onPress` | `Pressable`, `TouchableOpacity`, `TouchableHighlight`, `Button` | Натискання |
| `onChangeText` | `TextInput` | Зміна тексту |
| `onValueChange` | `Switch` | Зміна значення перемикача |
| `onRequestClose` | `Modal` | Запит на закриття модального вікна |
| `onFocus` | `TextInput` | Фокус на полі вводу |
| `onBlur` | `TextInput` | Втрата фокусу |

### Автоматичне відображення

Рендерер автоматично конвертує web події в React Native:

- `onClick` → `onPress` (для touchable компонентів)
- `onChange` → `onChangeText` (для TextInput)

### Приклад з подіями

```typescript
{
  type: "view",
  children: [
    {
      type: "pressable",
      events: {
        onPress: {
          actionType: "code",
          code: `
            console.log('Button pressed!', data, state);
            setStateValue('counter', (state.counter || 0) + 1);
          `
        }
      },
      style: {
        padding: 15,
        backgroundColor: "#1E1E1E",
        borderRadius: 8
      },
      children: [
        {
          type: "text",
          text: "Count: {$state.counter}",
          style: {
            color: "#FFFFFF"
          }
        }
      ]
    },
    {
      type: "text_input",
      value: "{$state.input}",
      placeholder: "Enter text",
      events: {
        onChangeText: {
          actionType: "setState",
          variable: "$state.input",
          value: "value"
        }
      },
      style: {
        borderWidth: 1,
        borderColor: "#CCCCCC",
        borderRadius: 8,
        padding: 10,
        marginTop: 10
      }
    }
  ]
}
```

---

## Детальна документація

Для детальної інформації про створення UI схем, інтерполяцію, форматування, умови, ітерацію та інші можливості дивіться:

- [Core README](../core/README.md) - Повна документація по створенню схем

Всі можливості core бібліотеки працюють з React Native рендерером:

- ✅ Інтерполяція змінних (`{variable}`, `{$item}`, `{$state}`)
- ✅ Форматування (number, volume, datetime, time)
- ✅ Умови (condition)
- ✅ Ітерація (iterate)
- ✅ Обчислення в text та стилях
- ✅ Умовні стилі
- ✅ Події (setState та code)
- ✅ Модальні вікна

---

## Особливості React Native

### 1. Flexbox за замовчуванням

React Native використовує Flexbox за замовчуванням. `display: flex` не потрібно вказувати.

### 2. Відсутність деяких CSS властивостей

Деякі CSS властивості не підтримуються в React Native:
- `display: grid` → використовуйте `flexDirection` та `flexWrap`
- `display: table` → використовуйте `View` з `flexDirection: "row"`
- CSS селектори → використовуйте умовні стилі

### 3. Текстові елементи

В React Native текст повинен бути всередині `<Text>` компонента:

```typescript
// ✅ Правильно
{
  type: "view",
  children: [
    {
      type: "text",
      text: "Hello"
    }
  ]
}

// ❌ Неправильно (не працює в React Native)
{
  type: "view",
  text: "Hello" // Текст не може бути прямим дочірнім View
}
```

### 4. Зображення

React Native вимагає вказати розміри для зображень:

```typescript
{
  type: "image",
  src: "{imageUrl}",
  style: {
    width: 100,  // Обов'язково
    height: 100   // Обов'язково
  }
}
```

### 5. Безпечні зони

Використовуйте `SafeAreaView` для обгортання всього контенту:

```typescript
import { SafeAreaView } from 'react-native';

<SafeAreaView style={{ flex: 1 }}>
  <UIRenderer
    schema={schema}
    data={data}
    renderEngine={new UIRendererEngineReactNative()}
    state={state}
    setStateValue={setStateValue}
  />
</SafeAreaView>
```

---

## Найкращі практики

### 1. Використання ScrollView для довгого контенту

```typescript
{
  type: "scroll_view",
  style: {
    flex: 1
  },
  children: [
    // Ваш контент
  ]
}
```

### 2. Оптимізація списків

Для великих списків використовуйте `limit` в ітерації:

```typescript
{
  iterate: {
    source: "items",
    limit: 20  // Обмежте кількість елементів
  }
}
```

### 3. Використання Pressable замість TouchableOpacity

`Pressable` - це сучасний компонент з кращою підтримкою:

```typescript
{
  type: "pressable",  // ✅ Рекомендовано
  // замість
  type: "touchable_opacity"  // ⚠️ Застаріло
}
```

### 4. Обробка клавіатури

Використовуйте `KeyboardAvoidingView` для форм:

```typescript
{
  type: "keyboard_avoiding_view",
  style: {
    flex: 1
  },
  children: [
    // Форма з TextInput
  ]
}
```