npm package: @schema-gui/core
Description: The core library for rendering UI based on JSON schemas. Contains basic React renderer and engine for rendering UI.
Latest version: 0.0.1

# @schema-gui/core

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

---

## 📋 Зміст

1. [Вступ](#вступ)
2. [Встановлення](#встановлення)
3. [Швидкий старт](#швидкий-старт)
4. [API](#api)
5. [Архітектура](#архітектура)
6. [Приклади](#приклади)
7. [Події (Events)](#події-events)
8. [Модальні вікна (Modal)](#модальні-вікна-modal)
9. [Інтерполяція змінних](#інтерполяція-змінних)
9. [Форматування](#форматування)
10. [Умови](#умови)
11. [Ітерація](#ітерація)
12. [Обчислення](#обчислення)
13. [Стилі](#стилі)
14. [Найкращі практики](#найкращі-практики)
15. [Обмеження](#обмеження)
16. [Налагодження](#налагодження)
17. [Типи](#типи)
18. [Розширення](#розширення)

---

## Вступ

`@schema-gui/core` - це TypeScript бібліотека для створення UI на основі декларативних JSON схем. Вона надає гнучку систему рендерингу, яка дозволяє створювати інтерфейси без написання React компонентів.

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

- **Декларативний підхід** - UI описується через JSON схеми
- **Підтримка React** - вбудований React рендерер
- **Розширюваність** - можливість створювати власні рендерери
- **Type-safe** - повна типізація TypeScript
- **Гнучкість** - підтримка умов, ітерацій, форматування та обчислень

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

```
JSON Schema → UIRenderer → UIRendererEngine → React Elements → DOM
```

---

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

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

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

- React 19+
- TypeScript 5+

---

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

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

```typescript
import { UIRenderer, UIRendererEngineReact } from '@schema-gui/core';

const schema = {
  type: "div",
  style: {
    padding: "20px",
    backgroundColor: "#1E1E1E",
    color: "#FFFFFF"
  },
  children: [
    {
      type: "h1",
      text: "Hello, {name}!"
    },
    {
      type: "p",
      text: "Age: {age}"
    }
  ]
};

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

const state = {};
const setStateValue = (variable: string, value: unknown) => {
  state[variable] = value;
};

const renderEngine = new UIRendererEngineReact();

function App() {
  return (
    <UIRenderer
      schema={schema}
      data={data}
      renderEngine={renderEngine}
      state={state}
      setStateValue={setStateValue}
    />
  );
}
```

**Результат:**

```html
<div style="padding: 20px; background-color: #1E1E1E; color: #FFFFFF">
  <h1>Hello, John!</h1>
  <p>Age: 30</p>
</div>
```

---

## API

### UIRenderer

Основний React компонент для рендерингу UI схем.

```typescript
<UIRenderer
  schema={UINode}
  data={Record<string, unknown>}
  renderEngine={UIRendererEngine}
  state={Record<string, unknown>}
  setStateValue={(variable: string, value: unknown) => void}
/>
```

#### Параметри

- **schema** (`UINode`) - JSON схема UI
- **data** (`Record<string, unknown>`) - Дані для інтерполяції
- **renderEngine** (`UIRendererEngine`) - Рендерер для створення елементів
- **state** (`Record<string, unknown>`) - Внутрішній стан компонента
- **setStateValue** (`(variable: string, value: unknown) => void`) - Функція для оновлення стану

### UIRendererEngine

Абстрактний клас для створення власних рендерерів.

```typescript
abstract class UIRendererEngine {
  abstract renderUINode(
    type: string,
    index: number,
    value: unknown | undefined,
    resolvedStyle?: React.CSSProperties,
    content?: string,
    childNodes?: React.ReactNode[],
    resolvedEvents?: Record<string, (value: unknown) => void>,
    resolvedClassName?: string,
    imgProps?: Record<string, string>,
    modalProps?: Record<string, any>
  ): React.ReactNode;
}
```

### UIRendererEngineReact

React-реалізація рендерера, яка створює нативні HTML елементи.

```typescript
import { UIRendererEngineReact } from '@schema-gui/core';

const renderEngine = new UIRendererEngineReact();
```

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

- Всі стандартні HTML елементи: `div`, `span`, `table`, `button`, `input`, тощо
- Спеціальні типи:
  - `image` → `img`
  - `container` → `div`
  - `text` → `span`
  - `view` → `div`
  - `modal` → `Modal` компонент

---

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

### Компоненти системи

1. **UIRenderer** - Основний компонент, який обробляє схему
2. **UIRendererEngine** - Абстракція для рендерерів
3. **UIRendererEngineReact** - React-реалізація
4. **Modal** - Компонент модального вікна

### Процес рендерингу

```
1. UIRenderer отримує схему та дані
2. Обробляє умови, ітерації, форматування
3. Викликає renderEngine.renderUINode()
4. Рендерер створює React елементи
5. React рендерить DOM
```

### Обробка даних

- **Інтерполяція** - `{variable}` замінюється на значення з `data`
- **Умови** - `condition` визначає чи рендериться елемент
- **Ітерація** - `iterate` створює множину елементів з масиву
- **Форматування** - `format` застосовує форматування до значень
- **Стилі** - `style` підтримує умовні та обчислювані значення

---

## Приклади

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

```typescript
const schema = {
  type: "ul",
  style: { listStyle: "none", padding: 0 },
  children: [
    {
      type: "li",
      iterate: {
        source: "items",
        limit: 10
      },
      style: {
        padding: "10px",
        borderBottom: "1px solid #333"
      },
      children: [
        {
          type: "span",
          text: "{$item.name}"
        }
      ]
    }
  ]
};

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

### Приклад 2: Таблиця з форматуванням

```typescript
const schema = {
  type: "table",
  children: [
    {
      type: "tbody",
      children: [
        {
          type: "tr",
          iterate: {
            source: "trades",
            limit: 20
          },
          children: [
            {
              type: "td",
              text: "{$item.timestamp}",
              format: { type: "time" }
            },
            {
              type: "td",
              text: "${$item.price}",
              format: { type: "number", decimals: 2 }
            }
          ]
        }
      ]
    }
  ]
};
```

### Приклад 3: Умовний рендеринг

```typescript
const schema = {
  type: "div",
  children: [
    {
      type: "span",
      text: "● Active",
      condition: {
        key: "active",
        operator: "===",
        value: true
      },
      style: {
        color: "#00C853"
      }
    }
  ]
};
```

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

```typescript
const schema = {
  type: "modal",
  visible: "{showModal}",
  animationType: "fade",
  transparent: false,
  children: [
    {
      type: "div",
      style: {
        padding: "20px",
        backgroundColor: "#FFFFFF"
      },
      children: [
        {
          type: "h2",
          text: "Modal Title"
        },
        {
          type: "p",
          text: "Modal content"
        }
      ]
    }
  ]
};
```

### Приклад 5: Події та стан (setState)

```typescript
const schema = {
  type: "div",
  children: [
    {
      type: "button",
      text: "Click me",
      events: {
        onClick: {
          actionType: "setState",
          variable: "$state.counter",
          value: "{$state.counter + 1}"
        }
      }
    },
    {
      type: "span",
      text: "Count: {$state.counter}"
    }
  ]
};
```

### Приклад 6: Події з кодом (onClick)

```typescript
const schema = {
  type: "div",
  children: [
    {
      type: "button",
      text: "Submit",
      events: {
        onClick: {
          actionType: "code",
          code: `
            console.log('Button clicked!', data, state);
            setStateValue('message', 'Form submitted!');
            if (data.validate) {
              alert('Validation passed');
            }
          `
        }
      }
    },
    {
      type: "span",
      text: "Message: {$state.message}"
    }
  ]
};
```

### Приклад 7: Події з кодом (onChange)

```typescript
const schema = {
  type: "div",
  children: [
    {
      type: "input",
      value: "{$state.inputValue}",
      events: {
        onChange: {
          actionType: "code",
          code: `
            const newValue = value;
            setStateValue('inputValue', newValue);
            setStateValue('charCount', newValue.length);
            
            // Валідація
            if (newValue.length > 10) {
              setStateValue('error', 'Too long!');
            } else {
              setStateValue('error', '');
            }
          `
        }
      }
    },
    {
      type: "span",
      text: "Characters: {$state.charCount}"
    },
    {
      type: "span",
      text: "{$state.error}",
      condition: {
        key: "$state.error",
        operator: "!==",
        value: ""
      },
      style: {
        color: "#D50000"
      }
    }
  ]
};
```

### Приклад 8: Комбінація onClick та onChange

```typescript
const schema = {
  type: "div",
  children: [
    {
      type: "input",
      value: "{$state.searchQuery}",
      placeholder: "Search...",
      events: {
        onChange: {
          actionType: "code",
          code: `
            setStateValue('searchQuery', value);
            // Фільтрація даних
            const filtered = data.items.filter(item => 
              item.name.toLowerCase().includes(value.toLowerCase())
            );
            setStateValue('filteredItems', filtered);
          `
        }
      }
    },
    {
      type: "button",
      text: "Clear",
      events: {
        onClick: {
          actionType: "code",
          code: `
            setStateValue('searchQuery', '');
            setStateValue('filteredItems', data.items);
          `
        }
      }
    },
    {
      type: "ul",
      children: [
        {
          type: "li",
          iterate: {
            source: "$state.filteredItems"
          },
          children: [
            {
              type: "span",
              text: "{$item.name}"
            }
          ]
        }
      ]
    }
  ]
};
```

---

## Події (Events)

Події дозволяють обробляти взаємодію користувача з елементами UI. Бібліотека підтримує два типи подій: `setState` для простого оновлення стану та `code` для виконання довільного JavaScript коду.

### Тип події: setState

Використовується для простого оновлення стану компонента.

```typescript
events: {
  onClick: {
    actionType: "setState",
    variable: "$state.counter",
    value: "{$state.counter + 1}"
  }
}
```

**Параметри:**
- `actionType: "setState"` - тип події
- `variable` - шлях до змінної стану (завжди починається з `$state.`)
- `value` - нове значення з підтримкою інтерполяції та обчислень

### Тип події: code

Використовується для виконання довільного JavaScript коду. Надає повний доступ до контексту компонента.

#### Доступний контекст

У коді події доступні наступні змінні:

- **`data`** - дані передані в UIRenderer через проп `data`
- **`item`** - поточний елемент ітерації (якщо подія викликана всередині `iterate`)
- **`state`** - внутрішній стан компонента
- **`setStateValue`** - функція для оновлення стану: `setStateValue(variable: string, value: unknown)`
- **`value`** - значення події (для `onChange` - нове значення input, для `onClick` - event object)

#### Приклад: onClick з кодом

```typescript
const schema = {
  type: "button",
  text: "Submit Form",
  events: {
    onClick: {
      actionType: "code",
      code: `
        console.log('Form submitted!', data, state);
        
        // Валідація
        if (!state.email) {
          setStateValue('error', 'Email is required');
          return;
        }
        
        // Оновлення стану
        setStateValue('submitted', true);
        setStateValue('error', '');
        
        // Виконання додаткових дій
        if (data.onSubmit) {
          data.onSubmit(state);
        }
      `
    }
  }
};
```

#### Приклад: onChange з кодом

```typescript
const schema = {
  type: "input",
  value: "{$state.email}",
  placeholder: "Enter email",
  events: {
    onChange: {
      actionType: "code",
      code: `
        // Оновлення значення
        setStateValue('email', value);
        
        // Валідація в реальному часі
        const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;
        if (value && !emailRegex.test(value)) {
          setStateValue('emailError', 'Invalid email format');
        } else {
          setStateValue('emailError', '');
        }
        
        // Оновлення лічильника символів
        setStateValue('emailLength', value.length);
      `
    }
  }
};
```

#### Приклад: Комбінація подій

```typescript
const schema = {
  type: "div",
  children: [
    {
      type: "input",
      value: "{$state.searchQuery}",
      placeholder: "Search...",
      events: {
        onChange: {
          actionType: "code",
          code: `
            setStateValue('searchQuery', value);
            
            // Фільтрація даних
            const query = value.toLowerCase();
            const filtered = data.items.filter(item => 
              item.name.toLowerCase().includes(query)
            );
            setStateValue('filteredItems', filtered);
            setStateValue('resultsCount', filtered.length);
          `
        },
        onFocus: {
          actionType: "code",
          code: `
            setStateValue('isSearching', true);
          `
        },
        onBlur: {
          actionType: "code",
          code: `
            // Затримка перед приховуванням результатів
            setTimeout(() => {
              setStateValue('isSearching', false);
            }, 200);
          `
        }
      }
    },
    {
      type: "button",
      text: "Clear",
      events: {
        onClick: {
          actionType: "code",
          code: `
            setStateValue('searchQuery', '');
            setStateValue('filteredItems', data.items);
            setStateValue('resultsCount', data.items.length);
          `
        }
      }
    }
  ]
};
```

#### Приклад: Робота з ітерацією

```typescript
const schema = {
  type: "ul",
  children: [
    {
      type: "li",
      iterate: {
        source: "items"
      },
      children: [
        {
          type: "span",
          text: "{$item.name}"
        },
        {
          type: "button",
          text: "Delete",
          events: {
            onClick: {
              actionType: "code",
              code: `
                // item - поточний елемент ітерації
                console.log('Deleting item:', item);
                
                // Видалення з масиву
                const updatedItems = data.items.filter(i => i.id !== item.id);
                setStateValue('items', updatedItems);
                
                // Оновлення лічильника
                setStateValue('itemsCount', updatedItems.length);
              `
            }
          }
        }
      ]
    }
  ]
};
```

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

Бібліотека підтримує всі стандартні React події:

- `onClick` - клік по елементу
- `onChange` - зміна значення (input, select, textarea)
- `onFocus` - фокус на елементі
- `onBlur` - втрата фокусу
- `onMouseEnter` - наведення миші
- `onMouseLeave` - відведення миші
- `onKeyDown` - натискання клавіші
- `onKeyUp` - відпускання клавіші
- `onSubmit` - відправка форми
- Інші стандартні React події

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

✅ **Правильно:**

```typescript
// Прості оновлення стану
events: {
  onClick: {
    actionType: "setState",
    variable: "$state.counter",
    value: "{$state.counter + 1}"
  }
}

// Складна логіка
events: {
  onChange: {
    actionType: "code",
    code: `
      setStateValue('value', value);
      // Додаткова логіка
    `
  }
}
```

❌ **Неправильно:**

```typescript
// Не використовуйте code для простих операцій
events: {
  onClick: {
    actionType: "code",
    code: "setStateValue('counter', state.counter + 1)" // Краще використати setState
  }
}

// Не забувайте про безпеку
events: {
  onClick: {
    actionType: "code",
    code: "eval(userInput)" // НЕБЕЗПЕЧНО!
  }
}
```

---

## Типи

### UINode

Основний інтерфейс для вузлів UI схеми.

```typescript
interface UINode {
  type: string;
  className?: string;
  style?: Record<string, unknown>;
  children?: UINode[];
  text?: string;
  src?: string;
  alt?: string;
  format?: FormatConfig;
  condition?: Condition;
  iterate?: IterateConfig;
  refreshInterval?: number;
  schemaRef?: string;
  selfChannel?: string;
  events?: Record<string, AnyEvent>;
  value?: string;
  visible?: boolean | string;
  transparent?: boolean | string;
  animationType?: 'none' | 'fade' | 'slide' | string;
}
```

### FormatConfig

Конфігурація форматування значень.

```typescript
interface FormatConfig {
  type: "number" | "volume" | "datetime" | "time";
  decimals?: number;
  prefix?: string;
  suffix?: string;
}
```

### Condition

Умова для умовного рендерингу.

```typescript
interface Condition {
  key: string;
  operator: "===" | ">" | "<" | ">=" | "<=";
  value: unknown;
}
```

### IterateConfig

Конфігурація ітерації по масиву.

```typescript
interface IterateConfig {
  source: string;
  limit?: number;
  reverse?: boolean;
}
```

### EventConfigState

Конфігурація події для оновлення стану.

```typescript
interface EventConfigState {
  actionType: "setState";
  variable: string;  // Шлях до змінної стану (наприклад, "$state.counter")
  value: string;      // Значення з інтерполяцією (наприклад, "{$state.counter + 1}")
}
```

### EventConfigCode

Конфігурація події для виконання JavaScript коду.

```typescript
interface EventConfigCode {
  actionType: "code";
  code: string;  // JavaScript код для виконання
}
```

### AnyEvent

Об'єднаний тип для всіх подій.

```typescript
type AnyEvent = EventConfigState | EventConfigCode;
```

### Події (Events)

Події дозволяють обробляти взаємодію користувача з елементами UI. Підтримуються два типи подій:

#### EventConfigState

Подія для оновлення стану компонента.

```typescript
interface EventConfigState {
  actionType: "setState";
  variable: string;  // Шлях до змінної стану (наприклад, "$state.counter")
  value: string;      // Значення з інтерполяцією (наприклад, "{$state.counter + 1}")
}
```

**Приклад:**

```typescript
events: {
  onClick: {
    actionType: "setState",
    variable: "$state.counter",
    value: "{$state.counter + 1}"
  }
}
```

#### EventConfigCode

Подія для виконання довільного JavaScript коду.

```typescript
interface EventConfigCode {
  actionType: "code";
  code: string;  // JavaScript код для виконання
}
```

**Доступний контекст у коді:**

- `data` - дані передані в UIRenderer
- `item` - поточний елемент ітерації (якщо є)
- `state` - внутрішній стан компонента
- `setStateValue` - функція для оновлення стану: `setStateValue(variable: string, value: unknown)`
- `value` - значення події (наприклад, значення input для onChange)

**Приклад з onClick:**

```typescript
events: {
  onClick: {
    actionType: "code",
    code: `
      console.log('Clicked!', data, state);
      setStateValue('clicked', true);
      if (data.validate) {
        alert('Valid!');
      }
    `
  }
}
```

**Приклад з onChange:**

```typescript
events: {
  onChange: {
    actionType: "code",
    code: `
      setStateValue('inputValue', value);
      setStateValue('charCount', value.length);
    `
  }
}
```

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

- `onClick` - клік по елементу
- `onChange` - зміна значення (для input, select тощо)
- `onFocus` - фокус на елементі
- `onBlur` - втрата фокусу
- Інші стандартні React події

---

## Розширення

### Створення власного рендерера

Ви можете створити власний рендерер, наслідуючи `UIRendererEngine`:

```typescript
import { UIRendererEngine } from '@schema-gui/core';
import * as React from 'react';

class CustomRenderer extends UIRendererEngine {
  renderUINode(
    type: string,
    index: number,
    value: unknown | undefined,
    resolvedStyle?: React.CSSProperties,
    content?: string,
    childNodes?: React.ReactNode[],
    resolvedEvents?: Record<string, (value: unknown) => void>,
    resolvedClassName?: string,
    imgProps?: Record<string, string>,
    modalProps?: Record<string, any>
  ): React.ReactNode {
    // Ваша логіка рендерингу
    if (type === 'custom-component') {
      return React.createElement(CustomComponent, {
        key: index,
        style: resolvedStyle,
        children: childNodes
      });
    }
    
    // Стандартна обробка
    return React.createElement('div', {
      key: index,
      style: resolvedStyle,
      children: childNodes
    });
  }
}
```

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

```typescript
const customEngine = new CustomRenderer();

<UIRenderer
  schema={schema}
  data={data}
  renderEngine={customEngine}
  state={state}
  setStateValue={setStateValue}
/>
```

---

## Модальні вікна (Modal)

Модальні вікна дозволяють відображати контент поверх основного інтерфейсу. Компонент `modal` створює повноекранне модальне вікно з підтримкою анімацій та закриття по кліку на overlay.

### Властивості Modal

```typescript
interface ModalProps {
  visible: boolean | string;        // Видимість модального вікна
  transparent?: boolean | string;    // Прозорий фон overlay
  animationType?: 'none' | 'fade' | 'slide' | string; // Тип анімації
  onRequestClose?: () => void;       // Callback для закриття
}
```

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

```typescript
const schema = {
  type: "div",
  children: [
    {
      type: "button",
      text: "Open Modal",
      events: {
        onClick: {
          actionType: "setState",
          variable: "$state.showModal",
          value: "true"
        }
      }
    },
    {
      type: "modal",
      visible: "{$state.showModal}",
      animationType: "fade",
      transparent: false,
      events: {
        onRequestClose: {
          actionType: "setState",
          variable: "$state.showModal",
          value: "false"
        }
      },
      children: [
        {
          type: "div",
          style: {
            padding: "40px",
            backgroundColor: "#FFFFFF",
            borderRadius: "12px",
            maxWidth: "500px",
            width: "100%",
            margin: "auto"
          },
          children: [
            {
              type: "h2",
              text: "Modal Title",
              style: {
                marginBottom: "20px",
                fontSize: "24px",
                fontWeight: "600"
              }
            },
            {
              type: "p",
              text: "This is modal content. Click outside to close.",
              style: {
                marginBottom: "30px",
                color: "#666"
              }
            },
            {
              type: "button",
              text: "Close",
              events: {
                onClick: {
                  actionType: "setState",
                  variable: "$state.showModal",
                  value: "false"
                }
              },
              style: {
                padding: "10px 20px",
                backgroundColor: "#1E1E1E",
                color: "#FFFFFF",
                border: "none",
                borderRadius: "6px",
                cursor: "pointer"
              }
            }
          ]
        }
      ]
    }
  ]
};
```

### Прозорий фон

```typescript
{
  type: "modal",
  visible: "{$state.showModal}",
  transparent: true,
  animationType: "fade",
  children: [
    {
      type: "div",
      style: {
        padding: "20px",
        backgroundColor: "#FFFFFF",
        borderRadius: "8px"
      },
      children: [
        {
          type: "p",
          text: "Modal with transparent overlay"
        }
      ]
    }
  ]
}
```

### Закриття модального вікна

Модальне вікно можна закрити кількома способами:

#### 1. Клік на overlay (onRequestClose)

```typescript
{
  type: "modal",
  visible: "{$state.showModal}",
  events: {
    onRequestClose: {
      actionType: "setState",
      variable: "$state.showModal",
      value: "false"
    }
  },
  children: [/* ... */]
}
```

#### 2. Кнопка закриття всередині модального вікна

```typescript
{
  type: "modal",
  visible: "{$state.showModal}",
  children: [
    {
      type: "div",
      children: [
        {
          type: "button",
          text: "✕",
          events: {
            onClick: {
              actionType: "setState",
              variable: "$state.showModal",
              value: "false"
            }
          },
          style: {
            position: "absolute",
            top: "10px",
            right: "10px",
            width: "30px",
            height: "30px",
            border: "none",
            backgroundColor: "transparent",
            cursor: "pointer",
            fontSize: "20px"
          }
        },
        {
          type: "div",
          children: [
            {
              type: "p",
              text: "Modal content"
            }
          ]
        }
      ]
    }
  ]
}
```

#### 3. Закриття через код

```typescript
{
  type: "button",
  text: "Close Modal",
  events: {
    onClick: {
      actionType: "code",
      code: `
        setStateValue('showModal', false);
        // Додаткова логіка при закритті
        console.log('Modal closed');
      `
    }
  }
}
```

### Повний приклад: Форма в модальному вікні

```typescript
const schema = {
  type: "div",
  children: [
    {
      type: "button",
      text: "Open Form",
      events: {
        onClick: {
          actionType: "setState",
          variable: "$state.showForm",
          value: "true"
        }
      },
      style: {
        padding: "10px 20px",
        backgroundColor: "#1E1E1E",
        color: "#FFFFFF",
        border: "none",
        borderRadius: "6px",
        cursor: "pointer"
      }
    },
    {
      type: "modal",
      visible: "{$state.showForm}",
      animationType: "fade",
      transparent: false,
      events: {
        onRequestClose: {
          actionType: "setState",
          variable: "$state.showForm",
          value: "false"
        }
      },
      children: [
        {
          type: "div",
          style: {
            padding: "40px",
            backgroundColor: "#FFFFFF",
            borderRadius: "12px",
            maxWidth: "400px",
            width: "100%",
            margin: "auto"
          },
          children: [
            {
              type: "h2",
              text: "Contact Form",
              style: {
                marginBottom: "20px"
              }
            },
            {
              type: "div",
              style: {
                marginBottom: "15px"
              },
              children: [
                {
                  type: "label",
                  text: "Name:",
                  style: {
                    display: "block",
                    marginBottom: "5px",
                    fontWeight: "500"
                  }
                },
                {
                  type: "input",
                  value: "{$state.formName}",
                  events: {
                    onChange: {
                      actionType: "setState",
                      variable: "$state.formName",
                      value: "value"
                    }
                  },
                  style: {
                    width: "100%",
                    padding: "8px",
                    border: "1px solid #ddd",
                    borderRadius: "4px"
                  }
                }
              ]
            },
            {
              type: "div",
              style: {
                marginBottom: "15px"
              },
              children: [
                {
                  type: "label",
                  text: "Email:",
                  style: {
                    display: "block",
                    marginBottom: "5px",
                    fontWeight: "500"
                  }
                },
                {
                  type: "input",
                  value: "{$state.formEmail}",
                  events: {
                    onChange: {
                      actionType: "setState",
                      variable: "$state.formEmail",
                      value: "value"
                    }
                  },
                  style: {
                    width: "100%",
                    padding: "8px",
                    border: "1px solid #ddd",
                    borderRadius: "4px"
                  }
                }
              ]
            },
            {
              type: "div",
              style: {
                display: "flex",
                gap: "10px",
                justifyContent: "flex-end"
              },
              children: [
                {
                  type: "button",
                  text: "Cancel",
                  events: {
                    onClick: {
                      actionType: "setState",
                      variable: "$state.showForm",
                      value: "false"
                    }
                  },
                  style: {
                    padding: "10px 20px",
                    backgroundColor: "#f5f5f5",
                    border: "none",
                    borderRadius: "6px",
                    cursor: "pointer"
                  }
                },
                {
                  type: "button",
                  text: "Submit",
                  events: {
                    onClick: {
                      actionType: "code",
                      code: `
                        console.log('Form submitted:', {
                          name: state.formName,
                          email: state.formEmail
                        });
                        setStateValue('showForm', false);
                        setStateValue('formName', '');
                        setStateValue('formEmail', '');
                      `
                    }
                  },
                  style: {
                    padding: "10px 20px",
                    backgroundColor: "#1E1E1E",
                    color: "#FFFFFF",
                    border: "none",
                    borderRadius: "6px",
                    cursor: "pointer"
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ]
};
```

---

## Інтерполяція змінних

### Синтаксис

Змінні оборачуються у фігурні дужки: `{variable}`

### Прості змінні

```typescript
{
  type: "span",
  text: "Exchange: {exchange}"
}
```

**Дані:** `{ exchange: "bybit" }`  
**Результат:** `<span>Exchange: bybit</span>`

### Вкладені шляхи

```typescript
{
  type: "span",
  text: "Last Price: {data.last}"
}
```

**Дані:** `{ data: { last: 1093.6, bid: 1093.7 } }`  
**Результат:** `<span>Last Price: 1093.6</span>`

### Доступ до масивів

```typescript
{
  type: "span",
  text: "First bid: {data.bids[0][0]}"
}
```

**Дані:** `{ data: { bids: [[1091.9, 0.5501], [1091.8, 0.3254]] } }`  
**Результат:** `<span>First bid: 1091.9</span>`

### В ітерації: $item

Всередині `iterate` використовується спеціальна змінна `$item`:

```typescript
{
  type: "div",
  iterate: {
    source: "data.bids",
    limit: 3
  },
  children: [
    {
      type: "span",
      text: "Price: {$item[0]}, Amount: {$item[1]}"
    }
  ]
}
```

### Доступ до стану: $state

Для доступу до внутрішнього стану використовується `$state`:

```typescript
{
  type: "span",
  text: "Counter: {$state.counter}"
}
```

### Літерали зі спеціальними символами

```typescript
{
  type: "span",
  text: "${data.price}" // $ - літерал, {data.price} - змінна
}
```

**Результат:** `$1093.6`

```typescript
{
  type: "span",
  text: "$${data.price}" // $$ → один $ у виводі
}
```

**Результат:** `$$1093.6` → `$1093.6` після інтерполяції

---

## Форматування

### FormatConfig

```typescript
interface FormatConfig {
  type: "number" | "volume" | "datetime" | "time";
  decimals?: number;
  prefix?: string;
  suffix?: string;
}
```

### number

Форматування чисел з фіксованою точністю:

```typescript
{
  type: "span",
  text: "{data.price}",
  format: {
    type: "number",
    decimals: 2
  }
}
```

**Дані:** `{ data: { price: 1093.6789 } }`  
**Результат:** `1093.68`

### volume

Форматування великих чисел (K, M, B):

```typescript
{
  type: "span",
  text: "{data.volume}",
  format: {
    type: "volume",
    decimals: 2
  }
}
```

**Дані:** `{ data: { volume: 27258850 } }`  
**Результат:** `27.26M`

**Логіка:**
- `>= 1e9` → `X.XXB` (мільярди)
- `>= 1e6` → `X.XXM` (мільйони)
- `>= 1e3` → `X.XXK` (тисячі)
- `< 1e3` → `X.XX` (як є)

### datetime

Форматування timestamp у локальну дату та час:

```typescript
{
  type: "span",
  text: "{timestamp}",
  format: {
    type: "datetime"
  }
}
```

**Дані:** `{ timestamp: 1760823377856 }`  
**Результат:** `18.10.2025, 21:36:17` (залежить від локалі)

### time

Форматування timestamp у локальний час:

```typescript
{
  type: "span",
  text: "{timestamp}",
  format: {
    type: "time"
  }
}
```

**Дані:** `{ timestamp: 1760823377856 }`  
**Результат:** `21:36:17`

### Комбінування з літералами

```typescript
{
  type: "span",
  text: "Price: ${data.price}",
  format: {
    type: "number",
    decimals: 2
  }
}
```

**Результат:** `Price: $1093.68`

---

## Умови

### Condition

```typescript
interface Condition {
  key: string;
  operator: "===" | ">" | "<" | ">=" | "<=";
  value: unknown;
}
```

### Умовний рендеринг елемента

Елемент рендериться тільки якщо умова істинна:

```typescript
{
  type: "span",
  text: "● Active",
  condition: {
    key: "active",
    operator: "===",
    value: true
  }
}
```

**Дані:** `{ active: true }` → елемент рендериться  
**Дані:** `{ active: false }` → елемент не рендериться (null)

### Оператори

- `===` - Строга рівність
- `>` - Більше
- `<` - Менше
- `>=` - Більше або дорівнює
- `<=` - Менше або дорівнює

### Приклади

**Перевірка на позитивне значення:**

```typescript
{
  condition: {
    key: "data.change",
    operator: ">",
    value: 0
  }
}
```

**Перевірка в ітерації:**

```typescript
{
  condition: {
    key: "$item.side",
    operator: "===",
    value: "buy"
  }
}
```

**Перевірка стану:**

```typescript
{
  condition: {
    key: "$state.isVisible",
    operator: "===",
    value: true
  }
}
```

---

## Ітерація

### IterateConfig

```typescript
interface IterateConfig {
  source: string;    // Шлях до масиву
  limit?: number;    // Обмеження кількості
  reverse?: boolean; // Зворотний порядок
}
```

### Базова ітерація

```typescript
{
  type: "div",
  iterate: {
    source: "data.items",
    limit: 5
  },
  children: [
    {
      type: "span",
      text: "{$item.name}"
    }
  ]
}
```

**Дані:**

```typescript
{
  data: {
    items: [
      { name: "Item 1" },
      { name: "Item 2" },
      { name: "Item 3" }
    ]
  }
}
```

**Результат:**

```html
<div><span>Item 1</span></div>
<div><span>Item 2</span></div>
<div><span>Item 3</span></div>
```

### Ітерація по масиву масивів

```typescript
{
  type: "div",
  iterate: {
    source: "data.bids",
    limit: 3
  },
  children: [
    {
      type: "span",
      text: "Price: {$item[0]}, Amount: {$item[1]}"
    }
  ]
}
```

**Дані:**

```typescript
{
  data: {
    bids: [
      [1091.9, 0.5501],
      [1091.8, 0.3254],
      [1091.7, 1.5266]
    ]
  }
}
```

### Зворотний порядок

```typescript
{
  iterate: {
    source: "data.asks",
    limit: 10,
    reverse: true
  }
}
```

Корисно для відображення asks у зворотному порядку (від найдорожчої до найдешевшої).

### Ітерація в таблиці

```typescript
{
  type: "table",
  children: [
    {
      type: "tbody",
      children: [
        {
          type: "tr",
          iterate: {
            source: "data",
            limit: 20
          },
          children: [
            {
              type: "td",
              text: "{$item.timestamp}",
              format: { type: "time" }
            },
            {
              type: "td",
              text: "{$item.side}"
            },
            {
              type: "td",
              text: "${$item.price}",
              format: { type: "number", decimals: 2 }
            }
          ]
        }
      ]
    }
  ]
}
```

---

## Обчислення

### У text (інтерполяція)

Підтримуються арифметичні операції: `+`, `-`, `*`, `/`

```typescript
{
  type: "span",
  text: "Total: {$item[0] * $item[1]}",
  format: {
    type: "number",
    decimals: 2
  }
}
```

**Дані (в ітерації):** `$item = [1091.9, 0.5501]`  
**Результат:** `Total: 600.65`

### Складні вирази

```typescript
{
  text: "Spread: ${data.asks[0][0] - data.bids[0][0]}"
}
```

**Дані:**

```typescript
{
  data: {
    asks: [[1092, 2.7287]],
    bids: [[1091.9, 0.5501]]
  }
}
```

**Результат:** `Spread: $0.1`

### У стилях (calculate)

Для динамічних значень у стилях (наприклад, ширина прогрес-бару):

```typescript
{
  style: {
    width: {
      calculate: "percentage",
      value: "{$item[1]}",
      max: "{data.volume[0]}"
    }
  }
}
```

**Приклад:** Якщо `$item[1] = 0.5501` та `data.volume[0] = 377.65`:

```
width = (0.5501 / 377.65) * 100 = 0.15%
```

---

## Стилі

### React CSSProperties

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

```typescript
{
  style: {
    display: "flex",
    flexDirection: "column",
    gap: "16px",
    padding: "20px",
    backgroundColor: "#1E1E1E",
    borderRadius: "8px",
    fontSize: "14px",
    fontWeight: "600",
    color: "#FFFFFF"
  }
}
```

### Важливо: camelCase

Використовуйте camelCase для властивостей CSS:

✅ **Правильно:**

```typescript
"backgroundColor": "#1E1E1E"
"flexDirection": "column"
"fontSize": "14px"
```

❌ **Неправильно:**

```typescript
"background-color": "#1E1E1E"  // Не працює в React
```

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

Стилі можуть залежати від умов:

```typescript
{
  style: {
    color: {
      condition: {
        key: "data.change",
        operator: ">",
        value: 0
      },
      true: "#00C853",
      false: "#D50000"
    }
  }
}
```

**Дані:** `{ data: { change: 7.6 } }` → `color: "#00C853"` (зелений)  
**Дані:** `{ data: { change: -3.2 } }` → `color: "#D50000"` (червоний)

### Множинні умовні стилі

```typescript
{
  style: {
    backgroundColor: {
      condition: {
        key: "$item.side",
        operator: "===",
        value: "buy"
      },
      true: "rgba(0, 200, 83, 0.15)",
      false: "rgba(213, 0, 0, 0.15)"
    },
    color: {
      condition: {
        key: "$item.side",
        operator: "===",
        value: "buy"
      },
      true: "#00C853",
      false: "#D50000"
    }
  }
}
```

### Обчислювані стилі

Для динамічної ширини прогрес-барів:

```typescript
{
  type: "div",
  style: {
    position: "absolute",
    top: 0,
    right: 0,
    bottom: 0,
    backgroundColor: "rgba(0, 200, 83, 0.08)",
    width: {
      calculate: "percentage",
      value: "{$item[1]}",
      max: "{data.volume[0]}"
    }
  }
}
```

---

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

### 1. Структура даних

✅ **Правильно:**

```typescript
const uiData = {
  ...stream.raw,
  active: stream.active,
  module: stream.module,
};
```

❌ **Неправильно:**

```typescript
const uiData = stream; // Неправильна структура для UIRenderer
```

### 2. Шляхи до даних

✅ **Правильно:**

```typescript
{
  text: "{exchange}",        // → data.exchange
  text: "{data.last}",       // → data.data.last
  text: "{$item.price}"      // → в ітерації
  text: "{$state.counter}"   // → в стані
}
```

❌ **Неправильно:**

```typescript
{
  text: "{raw.exchange}",    // raw не передається
  text: "{item.price}"       // Без $ в ітерації
}
```

### 3. Форматування

✅ **Правильно:**

```typescript
{
  text: "${data.price}",
  format: { type: "number", decimals: 2 }
}
```

❌ **Неправильно:**

```typescript
{
  text: "{data.price}",
  format: {
    type: "number",
    decimals: 2,
    prefix: "$" // Використовуйте літерали в text
  }
}
```

### 4. Умови

✅ **Правильно:**

```typescript
{
  condition: {
    key: "active",
    operator: "===",
    value: true
  }
}
```

❌ **Неправильно:**

```typescript
{
  condition: "active === true" // Рядок не працює
}
```

### 5. Ітерація

✅ **Правильно:**

```typescript
{
  iterate: {
    source: "data.items", // Шлях відносно data
    limit: 10
  }
}
```

❌ **Неправильно:**

```typescript
{
  iterate: {
    source: "items",      // Якщо items не на верхньому рівні
    max: 10              // Параметр називається limit, не max
  }
}
```

### 6. Стилі

✅ **Правильно:**

```typescript
{
  style: {
    backgroundColor: "#1E1E1E",
    fontSize: "14px"
  }
}
```

❌ **Неправильно:**

```typescript
{
  style: {
    "background-color": "#1E1E1E", // Kebab-case не працює
    "font-size": "14px"
  }
}
```

### 7. Продуктивність

- Використовуйте `limit` в ітерації для великих масивів
- Мінімізуйте глибину вкладеності
- Уникайте складних обчислень у text (краще передобчислити на бекенді)

### 8. Читабельність

- Групуйте пов'язані елементи
- Використовуйте осмислені ключі в даних
- Додавайте коментарі в JSON (якщо парсер підтримує)
- Форматуйте JSON для кращої читабельності

### 9. Валідація

Перед деплоєм перевіряйте:

- Всі шляхи до даних існують
- Умови коректні
- Формати підтримуються
- Стилі використовують camelCase

### 10. Тестування

```typescript
// Тестові дані
const testData = {
  exchange: "bybit",
  market: "SOL/USDT",
  data: {
    last: 1093.6,
    bid: 1093.7,
    ask: 1093.8,
  },
  active: true,
  timestamp: Date.now(),
  latency: 0,
};

// Рендеринг
<UIRenderer
  schema={schema}
  data={testData}
  renderEngine={renderEngine}
  state={state}
  setStateValue={setStateValue}
/>;
```

---

## Обмеження

### Не підтримується

1. **Користувацькі React компоненти** - тільки HTML елементи та вбудовані типи (modal, image тощо)
2. **Refs** - доступ до DOM елементів
3. **Fragments** - використовуйте обгортку div
4. **Async operations** - дані повинні бути готові
5. **Custom formatters** - тільки вбудовані типи (number, volume, datetime, time)

### Обхідні шляхи

**Custom formatters:**

```typescript
// Передформатуйте дані перед передачею в UIRenderer
const formattedData = {
  ...data,
  formattedPrice: customFormat(data.price),
};
```

**Складні обчислення:**

```typescript
// Передобчисліть складні значення
const processedData = {
  ...data,
  total: data.items.reduce((sum, item) => sum + item.price, 0),
};
```

---

## Налагодження

### Часті помилки

**1. NaN у виводі**

```
Причина: Невірний шлях до даних або некоректне обчислення
Рішення: Перевірте шлях та типи даних
```

**2. Елемент не рендериться**

```
Причина: Умова повертає false або шлях до даних невірний
Рішення: Перевірте condition та дані
```

**3. Стилі не застосовуються**

```
Причина: Використано kebab-case замість camelCase
Рішення: backgroundColor замість background-color
```

**4. Ітерація не працює**

```
Причина: source вказує на не-масив або невірний шлях
Рішення: Перевірте що source веде до масиву
```

**5. Події не спрацьовують**

```
Причина: Неправильний тип події або помилка в коді
Рішення: Перевірте синтаксис події та консоль на помилки
```