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 CHANGED
@@ -1,18 +1,39 @@
1
- # VEGAS-APP 1.1.5 (Апрель, 9, 2026)
2
- ## Новые фичи
3
- * В VGModal добавлены встроенные модули `VGModalDrag` и `VGModalResize` с поддержкой параметров `drag`/`resize` (включение, порог, зона edge, минимальные размеры, debug).
4
-
5
- ## Изменения
6
- * В VGFilePreview при `ui.nameOnly=true` блок `.preview` отключается (очистка + скрытие), отображается только имя файла.
7
- * В VGFilePreview добавлена интеграция метаданных для audio/video. Audio: `title` и `cover` из ID3; Video: генерация poster-кадра в `icon`.
8
- * В VGFilePreview унифицировано поведение имени для audio/video: `name` показывает title из метаданных (если есть), иначе имя файла; `original_name` при наличии title показывает имя файла.
9
- * В VGFilePreview клик по `.vg-filepreview-audio-inline__name` теперь запускает/ставит на паузу аудио.
10
- * В VGFilePreview клик по картинке в `icon` (image/audio cover/video poster) открывает ImageModal.
11
- * В стилях VGFilePreview добавлен `.is-preview-action { cursor: pointer; }`.
12
- * В VGFilePreview добавлена конфигурация предпросмотра по группам через `this._params.preview.<group>.enable` (`audio`, `video`, `image`, `archive`, `text`, `office`, `pdf`), в текущем состоянии по умолчанию группы включены.
13
- * В VGModal добавлено автоматическое управление drag/resize при `show/hide/resize` модального окна, с синхронизацией позиции и границ внутри viewport.
14
- * В VGFilePreview изменены дефолты групп предпросмотра: `image` и `office` включены по умолчанию (теперь включены все группы предпросмотра).
15
- * В VGFilePreview для image-файлов в `_shouldRenderPreviewForCurrentFile()` добавлено принудительное отключение inline-preview.
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
- existingNode.remove();
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
- this._revokeUrls();
939
- if (resetInput) {
940
- this._resetFileInput();
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
+