vgapp 0.3.9 → 0.4.1

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,59 +1,78 @@
1
+ # VEGAS-APP 0.4.0 (Май, 287, 2025)
2
+ * Исправлены ошибки в разных модулях
3
+ ## Модуль VGFORMSENDER
4
+ * Добавлен параметр interceptors (перехватчики) для состояний error и/или success, после чего вызывается соответствующий колбек afterError и/или afterSuccess
5
+ * Добавлен параметр delay в группу alert автоматическое закрытие, отсчет в миллисекундах
6
+ ---
7
+
1
8
  # VEGAS-APP 0.3.4 (Май, 20, 2025)
2
9
  ## Новый модуль VGSpy
10
+ ---
3
11
 
4
12
  # VEGAS-APP 0.3.0 (Май, 09, 2025)
5
13
  * Исправлены ошибки
6
14
  ## Модуль VGFORMSENDER
7
15
  * Добавлен параметр pass, будет добавлен глаз к полю с паролем
16
+ ---
8
17
 
9
18
  # VEGAS-APP 0.2.5 (Апрель, 12, 2025)
10
19
  * Исправлены ошибки в разных модулях
11
20
  ## Новый модуль VGRollup
21
+ ---
12
22
 
13
23
  # VEGAS-APP 0.2.4 (Апрель, 8, 2025)
14
24
  ## Модуль VGSidebar & VGModal
15
25
  * Добавлен параметр hash
16
-
17
26
  ## Новый модуль VGToast
27
+ ---
18
28
 
19
29
  # VEGAS-APP 0.2.3 (Апрель, 4, 2025)
20
30
  ## Модуль VGDROPDOWN
21
31
  * Добавлен параметр overflow и backdrop
32
+ ---
22
33
 
23
34
  # VEGAS-APP 0.2.1 (Март, 31, 2025)
24
35
  ## Модуль VGDROPDOWN
25
36
  * Теперь можно открыть выпадающих список через параметр data-vg-target
37
+ ---
26
38
 
27
39
  # VEGAS-APP 0.1.9 (Март, 13, 2025)
28
40
  ## Модуль VGFORMSENDER
29
41
  * Новый параметр timeout. Задержка показа модального окна после закрытия других открытых окон.
42
+ ---
30
43
 
31
44
 
32
45
  # VEGAS-APP 0.1.8 (Март, 13, 2025)
33
46
  ## Модуль VGSelect
34
47
  * Релиз
48
+ ---
35
49
 
36
50
 
37
51
  # VEGAS-APP 0.1.7 (Март, 12, 2025)
38
52
  ## Модуль VGSelect
39
53
  * Исправлены ошибки
54
+ ---
40
55
 
41
56
 
42
57
  # VEGAS-APP 0.1.5 (Март, 11, 2025)
43
58
  ## Модуль VGSelect
44
59
  ### Добавлено:
45
60
  * data-placeholder
61
+ ---
46
62
 
47
63
  ### Исправлено:
48
64
  * Открытие и закрытие списка
65
+ ---
49
66
 
50
67
  ## Для модулей VGSidebar и VGModal
51
68
  ### Добавлено:
52
69
  * Событие после аякс запроса NAME_KEY.loaded
70
+ ---
53
71
 
54
72
 
55
73
  # VEGAS-APP 0.1.4 (Март, 10, 2025)
56
74
  * Перенесен плагин (<a href="https://github.com/vegas-dev/vegas-select">VGSelect</a>) в модуль VGSelect
57
75
  * для модулей VGSidebar и VGModal добавлена возможность собирать параметры с кнопки вызова элемента
58
76
  * в модуль VGSidebar добавлен класс vg-sidebar-open в тело документа
59
- * исправлены некоторые ошибки
77
+ * исправлены некоторые ошибки
78
+ ---
@@ -0,0 +1,5 @@
1
+ // alert
2
+ import "./scss/vgalert.scss";
3
+ import VGAlert from "./js/vgalert";
4
+
5
+ export default VGAlert;
@@ -0,0 +1,3 @@
1
+ import VGCollapse from "./js/vgcollapse";
2
+
3
+ export default VGCollapse;
@@ -0,0 +1,4 @@
1
+ import './scss/vgdatatable.scss'
2
+ import VGDatatable from "./js/vgdatatable";
3
+
4
+ export default VGDatatable;
@@ -0,0 +1,4 @@
1
+ import './scss/vgdropdown.scss';
2
+ import VGDropdown from "./js/vgdropdown";
3
+
4
+ export default VGDropdown;
@@ -0,0 +1,4 @@
1
+ import './scss/vgformsender.scss';
2
+ import VGFormSender from "./js/vgformsender";
3
+
4
+ export default VGFormSender;
@@ -60,6 +60,7 @@ class VGFormSender extends BaseModule {
60
60
  enabled: true,
61
61
  type: 'modal',
62
62
  errors: true,
63
+ delay: 0
63
64
  },
64
65
  ajax: {
65
66
  route: '',
@@ -148,6 +149,7 @@ class VGFormSender extends BaseModule {
148
149
  } else {
149
150
  if (!_this._params.interceptors.error) {
150
151
  _this._alertError(event, data);
152
+ execute(_this._params.callback.afterError, [_this._element, _this, event, data]);
151
153
  } else {
152
154
  execute(_this._params.callback.afterError, [_this._element, _this, event, data]);
153
155
  }
@@ -158,6 +160,7 @@ class VGFormSender extends BaseModule {
158
160
  } else {
159
161
  if (!_this._params.interceptors.success) {
160
162
  _this._alertSuccess(event, data);
163
+ execute(_this._params.callback.afterSuccess, [_this._element, _this, event, data]);
161
164
  } else {
162
165
  execute(_this._params.callback.afterSuccess, [_this._element, _this, event, data]);
163
166
  }
@@ -344,6 +347,12 @@ class VGFormSender extends BaseModule {
344
347
  if ($body) $body.append(_this.setDataRelationStatus(element, status, data, 'modal'));
345
348
 
346
349
  self.toggle();
350
+
351
+ if (_this._params.alert.delay > 0) {
352
+ setTimeout(() => {
353
+ self.hide();
354
+ }, _this._params.alert.delay)
355
+ }
347
356
  });
348
357
  }, _this._params.timeout);
349
358
  }
@@ -362,7 +371,14 @@ class VGFormSender extends BaseModule {
362
371
  _this._element.prepend($collapse);
363
372
  }
364
373
 
365
- VGCollapse.getOrCreateInstance($collapse, {toggle: false}).toggle();
374
+ let collapse = VGCollapse.getOrCreateInstance($collapse, {toggle: false});
375
+ collapse.toggle();
376
+
377
+ if (_this._params.alert.delay > 0) {
378
+ setTimeout(() => {
379
+ collapse.hide();
380
+ }, _this._params.alert.delay)
381
+ }
366
382
  }
367
383
 
368
384
  setDataRelationStatus($element, status, data, type) {
@@ -0,0 +1,4 @@
1
+ import './scss/vglawcookie.scss';
2
+ import VGLawCookie from "./js/vglawcookie";
3
+
4
+ export default VGLawCookie;
@@ -0,0 +1,4 @@
1
+ import './scss/vgmodal.scss';
2
+ import VGModal from "./js/vgmodal";
3
+
4
+ export default VGModal;
@@ -0,0 +1,4 @@
1
+ import './scss/vgnav.scss';
2
+ import VGNav from "./js/vgnav";
3
+
4
+ export default VGNav;
@@ -39,6 +39,7 @@ $nav-drop-map: (
39
39
  z-index: 10,
40
40
  transition: map-get($nav-map, transition),
41
41
  width: 200px,
42
+ max-height: initial,
42
43
  border-color: $border-color,
43
44
  border-radius: 0,
44
45
  padding: 0,
@@ -54,6 +55,7 @@ $nav-drop-mega-map: (
54
55
  padding: 1rem,
55
56
  z-index: 10,
56
57
  transition: map-get($nav-map, transition),
58
+ max-height: initial,
57
59
  );
58
60
 
59
61
  $nav-drop-item-map: (
@@ -64,6 +64,8 @@
64
64
  border: 1px solid var(--vg-nav-drop-border-color);
65
65
  border-radius: var(--vg-nav-drop-border-radius);
66
66
  background-color: var(--vg-nav-drop-bg);
67
+ max-height: var(--vg-nav-drop-max-height);
68
+ overflow: auto;
67
69
 
68
70
  &:not(.show) {
69
71
  display: none;
@@ -0,0 +1,4 @@
1
+ import './scss/vgrollup.scss';
2
+ import VGRollup from "./js/vgrollup";
3
+
4
+ export default VGRollup;
@@ -0,0 +1,4 @@
1
+ import './scss/vgselect.scss';
2
+ import VGSelect from "./js/vgselect";
3
+
4
+ export default VGSelect;
@@ -0,0 +1,4 @@
1
+ import './scss/vgsidebar.scss';
2
+ import VGSidebar from "./js/vgsidebar";
3
+
4
+ export default VGSidebar;
@@ -0,0 +1,3 @@
1
+ import VGSpy from "./js/vgspy";
2
+
3
+ export default VGSpy;
@@ -1,7 +1,6 @@
1
1
  import BaseModule from "../../base-module";
2
+ import {mergeDeepObject, getElement, isDisabled, isVisible} from "../../../utils/js/functions";
2
3
  import EventHandler from "../../../utils/js/dom/event";
3
- import {dismissTrigger} from "../../module-fn";
4
- import {execute, isDisabled, makeRandomString, mergeDeepObject, noop} from "../../../utils/js/functions";
5
4
  import Selectors from "../../../utils/js/dom/selectors";
6
5
 
7
6
  /**
@@ -9,32 +8,52 @@ import Selectors from "../../../utils/js/dom/selectors";
9
8
  */
10
9
  const NAME = 'spy';
11
10
  const NAME_KEY = 'vg.spy';
11
+ const EVENT_KEY = `.${NAME_KEY}`
12
+ const DATA_API_KEY = '.data-api'
13
+
14
+ const EVENT_ACTIVATE = `activate${EVENT_KEY}`
15
+ const EVENT_CLICK = `click${EVENT_KEY}`
16
+ const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`
17
+
18
+ const CLASS_NAME_DROPDOWN_ITEM = 'vg-dropdown-item'
19
+ const CLASS_NAME_ACTIVE = 'active'
20
+
21
+ const SELECTOR_DATA_SPY = '[data-vg-toggle="spy"]'
22
+ const SELECTOR_TARGET_LINKS = '[href]'
23
+ const SELECTOR_NAV_LIST_GROUP = '.nav, .list-group'
24
+ const SELECTOR_NAV_LINKS = '.nav-link'
25
+ const SELECTOR_NAV_ITEMS = '.nav-item'
26
+ const SELECTOR_LIST_ITEMS = '.list-group-item'
27
+ const SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`
28
+ const SELECTOR_DROPDOWN = '.vg-dropdown'
29
+ const SELECTOR_DROPDOWN_TOGGLE = '[data-vg-toggle="dropdown"]'
12
30
 
13
31
 
14
32
  class VGSpy extends BaseModule {
15
- constructor(element, params = {}) {
33
+ constructor(element, params) {
16
34
  super(element, params);
17
35
 
18
36
  this._params = this._getParams(element, mergeDeepObject({
19
- speed: 1500,
20
- offset: 0,
21
- easing: 'easeInOutSine', // easeInOutSine:easeOutSine:easeInOutQuint
22
- isState: false,
23
- onActive: noop,
24
- onClick: noop,
25
- activeClass: ['active']
37
+ offset: null, // TODO: v6 @deprecated, keep it for backwards compatibility reasons
38
+ rootMargin: '0px 0px -25%',
39
+ smoothScroll: true,
40
+ target: this._element,
41
+ threshold: [0.1, 0.5, 1]
26
42
  }, params));
27
43
 
28
- this.isClick = false;
29
-
30
- this.links = this._element.querySelectorAll('[data-vg-target]').length ?
31
- this._element.querySelectorAll('[data-vg-target]') :
32
- this._element.querySelectorAll('a')
33
- ;
44
+ this._targetLinks = new Map()
45
+ this._observableSections = new Map()
46
+ this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element
47
+ this._activeTarget = null
48
+ this._observer = null
49
+ this._previousScrollData = {
50
+ visibleEntryTop: 0,
51
+ parentScrollTop: 0
52
+ }
53
+ this._params = this._configAfterMerge(this._params);
54
+ console.log(this._params)
34
55
 
35
- this.onLoad();
36
- this.onClick();
37
- this.onScroll();
56
+ this.refresh();
38
57
  }
39
58
 
40
59
  static get NAME() {
@@ -45,198 +64,166 @@ class VGSpy extends BaseModule {
45
64
  return NAME_KEY
46
65
  }
47
66
 
48
- onLoad() {
49
- let _this = this;
67
+ refresh() {
68
+ this._initializeTargetsAndObservables()
69
+ this._maybeEnableSmoothScroll()
50
70
 
51
- document.addEventListener('DOMContentLoaded', function () {
52
- _this.setCurrentSection(null);
53
- });
71
+ if (this._observer) {
72
+ this._observer.disconnect()
73
+ } else {
74
+ this._observer = this._getNewObserver()
75
+ }
76
+
77
+ for (const section of this._observableSections.values()) {
78
+ this._observer.observe(section)
79
+ }
54
80
  }
55
81
 
56
- onClick() {
57
- let _this = this;
82
+ dispose() {
83
+ this._observer.disconnect()
84
+ super.dispose()
85
+ }
58
86
 
59
- _this.links.forEach(el => {
60
- if (el) {
61
- el.onclick = function (e) {
62
- execute(_this._params.onClick, [e, this])
63
- _this.setCurrentSection(this);
87
+ _configAfterMerge(param) {
88
+ param.target = getElement(param.target) || document.body
89
+ param.rootMargin = param.offset ? `${param.offset}px 0px -30%` : param.rootMargin
64
90
 
65
- return false;
66
- }
67
- }
68
- });
91
+ if (typeof param.threshold === 'string') {
92
+ param.threshold = param.threshold.split(',').map(value => Number.parseFloat(value))
93
+ }
94
+
95
+ return param
69
96
  }
70
97
 
71
- onScroll() {
72
- let _this = this;
98
+ _maybeEnableSmoothScroll() {
99
+ if (!this._params.smoothScroll) {
100
+ return
101
+ }
73
102
 
74
- if (!_this.isClick) {
75
- window.onscroll = function () {
76
- _this.setCurrentSection(null);
103
+ EventHandler.off(this._params.target, EVENT_CLICK)
104
+
105
+ EventHandler.on(this._params.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => {
106
+ const observableSection = this._observableSections.get(event.target.hash)
107
+ if (observableSection) {
108
+ event.preventDefault()
109
+ const root = this._rootElement || window
110
+ const height = observableSection.offsetTop - this._element.offsetTop
111
+ if (root.scrollTo) {
112
+ root.scrollTo({ top: height, behavior: 'smooth' })
113
+ return
114
+ }
115
+ root.scrollTop = height
77
116
  }
78
- }
117
+ })
79
118
  }
80
119
 
81
- setCurrentSection($link = null) {
82
- this.removeCurrentActive();
120
+ _getNewObserver() {
121
+ const options = {
122
+ root: this._rootElement,
123
+ threshold: this._params.threshold,
124
+ rootMargin: this._params.rootMargin
125
+ }
83
126
 
84
- if (this._params.isState) {
85
- // TODO не тестили
86
- let target = window.location.hash;
87
- if (target) {
88
- let $element = document.querySelector('[href="'+ target +'"]') ||
89
- document.querySelector('[href="\/' + target +'"]') ||
90
- document.querySelector('[data-vg-target="'+ target.replace('#', '') +'"]') || null;
127
+ return new IntersectionObserver(entries => this._observerCallback(entries), options)
128
+ }
91
129
 
92
- if ($element !== null) {
93
- $link = $element;
94
- }
95
- }
130
+ _observerCallback(entries) {
131
+ const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`);
132
+
133
+ const activate = entry => {
134
+ this._previousScrollData.visibleEntryTop = entry.target.offsetTop;
135
+ this._process(targetElement(entry));
96
136
  }
97
137
 
98
- if ($link) {
99
- let target = this.attributes($link, 'target'),
100
- offset = this.attributes($link, 'offset'),
101
- section = document.getElementById(target);
102
-
103
- if (section) {
104
- let scrollTargetY = section.offsetTop + (offset) + (this._params.offset),
105
- scrollY = window.scrollY || document.documentElement.scrollTop,
106
- speed = this._params.speed,
107
- easing = this._params.easing,
108
- currentTime = 0;
109
-
110
- this.removeCurrentActive();
111
- this.setActive($link, section);
112
-
113
- let time = Math.max(.1, Math.min(Math.abs(scrollY - scrollTargetY) / speed, .8)),
114
- easingEquations = {
115
- easeOutSine: function (pos) {
116
- return Math.sin(pos * (Math.PI / 2));
117
- },
118
- easeInOutSine: function (pos) {
119
- return (-0.5 * (Math.cos(Math.PI * pos) - 1));
120
- },
121
- easeInOutQuint: function (pos) {
122
- if ((pos /= 0.5) < 1) {
123
- return 0.5 * Math.pow(pos, 5);
124
- }
125
- return 0.5 * (Math.pow((pos - 2), 5) + 2);
126
- }
127
- };
128
-
129
- window.requestAnimFrame = (function(){
130
- return window.requestAnimationFrame ||
131
- window.webkitRequestAnimationFrame ||
132
- window.mozRequestAnimationFrame ||
133
- function( callback ){
134
- window.setTimeout(callback, 1000 / 60);
135
- };
136
- })();
137
-
138
- function move() {
139
- currentTime += 1 / 60;
140
-
141
- let p = currentTime / time,
142
- t = easingEquations[easing](p);
143
-
144
- if (p < 1) {
145
- requestAnimFrame(move);
146
- window.scrollTo(0, scrollY + ((scrollTargetY - scrollY) * t));
147
- } else {
148
- window.scrollTo(0, scrollTargetY);
149
- }
150
- }
138
+ const parentScrollTop = (this._rootElement || document.documentElement).scrollTop
139
+ const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop
140
+ this._previousScrollData.parentScrollTop = parentScrollTop
151
141
 
152
- move();
142
+ for (const entry of entries) {
143
+ if (!entry.isIntersecting) {
144
+ this._activeTarget = null
145
+ this._clearActiveClass(targetElement(entry))
153
146
 
154
- this.isClick = false;
147
+ continue
155
148
  }
156
- } else {
157
- for (let i = 0; i < this.links.length; i++) {
158
- let target = this.attributes(this.links[i], 'target'),
159
- offset = this.attributes(this.links[i], 'offset'),
160
- section = document.getElementById(target);
161
-
162
- if (section) {
163
- let start = section.offsetTop + (offset) + (this._params.offset),
164
- end = start + section.offsetHeight,
165
- currentPosition = (document.documentElement.scrollTop || document.body.scrollTop),
166
- isInView = currentPosition >= start && currentPosition < end;
167
-
168
- if (isInView) {
169
- this.removeCurrentActive({ignore: this.links[i]});
170
- this.setActive(this.links[i], section);
171
- }
149
+
150
+ const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop
151
+ if (userScrollsDown && entryIsLowerThanPrevious) {
152
+ activate(entry)
153
+ if (!parentScrollTop) {
154
+ return
172
155
  }
156
+
157
+ continue
158
+ }
159
+
160
+ if (!userScrollsDown && !entryIsLowerThanPrevious) {
161
+ activate(entry)
173
162
  }
174
163
  }
175
164
  }
176
165
 
177
- setActive($link, $section) {
178
- const isActive = this._params.activeClass.every(function (value){
179
- return $link.classList.contains(value);
180
- });
166
+ _initializeTargetsAndObservables() {
167
+ this._targetLinks = new Map();
168
+ this._observableSections = new Map();
181
169
 
182
- if (this._params.isState) {
183
- let text = this.attributes($link, 'text'),
184
- target = this.attributes($link, 'target');
170
+ const targetLinks = Selectors.findAll(SELECTOR_TARGET_LINKS, this._params.target);
185
171
 
186
- history.pushState("", document.title + text, '#' + target);
187
- }
188
-
189
- if (!isActive) {
190
- if ($section) {
191
- $section.classList.add(...this._params.activeClass);
172
+ for (const anchor of targetLinks) {
173
+ if (!anchor.hash || isDisabled(anchor)) {
174
+ continue
192
175
  }
193
176
 
194
- if ($link) {
195
- $link.classList.add(...this._params.activeClass);
196
- }
177
+ const observableSection = Selectors.find(decodeURI(anchor.hash));
197
178
 
198
- execute(this._params.onActive, [$link, $section]);
179
+ if (isVisible(observableSection)) {
180
+ this._targetLinks.set(decodeURI(anchor.hash), anchor)
181
+ this._observableSections.set(anchor.hash, observableSection)
182
+ }
199
183
  }
200
184
  }
201
185
 
202
- removeCurrentActive(options = { ignore: null }) {
203
- for (let i = 0; i < this.links.length; i++) {
204
- let target = this.attributes(this.links[i], 'target'),
205
- section = document.getElementById(target);
206
-
207
- if ((options.ignore !== this.links[i]) && section) {
208
- this.links[i].classList.remove(...this._params.activeClass);
209
- section.classList.remove(...this._params.activeClass);
210
- }
186
+ _process(target) {
187
+ if (this._activeTarget === target) {
188
+ return
211
189
  }
212
- }
213
190
 
214
- attributes(self, prop = '') {
215
- let target = self.getAttribute('href') || self.dataset.vgTarget;
191
+ this._clearActiveClass(this._params.target)
192
+ this._activeTarget = target
193
+ target.classList.add(CLASS_NAME_ACTIVE)
194
+ this._activateParents(target)
216
195
 
217
- if (target !== 'undefined' && target.indexOf('#') !== -1) {
218
- target = target.replace(/(^.+)#/gm, '');
196
+ EventHandler.trigger(this._element, EVENT_ACTIVATE, { relatedTarget: target })
197
+ }
219
198
 
220
- if (target.indexOf('#') !== -1) {
221
- target = target.replace('#', '');
222
- }
223
- } else if (target !== 'undefined' && target.indexOf('#') === -1) {
224
- target = ''
199
+ _activateParents(target) {
200
+ if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {
201
+ Selectors.find(SELECTOR_DROPDOWN_TOGGLE, target.closest(SELECTOR_DROPDOWN))
202
+ .classList.add(CLASS_NAME_ACTIVE)
203
+ return
225
204
  }
226
205
 
227
- let offset = self.dataset.vgOffset ? parseInt(self.dataset.vgOffset) : 0;
228
- let text = self.innerHTML;
206
+ for (const listGroup of Selectors.parents(target, SELECTOR_NAV_LIST_GROUP)) {
207
+ for (const item of Selectors.prev(listGroup, SELECTOR_LINK_ITEMS)) {
208
+ item.classList.add(CLASS_NAME_ACTIVE)
209
+ }
210
+ }
211
+ }
229
212
 
230
- if (prop === 'target') return target;
231
- if (prop === 'offset') return offset;
232
- if (prop === 'text') return text;
213
+ _clearActiveClass(parent) {
214
+ parent.classList.remove(CLASS_NAME_ACTIVE)
233
215
 
234
- return {
235
- target: target,
236
- offset: offset,
237
- text: text
238
- };
216
+ const activeNodes = Selectors.findAll(`${SELECTOR_TARGET_LINKS}.${CLASS_NAME_ACTIVE}`, parent);
217
+ for (const node of activeNodes) {
218
+ node.classList.remove(CLASS_NAME_ACTIVE)
219
+ }
239
220
  }
240
221
  }
241
222
 
242
- export default VGSpy;
223
+ EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
224
+ for (const spy of Selectors.findAll(SELECTOR_DATA_SPY)) {
225
+ VGSpy.getOrCreateInstance(spy)
226
+ }
227
+ })
228
+
229
+ export default VGSpy;
@@ -0,0 +1,4 @@
1
+ import './scss/vgtoast.scss'
2
+ import VGToast from "./js/vgtoast";
3
+
4
+ export default VGToast;