npm package: @lasttrins/uniq-editor
Description: A modern WYSIWYG text editor library for Vue 3 with TypeScript support and file upload capabilities
Author: Василий Ткачук
Latest version: 1.2.27

# 🚀 UniqEditor

Современный WYSIWYG текстовый редактор для Vue 3 с поддержкой TypeScript, современными браузерными API и возможностями загрузки файлов.

[![NPM Version](https://img.shields.io/npm/v/@lasttrins/uniq-editor)](https://www.npmjs.com/package/@lasttrins/uniq-editor)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
[![Vue 3](https://img.shields.io/badge/Vue-3.x-green.svg)](https://vuejs.org/)

## ✨ Основные особенности

- 🎯 **Полноценный WYSIWYG редактор** с интуитивным интерфейсом
- 🔧 **Гибкая конфигурация** тулбара и функций
- 📁 **Загрузка файлов и изображений** с валидацией и прогрессом
- 🎨 **Богатое форматирование** текста с поддержкой всех стилей
- 📝 **Множество типов списков** (точки, кружки, квадраты, буквы, римские цифры)
- 📐 **Выравнивание текста** (лево, центр, право, по ширине)
- 📊 **Создание таблиц** с настраиваемыми размерами
- 🔄 **Отмена/повтор действий** с полной историей изменений
- 🌐 **Поддержка Vue 3** и vanilla JavaScript
- 📱 **Адаптивный дизайн** для мобильных устройств
- ⚡ **TypeScript** поддержка из коробки
- 🚀 **Высокая производительность** с современными браузерными API
- 🔌 **Система плагинов** для расширения функциональности
- 🛡️ **Безопасность** с санитизацией HTML и защитой от XSS

## 🆕 Последние обновления (v1.2.23+)

- ✅ **Замена устаревшего `document.execCommand`** на современные Selection/Range API
- ✅ **Исправлена типизация TypeScript** - убраны все `any` типы
- ✅ **Устранены циклические зависимости** в архитектуре
- ✅ **Добавлена санитизация HTML** для предотвращения XSS атак
- ✅ **Улучшена производительность** с дебаунсингом и MutationObserver
- ✅ **Восстановлены все функции** загрузки файлов и создания таблиц
- ✅ **Правильная синхронизация** между Model и View
- ✅ **Обработка ошибок** и валидация входных данных

## 📦 Установка

```bash
npm install @lasttrins/uniq-editor
```

```bash
yarn add @lasttrins/uniq-editor
```

```bash
pnpm add @lasttrins/uniq-editor
```

## 🚀 Быстрый старт

### Vue 3 Composition API

```vue
<template>
  <div>
    <UniqEditor
      v-model="content"
      :config="editorConfig"
      placeholder="Начните писать..."
      @ready="onEditorReady"
      @change="onContentChange"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { UniqEditor } from '@lasttrins/uniq-editor/vue'

const content = ref('<p>Добро пожаловать в UniqEditor!</p>')

const editorConfig = {
  readonly: false,
  toolbar: {
    items: [
      // Форматирование текста
      'bold', 'italic', 'underline', 'link',
      'fontSize', 'textColor', 'backgroundColor',
      
      // Заголовки
      'heading1', 'heading2', 'heading3',
      
      // Списки (все типы)
      'bulletList', 'bulletListCircle', 'bulletListSquare',
      'numberedList', 'numberedListAlpha', 'numberedListRoman',
      
      // Выравнивание
      'alignLeft', 'alignCenter', 'alignRight', 'alignJustify',
      
      // Медиа и файлы
      'insertImage', 'uploadImage', 'uploadFile', 'insertTable',
      
      // Действия
      'removeFormat', 'undo', 'redo'
    ]
  },
  upload: {
    uploadUrl: '/api/upload',
    maxFileSize: 10 * 1024 * 1024, // 10MB
    allowedTypes: ['image/*', '.pdf', '.doc', '.docx', '.txt'],
    headers: {
      'Authorization': 'Bearer your-token'
    },
    onSuccess: (response, file) => {
      console.log('Файл загружен:', response)
      return response.url || response.file_url
    },
    onError: (error, file) => {
      console.error('Ошибка загрузки:', error)
      alert('Ошибка при загрузке файла: ' + error.message)
    },
    onProgress: (progress, file) => {
      console.log(`Прогресс загрузки ${file.name}: ${progress}%`)
    }
  }
}

const onEditorReady = (editor) => {
  console.log('Редактор готов к работе:', editor)
}

const onContentChange = (newContent) => {
  console.log('Содержимое изменилось:', newContent)
}
</script>
```

### Vue 3 Options API

```vue
<template>
  <div>
    <UniqEditor
      v-model="content"
      :config="editorConfig"
      :readonly="isReadonly"
      placeholder="Введите текст..."
      @ready="handleEditorReady"
      @change="handleContentChange"
      @focus="handleFocus"
      @blur="handleBlur"
    />
  </div>
</template>

<script>
import { UniqEditor } from '@lasttrins/uniq-editor/vue'

export default {
  components: {
    UniqEditor
  },
  data() {
    return {
      content: '',
      isReadonly: false,
      editorConfig: {
        toolbar: {
          items: [
            'bold', 'italic', 'underline', 'link',
            'heading1', 'heading2', 'bulletList', 'numberedList',
            'alignLeft', 'alignCenter', 'alignRight',
            'insertImage', 'uploadImage', 'removeFormat'
          ]
        }
      }
    }
  },
  methods: {
    handleEditorReady(editor) {
      this.editor = editor
    },
    handleContentChange(content) {
      // Автоматически обновляется через v-model
      console.log('Контент изменен')
    },
    handleFocus() {
      console.log('Редактор получил фокус')
    },
    handleBlur() {
      console.log('Редактор потерял фокус')
    }
  }
}
</script>
```

### Vanilla JavaScript

```html
<!DOCTYPE html>
<html>
<head>
    <title>UniqEditor Vanilla JS</title>
</head>
<body>
    <div id="editor"></div>

    <script type="module">
        import { Editor } from '@lasttrins/uniq-editor/core'

        const editor = new Editor(document.getElementById('editor'), {
            placeholder: 'Начните писать...',
            toolbar: {
                items: [
                    'bold', 'italic', 'underline', 'link',
                    'heading1', 'heading2', 'heading3',
                    'bulletList', 'numberedList', 'insertTable',
                    'alignLeft', 'alignCenter', 'alignRight',
                    'insertImage', 'uploadImage', 'uploadFile'
                ]
            },
            upload: {
                uploadUrl: '/api/upload',
                onSuccess: (response) => response.url
            }
        })

        // Слушаем изменения
        editor.model.onChange(() => {
            console.log('Содержимое изменилось:', editor.getData())
        })

        // Программное управление
        editor.setData('<p>Начальный контент</p>')
        editor.execute('bold')
        editor.focus()
    </script>
</body>
</html>
```

## 📋 Полный список команд тулбара

### 📝 Форматирование текста
- `bold` - **Жирный текст** (Ctrl+B)
- `italic` - *Курсив* (Ctrl+I)  
- `underline` - <u>Подчеркнутый</u> (Ctrl+U)
- `link` - Создание ссылок (Ctrl+K)
- `fontSize` - Размер шрифта
- `textColor` - Цвет текста
- `backgroundColor` - Цвет фона текста
- `removeFormat` - Очистка форматирования

### 📰 Заголовки
- `heading1` - Заголовок 1 уровня
- `heading2` - Заголовок 2 уровня  
- `heading3` - Заголовок 3 уровня

### 📝 Списки
- `bulletList` - Маркированный список (точки •)
- `bulletListCircle` - Маркированный список (кружки ○)
- `bulletListSquare` - Маркированный список (квадраты ■)
- `numberedList` - Нумерованный список (1, 2, 3...)
- `numberedListAlpha` - Список с буквами (a, b, c...)
- `numberedListRoman` - Список с римскими цифрами (i, ii, iii...)

### 📐 Выравнивание
- `alignLeft` - По левому краю
- `alignCenter` - По центру
- `alignRight` - По правому краю
- `alignJustify` - По ширине

### 📁 Медиа и файлы
- `insertImage` - Вставка изображения по URL
- `uploadImage` - Загрузка изображения с компьютера
- `uploadFile` - Загрузка любого файла
- `insertTable` - Создание таблицы

### ⏮️ История действий
- `undo` - Отмена действия (Ctrl+Z)
- `redo` - Повтор действия (Ctrl+Shift+Z)

## ⚙️ Конфигурация

### Базовые настройки

```javascript
const config = {
  readonly: false,                    // Режим только для чтения
  placeholder: 'Введите текст...',    // Текст-подсказка
  language: 'ru',                     // Язык интерфейса
  toolbar: {
    items: [...],                     // Элементы тулбара
    shouldGroupWhenFull: true         // Группировка при переполнении
  }
}
```

### Настройка загрузки файлов

```javascript
const uploadConfig = {
  uploadUrl: '/api/upload',           // URL для загрузки (обязательно!)
  fieldName: 'file',                  // Имя поля в FormData
  maxFileSize: 10 * 1024 * 1024,     // Максимальный размер (10MB)
  allowedTypes: [                     // Разрешенные типы файлов
    'image/*',                        // Все изображения
    'image/jpeg',                     // Конкретные MIME типы
    '.pdf', '.doc', '.docx', '.txt'   // По расширениям
  ],
  headers: {                          // Дополнительные заголовки
    'Authorization': 'Bearer token',
    'X-Custom-Header': 'value'
  },
  
  // Callback функции
  onSuccess: (response, file) => {
    // Обработка успешной загрузки
    console.log('Загружен файл:', file.name)
    // ВАЖНО: Верните URL для вставки в редактор
    return response.url || response.file_url || response.path
  },
  
  onError: (error, file) => {
    // Обработка ошибок
    console.error('Ошибка загрузки файла:', file.name, error)
    alert('Не удалось загрузить файл: ' + error.message)
  },
  
  onProgress: (progress, file) => {
    // Отслеживание прогресса (0-100)
    console.log(`Загрузка ${file.name}: ${progress}%`)
  }
}
```

### Пример API сервера для загрузки

```javascript
// Express.js пример
const multer = require('multer')
const upload = multer({ dest: 'uploads/' })

app.post('/api/upload', upload.single('file'), (req, res) => {
  if (!req.file) {
    return res.status(400).json({ error: 'Файл не найден' })
  }
  
  const fileUrl = `/uploads/${req.file.filename}`
  
  res.json({
    success: true,
    url: fileUrl,           // Основное поле для URL
    file_url: fileUrl,      // Альтернативное поле
    filename: req.file.originalname,
    size: req.file.size
  })
})
```

## 🎨 Кастомизация стилей

### CSS переменные

```css
:root {
  --uniq-editor-border-color: #e5e7eb;
  --uniq-editor-toolbar-bg: #f9fafb;
  --uniq-editor-button-hover: #f3f4f6;
  --uniq-editor-button-active: #3b82f6;
  --uniq-editor-text-color: #374151;
  --uniq-editor-placeholder-color: #9ca3af;
}
```

### Кастомные стили

```css
/* Стилизация тулбара */
.uniq-editor-toolbar {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-radius: 8px;
  padding: 12px;
}

/* Стилизация кнопок */
.toolbar-button {
  border-radius: 6px;
  transition: all 0.2s ease;
}

.toolbar-button:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}

/* Стилизация контента */
.uniq-editor-content {
  font-family: 'Georgia', serif;
  line-height: 1.6;
  color: #2d3748;
}

/* Стилизация таблиц */
.uniq-editor-content table {
  border: 2px solid #e2e8f0;
  border-radius: 8px;
  overflow: hidden;
}

.uniq-editor-content th {
  background: linear-gradient(135deg, #4299e1 0%, #3182ce 100%);
  color: white;
  font-weight: 600;
}
```

## 🔌 API и методы

### Основные методы редактора

```javascript
// Создание редактора
const editor = new Editor(element, config)

// Управление содержимым
editor.setData('<p>HTML контент</p>')
const content = editor.getData()

// Выполнение команд
editor.execute('bold')
editor.execute('insertTable', 3, 4)  // 3 строки, 4 столбца
editor.execute('insertImage', 'https://example.com/image.jpg')

// Управление фокусом
editor.focus()
editor.blur()

// Очистка ресурсов
editor.destroy()

// Проверка возможности выполнения команды
if (editor.commands.canExecute('bold')) {
  editor.execute('bold')
}
```

### События

```javascript
// Vue компонент
<UniqEditor
  @ready="onReady"        // Редактор готов
  @change="onChange"      // Изменение контента
  @focus="onFocus"        // Получение фокуса
  @blur="onBlur"          // Потеря фокуса
/>

// Vanilla JS
editor.model.onChange(() => {
  console.log('Контент изменен')
})
```

## 🧪 Тестирование

Проект включает несколько тестовых файлов для проверки функциональности:

```bash
# Сборка проекта
npm run build

# Запуск тестов
npm test

# Проверка типов
npm run type-check
```

### Тестовые файлы

- `test/test-full-toolbar.html` - Полное тестирование всех функций
- `test/test-vue.html` - Тестирование Vue компонента
- `test/test-vanilla.html` - Тестирование vanilla JS версии
- `test/quick-test.js` - Быстрые автоматические тесты

## 🏗️ Архитектура

UniqEditor построен по модульной архитектуре:

```
src/
├── core/                    # Основная логика
│   ├── Editor.ts           # Главный класс редактора
│   ├── DocumentModel.ts    # Модель документа
│   ├── EditorView.ts       # Представление редактора
│   ├── SelectionUtils.ts   # Современные Selection API
│   ├── CommandManager.ts   # Менеджер команд
│   ├── PluginManager.ts    # Менеджер плагинов
│   └── UploadService.ts    # Сервис загрузки файлов
├── vue/                    # Vue 3 интеграция
│   ├── UniqEditor.vue     # Основной Vue компонент
│   └── index.ts           # Экспорты для Vue
├── plugins/               # Система плагинов
│   └── ToolbarPlugin.ts   # Плагин тулбара
└── types/                 # TypeScript типы
    └── index.ts           # Определения типов
```

## 🤝 Поддержка браузеров

- ✅ Chrome 88+
- ✅ Firefox 87+
- ✅ Safari 14+
- ✅ Edge 88+

Редактор использует современные браузерные API:
- Selection API
- Range API
- MutationObserver
- ES2020 модули

## 📝 Примеры использования

### Простой блог-редактор

```vue
<template>
  <div class="blog-editor">
    <h2>Написать статью</h2>
    <input v-model="title" placeholder="Заголовок статьи" class="title-input" />
    <UniqEditor
      v-model="content"
      :config="blogConfig"
      placeholder="Содержание статьи..."
    />
    <button @click="saveArticle">Опубликовать</button>
  </div>
</template>

<script setup>
const blogConfig = {
  toolbar: {
    items: [
      'heading1', 'heading2', 'heading3',
      'bold', 'italic', 'underline',
      'bulletList', 'numberedList',
      'link', 'insertImage', 'uploadImage',
      'alignLeft', 'alignCenter', 'alignRight'
    ]
  },
  upload: {
    uploadUrl: '/api/blog/upload-image',
    allowedTypes: ['image/*'],
    maxFileSize: 5 * 1024 * 1024
  }
}
</script>
```

### Редактор документов

```vue
<template>
  <UniqEditor
    v-model="document"
    :config="documentConfig"
    placeholder="Создайте документ..."
  />
</template>

<script setup>
const documentConfig = {
  toolbar: {
    items: [
      'bold', 'italic', 'underline', 'fontSize', 'textColor',
      'heading1', 'heading2', 'heading3',
      'bulletList', 'numberedList', 'bulletListCircle',
      'alignLeft', 'alignCenter', 'alignRight', 'alignJustify',
      'insertTable', 'insertImage', 'uploadFile',
      'undo', 'redo'
    ]
  },
  upload: {
    uploadUrl: '/api/documents/upload',
    allowedTypes: ['image/*', '.pdf', '.doc', '.docx'],
    maxFileSize: 50 * 1024 * 1024 // 50MB для документов
  }
}
</script>
```

## 🚀 Производительность

- **Дебаунсинг** - обновления контента с задержкой для снижения нагрузки
- **MutationObserver** - эффективное отслеживание изменений DOM
- **Lazy Loading** - плагины загружаются по требованию
- **Кэширование** - данные модели кэшируются для быстрого доступа
- **Санитизация** - безопасная очистка HTML с минимальным влиянием на производительность

## 🛡️ Безопасность

- **XSS защита** - автоматическая санитизация HTML
- **Валидация файлов** - проверка типов, размеров и имен файлов
- **Безопасные URL** - фильтрация опасных протоколов
- **CSRF защита** - поддержка токенов в заголовках загрузки

## 📄 Лицензия

MIT License. Подробности в файле [LICENSE](LICENSE).

## 🤝 Вклад в проект

Мы приветствуем ваш вклад в развитие проекта! 

1. Форкните репозиторий
2. Создайте ветку для новой функции (`git checkout -b feature/amazing-feature`)
3. Зафиксируйте изменения (`git commit -m 'Add amazing feature'`)
4. Отправьте в ветку (`git push origin feature/amazing-feature`)
5. Откройте Pull Request

## 🐛 Сообщения об ошибках

Если вы нашли ошибку, пожалуйста, создайте issue с:
- Описанием проблемы
- Шагами для воспроизведения
- Ожидаемым поведением
- Скриншотами (если применимо)
- Информацией о браузере и версии

## 📞 Поддержка

- 📧 Email: lasttrinex@gmail.com
- 🐛 Issues: [GitHub Issues](https://gitflic.ru/project/lasttrins/uniq-editor/issue)
- 📖 Документация: [GitFlic Wiki](https://gitflic.ru/project/lasttrins/uniq-editor)

## 🎉 Благодарности

Спасибо всем, кто вносит вклад в развитие проекта!

---

**UniqEditor** - делаем редактирование текста простым и мощным! 🚀