vgapp 1.2.3 → 1.2.5

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.
@@ -4,10 +4,10 @@ import Backdrop from "../../../utils/js/components/backdrop";
4
4
  import Selectors from "../../../utils/js/dom/selectors";
5
5
  import EventHandler from "../../../utils/js/dom/event";
6
6
  import {Manipulator} from "../../../utils/js/dom/manipulator";
7
- import {execute, isDisabled, isRTL, mergeDeepObject, reflow} from "../../../utils/js/functions";
8
- import {dismissTrigger} from "../../module-fn";
9
- import VGModalDrag from "./vgmodal.drag";
10
- import VGModalResize from "./vgmodal.resize";
7
+ import {execute, isDisabled, isRTL, mergeDeepObject, reflow} from "../../../utils/js/functions";
8
+ import {dismissTrigger} from "../../module-fn";
9
+ import VGModalDrag from "./vgmodal.drag";
10
+ import VGModalResize from "./vgmodal.resize";
11
11
 
12
12
  /**
13
13
  * Constants
@@ -17,15 +17,15 @@ const NAME_KEY = 'vg.modal';
17
17
 
18
18
  const ESCAPE_KEY = 'Escape';
19
19
 
20
- const OPEN_SELECTOR = '.vg-modal.show';
20
+ const OPEN_SELECTOR = '.vg-modal.show:not([data-vg-persistent="true"])';
21
21
  const SELECTOR_DIALOG = '.vg-modal-dialog';
22
22
  const SELECTOR_MODAL_BODY = '.vg-modal-body';
23
23
  const SELECTOR_DATA_TOGGLE = '[data-vg-toggle="modal"]';
24
24
 
25
- const CLASS_NAME_OPEN = 'vg-modal-open';
26
- const CLASS_NAME_SHOW = 'show';
27
- const CLASS_NAME_FADE = 'fade';
28
- const CLASS_NAME_STATIC = 'vg-modal-static';
25
+ const CLASS_NAME_OPEN = 'vg-modal-open';
26
+ const CLASS_NAME_SHOW = 'show';
27
+ const CLASS_NAME_FADE = 'fade';
28
+ const CLASS_NAME_STATIC = 'vg-modal-static';
29
29
 
30
30
  const EVENT_KEY_HIDE = `${NAME_KEY}.hide`;
31
31
  const EVENT_KEY_HIDDEN = `${NAME_KEY}.hidden`;
@@ -54,29 +54,34 @@ class VGModal extends BaseModule {
54
54
  resizeEdgeSize: 8,
55
55
  debug: false,
56
56
  },
57
- resize: {
58
- enable: false,
59
- edgeSize: 8,
60
- minWidth: 300,
61
- minHeight: 160,
62
- debug: false,
63
- },
64
- };
65
-
66
- this._params = this._getParams(element, mergeDeepObject({
67
- backdrop: true,
68
- focus: true,
69
- keyboard: true,
57
+ resize: {
58
+ enable: false,
59
+ edgeSize: 8,
60
+ minWidth: 300,
61
+ minHeight: 160,
62
+ debug: false,
63
+ },
64
+ };
65
+
66
+ this._params = this._getParams(element, mergeDeepObject({
67
+ backdrop: true,
68
+ persistent: false,
69
+ focus: true,
70
+ keyboard: true,
70
71
  fields: [],
71
72
  hash: false,
72
73
  centered: false,
73
74
  dismiss: true,
74
- resize: {
75
- enable: false,
76
- edgeSize: 8,
77
- minWidth: 300,
78
- minHeight: 160,
79
- debug: false,
75
+ resize: {
76
+ enable: false,
77
+ edgeSize: 8,
78
+ minWidth: 300,
79
+ minHeight: 160,
80
+ debug: false,
81
+ },
82
+ state: {
83
+ enable: true,
84
+ key: '',
80
85
  },
81
86
  drag: {
82
87
  enable: false,
@@ -117,21 +122,27 @@ class VGModal extends BaseModule {
117
122
 
118
123
  this._button = null;
119
124
  this._dialog = Selectors.find(SELECTOR_DIALOG, this._element);
125
+ this._content = Selectors.find('.vg-modal-content', this._dialog);
120
126
  this._isShown = false;
121
- this._isTransitioning = false;
122
- this._scrollBar = new ScrollBarHelper();
123
- this._dragHandler = new VGModalDrag(this._element, this._dialog);
124
- this._resizeHandler = new VGModalResize(this._element, this._dialog);
125
- this._interactionConfig = this._resolveInteractionConfig();
126
- this._dragHandler.setOptions(this._interactionConfig.drag);
127
- this._resizeHandler.setOptions(this._interactionConfig.resize);
127
+ this._isTransitioning = false;
128
+ this._scrollBar = new ScrollBarHelper();
129
+ this._dragHandler = new VGModalDrag(this._element, this._dialog);
130
+ this._resizeHandler = new VGModalResize(this._element, this._dialog);
131
+ this._interactionConfig = this._resolveInteractionConfig();
132
+ this._dragHandler.setOptions(this._interactionConfig.drag);
133
+ this._resizeHandler.setOptions(this._interactionConfig.resize);
128
134
 
129
135
  this._addEventListeners();
130
136
  this._dismissElement();
131
137
 
132
- this._params.animation.delay = !this._params.animation.enable ? 0 : this._params.animation.delay;
133
- this._animation(this._element, VGModal.NAME_KEY, this._params.animation);
134
- }
138
+ this._params.animation.delay = !this._params.animation.enable ? 0 : this._params.animation.delay;
139
+ this._animation(this._element, VGModal.NAME_KEY, this._params.animation);
140
+ if (this._params.persistent) {
141
+ this._element.setAttribute('data-vg-persistent', 'true');
142
+ } else {
143
+ this._element.removeAttribute('data-vg-persistent');
144
+ }
145
+ }
135
146
 
136
147
  static get NAME() {
137
148
  return NAME;
@@ -227,14 +238,28 @@ class VGModal extends BaseModule {
227
238
  });
228
239
  }
229
240
 
230
- this._scrollBar.hide();
231
-
232
- document.body.classList.add(CLASS_NAME_OPEN);
241
+ this._scrollBar.hide();
242
+
243
+ document.body.classList.add(CLASS_NAME_OPEN);
233
244
 
234
245
  this._addFieldsInModal(relatedTarget);
235
246
  this._adjustDialog();
236
247
 
237
- Backdrop.show(() => this._showElement(relatedTarget));
248
+ if (this._params.backdrop) {
249
+ Backdrop.show(() => {
250
+ const backdrop = Selectors.find('.vg-backdrop');
251
+ if (backdrop) {
252
+ EventHandler.one(backdrop, 'mousedown.vg.backdrop', () => {
253
+ this.hide();
254
+ });
255
+ }
256
+
257
+ this._showElement(relatedTarget);
258
+ });
259
+ return;
260
+ }
261
+
262
+ this._showElement(relatedTarget);
238
263
  }
239
264
 
240
265
  hide(openedModals = [], isLeaveBackDrop = false) {
@@ -243,37 +268,50 @@ class VGModal extends BaseModule {
243
268
  const hideEvent = EventHandler.trigger(this._element, EVENT_KEY_HIDE);
244
269
  if (hideEvent.defaultPrevented) return;
245
270
 
246
- this._isShown = false;
247
- this._isTransitioning = true;
248
-
249
- document.body.classList.remove(CLASS_NAME_OPEN);
250
-
251
- setTimeout(() => {
252
- this._element.classList.remove(CLASS_NAME_SHOW);
253
- this._queueCallback(() => this._hideModal(openedModals, isLeaveBackDrop), this._element, this._isAnimatedFade());
271
+ this._isShown = false;
272
+ this._isTransitioning = true;
273
+
274
+ setTimeout(() => {
275
+ this._element.classList.remove(CLASS_NAME_SHOW);
276
+ this._queueCallback(() => this._hideModal(openedModals, isLeaveBackDrop), this._element, this._isAnimatedFade());
254
277
  }, this._params.animation.delay);
255
278
  }
256
279
 
257
- _hideModal(openedModals, isLeaveBackDrop) {
258
- if (!isLeaveBackDrop) {
259
- this._disableInteractionHandlers();
260
- this._element.style.display = 'none';
261
- this._element.removeAttribute('aria-modal');
262
- this._element.removeAttribute('role');
263
- this._isTransitioning = false;
264
-
265
- if (openedModals.length) return;
266
-
267
- if (this._params.hash) {
268
- history.pushState("", document.title, window.location.pathname + window.location.search);
269
- }
270
-
271
- Backdrop.hide(() => {
272
- this._resetAdjustments();
273
- this._scrollBar.reset();
274
-
275
- EventHandler.trigger(this._element, EVENT_KEY_HIDDEN);
276
- })
280
+ _hideModal(openedModals, isLeaveBackDrop) {
281
+ if (!isLeaveBackDrop) {
282
+ this._saveInteractionState();
283
+ this._disableInteractionHandlers();
284
+ this._element.style.display = 'none';
285
+ this._element.removeAttribute('aria-modal');
286
+ this._element.removeAttribute('role');
287
+ this._isTransitioning = false;
288
+
289
+ const remainingOpenModals = Selectors.findAll(OPEN_SELECTOR).filter(modal => modal !== this._element);
290
+ if (remainingOpenModals.length || openedModals.length) {
291
+ EventHandler.trigger(this._element, EVENT_KEY_HIDDEN);
292
+ return;
293
+ }
294
+
295
+ document.body.classList.remove(CLASS_NAME_OPEN);
296
+
297
+ if (this._params.hash) {
298
+ history.pushState("", document.title, window.location.pathname + window.location.search);
299
+ }
300
+
301
+ if (!this._params.backdrop) {
302
+ this._resetAdjustments();
303
+ this._scrollBar.reset();
304
+
305
+ EventHandler.trigger(this._element, EVENT_KEY_HIDDEN);
306
+ return;
307
+ }
308
+
309
+ Backdrop.hide(() => {
310
+ this._resetAdjustments();
311
+ this._scrollBar.reset();
312
+
313
+ EventHandler.trigger(this._element, EVENT_KEY_HIDDEN);
314
+ })
277
315
  }
278
316
  }
279
317
 
@@ -297,27 +335,32 @@ class VGModal extends BaseModule {
297
335
  this._element.classList.add(CLASS_NAME_SHOW);
298
336
  this._toggleInteractionHandlers();
299
337
 
300
- const transitionComplete = () => {
301
- this._isTransitioning = false;
302
- EventHandler.trigger(this._element, EVENT_KEY_SHOWN, {
303
- relatedTarget
304
- });
305
- this._syncInteractiveBounds();
306
-
307
- this._params = this._getParams(relatedTarget, this._params);
308
- this._route((status, data) => {
309
- EventHandler.trigger(this._element, EVENT_KEY_LOADED, {stats: status, data: data, relatedTarget: relatedTarget});
310
- this._syncInteractiveBounds();
311
- });
312
- }
338
+ const transitionComplete = () => {
339
+ this._isTransitioning = false;
340
+ EventHandler.trigger(this._element, EVENT_KEY_SHOWN, {
341
+ relatedTarget
342
+ });
343
+ this._syncInteractiveBounds();
344
+
345
+ this._params = this._getParams(relatedTarget, this._params);
346
+ this._restoreInteractionState();
347
+ this._syncInteractiveBounds();
348
+ this._route((status, data) => {
349
+ EventHandler.trigger(this._element, EVENT_KEY_LOADED, {stats: status, data: data, relatedTarget: relatedTarget});
350
+ this._syncInteractiveBounds();
351
+ });
352
+ }
313
353
 
314
354
  this._queueCallback(transitionComplete, this._dialog, this._isAnimatedFade())
315
355
  }
316
356
 
317
- _toggleInteractionHandlers() {
318
- this._interactionConfig = this._resolveInteractionConfig();
319
- this._dragHandler.setOptions(this._interactionConfig.drag);
320
- this._resizeHandler.setOptions(this._interactionConfig.resize);
357
+ _bindInteractionHandlers() {
358
+ this._dragHandler.setOptions(this._interactionConfig.drag);
359
+ this._resizeHandler.setOptions(this._interactionConfig.resize);
360
+ }
361
+ _toggleInteractionHandlers() {
362
+ this._interactionConfig = this._resolveInteractionConfig();
363
+ this._bindInteractionHandlers();
321
364
 
322
365
  if (this._interactionConfig.drag.enable) {
323
366
  this._dragHandler.enable();
@@ -325,45 +368,68 @@ class VGModal extends BaseModule {
325
368
  this._dragHandler.disable();
326
369
  }
327
370
 
328
- if (this._interactionConfig.resize.enable) {
329
- this._resizeHandler.enable();
330
- } else {
331
- this._resizeHandler.disable();
332
- }
333
- }
334
-
335
- _disableInteractionHandlers() {
336
- this._dragHandler.disable();
337
- this._resizeHandler.disable();
338
- }
371
+ if (this._interactionConfig.resize.enable) {
372
+ this._resizeHandler.enable();
373
+ } else {
374
+ this._resizeHandler.disable();
375
+ }
376
+ }
377
+
378
+ _disableInteractionHandlers() {
379
+ this._dragHandler.disable();
380
+ this._resizeHandler.disable();
381
+ }
339
382
 
340
383
  _syncInteractiveBounds() {
341
384
  if (this._interactionConfig.resize.enable) {
342
385
  this._resizeHandler.syncToViewport();
343
386
  }
344
387
 
345
- if (this._interactionConfig.drag.enable) {
346
- this._dragHandler.syncPosition();
388
+ if (this._interactionConfig.drag.enable) {
389
+ this._dragHandler.syncPosition();
390
+ }
391
+ }
392
+
393
+ _resolveInteractionConfig() {
394
+ return {
395
+ drag: this._normalizeInteractionParams(this._params.drag, this._interactionDefaults.drag),
396
+ resize: this._normalizeInteractionParams(this._params.resize, this._interactionDefaults.resize),
397
+ };
398
+ }
399
+
400
+ _normalizeInteractionParams(paramsValue, defaults) {
401
+ if (typeof paramsValue === 'boolean') {
402
+ return {...defaults, enable: paramsValue};
347
403
  }
404
+
405
+ if (paramsValue && typeof paramsValue === 'object') {
406
+ const hasEnable = Object.prototype.hasOwnProperty.call(paramsValue, 'enable');
407
+ return {
408
+ ...defaults,
409
+ ...paramsValue,
410
+ enable: hasEnable ? Boolean(paramsValue.enable) : true,
411
+ };
412
+ }
413
+
414
+ return {...defaults};
348
415
  }
349
416
 
350
- _resolveInteractionConfig() {
351
- return {
352
- drag: this._normalizeInteractionParams(this._params.drag, this._interactionDefaults.drag),
353
- resize: this._normalizeInteractionParams(this._params.resize, this._interactionDefaults.resize),
354
- };
417
+ _getStateConfig() {
418
+ return this._normalizeStateParams(this._params.state);
355
419
  }
356
420
 
357
- _normalizeInteractionParams(paramsValue, defaults) {
421
+ _normalizeStateParams(paramsValue, defaults = { enable: true, key: '' }) {
358
422
  if (typeof paramsValue === 'boolean') {
359
423
  return {...defaults, enable: paramsValue};
360
424
  }
361
425
 
362
426
  if (paramsValue && typeof paramsValue === 'object') {
363
427
  const hasEnable = Object.prototype.hasOwnProperty.call(paramsValue, 'enable');
428
+ const key = typeof paramsValue.key === 'string' ? paramsValue.key.trim() : '';
364
429
  return {
365
430
  ...defaults,
366
431
  ...paramsValue,
432
+ key,
367
433
  enable: hasEnable ? Boolean(paramsValue.enable) : true,
368
434
  };
369
435
  }
@@ -371,6 +437,95 @@ class VGModal extends BaseModule {
371
437
  return {...defaults};
372
438
  }
373
439
 
440
+ _getStateStorageKey(stateConfig = this._getStateConfig()) {
441
+ if (!stateConfig.enable) return '';
442
+
443
+ if (typeof stateConfig.key === 'string' && stateConfig.key.trim()) {
444
+ return stateConfig.key.trim();
445
+ }
446
+
447
+ if (!this._element || !this._element.id) return '';
448
+
449
+ return `vg.modal.state:${window.location.pathname}:${this._element.id}`;
450
+ }
451
+
452
+ _captureInteractionState() {
453
+ const dialogStyle = this._dialog ? this._dialog.style : null;
454
+ const contentElement = this._content || Selectors.find('.vg-modal-content', this._dialog);
455
+ const contentStyle = contentElement ? contentElement.style : null;
456
+
457
+ return {
458
+ dialog: dialogStyle ? {
459
+ position: dialogStyle.position,
460
+ margin: dialogStyle.margin,
461
+ left: dialogStyle.left,
462
+ top: dialogStyle.top,
463
+ width: dialogStyle.width,
464
+ height: dialogStyle.height,
465
+ transform: dialogStyle.transform,
466
+ maxWidth: dialogStyle.maxWidth,
467
+ maxHeight: dialogStyle.maxHeight,
468
+ minHeight: dialogStyle.minHeight,
469
+ overflow: dialogStyle.overflow,
470
+ pointerEvents: dialogStyle.pointerEvents,
471
+ transition: dialogStyle.transition,
472
+ willChange: dialogStyle.willChange,
473
+ } : {},
474
+ content: contentStyle ? {
475
+ height: contentStyle.height,
476
+ maxHeight: contentStyle.maxHeight,
477
+ overflow: contentStyle.overflow,
478
+ } : {},
479
+ };
480
+ }
481
+
482
+ _applyInteractionState(state) {
483
+ if (!state || typeof state !== 'object') return;
484
+
485
+ const dialogState = state.dialog && typeof state.dialog === 'object' ? state.dialog : {};
486
+ const contentState = state.content && typeof state.content === 'object' ? state.content : {};
487
+ if (this._dialog) {
488
+ Object.keys(dialogState).forEach(propertyName => {
489
+ this._dialog.style[propertyName] = dialogState[propertyName] || '';
490
+ });
491
+ }
492
+
493
+ const contentElement = this._content || Selectors.find('.vg-modal-content', this._dialog);
494
+ if (contentElement) {
495
+ Object.keys(contentState).forEach(propertyName => {
496
+ contentElement.style[propertyName] = contentState[propertyName] || '';
497
+ });
498
+ }
499
+ }
500
+
501
+ _saveInteractionState() {
502
+ const stateConfig = this._getStateConfig();
503
+ const storageKey = this._getStateStorageKey(stateConfig);
504
+ if (!stateConfig.enable || !storageKey || typeof window === 'undefined' || !window.localStorage) return;
505
+
506
+ try {
507
+ window.localStorage.setItem(storageKey, JSON.stringify(this._captureInteractionState()));
508
+ } catch (error) {
509
+ }
510
+ }
511
+
512
+ _restoreInteractionState() {
513
+ const stateConfig = this._getStateConfig();
514
+ const storageKey = this._getStateStorageKey(stateConfig);
515
+ if (!stateConfig.enable || !storageKey || typeof window === 'undefined' || !window.localStorage) return false;
516
+
517
+ try {
518
+ const rawState = window.localStorage.getItem(storageKey);
519
+ if (!rawState) return false;
520
+
521
+ const parsedState = JSON.parse(rawState);
522
+ this._applyInteractionState(parsedState);
523
+ return true;
524
+ } catch (error) {
525
+ return false;
526
+ }
527
+ }
528
+
374
529
  _isAnimatedFade() {
375
530
  return this._element.classList.contains(CLASS_NAME_FADE)
376
531
  }
@@ -480,21 +635,26 @@ dismissTrigger(VGModal);
480
635
  /**
481
636
  * Data API implementation
482
637
  */
483
- EventHandler.on(document, EVENT_KEY_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
484
- const target = Selectors.getElementFromSelector(this);
638
+ EventHandler.on(document, EVENT_KEY_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
639
+ const target = Selectors.getElementFromSelector(this);
485
640
 
486
641
  if (['A', 'AREA'].includes(this.tagName)) event.preventDefault();
487
642
 
488
- EventHandler.one(target, EVENT_KEY_SHOW, showEvent => {
489
- if (showEvent.defaultPrevented) return;
490
- });
491
-
492
- const alreadyOpen = Selectors.find(OPEN_SELECTOR);
493
- if (alreadyOpen) VGModal.getInstance(alreadyOpen).hide([alreadyOpen]);
494
-
495
- const data = VGModal.getOrCreateInstance(target);
496
- data.toggle(this);
497
- });
643
+ EventHandler.one(target, EVENT_KEY_SHOW, showEvent => {
644
+ if (showEvent.defaultPrevented) return;
645
+ });
646
+
647
+ const alreadyOpen = Selectors.find(OPEN_SELECTOR);
648
+ if (alreadyOpen) {
649
+ const alreadyOpenInstance = VGModal.getInstance(alreadyOpen);
650
+ if (alreadyOpenInstance && !alreadyOpenInstance._params.persistent) {
651
+ alreadyOpenInstance.hide([alreadyOpen]);
652
+ }
653
+ }
654
+
655
+ const data = VGModal.getOrCreateInstance(target);
656
+ data.toggle(this);
657
+ });
498
658
 
499
659
  EventHandler.on(document, EVENT_KEY_DOM_LOADED_DATA_API, function () {
500
660
  let targetHash = window.location.hash.slice(1);