# ⚛️ React интеграция

CSElement предоставляет мощные хуки и компоненты для бесшовной интеграции с React приложениями, обеспечивая реактивность и автоматические обновления UI.

## 📋 Содержание

1. [Основы React интеграции](#основы-react-интеграции)
2. [Хуки для работы с элементами](#хуки-для-работы-с-элементами)
3. [Хуки для системы CSElement](#хуки-для-системы-cselement)
4. [Реактивные компоненты](#реактивные-компоненты)
5. [Управление состоянием](#управление-состоянием)
6. [Оптимизация производительности](#оптимизация-производительности)
7. [Полный пример: React приложение с CSElement](#полный-пример-react-приложение-с-cselement)

## Основы React интеграции

CSElement интегрируется с React через специальные хуки, которые автоматически подписываются на изменения элементов и обновляют компоненты.

### Установка и настройка

```bash
npm install cs-element react react-dom
```

```typescript
import React from 'react';
import { CSElement } from 'cs-element';
import { useElement, useCSElementById, useQuery } from 'cs-element/react';

// Инициализация CSElement для React
CSElement.configureReactivity({ 
  debug: true,
  batchUpdates: true 
});
```

### Простой пример

```tsx
import React from 'react';
import { useElement } from 'cs-element/react';

function SimpleCounter() {
  const { element, loading, error, update } = useElement({
    name: 'counter',
    data: { count: 0 }
  });

  if (loading) return <div>Загрузка...</div>;
  if (error) return <div>Ошибка: {error.message}</div>;
  if (!element) return <div>Элемент не найден</div>;

  const count = element.getData('count') || 0;

  const increment = () => {
    update({ count: count + 1 });
  };

  return (
    <div>
      <h2>Счетчик: {count}</h2>
      <button onClick={increment}>+1</button>
    </div>
  );
}
```

## Хуки для работы с элементами

### useElement - Управление одним элементом

```tsx
import React from 'react';
import { useElement } from 'cs-element/react';

interface UserProfileProps {
  userId?: string;
  parentElement?: CSElement;
}

function UserProfile({ userId, parentElement }: UserProfileProps) {
  const { element, loading, error, update, remove, save } = useElement({
    name: userId ? `user-${userId}` : undefined,
    parent: parentElement,
    data: {
      name: '',
      email: '',
      avatar: '',
      preferences: {
        theme: 'light',
        notifications: true
      }
    }
  });

  if (loading) return <div className="loading">Загрузка профиля...</div>;
  if (error) return <div className="error">Ошибка: {error.message}</div>;
  if (!element) return <div>Профиль не найден</div>;

  const handleUpdateProfile = async (updates: Record<string, any>) => {
    try {
      await update(updates);
      console.log('Профиль обновлен');
    } catch (err) {
      console.error('Ошибка обновления:', err);
    }
  };

  const handleSaveProfile = async () => {
    try {
      await save();
      console.log('Профиль сохранен');
    } catch (err) {
      console.error('Ошибка сохранения:', err);
    }
  };

  const handleDeleteProfile = async () => {
    if (confirm('Удалить профиль?')) {
      try {
        await remove();
        console.log('Профиль удален');
      } catch (err) {
        console.error('Ошибка удаления:', err);
      }
    }
  };

  return (
    <div className="user-profile">
      <h2>Профиль пользователя</h2>
      
      <div className="form-group">
        <label>Имя:</label>
        <input
          type="text"
          value={element.getData('name') || ''}
          onChange={(e) => handleUpdateProfile({ name: e.target.value })}
        />
      </div>

      <div className="form-group">
        <label>Email:</label>
        <input
          type="email"
          value={element.getData('email') || ''}
          onChange={(e) => handleUpdateProfile({ email: e.target.value })}
        />
      </div>

      <div className="form-group">
        <label>Тема:</label>
        <select
          value={element.getData('preferences')?.theme || 'light'}
          onChange={(e) => handleUpdateProfile({ 
            preferences: { 
              ...element.getData('preferences'),
              theme: e.target.value 
            }
          })}
        >
          <option value="light">Светлая</option>
          <option value="dark">Темная</option>
        </select>
      </div>

      <div className="actions">
        <button onClick={handleSaveProfile}>Сохранить</button>
        <button onClick={handleDeleteProfile} className="danger">
          Удалить
        </button>
      </div>
    </div>
  );
}
```

### useCSElementById - Получение элемента по ID

```tsx
import React from 'react';
import { useCSElementById } from 'cs-element/react';

interface ElementViewerProps {
  elementId: string;
}

function ElementViewer({ elementId }: ElementViewerProps) {
  const element = useCSElementById(elementId);

  if (!element) {
    return <div>Элемент с ID {elementId} не найден</div>;
  }

  return (
    <div className="element-viewer">
      <h3>Элемент: {element.name}</h3>
      <div className="element-info">
        <p><strong>ID:</strong> {element.id}</p>
        <p><strong>Создан:</strong> {element.getData('createdAt')}</p>
        <p><strong>Тип:</strong> {element.getData('type')}</p>
      </div>
      
      <div className="element-data">
        <h4>Данные:</h4>
        <pre>{JSON.stringify(element.getAllData(), null, 2)}</pre>
      </div>
    </div>
  );
}
```

### useQuery - Поиск элементов

```tsx
import React, { useState } from 'react';
import { useQuery } from 'cs-element/react';

interface TaskListProps {
  project: CSElement;
}

function TaskList({ project }: TaskListProps) {
  const [filter, setFilter] = useState('all');
  
  // Динамический селектор на основе фильтра
  const selector = React.useMemo(() => {
    switch (filter) {
      case 'completed':
        return '[type="task"][completed="true"]';
      case 'pending':
        return '[type="task"][completed="false"]';
      default:
        return '[type="task"]';
    }
  }, [filter]);

  const tasks = useQuery(project, selector);

  return (
    <div className="task-list">
      <div className="filters">
        <button 
          className={filter === 'all' ? 'active' : ''}
          onClick={() => setFilter('all')}
        >
          Все ({tasks.length})
        </button>
        <button 
          className={filter === 'pending' ? 'active' : ''}
          onClick={() => setFilter('pending')}
        >
          В работе
        </button>
        <button 
          className={filter === 'completed' ? 'active' : ''}
          onClick={() => setFilter('completed')}
        >
          Завершенные
        </button>
      </div>

      <div className="tasks">
        {tasks.map(task => (
          <TaskItem key={task.id} task={task} />
        ))}
      </div>
    </div>
  );
}

function TaskItem({ task }: { task: CSElement }) {
  const [isEditing, setIsEditing] = useState(false);

  const toggleComplete = async () => {
    const completed = task.getData('completed');
    await task.setData('completed', !completed);
  };

  const updateTitle = async (newTitle: string) => {
    await task.setData('title', newTitle);
    setIsEditing(false);
  };

  return (
    <div className={`task-item ${task.getData('completed') ? 'completed' : ''}`}>
      <input
        type="checkbox"
        checked={task.getData('completed') || false}
        onChange={toggleComplete}
      />
      
      {isEditing ? (
        <input
          type="text"
          defaultValue={task.getData('title')}
          onBlur={(e) => updateTitle(e.target.value)}
          onKeyPress={(e) => {
            if (e.key === 'Enter') {
              updateTitle(e.currentTarget.value);
            }
          }}
          autoFocus
        />
      ) : (
        <span 
          className="task-title"
          onDoubleClick={() => setIsEditing(true)}
        >
          {task.getData('title')}
        </span>
      )}
      
      <div className="task-meta">
        <span className="priority">{task.getData('priority')}</span>
        <span className="assignee">{task.getData('assignee')}</span>
      </div>
    </div>
  );
}
```

### useQueryOne - Получение одного элемента

```tsx
import React from 'react';
import { useQueryOne } from 'cs-element/react';

interface ActiveUserProps {
  workspace: CSElement;
}

function ActiveUser({ workspace }: ActiveUserProps) {
  // Находим текущего активного пользователя
  const activeUser = useQueryOne(workspace, '[type="user"][status="active"]');

  if (!activeUser) {
    return <div>Нет активного пользователя</div>;
  }

  return (
    <div className="active-user">
      <img 
        src={activeUser.getData('avatar')} 
        alt={activeUser.getData('name')}
        className="avatar"
      />
      <div className="user-info">
        <h4>{activeUser.getData('name')}</h4>
        <p>{activeUser.getData('role')}</p>
        <span className="status online">В сети</span>
      </div>
    </div>
  );
}
```

## Хуки для системы CSElement

### useCSElementSystem - Управление всей системой

```tsx
import React, { useState } from 'react';
import { useCSElementSystem } from 'cs-element/react';

function AppRoot() {
  const [autoConnect] = useState(true);
  
  const {
    root,
    connected,
    error,
    stats,
    connect,
    disconnect,
    reset
  } = useCSElementSystem({
    autoConnect,
    enableHistory: true,
    enableReactivity: true,
    enablePersistence: true
  });

  if (error) {
    return (
      <div className="error-screen">
        <h2>Ошибка инициализации</h2>
        <p>{error.message}</p>
        <button onClick={() => window.location.reload()}>
          Перезагрузить
        </button>
      </div>
    );
  }

  if (!connected || !root) {
    return (
      <div className="loading-screen">
        <h2>Инициализация CSElement...</h2>
        <div className="spinner"></div>
      </div>
    );
  }

  return (
    <div className="app">
      <header className="app-header">
        <h1>CSElement React App</h1>
        <div className="system-stats">
          <span>Элементов: {stats.elementCount}</span>
          <span>Подключено: {connected ? '✅' : '❌'}</span>
          {stats.lastUpdate && (
            <span>Обновлено: {stats.lastUpdate.toLocaleTimeString()}</span>
          )}
        </div>
        <div className="system-controls">
          <button onClick={disconnect}>Отключить</button>
          <button onClick={connect}>Подключить</button>
          <button onClick={reset} className="danger">Сброс</button>
        </div>
      </header>
      
      <main className="app-content">
        <WorkspaceManager root={root} />
      </main>
    </div>
  );
}

function WorkspaceManager({ root }: { root: CSElement }) {
  const workspaces = useQuery(root, '[type="workspace"]');

  return (
    <div className="workspace-manager">
      <h2>Рабочие пространства</h2>
      
      {workspaces.length === 0 ? (
        <EmptyWorkspaceState root={root} />
      ) : (
        <div className="workspaces">
          {workspaces.map(workspace => (
            <WorkspaceCard key={workspace.id} workspace={workspace} />
          ))}
        </div>
      )}
    </div>
  );
}
```

## Реактивные компоненты

### Компонент с автоматическими обновлениями

```tsx
import React, { useEffect, useState } from 'react';
import { CSElement } from 'cs-element';

interface ReactiveElementProps {
  element: CSElement;
  children: (element: CSElement, data: any) => React.ReactNode;
}

function ReactiveElement({ element, children }: ReactiveElementProps) {
  const [data, setData] = useState(element.getAllData());
  const [updateCount, setUpdateCount] = useState(0);

  useEffect(() => {
    // Подписываемся на изменения элемента
    const unsubscribe = element.subscribe('dataChanged', (newData) => {
      setData({ ...newData });
      setUpdateCount(prev => prev + 1);
    });

    // Подписываемся на изменения структуры
    const unsubscribeStructure = element.subscribe('elementAdded', () => {
      setUpdateCount(prev => prev + 1);
    });

    const unsubscribeRemoval = element.subscribe('elementRemoved', () => {
      setUpdateCount(prev => prev + 1);
    });

    return () => {
      unsubscribe();
      unsubscribeStructure();
      unsubscribeRemoval();
    };
  }, [element]);

  return (
    <div className="reactive-element" data-updates={updateCount}>
      {children(element, data)}
    </div>
  );
}

// Использование реактивного компонента
function DocumentEditor({ document }: { document: CSElement }) {
  return (
    <ReactiveElement element={document}>
      {(doc, data) => (
        <div className="document-editor">
          <h2>{data.title || 'Без названия'}</h2>
          <div className="document-meta">
            <span>Автор: {data.author}</span>
            <span>Изменен: {data.lastModified}</span>
            <span>Элементов: {doc.elementsCount()}</span>
          </div>
          
          <div className="document-content">
            <textarea
              value={data.content || ''}
              onChange={(e) => doc.setData('content', e.target.value)}
              placeholder="Введите содержимое документа..."
            />
          </div>

          <div className="document-sections">
            <h3>Разделы</h3>
            <SectionList document={doc} />
          </div>
        </div>
      )}
    </ReactiveElement>
  );
}

function SectionList({ document }: { document: CSElement }) {
  const sections = useQuery(document, '[type="section"]');

  const addSection = async () => {
    const section = await document.addElement(`section-${Date.now()}`) as CSElement;
    await section.setData('type', 'section');
    await section.setData('title', 'Новый раздел');
    await section.setData('content', '');
  };

  return (
    <div className="section-list">
      {sections.map(section => (
        <SectionItem key={section.id} section={section} />
      ))}
      <button onClick={addSection} className="add-section">
        + Добавить раздел
      </button>
    </div>
  );
}

function SectionItem({ section }: { section: CSElement }) {
  const { update, remove } = useElement({ name: section.name });

  return (
    <div className="section-item">
      <input
        type="text"
        value={section.getData('title') || ''}
        onChange={(e) => update({ title: e.target.value })}
        className="section-title"
      />
      <textarea
        value={section.getData('content') || ''}
        onChange={(e) => update({ content: e.target.value })}
        className="section-content"
        rows={3}
      />
      <button onClick={remove} className="remove-section">
        Удалить
      </button>
    </div>
  );
}
```

## Управление состоянием

### Глобальное состояние через CSElement

```tsx
import React, { createContext, useContext } from 'react';
import { useCSElementSystem } from 'cs-element/react';

// Создаем контекст для глобального состояния
const AppStateContext = createContext<{
  appState: CSElement | null;
  updateAppState: (updates: Record<string, any>) => Promise<void>;
} | null>(null);

function AppStateProvider({ children }: { children: React.ReactNode }) {
  const { root } = useCSElementSystem({ autoConnect: true });
  
  const appState = useQueryOne(root, '[name="app-state"]') || 
    React.useMemo(() => {
      if (root) {
        const state = new CSElement('app-state');
        root.addElement(state);
        
        // Инициализируем начальное состояние
        state.setData('theme', 'light');
        state.setData('language', 'ru');
        state.setData('user', null);
        state.setData('notifications', []);
        
        return state;
      }
      return null;
    }, [root]);

  const updateAppState = async (updates: Record<string, any>) => {
    if (appState) {
      for (const [key, value] of Object.entries(updates)) {
        await appState.setData(key, value);
      }
    }
  };

  return (
    <AppStateContext.Provider value={{ appState, updateAppState }}>
      {children}
    </AppStateContext.Provider>
  );
}

// Хук для использования глобального состояния
function useAppState() {
  const context = useContext(AppStateContext);
  if (!context) {
    throw new Error('useAppState must be used within AppStateProvider');
  }
  return context;
}

// Компонент для управления темой
function ThemeToggle() {
  const { appState, updateAppState } = useAppState();
  
  if (!appState) return null;

  const currentTheme = appState.getData('theme');

  const toggleTheme = () => {
    const newTheme = currentTheme === 'light' ? 'dark' : 'light';
    updateAppState({ theme: newTheme });
  };

  return (
    <button onClick={toggleTheme} className="theme-toggle">
      {currentTheme === 'light' ? '🌙' : '☀️'}
      {currentTheme === 'light' ? 'Темная тема' : 'Светлая тема'}
    </button>
  );
}

// Компонент уведомлений
function NotificationCenter() {
  const { appState, updateAppState } = useAppState();
  
  if (!appState) return null;

  const notifications = appState.getData('notifications') || [];

  const addNotification = (message: string, type: 'info' | 'success' | 'error' = 'info') => {
    const newNotification = {
      id: Date.now(),
      message,
      type,
      timestamp: new Date()
    };
    
    updateAppState({
      notifications: [...notifications, newNotification]
    });

    // Автоматически удаляем через 5 секунд
    setTimeout(() => {
      removeNotification(newNotification.id);
    }, 5000);
  };

  const removeNotification = (id: number) => {
    updateAppState({
      notifications: notifications.filter((n: any) => n.id !== id)
    });
  };

  return (
    <div className="notification-center">
      <div className="notifications">
        {notifications.map((notification: any) => (
          <div 
            key={notification.id} 
            className={`notification ${notification.type}`}
          >
            <span>{notification.message}</span>
            <button onClick={() => removeNotification(notification.id)}>
              ×
            </button>
          </div>
        ))}
      </div>
      
      <div className="notification-controls">
        <button onClick={() => addNotification('Информационное сообщение')}>
          Добавить уведомление
        </button>
        <button onClick={() => addNotification('Успешная операция', 'success')}>
          Успех
        </button>
        <button onClick={() => addNotification('Произошла ошибка', 'error')}>
          Ошибка
        </button>
      </div>
    </div>
  );
}
```

## Оптимизация производительности

### Мемоизация и оптимизация рендеров

```tsx
import React, { memo, useMemo, useCallback } from 'react';
import { useQuery } from 'cs-element/react';

// Оптимизированный компонент списка
const OptimizedList = memo(function OptimizedList({ 
  container, 
  filter 
}: { 
  container: CSElement;
  filter: string;
}) {
  // Мемоизируем селектор
  const selector = useMemo(() => {
    if (!filter) return '*';
    return `[type="${filter}"]`;
  }, [filter]);

  const items = useQuery(container, selector);

  // Мемоизируем обработчики
  const handleItemClick = useCallback((item: CSElement) => {
    console.log('Clicked item:', item.name);
  }, []);

  const handleItemUpdate = useCallback(async (item: CSElement, updates: any) => {
    for (const [key, value] of Object.entries(updates)) {
      await item.setData(key, value);
    }
  }, []);

  return (
    <div className="optimized-list">
      {items.map(item => (
        <OptimizedListItem
          key={item.id}
          item={item}
          onClick={handleItemClick}
          onUpdate={handleItemUpdate}
        />
      ))}
    </div>
  );
});

// Оптимизированный элемент списка
const OptimizedListItem = memo(function OptimizedListItem({
  item,
  onClick,
  onUpdate
}: {
  item: CSElement;
  onClick: (item: CSElement) => void;
  onUpdate: (item: CSElement, updates: any) => Promise<void>;
}) {
  // Мемоизируем данные элемента
  const itemData = useMemo(() => item.getAllData(), [item]);

  const handleClick = useCallback(() => {
    onClick(item);
  }, [item, onClick]);

  const handleUpdate = useCallback((updates: any) => {
    onUpdate(item, updates);
  }, [item, onUpdate]);

  return (
    <div className="list-item" onClick={handleClick}>
      <h4>{itemData.title}</h4>
      <p>{itemData.description}</p>
      <div className="item-controls">
        <button onClick={() => handleUpdate({ featured: !itemData.featured })}>
          {itemData.featured ? 'Убрать из избранного' : 'В избранное'}
        </button>
      </div>
    </div>
  );
});

// Виртуализированный список для больших коллекций
function VirtualizedList({ container }: { container: CSElement }) {
  const [visibleRange, setVisibleRange] = useState({ start: 0, end: 50 });
  const allItems = useQuery(container, '*');

  // Показываем только видимые элементы
  const visibleItems = useMemo(() => {
    return allItems.slice(visibleRange.start, visibleRange.end);
  }, [allItems, visibleRange]);

  const handleScroll = useCallback((e: React.UIEvent<HTMLDivElement>) => {
    const { scrollTop, clientHeight, scrollHeight } = e.currentTarget;
    const itemHeight = 60; // Примерная высота элемента
    const itemsPerView = Math.ceil(clientHeight / itemHeight);
    const start = Math.floor(scrollTop / itemHeight);
    const end = Math.min(start + itemsPerView + 10, allItems.length);

    setVisibleRange({ start, end });
  }, [allItems.length]);

  return (
    <div 
      className="virtualized-list"
      onScroll={handleScroll}
      style={{ height: '400px', overflowY: 'auto' }}
    >
      <div style={{ height: allItems.length * 60 }}>
        <div style={{ transform: `translateY(${visibleRange.start * 60}px)` }}>
          {visibleItems.map(item => (
            <OptimizedListItem
              key={item.id}
              item={item}
              onClick={() => {}}
              onUpdate={async () => {}}
            />
          ))}
        </div>
      </div>
    </div>
  );
}
```

## Полный пример: React приложение с CSElement

```tsx
import React, { useState } from 'react';
import { createRoot } from 'react-dom/client';
import { 
  useCSElementSystem, 
  useElement, 
  useQuery,
  useQueryOne 
} from 'cs-element/react';
import './App.css';

// Главный компонент приложения
function App() {
  return (
    <AppStateProvider>
      <div className="app">
        <Header />
        <main className="main-content">
          <ProjectManager />
        </main>
      </div>
    </AppStateProvider>
  );
}

// Провайдер состояния приложения
function AppStateProvider({ children }: { children: React.ReactNode }) {
  const { root, connected, error } = useCSElementSystem({
    autoConnect: true,
    enableHistory: true,
    enableReactivity: true
  });

  if (error) {
    return <div className="error">Ошибка: {error.message}</div>;
  }

  if (!connected || !root) {
    return <div className="loading">Загрузка...</div>;
  }

  return (
    <AppContext.Provider value={{ root }}>
      {children}
    </AppContext.Provider>
  );
}

const AppContext = React.createContext<{ root: CSElement | null }>({ root: null });

function useAppContext() {
  const context = React.useContext(AppContext);
  if (!context.root) {
    throw new Error('useAppContext must be used within AppStateProvider');
  }
  return context;
}

// Компонент шапки
function Header() {
  const { root } = useAppContext();
  const projects = useQuery(root, '[type="project"]');

  return (
    <header className="header">
      <h1>🚀 Project Manager</h1>
      <div className="header-stats">
        <span className="stat">
          📁 Проектов: {projects.length}
        </span>
        <span className="stat">
          ✅ Задач: {projects.reduce((total, project) => {
            return total + project.query('[type="task"]').length;
          }, 0)}
        </span>
      </div>
      <UserProfile />
    </header>
  );
}

// Профиль пользователя
function UserProfile() {
  const { root } = useAppContext();
  const { element: user, update } = useElement({
    name: 'current-user',
    parent: root,
    data: {
      name: 'Пользователь',
      avatar: '👤',
      preferences: {
        theme: 'light'
      }
    }
  });

  if (!user) return null;

  const toggleTheme = () => {
    const currentTheme = user.getData('preferences')?.theme || 'light';
    const newTheme = currentTheme === 'light' ? 'dark' : 'light';
    
    update({
      preferences: {
        ...user.getData('preferences'),
        theme: newTheme
      }
    });

    // Применяем тему к документу
    document.body.className = newTheme;
  };

  return (
    <div className="user-profile">
      <span className="user-avatar">{user.getData('avatar')}</span>
      <span className="user-name">{user.getData('name')}</span>
      <button onClick={toggleTheme} className="theme-toggle">
        {user.getData('preferences')?.theme === 'light' ? '🌙' : '☀️'}
      </button>
    </div>
  );
}

// Менеджер проектов
function ProjectManager() {
  const { root } = useAppContext();
  const projects = useQuery(root, '[type="project"]');
  const [selectedProjectId, setSelectedProjectId] = useState<string | null>(null);

  const selectedProject = selectedProjectId 
    ? projects.find(p => p.id === selectedProjectId) || null
    : null;

  const createProject = async () => {
    const projectName = prompt('Название проекта:');
    if (!projectName) return;

    const project = await root.addElement(`project-${Date.now()}`) as CSElement;
    await project.setData('type', 'project');
    await project.setData('name', projectName);
    await project.setData('description', '');
    await project.setData('status', 'active');
    await project.setData('createdAt', new Date().toISOString());

    setSelectedProjectId(project.id);
  };

  return (
    <div className="project-manager">
      <aside className="sidebar">
        <div className="sidebar-header">
          <h2>Проекты</h2>
          <button onClick={createProject} className="create-btn">
            + Создать
          </button>
        </div>
        
        <div className="project-list">
          {projects.map(project => (
            <ProjectItem
              key={project.id}
              project={project}
              isSelected={project.id === selectedProjectId}
              onSelect={() => setSelectedProjectId(project.id)}
            />
          ))}
        </div>
      </aside>

      <main className="content">
        {selectedProject ? (
          <ProjectDetails project={selectedProject} />
        ) : (
          <div className="empty-state">
            <h2>Выберите проект</h2>
            <p>Выберите проект из списка или создайте новый</p>
          </div>
        )}
      </main>
    </div>
  );
}

// Элемент проекта в списке
function ProjectItem({ 
  project, 
  isSelected, 
  onSelect 
}: { 
  project: CSElement;
  isSelected: boolean;
  onSelect: () => void;
}) {
  const tasks = useQuery(project, '[type="task"]');
  const completedTasks = tasks.filter(task => task.getData('completed'));

  return (
    <div 
      className={`project-item ${isSelected ? 'selected' : ''}`}
      onClick={onSelect}
    >
      <h3>{project.getData('name')}</h3>
      <div className="project-stats">
        <span className="task-count">
          {completedTasks.length}/{tasks.length} задач
        </span>
        <span className={`status ${project.getData('status')}`}>
          {project.getData('status')}
        </span>
      </div>
    </div>
  );
}

// Детали проекта
function ProjectDetails({ project }: { project: CSElement }) {
  const { update } = useElement({ name: project.name });
  const tasks = useQuery(project, '[type="task"]');
  const [showCompleted, setShowCompleted] = useState(true);

  const filteredTasks = showCompleted 
    ? tasks 
    : tasks.filter(task => !task.getData('completed'));

  const createTask = async () => {
    const taskTitle = prompt('Название задачи:');
    if (!taskTitle) return;

    const task = await project.addElement(`task-${Date.now()}`) as CSElement;
    await task.setData('type', 'task');
    await task.setData('title', taskTitle);
    await task.setData('description', '');
    await task.setData('completed', false);
    await task.setData('priority', 'medium');
    await task.setData('createdAt', new Date().toISOString());
  };

  return (
    <div className="project-details">
      <header className="project-header">
        <div className="project-info">
          <input
            type="text"
            value={project.getData('name') || ''}
            onChange={(e) => update({ name: e.target.value })}
            className="project-title"
          />
          <textarea
            value={project.getData('description') || ''}
            onChange={(e) => update({ description: e.target.value })}
            placeholder="Описание проекта..."
            className="project-description"
          />
        </div>
        
        <div className="project-actions">
          <select
            value={project.getData('status') || 'active'}
            onChange={(e) => update({ status: e.target.value })}
          >
            <option value="active">Активный</option>
            <option value="paused">Приостановлен</option>
            <option value="completed">Завершен</option>
          </select>
        </div>
      </header>

      <div className="tasks-section">
        <div className="tasks-header">
          <h3>Задачи</h3>
          <div className="tasks-controls">
            <label>
              <input
                type="checkbox"
                checked={showCompleted}
                onChange={(e) => setShowCompleted(e.target.checked)}
              />
              Показать завершенные
            </label>
            <button onClick={createTask} className="create-task-btn">
              + Добавить задачу
            </button>
          </div>
        </div>

        <div className="tasks-list">
          {filteredTasks.map(task => (
            <TaskItem key={task.id} task={task} />
          ))}
        </div>
      </div>
    </div>
  );
}

// Элемент задачи
function TaskItem({ task }: { task: CSElement }) {
  const { update, remove } = useElement({ name: task.name });
  const [isEditing, setIsEditing] = useState(false);

  const toggleCompleted = () => {
    update({ completed: !task.getData('completed') });
  };

  const updatePriority = (priority: string) => {
    update({ priority });
  };

  const deleteTask = () => {
    if (confirm('Удалить задачу?')) {
      remove();
    }
  };

  return (
    <div className={`task-item ${task.getData('completed') ? 'completed' : ''}`}>
      <div className="task-main">
        <input
          type="checkbox"
          checked={task.getData('completed') || false}
          onChange={toggleCompleted}
        />
        
        {isEditing ? (
          <input
            type="text"
            value={task.getData('title') || ''}
            onChange={(e) => update({ title: e.target.value })}
            onBlur={() => setIsEditing(false)}
            onKeyPress={(e) => {
              if (e.key === 'Enter') {
                setIsEditing(false);
              }
            }}
            autoFocus
          />
        ) : (
          <span 
            className="task-title"
            onDoubleClick={() => setIsEditing(true)}
          >
            {task.getData('title')}
          </span>
        )}
      </div>

      <div className="task-controls">
        <select
          value={task.getData('priority') || 'medium'}
          onChange={(e) => updatePriority(e.target.value)}
          className={`priority ${task.getData('priority')}`}
        >
          <option value="low">Низкий</option>
          <option value="medium">Средний</option>
          <option value="high">Высокий</option>
        </select>
        
        <button onClick={deleteTask} className="delete-btn">
          🗑️
        </button>
      </div>
    </div>
  );
}

// CSS стили
const styles = `
.app {
  min-height: 100vh;
  background: var(--bg-color, #f5f5f5);
  color: var(--text-color, #333);
}

.header {
  background: white;
  padding: 1rem 2rem;
  border-bottom: 1px solid #eee;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.project-manager {
  display: flex;
  height: calc(100vh - 80px);
}

.sidebar {
  width: 300px;
  background: white;
  border-right: 1px solid #eee;
  padding: 1rem;
}

.content {
  flex: 1;
  padding: 2rem;
}

.project-item {
  padding: 1rem;
  border: 1px solid #eee;
  border-radius: 8px;
  margin-bottom: 0.5rem;
  cursor: pointer;
  transition: all 0.2s;
}

.project-item:hover {
  background: #f9f9f9;
}

.project-item.selected {
  background: #e3f2fd;
  border-color: #2196f3;
}

.task-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1rem;
  border: 1px solid #eee;
  border-radius: 8px;
  margin-bottom: 0.5rem;
}

.task-item.completed {
  opacity: 0.6;
}

.task-item.completed .task-title {
  text-decoration: line-through;
}

/* Темная тема */
.dark {
  --bg-color: #1a1a1a;
  --text-color: #ffffff;
}

.dark .header,
.dark .sidebar,
.dark .project-item,
.dark .task-item {
  background: #2d2d2d;
  border-color: #444;
  color: white;
}
`;

// Добавляем стили
const styleSheet = document.createElement('style');
styleSheet.textContent = styles;
document.head.appendChild(styleSheet);

// Рендерим приложение
const container = document.getElementById('root');
if (container) {
  const root = createRoot(container);
  root.render(<App />);
}
```

## 🎯 Заключение

React интеграция CSElement предоставляет:

- **⚛️ Нативную интеграцию** - хуки для всех основных операций
- **🔄 Автоматическую реактивность** - UI обновляется при изменении данных
- **⚡ Оптимизированную производительность** - мемоизация и виртуализация
- **🎛️ Гибкое управление состоянием** - от локального до глобального
- **🔧 Простоту использования** - минимум кода для максимума функциональности

Используйте React интеграцию для создания современных, реактивных приложений с CSElement в качестве источника данных и состояния.

---

**Следующий раздел:** [Продвинутые возможности](07-advanced-features.md) - изучите плагины, DevTools, миграции и другие продвинутые функции. 