@ozdao/martyrs 0.2.541 → 0.2.542
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_virtual/index.cjs +4 -4
- package/dist/_virtual/index.js +4 -4
- package/dist/_virtual/index2.cjs +3 -6
- package/dist/_virtual/index2.cjs.map +1 -1
- package/dist/_virtual/index2.js +2 -5
- package/dist/_virtual/index2.js.map +1 -1
- package/dist/{main-BpBtIUcJ.js → main-ByKkD9qa.js} +2724 -3143
- package/dist/main-Czyu-VcC.cjs +11 -0
- package/dist/martyrs/src/components/FieldTags/BlockTags.vue.cjs +30 -21
- package/dist/martyrs/src/components/FieldTags/BlockTags.vue.cjs.map +1 -1
- package/dist/martyrs/src/components/FieldTags/BlockTags.vue.js +32 -23
- package/dist/martyrs/src/components/FieldTags/BlockTags.vue.js.map +1 -1
- package/dist/martyrs/src/components/FieldTags/FieldTags.vue2.cjs +235 -0
- package/dist/martyrs/src/components/FieldTags/FieldTags.vue2.cjs.map +1 -0
- package/dist/martyrs/src/components/FieldTags/FieldTags.vue2.js +235 -0
- package/dist/martyrs/src/components/FieldTags/FieldTags.vue2.js.map +1 -0
- package/dist/martyrs/src/components/Marquee/Marquee.vue.cjs +17 -8
- package/dist/martyrs/src/components/Marquee/Marquee.vue.cjs.map +1 -1
- package/dist/martyrs/src/components/Marquee/Marquee.vue.js +17 -8
- package/dist/martyrs/src/components/Marquee/Marquee.vue.js.map +1 -1
- package/dist/martyrs/src/components/Spoiler/{Spoiler.vue.cjs → Spoiler.vue2.cjs} +2 -2
- package/dist/martyrs/src/components/Spoiler/Spoiler.vue2.cjs.map +1 -0
- package/dist/martyrs/src/components/Spoiler/{Spoiler.vue.js → Spoiler.vue2.js} +2 -2
- package/dist/martyrs/src/components/Spoiler/{Spoiler.vue.cjs.map → Spoiler.vue2.js.map} +1 -1
- package/dist/martyrs/src/components/Tab/{Tab.vue.cjs → Tab.vue2.cjs} +2 -2
- package/dist/martyrs/src/components/Tab/Tab.vue2.cjs.map +1 -0
- package/dist/martyrs/src/components/Tab/{Tab.vue.js → Tab.vue2.js} +2 -2
- package/dist/martyrs/src/components/Tab/{Tab.vue.cjs.map → Tab.vue2.js.map} +1 -1
- package/dist/martyrs/src/components/UploadImage/UploadImage.vue.cjs +161 -42
- package/dist/martyrs/src/components/UploadImage/UploadImage.vue.cjs.map +1 -1
- package/dist/martyrs/src/components/UploadImage/UploadImage.vue.js +162 -43
- package/dist/martyrs/src/components/UploadImage/UploadImage.vue.js.map +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/EnterPassword.vue.cjs +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/EnterPassword.vue.js +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/Invite.vue.cjs +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/Invite.vue.js +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/ProfileBlogposts.vue.cjs +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/ProfileBlogposts.vue.js +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/ProfileEditProfile.vue.js +4 -4
- package/dist/martyrs/src/modules/auth/views/components/pages/ResetPassword.vue.cjs +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/ResetPassword.vue.js +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/SignIn.vue.cjs +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/SignIn.vue.js +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/SignUp.vue.cjs +1 -1
- package/dist/martyrs/src/modules/auth/views/components/pages/SignUp.vue.js +1 -1
- package/dist/martyrs/src/modules/community/components/pages/CreateBlogPost.vue.cjs +0 -3
- package/dist/martyrs/src/modules/community/components/pages/CreateBlogPost.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/community/components/pages/CreateBlogPost.vue.js +0 -3
- package/dist/martyrs/src/modules/community/components/pages/CreateBlogPost.vue.js.map +1 -1
- package/dist/martyrs/src/modules/constructor/components/elements/Card.vue.js +2 -2
- package/dist/martyrs/src/modules/events/components/pages/EditEvent.vue.js +21 -21
- package/dist/martyrs/src/modules/events/components/pages/EventsBackoffice.vue.cjs +1 -1
- package/dist/martyrs/src/modules/events/components/pages/EventsBackoffice.vue.js +1 -1
- package/dist/martyrs/src/modules/globals/views/classes/globals.i18n.cjs +1 -1
- package/dist/martyrs/src/modules/globals/views/classes/globals.i18n.js +1 -1
- package/dist/martyrs/src/modules/globals/views/components/partials/Navigation.vue.cjs +1 -1
- package/dist/martyrs/src/modules/globals/views/components/partials/Navigation.vue.js +1 -1
- package/dist/martyrs/src/modules/globals/views/components/sections/SectionPageTitle.vue.cjs +1 -1
- package/dist/martyrs/src/modules/globals/views/components/sections/SectionPageTitle.vue.js +1 -1
- package/dist/martyrs/src/modules/globals/views/components/sections/Walkthrough.vue.cjs +1 -1
- package/dist/martyrs/src/modules/globals/views/components/sections/Walkthrough.vue.js +1 -1
- package/dist/martyrs/src/modules/landing/components/sections/SectionGuide.vue.cjs +1 -1
- package/dist/martyrs/src/modules/landing/components/sections/SectionGuide.vue.js +1 -1
- package/dist/martyrs/src/modules/marketplace/views/components/layouts/Marketplace.vue.cjs +1 -1
- package/dist/martyrs/src/modules/marketplace/views/components/layouts/Marketplace.vue.js +1 -1
- package/dist/martyrs/src/modules/music/components/forms/AlbumForm.vue.js +11 -11
- package/dist/martyrs/src/modules/music/components/forms/ArtistForm.vue.js +12 -12
- package/dist/martyrs/src/modules/music/components/forms/PlaylistForm.vue.js +16 -16
- package/dist/martyrs/src/modules/music/components/forms/TrackForm.vue.js +16 -16
- package/dist/martyrs/src/modules/notifications/components/pages/Notifications.vue.cjs +1 -1
- package/dist/martyrs/src/modules/notifications/components/pages/Notifications.vue.js +1 -1
- package/dist/martyrs/src/modules/orders/components/pages/OrderCreateBackoffice.vue.cjs +1 -1
- package/dist/martyrs/src/modules/orders/components/pages/OrderCreateBackoffice.vue.js +1 -1
- package/dist/martyrs/src/modules/orders/components/pages/Orders.vue.cjs +1 -1
- package/dist/martyrs/src/modules/orders/components/pages/Orders.vue.js +1 -1
- package/dist/martyrs/src/modules/organizations/components/blocks/CardDepartment.vue.cjs +3 -4
- package/dist/martyrs/src/modules/organizations/components/blocks/CardDepartment.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/organizations/components/blocks/CardDepartment.vue.js +3 -4
- package/dist/martyrs/src/modules/organizations/components/blocks/CardDepartment.vue.js.map +1 -1
- package/dist/martyrs/src/modules/organizations/components/forms/DepartmentForm.vue.cjs +1 -1
- package/dist/martyrs/src/modules/organizations/components/forms/DepartmentForm.vue.js +20 -20
- package/dist/martyrs/src/modules/organizations/components/pages/Department.vue.cjs +3 -4
- package/dist/martyrs/src/modules/organizations/components/pages/Department.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/Department.vue.js +3 -4
- package/dist/martyrs/src/modules/organizations/components/pages/Department.vue.js.map +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/Members.vue.cjs +4 -5
- package/dist/martyrs/src/modules/organizations/components/pages/Members.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/Members.vue.js +4 -5
- package/dist/martyrs/src/modules/organizations/components/pages/Members.vue.js.map +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/Organization.vue.cjs +3 -4
- package/dist/martyrs/src/modules/organizations/components/pages/Organization.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/Organization.vue.js +3 -4
- package/dist/martyrs/src/modules/organizations/components/pages/Organization.vue.js.map +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/OrganizationEdit.vue.cjs +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/OrganizationEdit.vue.js +7 -7
- package/dist/martyrs/src/modules/organizations/components/pages/Organizations.vue.cjs +1 -1
- package/dist/martyrs/src/modules/organizations/components/pages/Organizations.vue.js +1 -1
- package/dist/martyrs/src/modules/organizations/components/sections/Organizations.vue.cjs +1 -1
- package/dist/martyrs/src/modules/organizations/components/sections/Organizations.vue.js +1 -1
- package/dist/martyrs/src/modules/products/components/pages/CategoryEdit.vue.cjs +46 -38
- package/dist/martyrs/src/modules/products/components/pages/CategoryEdit.vue.cjs.map +1 -1
- package/dist/martyrs/src/modules/products/components/pages/CategoryEdit.vue.js +52 -44
- package/dist/martyrs/src/modules/products/components/pages/CategoryEdit.vue.js.map +1 -1
- package/dist/martyrs/src/modules/products/components/pages/Product.vue.cjs +1 -1
- package/dist/martyrs/src/modules/products/components/pages/Product.vue.js +1 -1
- package/dist/martyrs/src/modules/products/components/pages/ProductEdit.vue.cjs +1 -1
- package/dist/martyrs/src/modules/products/components/pages/ProductEdit.vue.js +1 -1
- package/dist/martyrs/src/modules/products/components/pages/Products.vue.cjs +2 -2
- package/dist/martyrs/src/modules/products/components/pages/Products.vue.js +2 -2
- package/dist/martyrs/src/modules/products/components/sections/FilterProducts.vue.cjs +1 -1
- package/dist/martyrs/src/modules/products/components/sections/FilterProducts.vue.js +1 -1
- package/dist/martyrs/src/modules/products/components/sections/SectionProduct.vue.cjs +1 -1
- package/dist/martyrs/src/modules/products/components/sections/SectionProduct.vue.js +1 -1
- package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttToolbar.vue.cjs +1 -1
- package/dist/martyrs/src/modules/rents/views/components/pages/Gant/GanttToolbar.vue.js +1 -1
- package/dist/martyrs/src/modules/rents/views/components/pages/Rents.vue.cjs +1 -1
- package/dist/martyrs/src/modules/rents/views/components/pages/Rents.vue.js +1 -1
- package/dist/martyrs/src/modules/spots/components/layouts/Spots.vue.cjs +1 -1
- package/dist/martyrs/src/modules/spots/components/layouts/Spots.vue.js +1 -1
- package/dist/martyrs/src/modules/spots/components/pages/Spot.vue.cjs +1 -1
- package/dist/martyrs/src/modules/spots/components/pages/Spot.vue.js +1 -1
- package/dist/martyrs/src/modules/spots/components/pages/SpotEdit.vue.cjs +1 -1
- package/dist/martyrs/src/modules/spots/components/pages/SpotEdit.vue.js +11 -11
- package/dist/martyrs.cjs.js +1 -1
- package/dist/martyrs.css +1 -1
- package/dist/martyrs.es.js +1 -1
- package/dist/node_modules/.pnpm/lodash.merge@4.6.2/node_modules/lodash.merge/index.cjs +1 -1
- package/dist/node_modules/.pnpm/lodash.merge@4.6.2/node_modules/lodash.merge/index.js +1 -1
- package/dist/style.css +129 -129
- package/dist/{web-DsdyXC8n.js → web-BklgIiYr.js} +1 -1
- package/dist/{web-C9bVm6Nw.cjs → web-CQBm7C6L.cjs} +1 -1
- package/package.json +1 -1
- package/src/components/FieldTags/BlockTags.vue +28 -16
- package/src/components/FieldTags/FieldTags.vue +327 -508
- package/src/components/Marquee/Marquee.vue +25 -14
- package/src/components/UploadImage/UploadImage.vue +192 -18
- package/src/modules/community/components/pages/CreateBlogPost.vue +0 -1
- package/src/modules/products/components/pages/CategoryEdit.vue +53 -39
- package/dist/_virtual/index3.cjs +0 -5
- package/dist/_virtual/index3.cjs.map +0 -1
- package/dist/_virtual/index3.js +0 -5
- package/dist/_virtual/index3.js.map +0 -1
- package/dist/main-7IA3UHca.cjs +0 -11
- package/dist/martyrs/src/components/FieldTags/FieldTags.vue.cjs +0 -480
- package/dist/martyrs/src/components/FieldTags/FieldTags.vue.cjs.map +0 -1
- package/dist/martyrs/src/components/FieldTags/FieldTags.vue.js +0 -480
- package/dist/martyrs/src/components/FieldTags/FieldTags.vue.js.map +0 -1
- package/dist/martyrs/src/components/FieldTags/create-tags.cjs +0 -52
- package/dist/martyrs/src/components/FieldTags/create-tags.cjs.map +0 -1
- package/dist/martyrs/src/components/FieldTags/create-tags.js +0 -52
- package/dist/martyrs/src/components/FieldTags/create-tags.js.map +0 -1
- package/dist/martyrs/src/components/FieldTags/tag-input.vue2.cjs +0 -32
- package/dist/martyrs/src/components/FieldTags/tag-input.vue2.cjs.map +0 -1
- package/dist/martyrs/src/components/FieldTags/tag-input.vue2.js +0 -32
- package/dist/martyrs/src/components/FieldTags/tag-input.vue2.js.map +0 -1
- package/dist/martyrs/src/components/FieldTags/vue-tags-input.props.cjs +0 -329
- package/dist/martyrs/src/components/FieldTags/vue-tags-input.props.cjs.map +0 -1
- package/dist/martyrs/src/components/FieldTags/vue-tags-input.props.js +0 -329
- package/dist/martyrs/src/components/FieldTags/vue-tags-input.props.js.map +0 -1
- package/dist/martyrs/src/components/Spoiler/Spoiler.vue.js.map +0 -1
- package/dist/martyrs/src/components/Tab/Tab.vue.js.map +0 -1
- package/dist/node_modules/.pnpm/fast-deep-equal@3.1.3/node_modules/fast-deep-equal/index.cjs +0 -39
- package/dist/node_modules/.pnpm/fast-deep-equal@3.1.3/node_modules/fast-deep-equal/index.cjs.map +0 -1
- package/dist/node_modules/.pnpm/fast-deep-equal@3.1.3/node_modules/fast-deep-equal/index.js +0 -39
- package/dist/node_modules/.pnpm/fast-deep-equal@3.1.3/node_modules/fast-deep-equal/index.js.map +0 -1
- package/src/components/FieldTags/FieldTagsNew.vue +0 -366
- package/src/components/FieldTags/README.md +0 -513
- package/src/components/FieldTags/create-tags.js +0 -97
- package/src/components/FieldTags/tag-input.vue +0 -55
- package/src/components/FieldTags/vue-tags-input.props.js +0 -342
- package/src/components/FieldTags/vue-tags-input.scss +0 -149
|
@@ -1,513 +0,0 @@
|
|
|
1
|
-
# FieldTags Component
|
|
2
|
-
|
|
3
|
-
Ультра-мощный Vue 3 компонент для работы с тегами с огромным количеством возможностей для создания, редактирования, валидации и управления тегами.
|
|
4
|
-
|
|
5
|
-
## Архитектура компонента
|
|
6
|
-
|
|
7
|
-
### Основные компоненты
|
|
8
|
-
1. **FieldTags.vue** - главный компонент с полным функционалом
|
|
9
|
-
2. **BlockTags.vue** - готовая обертка с предложенными тегами
|
|
10
|
-
3. **tag-input.vue** - вспомогательный компонент для inline-редактирования
|
|
11
|
-
4. **create-tags.js** - утилиты для создания и валидации тегов
|
|
12
|
-
5. **vue-tags-input.props.js** - валидаторы и определения всех props
|
|
13
|
-
|
|
14
|
-
## Все возможности компонента
|
|
15
|
-
|
|
16
|
-
### 1. СОЗДАНИЕ ТЕГОВ (10+ способов)
|
|
17
|
-
|
|
18
|
-
#### Клавиатурный ввод
|
|
19
|
-
- **Настраиваемые триггер-клавиши** - любые комбинации клавиш для создания (Enter, Tab, запятая, точка с запятой и т.д.)
|
|
20
|
-
- **Коды клавиш и строки** - поддержка как keyCode так и строковых значений клавиш
|
|
21
|
-
- **Предотвращение дефолтного поведения** - автоматическая отмена стандартных действий браузера
|
|
22
|
-
|
|
23
|
-
#### Вставка из буфера обмена
|
|
24
|
-
- **Умная обработка вставки** - автоматическое создание множественных тегов из вставленного текста
|
|
25
|
-
- **Отложенная обработка** - обработка через setTimeout для корректной работы с буфером
|
|
26
|
-
- **Фильтрация пустых значений** - автоматическое удаление пустых тегов
|
|
27
|
-
|
|
28
|
-
#### Разделители текста
|
|
29
|
-
- **Множественные сепараторы** - любое количество разделителей (`;`, `,`, `:`, `|` и т.д.)
|
|
30
|
-
- **Regex-обработка** - автоматическое экранирование спецсимволов
|
|
31
|
-
- **Умное разбиение** - создание массива тегов из одной строки
|
|
32
|
-
|
|
33
|
-
#### Автоматическое создание
|
|
34
|
-
- **При потере фокуса** - создание тега когда пользователь кликает вне поля
|
|
35
|
-
- **При выборе из автодополнения** - мгновенное добавление при клике на предложение
|
|
36
|
-
- **При навигации клавиатурой** - добавление выбранного элемента через Enter
|
|
37
|
-
|
|
38
|
-
### 2. РЕДАКТИРОВАНИЕ ТЕГОВ (продвинутые возможности)
|
|
39
|
-
|
|
40
|
-
#### Режимы редактирования
|
|
41
|
-
- **Inline-редактирование** - прямо в теге без модальных окон
|
|
42
|
-
- **Статус каждого тега** - независимое отслеживание режима редактирования для каждого
|
|
43
|
-
- **Фокусировка на input** - автоматический фокус при входе в режим редактирования
|
|
44
|
-
|
|
45
|
-
#### Управление редактированием
|
|
46
|
-
- **Валидация при редактировании** - проверка в реальном времени
|
|
47
|
-
- **Сохранение по триггерам** - настраиваемые клавиши для сохранения
|
|
48
|
-
- **Отмена изменений** - восстановление оригинального значения
|
|
49
|
-
- **Blur-отмена** - автоматическая отмена при потере фокуса
|
|
50
|
-
|
|
51
|
-
### 3. АВТОДОПОЛНЕНИЕ (умная система)
|
|
52
|
-
|
|
53
|
-
#### Фильтрация и поиск
|
|
54
|
-
- **Case-insensitive поиск** - поиск без учета регистра
|
|
55
|
-
- **Минимальная длина запроса** - настраиваемый порог для показа
|
|
56
|
-
- **Фильтр дубликатов** - автоматическое скрытие уже добавленных
|
|
57
|
-
- **Кастомная фильтрация** - возможность передать свою функцию фильтрации
|
|
58
|
-
|
|
59
|
-
#### Навигация и выбор
|
|
60
|
-
- **Клавиатурная навигация** - стрелки вверх/вниз с циклическим переходом
|
|
61
|
-
- **Выбор мышью** - hover для предварительного выбора
|
|
62
|
-
- **Автовыбор первого** - опциональный выбор первого элемента
|
|
63
|
-
- **Запоминание позиции** - сохранение выбранного элемента
|
|
64
|
-
|
|
65
|
-
#### Режимы работы
|
|
66
|
-
- **Всегда открыт** - постоянное отображение списка
|
|
67
|
-
- **Только из списка** - запрет на создание произвольных тегов
|
|
68
|
-
- **Динамическое обновление** - реактивное обновление при изменении items
|
|
69
|
-
|
|
70
|
-
### 4. ВАЛИДАЦИЯ (мощная система правил)
|
|
71
|
-
|
|
72
|
-
#### Типы правил
|
|
73
|
-
- **RegExp правила** - проверка через регулярные выражения
|
|
74
|
-
- **Функциональные правила** - произвольная логика валидации
|
|
75
|
-
- **Строковые правила** - автоматическое преобразование в RegExp
|
|
76
|
-
- **Множественные правила** - применение нескольких правил одновременно
|
|
77
|
-
|
|
78
|
-
#### CSS-классы и состояния
|
|
79
|
-
- **Динамические классы** - автоматическое добавление классов по результатам валидации
|
|
80
|
-
- **ti-valid/ti-invalid** - базовые классы состояния
|
|
81
|
-
- **ti-duplicate** - специальный класс для дубликатов
|
|
82
|
-
- **Кастомные классы** - любые пользовательские классы через правила
|
|
83
|
-
|
|
84
|
-
#### Блокировка действий
|
|
85
|
-
- **disableAdd флаг** - запрет добавления невалидных тегов
|
|
86
|
-
- **Проверка при сохранении** - валидация при редактировании
|
|
87
|
-
- **Предварительная валидация** - проверка до добавления в массив
|
|
88
|
-
|
|
89
|
-
### 5. УДАЛЕНИЕ ТЕГОВ (множественные способы)
|
|
90
|
-
|
|
91
|
-
#### Визуальное удаление
|
|
92
|
-
- **Иконка закрытия** - кликабельная кнопка на каждом теге
|
|
93
|
-
- **Кастомные действия** - слот для собственных кнопок удаления
|
|
94
|
-
|
|
95
|
-
#### Backspace удаление
|
|
96
|
-
- **Двухэтапное удаление** - сначала пометка, потом удаление
|
|
97
|
-
- **Визуальная индикация** - класс deletion-mark для помеченного тега
|
|
98
|
-
- **Таймер сброса** - автоматический сброс пометки через 1 секунду
|
|
99
|
-
- **Очистка таймеров** - корректная очистка при unmount
|
|
100
|
-
|
|
101
|
-
### 6. ХУКИ И СОБЫТИЯ (полный контроль)
|
|
102
|
-
|
|
103
|
-
#### Before-хуки (можно отменить действие)
|
|
104
|
-
- **onBeforeAddingTag** - перед добавлением с возможностью отмены
|
|
105
|
-
- **onBeforeDeletingTag** - перед удалением с подтверждением
|
|
106
|
-
- **onBeforeEditingTag** - перед входом в режим редактирования
|
|
107
|
-
- **onBeforeSavingTag** - перед сохранением изменений
|
|
108
|
-
|
|
109
|
-
#### События изменений
|
|
110
|
-
- **tags-changed** - при любом изменении массива тегов
|
|
111
|
-
- **update:tags** - v-model синхронизация для тегов
|
|
112
|
-
- **update:modelValue** - v-model для поля ввода
|
|
113
|
-
- **tag-clicked** - клик по тегу с полной информацией
|
|
114
|
-
|
|
115
|
-
#### События состояний
|
|
116
|
-
- **adding-duplicate** - попытка добавить дубликат
|
|
117
|
-
- **saving-duplicate** - попытка сохранить дубликат при редактировании
|
|
118
|
-
- **max-tags-reached** - достижение лимита тегов
|
|
119
|
-
|
|
120
|
-
### 7. УПРАВЛЕНИЕ ФОКУСОМ И ВЗАИМОДЕЙСТВИЕМ
|
|
121
|
-
|
|
122
|
-
#### Фокус и Blur
|
|
123
|
-
- **Отслеживание фокуса** - реактивная переменная focused
|
|
124
|
-
- **addOnBlur** - создание тега при потере фокуса
|
|
125
|
-
- **Глобальный click listener** - обработка кликов вне компонента
|
|
126
|
-
- **Предотвращение blur** - при клике внутри компонента
|
|
127
|
-
|
|
128
|
-
#### Размеры и ограничения
|
|
129
|
-
- **maxlength** - максимальная длина текста тега
|
|
130
|
-
- **maxTags** - максимальное количество тегов
|
|
131
|
-
- **Автоматический размер input** - size="1" с flex-grow
|
|
132
|
-
|
|
133
|
-
### 8. ВНУТРЕННЯЯ АРХИТЕКТУРА
|
|
134
|
-
|
|
135
|
-
#### Управление состоянием
|
|
136
|
-
- **tagsCopy** - внутреннее представление с tiClasses
|
|
137
|
-
- **originalTags** - хранение оригинальных данных
|
|
138
|
-
- **tagsEditStatus** - массив статусов редактирования
|
|
139
|
-
- **shallowRef оптимизация** - для больших массивов тегов
|
|
140
|
-
|
|
141
|
-
#### Refs и DOM
|
|
142
|
-
- **tagCenterRefs Map** - хранение ссылок на DOM элементы
|
|
143
|
-
- **newTagInputRef** - прямая ссылка на поле ввода
|
|
144
|
-
- **nextTick синхронизация** - корректная работа с DOM
|
|
145
|
-
|
|
146
|
-
#### Оптимизации
|
|
147
|
-
- **fast-deep-equal** - быстрое сравнение объектов
|
|
148
|
-
- **Клонирование без мутаций** - иммутабельные операции
|
|
149
|
-
- **Batch обновления** - группировка изменений
|
|
150
|
-
|
|
151
|
-
### 9. СЛОТЫ (полная кастомизация)
|
|
152
|
-
|
|
153
|
-
#### Слоты тегов
|
|
154
|
-
- **tag-left** - левая часть тега (иконки, чекбоксы)
|
|
155
|
-
- **tag-center** - центральная часть (текст, редактирование)
|
|
156
|
-
- **tag-right** - правая часть (дополнительная информация)
|
|
157
|
-
- **tag-actions** - кнопки действий (удаление, редактирование)
|
|
158
|
-
|
|
159
|
-
#### Слоты автодополнения
|
|
160
|
-
- **autocomplete-item** - кастомный рендеринг элементов
|
|
161
|
-
- **autocomplete-header** - шапка списка автодополнения
|
|
162
|
-
- **autocomplete-footer** - подвал списка
|
|
163
|
-
- **between-elements** - контент между input и автодополнением
|
|
164
|
-
|
|
165
|
-
#### Передаваемые в слоты данные
|
|
166
|
-
- Полная информация о теге и индексе
|
|
167
|
-
- Состояние редактирования
|
|
168
|
-
- Функции для выполнения действий
|
|
169
|
-
- Состояние deletion-mark
|
|
170
|
-
|
|
171
|
-
### 10. СТИЛИЗАЦИЯ И ВИЗУАЛИЗАЦИЯ
|
|
172
|
-
|
|
173
|
-
#### CSS классы состояний
|
|
174
|
-
- `.ti-disabled` - отключенный компонент
|
|
175
|
-
- `.ti-focus` - компонент в фокусе
|
|
176
|
-
- `.ti-editing` - тег в режиме редактирования
|
|
177
|
-
- `.ti-deletion-mark` - тег помечен для удаления
|
|
178
|
-
- `.ti-selected-item` - выбранный элемент автодополнения
|
|
179
|
-
|
|
180
|
-
#### Классы валидации
|
|
181
|
-
- `.ti-valid` - валидный тег
|
|
182
|
-
- `.ti-invalid` - невалидный тег
|
|
183
|
-
- `.ti-duplicate` - дубликат
|
|
184
|
-
- Кастомные классы через validation rules
|
|
185
|
-
|
|
186
|
-
#### Визуальные эффекты
|
|
187
|
-
- Плавная анимация при добавлении/удалении
|
|
188
|
-
- Hover эффекты на элементах
|
|
189
|
-
- Визуальная индикация состояний
|
|
190
|
-
- Адаптивная верстка с flex-wrap
|
|
191
|
-
|
|
192
|
-
### 11. ДОПОЛНИТЕЛЬНЫЕ ВОЗМОЖНОСТИ
|
|
193
|
-
|
|
194
|
-
#### Кастомная проверка дубликатов
|
|
195
|
-
- **isDuplicate функция** - полностью кастомная логика
|
|
196
|
-
- **Сравнение по любым полям** - не только по тексту
|
|
197
|
-
- **Контекстная проверка** - с учетом других тегов
|
|
198
|
-
|
|
199
|
-
#### Inline стили и классы
|
|
200
|
-
- **style property** - inline стили для каждого тега
|
|
201
|
-
- **classes property** - дополнительные CSS классы
|
|
202
|
-
- **tiClasses** - внутренние классы компонента
|
|
203
|
-
|
|
204
|
-
#### Дополнительные данные в тегах
|
|
205
|
-
- **Любые кастомные поля** - сохранение дополнительной информации
|
|
206
|
-
- **Неизменность кастомных данных** - компонент не модифицирует их
|
|
207
|
-
- **Передача через события** - полная информация в event handlers
|
|
208
|
-
|
|
209
|
-
## Использование
|
|
210
|
-
|
|
211
|
-
### Базовый пример
|
|
212
|
-
```vue
|
|
213
|
-
<FieldTags
|
|
214
|
-
v-model="inputValue"
|
|
215
|
-
:tags="tags"
|
|
216
|
-
@tags-changed="handleTagsChanged"
|
|
217
|
-
/>
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
### Продвинутый пример со всеми возможностями
|
|
221
|
-
```vue
|
|
222
|
-
<FieldTags
|
|
223
|
-
v-model="inputValue"
|
|
224
|
-
:tags="tags"
|
|
225
|
-
:autocomplete-items="suggestions"
|
|
226
|
-
:autocomplete-min-length="0"
|
|
227
|
-
:autocomplete-filter-duplicates="true"
|
|
228
|
-
:add-on-key="[13, ':', ';', ',']"
|
|
229
|
-
:save-on-key="[13, 'Tab']"
|
|
230
|
-
:separators="[';', ',', '|']"
|
|
231
|
-
:max-tags="20"
|
|
232
|
-
:maxlength="50"
|
|
233
|
-
:allow-edit-tags="true"
|
|
234
|
-
:add-from-paste="true"
|
|
235
|
-
:add-on-blur="true"
|
|
236
|
-
:delete-on-backspace="true"
|
|
237
|
-
:avoid-adding-duplicates="true"
|
|
238
|
-
:validation="validationRules"
|
|
239
|
-
:is-duplicate="customDuplicateCheck"
|
|
240
|
-
:placeholder="'Добавьте теги'"
|
|
241
|
-
@tags-changed="handleTagsChanged"
|
|
242
|
-
@before-adding-tag="validateBeforeAdd"
|
|
243
|
-
@before-deleting-tag="confirmDelete"
|
|
244
|
-
@before-editing-tag="checkEditPermission"
|
|
245
|
-
@before-saving-tag="validateBeforeSave"
|
|
246
|
-
@adding-duplicate="handleDuplicate"
|
|
247
|
-
@max-tags-reached="showMaxTagsWarning"
|
|
248
|
-
@tag-clicked="handleTagClick"
|
|
249
|
-
>
|
|
250
|
-
<!-- Кастомный рендеринг тега -->
|
|
251
|
-
<template #tag-left="{ tag, index }">
|
|
252
|
-
<Icon :name="tag.icon" />
|
|
253
|
-
</template>
|
|
254
|
-
|
|
255
|
-
<template #tag-actions="{ performDelete, performOpenEdit, index }">
|
|
256
|
-
<button @click="performOpenEdit(index)">✏️</button>
|
|
257
|
-
<button @click="performDelete(index)">❌</button>
|
|
258
|
-
</template>
|
|
259
|
-
|
|
260
|
-
<!-- Кастомный элемент автодополнения -->
|
|
261
|
-
<template #autocomplete-item="{ item, performAdd }">
|
|
262
|
-
<div @click="performAdd(item)" class="custom-item">
|
|
263
|
-
<span>{{ item.text }}</span>
|
|
264
|
-
<span class="item-count">({{ item.count }})</span>
|
|
265
|
-
</div>
|
|
266
|
-
</template>
|
|
267
|
-
</FieldTags>
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
### Пример с before-хуками
|
|
271
|
-
```vue
|
|
272
|
-
<script setup>
|
|
273
|
-
function validateBeforeAdd({ tag, addTag }) {
|
|
274
|
-
// Асинхронная проверка на сервере
|
|
275
|
-
checkTagOnServer(tag).then(isValid => {
|
|
276
|
-
if (isValid) {
|
|
277
|
-
addTag(); // Вызываем только если валидно
|
|
278
|
-
} else {
|
|
279
|
-
showError('Тег не прошел проверку');
|
|
280
|
-
}
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
function confirmDelete({ tag, deleteTag, index }) {
|
|
285
|
-
if (confirm(`Удалить тег "${tag.text}"?`)) {
|
|
286
|
-
deleteTag(); // Подтверждаем удаление
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
</script>
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
### Компонент-обертка BlockTags
|
|
293
|
-
Готовый компонент с предложениями тегов и умной фильтрацией:
|
|
294
|
-
|
|
295
|
-
```vue
|
|
296
|
-
<BlockTags
|
|
297
|
-
:tags="existingTags"
|
|
298
|
-
:tags-suggested="[
|
|
299
|
-
{ text: 'story' },
|
|
300
|
-
{ text: 'news' },
|
|
301
|
-
{ text: 'guide' },
|
|
302
|
-
{ text: 'discussion' },
|
|
303
|
-
{ text: 'photos' }
|
|
304
|
-
]"
|
|
305
|
-
@tags-changed="updateTags"
|
|
306
|
-
/>
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
Компонент BlockTags автоматически:
|
|
310
|
-
- Фильтрует предложения при вводе
|
|
311
|
-
- Показывает оставшиеся неиспользованные теги
|
|
312
|
-
- Позволяет добавлять теги кликом на предложения
|
|
313
|
-
- Синхронизирует состояние с родителем
|
|
314
|
-
|
|
315
|
-
## Props
|
|
316
|
-
|
|
317
|
-
| Prop | Тип | По умолчанию | Описание |
|
|
318
|
-
|------|-----|--------------|----------|
|
|
319
|
-
| `modelValue` | String | `''` | Связанное значение поля ввода |
|
|
320
|
-
| `tags` | Array | `[]` | Массив существующих тегов |
|
|
321
|
-
| `autocomplete-items` | Array | `[]` | Элементы для автодополнения |
|
|
322
|
-
| `allow-edit-tags` | Boolean | `false` | Разрешить редактирование тегов |
|
|
323
|
-
| `add-on-key` | Array | `[13]` | Клавиши для создания тега |
|
|
324
|
-
| `save-on-key` | Array | `[13]` | Клавиши для сохранения при редактировании |
|
|
325
|
-
| `separators` | Array | `[';']` | Символы-разделители |
|
|
326
|
-
| `max-tags` | Number | - | Максимальное количество тегов |
|
|
327
|
-
| `maxlength` | Number | - | Максимальная длина тега |
|
|
328
|
-
| `placeholder` | String | `'Add Tag'` | Текст-заполнитель |
|
|
329
|
-
| `validation` | Array | `[]` | Правила валидации |
|
|
330
|
-
| `avoid-adding-duplicates` | Boolean | `true` | Запретить дубликаты |
|
|
331
|
-
| `add-on-blur` | Boolean | `true` | Создать тег при потере фокуса |
|
|
332
|
-
| `add-from-paste` | Boolean | `true` | Создавать теги из вставленного текста |
|
|
333
|
-
| `delete-on-backspace` | Boolean | `true` | Удаление через Backspace |
|
|
334
|
-
| `disabled` | Boolean | `false` | Отключить компонент |
|
|
335
|
-
| `autocomplete-min-length` | Number | `1` | Минимум символов для автодополнения |
|
|
336
|
-
| `autocomplete-filter-duplicates` | Boolean | `true` | Фильтровать дубликаты в автодополнении |
|
|
337
|
-
| `add-only-from-autocomplete` | Boolean | `false` | Добавлять только из списка автодополнения |
|
|
338
|
-
| `is-duplicate` | Function | - | Кастомная функция проверки дубликатов |
|
|
339
|
-
|
|
340
|
-
## События
|
|
341
|
-
|
|
342
|
-
| Событие | Payload | Описание |
|
|
343
|
-
|---------|---------|----------|
|
|
344
|
-
| `tags-changed` | Array | Изменение массива тегов |
|
|
345
|
-
| `before-adding-tag` | {tag, addTag} | Перед добавлением тега |
|
|
346
|
-
| `before-deleting-tag` | {index, tag, deleteTag} | Перед удалением тега |
|
|
347
|
-
| `before-editing-tag` | {index, tag, editTag} | Перед редактированием |
|
|
348
|
-
| `before-saving-tag` | {index, tag, saveTag} | Перед сохранением изменений |
|
|
349
|
-
| `adding-duplicate` | tag | Попытка добавить дубликат |
|
|
350
|
-
| `saving-duplicate` | tag | Попытка сохранить дубликат |
|
|
351
|
-
| `max-tags-reached` | tag | Достигнут лимит тегов |
|
|
352
|
-
| `tag-clicked` | {tag, index} | Клик по тегу |
|
|
353
|
-
|
|
354
|
-
## Формат тега
|
|
355
|
-
|
|
356
|
-
```javascript
|
|
357
|
-
{
|
|
358
|
-
text: 'Tag text', // Текст тега (обязательно)
|
|
359
|
-
classes: 'custom-class', // CSS классы (опционально)
|
|
360
|
-
style: 'color: red', // Inline стили (опционально)
|
|
361
|
-
// Любые дополнительные свойства сохраняются
|
|
362
|
-
}
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
## Правила валидации (продвинутые примеры)
|
|
366
|
-
|
|
367
|
-
```javascript
|
|
368
|
-
const validationRules = [
|
|
369
|
-
// RegExp валидация
|
|
370
|
-
{
|
|
371
|
-
classes: 'no-numbers',
|
|
372
|
-
rule: /^[^0-9]*$/,
|
|
373
|
-
},
|
|
374
|
-
|
|
375
|
-
// Функциональная валидация
|
|
376
|
-
{
|
|
377
|
-
classes: 'min-length',
|
|
378
|
-
rule: (tag) => tag.text.length >= 3,
|
|
379
|
-
disableAdd: true // Блокирует добавление
|
|
380
|
-
},
|
|
381
|
-
|
|
382
|
-
// Строковая валидация (преобразуется в RegExp)
|
|
383
|
-
{
|
|
384
|
-
classes: 'email-format',
|
|
385
|
-
rule: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
|
|
386
|
-
},
|
|
387
|
-
|
|
388
|
-
// Сложная валидация с контекстом
|
|
389
|
-
{
|
|
390
|
-
classes: 'no-special-chars',
|
|
391
|
-
rule: (tag) => {
|
|
392
|
-
const forbidden = ['@', '#', '$', '%'];
|
|
393
|
-
return !forbidden.some(char => tag.text.includes(char));
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
];
|
|
397
|
-
|
|
398
|
-
// Кастомная функция проверки дубликатов
|
|
399
|
-
function customDuplicateCheck(tags, tag) {
|
|
400
|
-
// Проверка не только по тексту, но и по другим полям
|
|
401
|
-
return tags.some(t =>
|
|
402
|
-
t.text.toLowerCase() === tag.text.toLowerCase() ||
|
|
403
|
-
t.id === tag.id
|
|
404
|
-
);
|
|
405
|
-
}
|
|
406
|
-
```
|
|
407
|
-
|
|
408
|
-
## Стилизация и CSS
|
|
409
|
-
|
|
410
|
-
### Основные классы компонента
|
|
411
|
-
```scss
|
|
412
|
-
// Контейнер
|
|
413
|
-
.vue-tags-input // Основной контейнер
|
|
414
|
-
.ti-disabled // Отключенное состояние
|
|
415
|
-
.ti-focus // Фокус на компоненте
|
|
416
|
-
|
|
417
|
-
// Теги
|
|
418
|
-
.ti-tag // Основной класс тега
|
|
419
|
-
.ti-valid // Валидный тег
|
|
420
|
-
.ti-invalid // Невалидный тег
|
|
421
|
-
.ti-duplicate // Тег-дубликат
|
|
422
|
-
.ti-editing // Тег в режиме редактирования
|
|
423
|
-
.ti-deletion-mark // Тег помечен для удаления
|
|
424
|
-
|
|
425
|
-
// Структура тега
|
|
426
|
-
.ti-content // Контейнер содержимого тега
|
|
427
|
-
.ti-tag-center // Центральная часть тега
|
|
428
|
-
.ti-tag-left // Левая часть (слот)
|
|
429
|
-
.ti-tag-right // Правая часть (слот)
|
|
430
|
-
.ti-actions // Контейнер кнопок действий
|
|
431
|
-
|
|
432
|
-
// Input
|
|
433
|
-
.ti-input // Контейнер input
|
|
434
|
-
.ti-tags // Список тегов
|
|
435
|
-
.ti-new-tag-input // Поле ввода нового тега
|
|
436
|
-
.ti-new-tag-input-wrapper // Обертка поля ввода
|
|
437
|
-
.ti-tag-input // Input для редактирования тега
|
|
438
|
-
|
|
439
|
-
// Автодополнение
|
|
440
|
-
.ti-autocomplete // Контейнер автодополнения
|
|
441
|
-
.ti-item // Элемент списка автодополнения
|
|
442
|
-
.ti-selected-item // Выбранный элемент
|
|
443
|
-
|
|
444
|
-
// Иконки
|
|
445
|
-
.ti-icon-close // Иконка закрытия (✕)
|
|
446
|
-
.ti-icon-undo // Иконка отмены (↺)
|
|
447
|
-
.ti-icon-check // Иконка подтверждения
|
|
448
|
-
|
|
449
|
-
// Вспомогательные
|
|
450
|
-
.ti-hidden // Скрытый элемент для расчета размеров
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
### Переменные SCSS
|
|
454
|
-
```scss
|
|
455
|
-
$error: #e54d42; // Цвет ошибки
|
|
456
|
-
$success: #68cd86; // Цвет успеха
|
|
457
|
-
$warn: #ffb648; // Цвет предупреждения
|
|
458
|
-
```
|
|
459
|
-
|
|
460
|
-
### Кастомизация через CSS переменные
|
|
461
|
-
```css
|
|
462
|
-
.vue-tags-input {
|
|
463
|
-
--main: 255, 215, 0; /* Основной цвет */
|
|
464
|
-
--black: 0, 0, 0; /* Цвет текста */
|
|
465
|
-
}
|
|
466
|
-
```
|
|
467
|
-
|
|
468
|
-
### Пример полной кастомизации
|
|
469
|
-
```scss
|
|
470
|
-
.my-custom-tags {
|
|
471
|
-
.ti-tag {
|
|
472
|
-
background: linear-gradient(45deg, #667eea, #764ba2);
|
|
473
|
-
color: white;
|
|
474
|
-
border-radius: 20px;
|
|
475
|
-
padding: 5px 12px;
|
|
476
|
-
font-weight: 600;
|
|
477
|
-
transition: all 0.3s ease;
|
|
478
|
-
|
|
479
|
-
&:hover {
|
|
480
|
-
transform: scale(1.05);
|
|
481
|
-
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
&.ti-editing {
|
|
485
|
-
background: #f0f0f0;
|
|
486
|
-
color: #333;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
&.ti-deletion-mark {
|
|
490
|
-
animation: shake 0.5s;
|
|
491
|
-
background: #ff4757;
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
.ti-autocomplete {
|
|
496
|
-
background: white;
|
|
497
|
-
border: 1px solid #e0e0e0;
|
|
498
|
-
border-radius: 8px;
|
|
499
|
-
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
|
500
|
-
|
|
501
|
-
.ti-selected-item {
|
|
502
|
-
background: #667eea;
|
|
503
|
-
color: white;
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
@keyframes shake {
|
|
509
|
-
0%, 100% { transform: translateX(0); }
|
|
510
|
-
25% { transform: translateX(-5px); }
|
|
511
|
-
75% { transform: translateX(5px); }
|
|
512
|
-
}
|
|
513
|
-
```
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
// helper functions
|
|
2
|
-
|
|
3
|
-
const validateUserRules = (tag, validation) => {
|
|
4
|
-
return validation
|
|
5
|
-
.filter(val => {
|
|
6
|
-
const { text } = tag;
|
|
7
|
-
// if the rule is a string, we convert it to RegExp
|
|
8
|
-
if (typeof val.rule === 'string') return !new RegExp(val.rule).test(text);
|
|
9
|
-
|
|
10
|
-
if (val.rule instanceof RegExp) return !val.rule.test(text);
|
|
11
|
-
|
|
12
|
-
// if we deal with a function, invoke it
|
|
13
|
-
const isFunction = {}.toString.call(val.rule) === '[object Function]';
|
|
14
|
-
if (isFunction) return val.rule(tag);
|
|
15
|
-
})
|
|
16
|
-
.map(val => val.classes);
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const clone = node => JSON.parse(JSON.stringify(node));
|
|
20
|
-
|
|
21
|
-
const findIndex = (arr, callback) => {
|
|
22
|
-
let index = 0;
|
|
23
|
-
while (index < arr.length) {
|
|
24
|
-
if (callback(arr[index], index, arr)) return index;
|
|
25
|
-
index++;
|
|
26
|
-
}
|
|
27
|
-
return -1;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const createClasses = (tag, tags, validation = [], customDuplicateFn) => {
|
|
31
|
-
if (tag.text === undefined) tag = { text: tag };
|
|
32
|
-
|
|
33
|
-
// Ensure tags is an array
|
|
34
|
-
if (!Array.isArray(tags)) tags = [];
|
|
35
|
-
|
|
36
|
-
// create css classes from the user validation array
|
|
37
|
-
const classes = validateUserRules(tag, validation);
|
|
38
|
-
|
|
39
|
-
// if we find the tag, it's an exsting one which is edited.
|
|
40
|
-
// in this case we must splice it out
|
|
41
|
-
const index = findIndex(tags, t => t === tag);
|
|
42
|
-
const tagsDiff = clone(tags);
|
|
43
|
-
const inputTag = index !== -1 ? tagsDiff.splice(index, 1)[0] : clone(tag);
|
|
44
|
-
|
|
45
|
-
// check whether the tag is a duplicate or not
|
|
46
|
-
const duplicate = customDuplicateFn ? customDuplicateFn(tagsDiff, inputTag) : (Array.isArray(tagsDiff) ? tagsDiff.map(t => t.text).indexOf(inputTag.text) !== -1 : false);
|
|
47
|
-
|
|
48
|
-
// if it's a duplicate, push the class duplicate to the array
|
|
49
|
-
if (duplicate) classes.push('ti-duplicate');
|
|
50
|
-
|
|
51
|
-
// if we find no classes, the tag is valid → push the class valid
|
|
52
|
-
classes.length === 0 ? classes.push('ti-valid') : classes.push('ti-invalid');
|
|
53
|
-
return classes;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* @description Create one tag out of a String or validate an existing one
|
|
58
|
-
* @property {helpers}
|
|
59
|
-
* @param {Object|String} tag A tag which should be validated | A String to create a tag
|
|
60
|
-
* @param {Array} tagsarray The tags array
|
|
61
|
-
* @param {Array} [validation=[]] The validation Array is optional (pass it if you use one)
|
|
62
|
-
* @returns {Object} The created (validated) tag
|
|
63
|
-
*/
|
|
64
|
-
const createTag = (tag, ...rest) => {
|
|
65
|
-
// if text is undefined, a string is passed. let's make a tag out of it
|
|
66
|
-
if (tag.text === undefined) tag = { text: tag };
|
|
67
|
-
|
|
68
|
-
// we better make a clone to not getting reference trouble
|
|
69
|
-
const t = clone(tag);
|
|
70
|
-
|
|
71
|
-
// Ensure the first argument in rest (tags array) is actually an array
|
|
72
|
-
if (rest[0] && !Array.isArray(rest[0])) {
|
|
73
|
-
rest[0] = [];
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// create the validation classes
|
|
77
|
-
t.tiClasses = createClasses(tag, ...rest);
|
|
78
|
-
return t;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* @description Create multiple tags out of Strings or validate existing tags
|
|
83
|
-
* @property {helpers}
|
|
84
|
-
* @param {Array} tagsarray An Array containing tags or strings. See example below.
|
|
85
|
-
* @param {Array} [validation=[]] The validation Array is optional (pass it if you use one)
|
|
86
|
-
* @returns {Array} An array containing (validated) tags
|
|
87
|
-
* @example /* Example to call the function */
|
|
88
|
-
const validatedTags = createTags(['tag1Text', 'tag2Text'], [{ type: 'length', rule: /[0-9]/ }])
|
|
89
|
-
*/
|
|
90
|
-
const createTags = (tags, ...rest) => {
|
|
91
|
-
if (!Array.isArray(tags)) {
|
|
92
|
-
return [];
|
|
93
|
-
}
|
|
94
|
-
return tags.map(t => createTag(t, tags, ...rest));
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
export { clone, createClasses, createTag, createTags };
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
<!-- A helper component used by VueTagsInput and can be put into the tag-center slot -->
|
|
2
|
-
|
|
3
|
-
<template>
|
|
4
|
-
<input
|
|
5
|
-
v-if="scope.edit"
|
|
6
|
-
v-model="scope.tag.text"
|
|
7
|
-
:maxlength="scope.maxlength"
|
|
8
|
-
type="text"
|
|
9
|
-
class="ti-tag-input"
|
|
10
|
-
size="1"
|
|
11
|
-
@input="scope.validateTag(scope.index, $event)"
|
|
12
|
-
@blur="scope.performCancelEdit(scope.index)"
|
|
13
|
-
@keydown="scope.performSaveEdit(scope.index, $event)"
|
|
14
|
-
>
|
|
15
|
-
</template>
|
|
16
|
-
|
|
17
|
-
<script>
|
|
18
|
-
|
|
19
|
-
export default {
|
|
20
|
-
name: 'TagInput',
|
|
21
|
-
props: {
|
|
22
|
-
scope: {
|
|
23
|
-
type: Object,
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
</script>
|
|
29
|
-
|
|
30
|
-
<style lang="css" >
|
|
31
|
-
.ti-tag-input {
|
|
32
|
-
background-color: transparent;
|
|
33
|
-
color: inherit;
|
|
34
|
-
border: none;
|
|
35
|
-
padding: 0px;
|
|
36
|
-
margin: 0px;
|
|
37
|
-
display: flex;
|
|
38
|
-
top: 0px;
|
|
39
|
-
position: absolute;
|
|
40
|
-
width: 100%;
|
|
41
|
-
line-height: inherit;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.ti-tag-input::-ms-clear {
|
|
45
|
-
display: none;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
input:focus {
|
|
49
|
-
outline: none;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
input[disabled] {
|
|
53
|
-
background-color: transparent;
|
|
54
|
-
}
|
|
55
|
-
</style>
|