@lasttrins/uniq-editor
v1.2.27
Published
A modern WYSIWYG text editor library for Vue 3 with TypeScript support and file upload capabilities
Maintainers
Readme
🚀 UniqEditor
Современный WYSIWYG текстовый редактор для Vue 3 с поддержкой TypeScript, современными браузерными API и возможностями загрузки файлов.
✨ Основные особенности
- 🎯 Полноценный WYSIWYG редактор с интуитивным интерфейсом
- 🔧 Гибкая конфигурация тулбара и функций
- 📁 Загрузка файлов и изображений с валидацией и прогрессом
- 🎨 Богатое форматирование текста с поддержкой всех стилей
- 📝 Множество типов списков (точки, кружки, квадраты, буквы, римские цифры)
- 📐 Выравнивание текста (лево, центр, право, по ширине)
- 📊 Создание таблиц с настраиваемыми размерами
- 🔄 Отмена/повтор действий с полной историей изменений
- 🌐 Поддержка Vue 3 и vanilla JavaScript
- 📱 Адаптивный дизайн для мобильных устройств
- ⚡ TypeScript поддержка из коробки
- 🚀 Высокая производительность с современными браузерными API
- 🔌 Система плагинов для расширения функциональности
- 🛡️ Безопасность с санитизацией HTML и защитой от XSS
🆕 Последние обновления (v1.2.23+)
- ✅ Замена устаревшего
document.execCommandна современные Selection/Range API - ✅ Исправлена типизация TypeScript - убраны все
anyтипы - ✅ Устранены циклические зависимости в архитектуре
- ✅ Добавлена санитизация HTML для предотвращения XSS атак
- ✅ Улучшена производительность с дебаунсингом и MutationObserver
- ✅ Восстановлены все функции загрузки файлов и создания таблиц
- ✅ Правильная синхронизация между Model и View
- ✅ Обработка ошибок и валидация входных данных
📦 Установка
npm install @lasttrins/uniq-editoryarn add @lasttrins/uniq-editorpnpm add @lasttrins/uniq-editor🚀 Быстрый старт
Vue 3 Composition API
<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
<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
<!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- Подчеркнутый (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- Вставка изображения по URLuploadImage- Загрузка изображения с компьютераuploadFile- Загрузка любого файлаinsertTable- Создание таблицы
⏮️ История действий
undo- Отмена действия (Ctrl+Z)redo- Повтор действия (Ctrl+Shift+Z)
⚙️ Конфигурация
Базовые настройки
const config = {
readonly: false, // Режим только для чтения
placeholder: 'Введите текст...', // Текст-подсказка
language: 'ru', // Язык интерфейса
toolbar: {
items: [...], // Элементы тулбара
shouldGroupWhenFull: true // Группировка при переполнении
}
}Настройка загрузки файлов
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 сервера для загрузки
// 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 переменные
: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;
}Кастомные стили
/* Стилизация тулбара */
.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 и методы
Основные методы редактора
// Создание редактора
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')
}События
// Vue компонент
<UniqEditor
@ready="onReady" // Редактор готов
@change="onChange" // Изменение контента
@focus="onFocus" // Получение фокуса
@blur="onBlur" // Потеря фокуса
/>
// Vanilla JS
editor.model.onChange(() => {
console.log('Контент изменен')
})🧪 Тестирование
Проект включает несколько тестовых файлов для проверки функциональности:
# Сборка проекта
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 модули
📝 Примеры использования
Простой блог-редактор
<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>Редактор документов
<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.
🤝 Вклад в проект
Мы приветствуем ваш вклад в развитие проекта!
- Форкните репозиторий
- Создайте ветку для новой функции (
git checkout -b feature/amazing-feature) - Зафиксируйте изменения (
git commit -m 'Add amazing feature') - Отправьте в ветку (
git push origin feature/amazing-feature) - Откройте Pull Request
🐛 Сообщения об ошибках
Если вы нашли ошибку, пожалуйста, создайте issue с:
- Описанием проблемы
- Шагами для воспроизведения
- Ожидаемым поведением
- Скриншотами (если применимо)
- Информацией о браузере и версии
📞 Поддержка
- 📧 Email: [email protected]
- 🐛 Issues: GitHub Issues
- 📖 Документация: GitFlic Wiki
🎉 Благодарности
Спасибо всем, кто вносит вклад в развитие проекта!
UniqEditor - делаем редактирование текста простым и мощным! 🚀
