vgapp 1.1.4 → 1.1.6

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.
@@ -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
+