vgapp 1.1.5 → 1.1.7
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/CHANGELOG.md +37 -21
- package/agents.md +7 -0
- package/app/modules/vgfilepreview/js/vgfilepreview.js +8 -3
- package/app/modules/vgfiles/js/base.js +26 -8
- package/app/modules/vgformsender/js/vgformsender.js +14 -14
- package/app/modules/vgtoast/js/vgtoast.drag.js +335 -0
- package/app/modules/vgtoast/js/vgtoast.js +198 -76
- package/app/modules/vgtoast/js/vgtoast.resize.js +437 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,18 +1,39 @@
|
|
|
1
|
-
# VEGAS-APP 1.1.
|
|
2
|
-
## Новые фичи
|
|
3
|
-
* В
|
|
4
|
-
|
|
5
|
-
## Изменения
|
|
6
|
-
*
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
*
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
* В
|
|
1
|
+
# VEGAS-APP 1.1.7 (Апрель, 13, 2026)
|
|
2
|
+
## Новые фичи
|
|
3
|
+
* В VGFilePreview добавлена поддержка атрибута `data-name` для явной установки отображаемого имени файла (в приоритете над `data-vg-filepreview-display-name`).
|
|
4
|
+
|
|
5
|
+
## Изменения
|
|
6
|
+
* Обновлена версия пакета в `package.json`: `1.1.6` -> `1.1.7`.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# VEGAS-APP 1.1.6 (Апрель, 10, 2026)
|
|
11
|
+
## Новые фичи
|
|
12
|
+
* **VGToast**: добавлены встроенные модули VGToastDrag и VGToastResize с поддержкой параметров drag/resize (enable, threshold, edgeSize, minWidth/minHeight, debug).
|
|
13
|
+
|
|
14
|
+
## Изменения
|
|
15
|
+
* В VGToast добавлено автоматическое управление drag/resize при show/hide/dispose и синхронизация позиции/размера внутри viewport.
|
|
16
|
+
* Исправлен скачок тоста при начале drag/resize: при подготовке позиции сбрасывается translate.
|
|
17
|
+
* Исправлено поведение автоскрытия при интеракции: таймер останавливается на pointerdown и корректно возобновляется на pointerup/pointercancel.
|
|
18
|
+
* В VGToast.build() при static: true и отсутствии явного autohide автоскрытие по умолчанию отключается.
|
|
19
|
+
* Исправлены некорректно отображавшиеся комментарии в vgtoast.js.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
# VEGAS-APP 1.1.5 (Апрель, 9, 2026)
|
|
23
|
+
## Новые фичи
|
|
24
|
+
* В VGModal добавлены встроенные модули `VGModalDrag` и `VGModalResize` с поддержкой параметров `drag`/`resize` (включение, порог, зона edge, минимальные размеры, debug).
|
|
25
|
+
|
|
26
|
+
## Изменения
|
|
27
|
+
* В VGFilePreview при `ui.nameOnly=true` блок `.preview` отключается (очистка + скрытие), отображается только имя файла.
|
|
28
|
+
* В VGFilePreview добавлена интеграция метаданных для audio/video. Audio: `title` и `cover` из ID3; Video: генерация poster-кадра в `icon`.
|
|
29
|
+
* В VGFilePreview унифицировано поведение имени для audio/video: `name` показывает title из метаданных (если есть), иначе имя файла; `original_name` при наличии title показывает имя файла.
|
|
30
|
+
* В VGFilePreview клик по `.vg-filepreview-audio-inline__name` теперь запускает/ставит на паузу аудио.
|
|
31
|
+
* В VGFilePreview клик по картинке в `icon` (image/audio cover/video poster) открывает ImageModal.
|
|
32
|
+
* В стилях VGFilePreview добавлен `.is-preview-action { cursor: pointer; }`.
|
|
33
|
+
* В VGFilePreview добавлена конфигурация предпросмотра по группам через `this._params.preview.<group>.enable` (`audio`, `video`, `image`, `archive`, `text`, `office`, `pdf`), в текущем состоянии по умолчанию группы включены.
|
|
34
|
+
* В VGModal добавлено автоматическое управление drag/resize при `show/hide/resize` модального окна, с синхронизацией позиции и границ внутри viewport.
|
|
35
|
+
* В VGFilePreview изменены дефолты групп предпросмотра: `image` и `office` включены по умолчанию (теперь включены все группы предпросмотра).
|
|
36
|
+
* В VGFilePreview для image-файлов в `_shouldRenderPreviewForCurrentFile()` добавлено принудительное отключение inline-preview.
|
|
16
37
|
|
|
17
38
|
---
|
|
18
39
|
# VEGAS-APP 1.1.4 (Апрель, 9, 2026)
|
|
@@ -23,7 +44,6 @@
|
|
|
23
44
|
* В модуле VGFilePreview параметр `ui.preview` сделан опциональным и выключен по умолчанию.
|
|
24
45
|
* В модуле VGFilePreview длинные названия файлов и original name ограничены: `min-width: 60px` и `text-overflow: ellipsis`, чтобы название не схлопывалось.
|
|
25
46
|
|
|
26
|
-
|
|
27
47
|
---
|
|
28
48
|
|
|
29
49
|
# VEGAS-APP 1.0.0 - 1.1.3 (Апрель, 9, 2026)
|
|
@@ -125,7 +145,6 @@
|
|
|
125
145
|
|
|
126
146
|
* Исправлены ошибки в разных модулях
|
|
127
147
|
|
|
128
|
-
|
|
129
148
|
---
|
|
130
149
|
|
|
131
150
|
# VEGAS-APP 0.7.2 - 0.7.4 (Декабрь, 04, 2025)
|
|
@@ -246,19 +265,16 @@
|
|
|
246
265
|
* Новый параметр timeout. Задержка показа модального окна после закрытия других открытых окон.
|
|
247
266
|
---
|
|
248
267
|
|
|
249
|
-
|
|
250
268
|
# VEGAS-APP 0.1.8 (Март, 13, 2025)
|
|
251
269
|
## Модуль VGSelect
|
|
252
270
|
* Релиз
|
|
253
271
|
---
|
|
254
272
|
|
|
255
|
-
|
|
256
273
|
# VEGAS-APP 0.1.7 (Март, 12, 2025)
|
|
257
274
|
## Модуль VGSelect
|
|
258
275
|
* Исправлены ошибки
|
|
259
276
|
---
|
|
260
277
|
|
|
261
|
-
|
|
262
278
|
# VEGAS-APP 0.1.5 (Март, 11, 2025)
|
|
263
279
|
## Модуль VGSelect
|
|
264
280
|
### Добавлено:
|
|
@@ -274,7 +290,6 @@
|
|
|
274
290
|
* Событие после аякс запроса NAME_KEY.loaded
|
|
275
291
|
---
|
|
276
292
|
|
|
277
|
-
|
|
278
293
|
# VEGAS-APP 0.1.4 (Март, 10, 2025)
|
|
279
294
|
* Перенесен плагин (<a href="https://github.com/vegas-dev/vegas-select">VGSelect</a>) в модуль VGSelect
|
|
280
295
|
* для модулей VGSidebar и VGModal добавлена возможность собирать параметры с кнопки вызова элемента
|
|
@@ -282,3 +297,4 @@
|
|
|
282
297
|
* исправлены некоторые ошибки
|
|
283
298
|
---
|
|
284
299
|
|
|
300
|
+
|
package/agents.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
### Окружение
|
|
2
|
+
- Проект, репозиторий, vegas-app является основным, фундаментальным. Тут идет разработка, редактирование проекта vgapp.
|
|
3
|
+
- Проект, репозиторий, vegas-app-backend является демонстрационным, для тестирования и проверки работоспособности.
|
|
4
|
+
- Оба проекта связаны между собой симлинками, но при этом имеют разные цели и функциональность.
|
|
5
|
+
|
|
6
|
+
|
|
1
7
|
### Свод правил:
|
|
2
8
|
1. Правила по changelog для проекта vegas-app:
|
|
3
9
|
- Правило: при команде "записать в changelog" или "дополни changelog" записываем вкратце изменения в файл `CHANGELOG.md`, которые были сделаны с прошлой версии. Записываем так: Версия (смотри `package.json`), дата и построчно что было внесено, нужно как-то выделить новые фичи.
|
|
@@ -5,3 +11,4 @@
|
|
|
5
11
|
- Если версия в `package.json` не изменилась, нужно проверить, есть ли эта версия в `CHANGELOG.md`.
|
|
6
12
|
- Если запись версии уже есть, дописывать изменения в существующую запись (как дополнение), а не создавать новый блок той же версии.
|
|
7
13
|
- Если записи версии нет, создавать новый блок с версией из `package.json`, датой и списком изменений, запись добавляется сверху.
|
|
14
|
+
2. Все файлы должны иметь кодировку UTF-8.
|
|
@@ -760,9 +760,9 @@ class VGFilePreview extends BaseModule {
|
|
|
760
760
|
return this._isPreviewGroupEnabled('video');
|
|
761
761
|
}
|
|
762
762
|
|
|
763
|
-
if (this._isImageFile()) {
|
|
764
|
-
return false;
|
|
765
|
-
}
|
|
763
|
+
if (this._isImageFile()) {
|
|
764
|
+
return false;
|
|
765
|
+
}
|
|
766
766
|
|
|
767
767
|
if (this._isOfficeFile()) {
|
|
768
768
|
return this._isPreviewGroupEnabled('office');
|
|
@@ -937,6 +937,11 @@ class VGFilePreview extends BaseModule {
|
|
|
937
937
|
}
|
|
938
938
|
|
|
939
939
|
_getDataDisplayName() {
|
|
940
|
+
const dataName = String(this._element?.getAttribute('data-name') || '').trim();
|
|
941
|
+
if (dataName) {
|
|
942
|
+
return dataName;
|
|
943
|
+
}
|
|
944
|
+
|
|
940
945
|
return String(this._element?.getAttribute('data-vg-filepreview-display-name') || '').trim();
|
|
941
946
|
}
|
|
942
947
|
|
|
@@ -528,9 +528,10 @@ class VGFilesBase extends BaseModule {
|
|
|
528
528
|
return;
|
|
529
529
|
}
|
|
530
530
|
|
|
531
|
-
if (existingNode) {
|
|
532
|
-
|
|
533
|
-
|
|
531
|
+
if (existingNode) {
|
|
532
|
+
VGFilePreview.stopActiveInlineAudioIfDetached([existingNode]);
|
|
533
|
+
existingNode.remove();
|
|
534
|
+
}
|
|
534
535
|
|
|
535
536
|
const nextNode = this._createInfoListItem(file, i, $itemsTemplate, $itemsTemplateClasses, fileKey, signature);
|
|
536
537
|
nextNodes.push(nextNode);
|
|
@@ -934,11 +935,28 @@ class VGFilesBase extends BaseModule {
|
|
|
934
935
|
Selectors.findAll('[data-vg-files="generated"]', this._element).forEach(el => el.remove());
|
|
935
936
|
}
|
|
936
937
|
|
|
937
|
-
clear(resetInput = true) {
|
|
938
|
-
|
|
939
|
-
if (
|
|
940
|
-
this.
|
|
941
|
-
|
|
938
|
+
clear(resetInput = true) {
|
|
939
|
+
const detachedNodes = [];
|
|
940
|
+
if (this._nodes.info) {
|
|
941
|
+
const $infoList = Selectors.find(`.${this._getClass('info-list')}`, this._element);
|
|
942
|
+
if ($infoList?.children?.length) {
|
|
943
|
+
detachedNodes.push(...Array.from($infoList.children));
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
if (this._nodes.drop) {
|
|
947
|
+
const $dropList = Selectors.find(`.${this._getClass('drop-list')}`, this._element);
|
|
948
|
+
if ($dropList?.children?.length) {
|
|
949
|
+
detachedNodes.push(...Array.from($dropList.children));
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
if (detachedNodes.length) {
|
|
953
|
+
VGFilePreview.stopActiveInlineAudioIfDetached(detachedNodes);
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
this._revokeUrls();
|
|
957
|
+
if (resetInput) {
|
|
958
|
+
this._resetFileInput();
|
|
959
|
+
}
|
|
942
960
|
this._cleanupFakeInputs();
|
|
943
961
|
this._cleanupErrors();
|
|
944
962
|
this._files = [];
|
|
@@ -274,18 +274,18 @@ class VGFormSender extends BaseModule {
|
|
|
274
274
|
* @param {FormData|null} data - Дополнительные данные для отправки
|
|
275
275
|
*/
|
|
276
276
|
request(event, data = null) {
|
|
277
|
-
const _this = this;
|
|
278
|
-
const mergeFormData = (target, source) => {
|
|
279
|
-
const replacedKeys = new Set();
|
|
280
|
-
source.forEach((value, key) => {
|
|
281
|
-
if (!replacedKeys.has(key)) {
|
|
282
|
-
target.delete(key);
|
|
283
|
-
replacedKeys.add(key);
|
|
284
|
-
}
|
|
285
|
-
target.append(key, value);
|
|
286
|
-
});
|
|
287
|
-
return target;
|
|
288
|
-
}
|
|
277
|
+
const _this = this;
|
|
278
|
+
const mergeFormData = (target, source) => {
|
|
279
|
+
const replacedKeys = new Set();
|
|
280
|
+
source.forEach((value, key) => {
|
|
281
|
+
if (!replacedKeys.has(key)) {
|
|
282
|
+
target.delete(key);
|
|
283
|
+
replacedKeys.add(key);
|
|
284
|
+
}
|
|
285
|
+
target.append(key, value);
|
|
286
|
+
});
|
|
287
|
+
return target;
|
|
288
|
+
}
|
|
289
289
|
|
|
290
290
|
_this._alertBefore();
|
|
291
291
|
|
|
@@ -621,7 +621,7 @@ class VGFormSender extends BaseModule {
|
|
|
621
621
|
response = data.response.view
|
|
622
622
|
} else if (typeof response !== 'string') {
|
|
623
623
|
if (status === 'danger') {
|
|
624
|
-
response.title = lang_titles(this._params.lang, 'errors').title;
|
|
624
|
+
response.title = ('title' in response) ? response.title : lang_titles(this._params.lang, 'errors').title;
|
|
625
625
|
|
|
626
626
|
if ('code' in data && data.code !== 200) {
|
|
627
627
|
const messages = {
|
|
@@ -782,4 +782,4 @@ EventHandler.on(document, EVENT_SUBMIT_DATA_API, function (event) {
|
|
|
782
782
|
}
|
|
783
783
|
})
|
|
784
784
|
|
|
785
|
-
export default VGFormSender;
|
|
785
|
+
export default VGFormSender;
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
const DEFAULT_OPTIONS = {
|
|
2
|
+
enable: false,
|
|
3
|
+
selector: '.vg-toast-wrapper',
|
|
4
|
+
threshold: 4,
|
|
5
|
+
resizeEdgeSize: 8,
|
|
6
|
+
debug: false,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
class VGToastDrag {
|
|
10
|
+
constructor(modalElement, dialogElement, options = {}) {
|
|
11
|
+
this._modalElement = modalElement;
|
|
12
|
+
this._dialogElement = dialogElement;
|
|
13
|
+
this._options = this._normalizeOptions(options);
|
|
14
|
+
this._pointerId = null;
|
|
15
|
+
this._dragTarget = null;
|
|
16
|
+
this._isDragging = false;
|
|
17
|
+
this._startX = 0;
|
|
18
|
+
this._startY = 0;
|
|
19
|
+
this._currentX = 0;
|
|
20
|
+
this._currentY = 0;
|
|
21
|
+
this._dialogStartLeft = 0;
|
|
22
|
+
this._dialogStartTop = 0;
|
|
23
|
+
this._dialogWidth = 0;
|
|
24
|
+
this._dialogHeight = 0;
|
|
25
|
+
this._isEnabled = false;
|
|
26
|
+
this._previousUserSelect = '';
|
|
27
|
+
this._previousTransition = '';
|
|
28
|
+
this._previousWillChange = '';
|
|
29
|
+
this._initialRect = null;
|
|
30
|
+
this._lockedIframes = [];
|
|
31
|
+
this._debugElement = null;
|
|
32
|
+
|
|
33
|
+
this._onPointerMove = this._onPointerMove.bind(this);
|
|
34
|
+
this._onPointerUp = this._onPointerUp.bind(this);
|
|
35
|
+
this._onPointerDown = this._onPointerDown.bind(this);
|
|
36
|
+
this._onNativeDragStart = this._onNativeDragStart.bind(this);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
setOptions(options = {}) {
|
|
40
|
+
this._options = this._normalizeOptions(options, this._options);
|
|
41
|
+
this._updateDebugOverlay();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
enable() {
|
|
45
|
+
if (!this._dialogElement || this._isEnabled) return;
|
|
46
|
+
|
|
47
|
+
this._updateDebugOverlay();
|
|
48
|
+
this._dialogElement.addEventListener('pointerdown', this._onPointerDown);
|
|
49
|
+
this._isEnabled = true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
disable() {
|
|
53
|
+
if (!this._dialogElement || !this._isEnabled) return;
|
|
54
|
+
|
|
55
|
+
this._dialogElement.removeEventListener('pointerdown', this._onPointerDown);
|
|
56
|
+
document.removeEventListener('pointermove', this._onPointerMove);
|
|
57
|
+
document.removeEventListener('pointerup', this._onPointerUp);
|
|
58
|
+
document.removeEventListener('pointercancel', this._onPointerUp);
|
|
59
|
+
document.removeEventListener('dragstart', this._onNativeDragStart, true);
|
|
60
|
+
this._dialogElement.style.touchAction = '';
|
|
61
|
+
document.body.style.userSelect = this._previousUserSelect;
|
|
62
|
+
this._pointerId = null;
|
|
63
|
+
this._dragTarget = null;
|
|
64
|
+
this._isDragging = false;
|
|
65
|
+
this._initialRect = null;
|
|
66
|
+
this._unlockEmbeddedFrames();
|
|
67
|
+
this._restoreDragStyles();
|
|
68
|
+
delete this._modalElement.dataset.vgToastDragging;
|
|
69
|
+
this._setDebugVisibility(false);
|
|
70
|
+
this._isEnabled = false;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
syncPosition() {
|
|
74
|
+
if (!this._isEnabled) return;
|
|
75
|
+
if (!this._isPreparedForInteraction()) return;
|
|
76
|
+
|
|
77
|
+
const rect = this._dialogElement.getBoundingClientRect();
|
|
78
|
+
const width = this._dialogElement.offsetWidth || rect.width;
|
|
79
|
+
const height = this._dialogElement.offsetHeight || rect.height;
|
|
80
|
+
const currentLeft = Number.parseFloat(this._dialogElement.style.left);
|
|
81
|
+
const currentTop = Number.parseFloat(this._dialogElement.style.top);
|
|
82
|
+
const left = Number.isFinite(currentLeft) ? currentLeft : rect.left;
|
|
83
|
+
const top = Number.isFinite(currentTop) ? currentTop : rect.top;
|
|
84
|
+
const maxLeft = Math.max(0, window.innerWidth - width);
|
|
85
|
+
const maxTop = Math.max(0, window.innerHeight - height);
|
|
86
|
+
const nextLeft = Math.min(maxLeft, Math.max(0, left));
|
|
87
|
+
const nextTop = Math.min(maxTop, Math.max(0, top));
|
|
88
|
+
|
|
89
|
+
this._dialogElement.style.left = `${nextLeft}px`;
|
|
90
|
+
this._dialogElement.style.top = `${nextTop}px`;
|
|
91
|
+
this._updateDebugValues('synced');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
_onPointerDown(event) {
|
|
95
|
+
if (event.button !== 0) return;
|
|
96
|
+
|
|
97
|
+
const dragArea = this._resolveDragArea(event.target);
|
|
98
|
+
if (!dragArea || !this._dialogElement.contains(dragArea)) return;
|
|
99
|
+
if (this._isPointerOnResizeEdge(event)) return;
|
|
100
|
+
if (event.target.closest('input, textarea, select, button, a, [contenteditable="true"]')) return;
|
|
101
|
+
if (dragArea && dragArea.closest('[data-vg-dismiss="toast"]')) return;
|
|
102
|
+
|
|
103
|
+
this._pointerId = event.pointerId;
|
|
104
|
+
this._dragTarget = event.target;
|
|
105
|
+
this._isDragging = false;
|
|
106
|
+
event.preventDefault();
|
|
107
|
+
this._previousUserSelect = document.body.style.userSelect;
|
|
108
|
+
document.body.style.userSelect = 'none';
|
|
109
|
+
this._initialRect = this._dialogElement.getBoundingClientRect();
|
|
110
|
+
this._dialogStartLeft = this._initialRect.left;
|
|
111
|
+
this._dialogStartTop = this._initialRect.top;
|
|
112
|
+
this._dialogWidth = this._initialRect.width;
|
|
113
|
+
this._dialogHeight = this._initialRect.height;
|
|
114
|
+
this._startX = event.clientX;
|
|
115
|
+
this._startY = event.clientY;
|
|
116
|
+
this._currentX = event.clientX;
|
|
117
|
+
this._currentY = event.clientY;
|
|
118
|
+
this._lockEmbeddedFrames();
|
|
119
|
+
this._modalElement.dataset.vgToastDragging = 'true';
|
|
120
|
+
this._setDebugVisibility(true);
|
|
121
|
+
this._updateDebugValues('armed');
|
|
122
|
+
|
|
123
|
+
document.addEventListener('dragstart', this._onNativeDragStart, true);
|
|
124
|
+
|
|
125
|
+
if (this._dialogElement && this._dialogElement.setPointerCapture) {
|
|
126
|
+
this._dialogElement.setPointerCapture(event.pointerId);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
document.addEventListener('pointermove', this._onPointerMove);
|
|
130
|
+
document.addEventListener('pointerup', this._onPointerUp);
|
|
131
|
+
document.addEventListener('pointercancel', this._onPointerUp);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
_onPointerMove(event) {
|
|
135
|
+
if (event.pointerId !== this._pointerId) return;
|
|
136
|
+
|
|
137
|
+
this._currentX = event.clientX;
|
|
138
|
+
this._currentY = event.clientY;
|
|
139
|
+
if (!this._isDragging) {
|
|
140
|
+
const deltaX = this._currentX - this._startX;
|
|
141
|
+
const deltaY = this._currentY - this._startY;
|
|
142
|
+
const distance = Math.hypot(deltaX, deltaY);
|
|
143
|
+
if (distance < this._options.threshold) return;
|
|
144
|
+
|
|
145
|
+
this._isDragging = true;
|
|
146
|
+
this._dialogElement.style.touchAction = 'none';
|
|
147
|
+
this._applyDragStyles();
|
|
148
|
+
this._preparePosition(this._initialRect);
|
|
149
|
+
event.preventDefault();
|
|
150
|
+
this._updateDebugValues('dragging');
|
|
151
|
+
}
|
|
152
|
+
this._applyDragPosition();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
_onPointerUp(event) {
|
|
156
|
+
if (event.pointerId !== this._pointerId) return;
|
|
157
|
+
|
|
158
|
+
document.removeEventListener('pointermove', this._onPointerMove);
|
|
159
|
+
document.removeEventListener('pointerup', this._onPointerUp);
|
|
160
|
+
document.removeEventListener('pointercancel', this._onPointerUp);
|
|
161
|
+
document.removeEventListener('dragstart', this._onNativeDragStart, true);
|
|
162
|
+
if (this._isDragging) {
|
|
163
|
+
this._applyDragPosition();
|
|
164
|
+
}
|
|
165
|
+
this._dialogElement.style.touchAction = '';
|
|
166
|
+
document.body.style.userSelect = this._previousUserSelect;
|
|
167
|
+
|
|
168
|
+
if (this._dialogElement && this._dialogElement.releasePointerCapture) {
|
|
169
|
+
this._dialogElement.releasePointerCapture(event.pointerId);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
this._pointerId = null;
|
|
173
|
+
this._dragTarget = null;
|
|
174
|
+
this._isDragging = false;
|
|
175
|
+
this._initialRect = null;
|
|
176
|
+
this._unlockEmbeddedFrames();
|
|
177
|
+
this._restoreDragStyles();
|
|
178
|
+
delete this._modalElement.dataset.vgToastDragging;
|
|
179
|
+
this._setDebugVisibility(this._options.debug);
|
|
180
|
+
this._updateDebugValues('idle');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
_preparePosition(rect = null) {
|
|
184
|
+
if (!this._dialogElement) return;
|
|
185
|
+
|
|
186
|
+
const currentRect = rect || this._dialogElement.getBoundingClientRect();
|
|
187
|
+
this._dialogElement.style.position = 'fixed';
|
|
188
|
+
this._dialogElement.style.margin = '0';
|
|
189
|
+
this._dialogElement.style.left = `${currentRect.left}px`;
|
|
190
|
+
this._dialogElement.style.top = `${currentRect.top}px`;
|
|
191
|
+
this._dialogElement.style.width = `${currentRect.width}px`;
|
|
192
|
+
this._dialogElement.style.height = `${currentRect.height}px`;
|
|
193
|
+
this._dialogElement.style.transform = 'none';
|
|
194
|
+
this._dialogElement.style.translate = 'none';
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
_isPreparedForInteraction() {
|
|
198
|
+
return this._dialogElement.style.position === 'fixed' && this._dialogElement.style.transform === 'none';
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
_isPointerOnResizeEdge(event) {
|
|
202
|
+
const rect = this._dialogElement.getBoundingClientRect();
|
|
203
|
+
const offsetX = event.clientX - rect.left;
|
|
204
|
+
const offsetY = event.clientY - rect.top;
|
|
205
|
+
const edgeSize = this._options.resizeEdgeSize;
|
|
206
|
+
|
|
207
|
+
const nearTop = offsetY >= 0 && offsetY <= edgeSize;
|
|
208
|
+
const nearBottom = offsetY <= rect.height && offsetY >= rect.height - edgeSize;
|
|
209
|
+
const nearLeft = offsetX >= 0 && offsetX <= edgeSize;
|
|
210
|
+
const nearRight = offsetX <= rect.width && offsetX >= rect.width - edgeSize;
|
|
211
|
+
|
|
212
|
+
return nearTop || nearBottom || nearLeft || nearRight;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
_applyDragPosition() {
|
|
216
|
+
if (this._pointerId === null || !this._isDragging) return;
|
|
217
|
+
|
|
218
|
+
const deltaX = this._currentX - this._startX;
|
|
219
|
+
const deltaY = this._currentY - this._startY;
|
|
220
|
+
const maxLeft = Math.max(0, window.innerWidth - this._dialogWidth);
|
|
221
|
+
const maxTop = Math.max(0, window.innerHeight - this._dialogHeight);
|
|
222
|
+
const nextLeft = Math.min(maxLeft, Math.max(0, this._dialogStartLeft + deltaX));
|
|
223
|
+
const nextTop = Math.min(maxTop, Math.max(0, this._dialogStartTop + deltaY));
|
|
224
|
+
|
|
225
|
+
this._dialogElement.style.left = `${nextLeft}px`;
|
|
226
|
+
this._dialogElement.style.top = `${nextTop}px`;
|
|
227
|
+
this._updateDebugValues('dragging');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
_onNativeDragStart(event) {
|
|
231
|
+
if (this._pointerId === null) return;
|
|
232
|
+
event.preventDefault();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
_applyDragStyles() {
|
|
236
|
+
this._previousTransition = this._dialogElement.style.transition;
|
|
237
|
+
this._previousWillChange = this._dialogElement.style.willChange;
|
|
238
|
+
this._dialogElement.style.transition = 'none';
|
|
239
|
+
this._dialogElement.style.willChange = 'left, top';
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
_restoreDragStyles() {
|
|
243
|
+
this._dialogElement.style.transition = this._previousTransition;
|
|
244
|
+
this._dialogElement.style.willChange = this._previousWillChange;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
_lockEmbeddedFrames() {
|
|
248
|
+
if (!this._dialogElement || this._lockedIframes.length) return;
|
|
249
|
+
|
|
250
|
+
const iframes = this._dialogElement.querySelectorAll('iframe');
|
|
251
|
+
for (const frame of iframes) {
|
|
252
|
+
this._lockedIframes.push({
|
|
253
|
+
element: frame,
|
|
254
|
+
pointerEvents: frame.style.pointerEvents,
|
|
255
|
+
});
|
|
256
|
+
frame.style.pointerEvents = 'none';
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
_unlockEmbeddedFrames() {
|
|
261
|
+
if (!this._lockedIframes.length) return;
|
|
262
|
+
|
|
263
|
+
for (const item of this._lockedIframes) {
|
|
264
|
+
item.element.style.pointerEvents = item.pointerEvents;
|
|
265
|
+
}
|
|
266
|
+
this._lockedIframes = [];
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
_normalizeOptions(options, base = DEFAULT_OPTIONS) {
|
|
270
|
+
const merged = {...base, ...options};
|
|
271
|
+
const threshold = Number(merged.threshold);
|
|
272
|
+
const resizeEdgeSize = Number(merged.resizeEdgeSize);
|
|
273
|
+
const selector = typeof merged.selector === 'string' && merged.selector.trim()
|
|
274
|
+
? merged.selector
|
|
275
|
+
: DEFAULT_OPTIONS.selector;
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
...merged,
|
|
279
|
+
selector,
|
|
280
|
+
threshold: Number.isFinite(threshold) && threshold >= 0 ? threshold : DEFAULT_OPTIONS.threshold,
|
|
281
|
+
resizeEdgeSize: Number.isFinite(resizeEdgeSize) && resizeEdgeSize > 0 ? resizeEdgeSize : DEFAULT_OPTIONS.resizeEdgeSize,
|
|
282
|
+
debug: Boolean(merged.debug),
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
_resolveDragArea(target) {
|
|
287
|
+
try {
|
|
288
|
+
return target.closest(this._options.selector);
|
|
289
|
+
} catch (error) {
|
|
290
|
+
return target.closest(DEFAULT_OPTIONS.selector);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
_updateDebugOverlay() {
|
|
295
|
+
if (!this._options.debug) {
|
|
296
|
+
this._setDebugVisibility(false);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (!this._debugElement) {
|
|
301
|
+
this._debugElement = document.createElement('div');
|
|
302
|
+
this._debugElement.style.position = 'absolute';
|
|
303
|
+
this._debugElement.style.left = '170px';
|
|
304
|
+
this._debugElement.style.bottom = '8px';
|
|
305
|
+
this._debugElement.style.zIndex = '7';
|
|
306
|
+
this._debugElement.style.padding = '4px 6px';
|
|
307
|
+
this._debugElement.style.borderRadius = '4px';
|
|
308
|
+
this._debugElement.style.background = 'rgba(0, 0, 0, 0.72)';
|
|
309
|
+
this._debugElement.style.color = '#fff';
|
|
310
|
+
this._debugElement.style.fontSize = '11px';
|
|
311
|
+
this._debugElement.style.lineHeight = '1.3';
|
|
312
|
+
this._debugElement.style.pointerEvents = 'none';
|
|
313
|
+
this._dialogElement.append(this._debugElement);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
this._setDebugVisibility(true);
|
|
317
|
+
this._updateDebugValues(this._isDragging ? 'dragging' : 'idle');
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
_setDebugVisibility(visible) {
|
|
321
|
+
if (!this._debugElement) return;
|
|
322
|
+
this._debugElement.style.display = visible ? 'block' : 'none';
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
_updateDebugValues(state = 'idle') {
|
|
326
|
+
if (!this._debugElement || !this._options.debug) return;
|
|
327
|
+
|
|
328
|
+
const rect = this._dialogElement.getBoundingClientRect();
|
|
329
|
+
this._debugElement.textContent = `drag:${state} x:${Math.round(rect.left)} y:${Math.round(rect.top)} w:${Math.round(rect.width)} h:${Math.round(rect.height)}`;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export default VGToastDrag;
|
|
334
|
+
|
|
335
|
+
|