vgapp 0.8.0 → 0.8.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,4 +1,9 @@
1
- # VEGAS-APP 0.8.0 (Декабрь, 31, 2025)
1
+ # VEGAS-APP 0.8.1 (Январь, 2, 2025)
2
+ * Оптимизирован и дополнен модуль VGLoadMore, см. файл readme.md
3
+
4
+ ---
5
+
6
+ # VEGAS-APP 0.8.0 (Декабрь, 31, 2025)
2
7
  * Полностью переписан модуль VGFiles, см. файл readme.md
3
8
  * Рефакторинг модуля VGLawCookie, см. файл readme.md
4
9
 
@@ -6,5 +6,11 @@
6
6
  "files": {
7
7
  "agree": "Yeah, I agree",
8
8
  "cancel": "Cancel"
9
+ },
10
+ "loadmore": {
11
+ "send": "Uploading...",
12
+ "show": "I'm showing you...",
13
+ "text-ajax": "Upload more",
14
+ "text-more": "Show more"
9
15
  }
10
16
  }
@@ -6,5 +6,11 @@
6
6
  "files": {
7
7
  "agree": "Да",
8
8
  "cancel": "Нет"
9
+ },
10
+ "loadmore": {
11
+ "send": "Загружаю...",
12
+ "show": "Показываю...",
13
+ "text-ajax": "Загрузить еще",
14
+ "text-more": "Показать еще"
9
15
  }
10
16
  }
@@ -1,37 +1,42 @@
1
1
  import BaseModule from "../../base-module";
2
2
  import EventHandler from "../../../utils/js/dom/event";
3
- import {execute, isObject, mergeDeepObject, normalizeData} from "../../../utils/js/functions";
3
+ import { execute, isObject, mergeDeepObject, normalizeData } from "../../../utils/js/functions";
4
4
  import Selectors from "../../../utils/js/dom/selectors";
5
- import {Manipulator} from "../../../utils/js/dom/manipulator";
6
-
7
- const NAME = 'loadmore';
8
- const NAME_KEY = 'vg.loadmore';
5
+ import { Manipulator } from "../../../utils/js/dom/manipulator";
6
+ import {lang_buttons} from "../../../utils/js/components/lang";
9
7
 
8
+ const NAME = 'loadmore';
9
+ const NAME_KEY = 'vg.loadmore';
10
10
  const SELECTOR_DATA_TOGGLE = '[data-vg-toggle="loadmore"]';
11
+ const SELECTOR_DATA_MODULE = '[data-vgloadmore]';
11
12
 
12
- const EVENT_KEY_HIDE = `${NAME_KEY}.hide`;
13
- const EVENT_KEY_HIDDEN = `${NAME_KEY}.hidden`;
14
- const EVENT_KEY_SHOW = `${NAME_KEY}.show`;
15
- const EVENT_KEY_SHOWN = `${NAME_KEY}.shown`;
16
13
  const EVENT_KEY_LOADED = `${NAME_KEY}.loaded`;
17
-
14
+ const EVENT_KEY_BEFORE_LOAD = `${NAME_KEY}.before.load`;
18
15
  const CLASS_NAME_HIDE = 'vg-collapse';
19
16
  const CLASS_NAME_SHOW = 'show';
17
+ const EVENT_KEY_CLICK_DATA_API = `click.${NAME_KEY}.data.api`;
20
18
 
21
- const EVENT_KEY_CLICK_DATA_API = `click.${NAME_KEY}.data.api`;
22
-
23
- class VGLoadMore extends BaseModule{
19
+ class VGLoadMore extends BaseModule {
24
20
  constructor(element, params) {
25
21
  super(element, params);
26
22
 
27
23
  this._params = this._getParams(element, mergeDeepObject({
24
+ lang: document.documentElement.lang || 'ru',
28
25
  limit: 0,
29
26
  offset: 0,
30
27
  output: true,
31
28
  autohide: true,
29
+ animate: false,
30
+ append: 'after',
31
+ mode: 'button',
32
+ threshold: 100,
33
+ debug: false,
34
+ detach: false,
32
35
  button: {
33
36
  text: '',
34
- send: 'Загружаем...',
37
+ send: 'Загружаю...',
38
+ show: 'Показываю...',
39
+ loader: false,
35
40
  classes: []
36
41
  },
37
42
  ajax: {
@@ -41,168 +46,263 @@ class VGLoadMore extends BaseModule{
41
46
  loader: false,
42
47
  once: false,
43
48
  output: false,
44
- },
49
+ data: {}
50
+ }
45
51
  }, params));
46
52
 
47
- this.fOffset = this._params.offset;
53
+ this._observer = null;
54
+ this._isScrollMode = this._params.mode === 'scroll';
55
+ this._isToggleElement = element.hasAttribute('data-vg-toggle');
56
+
57
+ this._params.button.send = lang_buttons(this._params.lang, NAME)['send'];
58
+ this._params.button.show = lang_buttons(this._params.lang, NAME)['show'];
48
59
 
49
60
  if (!this._params.button.text) {
50
- this._params.button.text = this._element.innerHTML;
61
+ this._params.button.text = this._isToggleElement
62
+ ? this._element.innerHTML.trim() || lang_buttons(this._params.lang, NAME)['text-ajax']
63
+ : this._params.ajax.route ? lang_buttons(this._params.lang, NAME)['text-ajax'] : lang_buttons(this._params.lang, NAME)['text-more'];
51
64
  }
52
- }
53
65
 
54
- static get NAME() {
55
- return NAME;
66
+ if (this._isToggleElement) {
67
+ this._initializeAsButton();
68
+ } else {
69
+ this._initializeContainer();
70
+ }
56
71
  }
57
72
 
58
- static get NAME_KEY() {
59
- return NAME_KEY;
60
- }
73
+ static get NAME() { return NAME; }
74
+ static get NAME_KEY() { return NAME_KEY; }
61
75
 
62
- static init(el, callback) {
63
- let id = el.id,
64
- items = normalizeData(el.dataset.elements),
65
- limit = normalizeData(el.dataset.limit),
66
- offset = normalizeData(el.dataset.offset),
67
- output = el.dataset.output || 'true',
68
- autohide = el.dataset.autohide || 'true',
69
- params = el.dataset.params,
70
- buttonParams = normalizeData(el.dataset.button);
71
-
72
- if (!isObject(buttonParams)) {
73
- console.error('Дата атрибут data-button должен быть в формате json и передавать объект');
74
- return;
76
+ _initializeAsButton() {
77
+ if (this._isScrollMode) {
78
+ this._initScrollMode();
75
79
  }
80
+ }
81
+
82
+ _initializeContainer() {
83
+ const { elements: itemClass, limit } = this._params;
84
+ const container = this._element;
85
+ const items = Selectors.findAll(`.${itemClass}`, container);
76
86
 
77
- if (limit < offset) {
78
- console.error('Параметр offset должен быть меньше или равен параметру limit');
87
+ if (!this._params.ajax.route && (items.length <= limit || this._params.offset >= items.length)) {
79
88
  return;
80
89
  }
81
90
 
82
- if (!id && !items && !limit && !offset) return;
91
+ items.forEach((item, i) => {
92
+ item.classList.toggle(CLASS_NAME_SHOW, i < limit);
93
+ item.classList.toggle(CLASS_NAME_HIDE, i >= limit);
94
+ });
95
+
96
+ if (this._params.mode === 'button') {
97
+ this._createAndInsertButton(container);
98
+ } else if (this._isScrollMode) {
99
+ this._initScrollMode();
100
+ }
83
101
 
84
- let itemsElements = [... Selectors.findAll('.' + items, el)];
102
+ if (this._params.offset === 0) {
103
+ this._params.offset = limit;
104
+ }
105
+ }
85
106
 
86
- if (itemsElements.length <= limit) return;
107
+ _createAndInsertButton(container) {
108
+ const button = document.createElement('button');
109
+ const buttonText = normalizeData(container.dataset.buttonText) || this._params.button.text;
87
110
 
88
- itemsElements.forEach((item, i) => {
89
- item.classList.add(CLASS_NAME_HIDE)
90
- if ((i + 1) <= limit) item.classList.add(CLASS_NAME_SHOW)
111
+ const buttonData = {
112
+ limit: this._params.limit,
113
+ offset: this._params.offset,
114
+ output: this._params.output,
115
+ autohide: this._params.autohide,
116
+ animate: this._params.animate,
117
+ append: this._params.append,
118
+ mode: this._params.mode,
119
+ threshold: this._params.threshold,
120
+ debug: this._params.debug,
121
+ detach: this._params.detach,
122
+ elements: this._params.elements,
123
+ 'vg-toggle': 'loadmore',
124
+ target: `#${container.id}`
125
+ };
126
+
127
+ Object.assign(buttonData, normalizeData(container.dataset.params));
128
+
129
+ Object.keys(buttonData).forEach(key => {
130
+ Manipulator.set(button, `data-${key}`, buttonData[key]);
91
131
  });
92
132
 
93
- let button = document.createElement('button');
133
+ button.textContent = buttonText;
134
+ button.classList.add(...this._params.button.classes);
94
135
 
95
- buttonParams.text = normalizeData(el.dataset.buttonText) || 'Показать еще';
136
+ container.parentNode.insertBefore(button, container.nextSibling);
137
+ }
96
138
 
97
- Manipulator.set(button, 'data-limit', limit);
98
- Manipulator.set(button, 'data-offset', offset);
99
- Manipulator.set(button, 'data-output', output);
100
- Manipulator.set(button, 'data-autohide', autohide);
101
- Manipulator.set(button, 'data-elements', items);
102
- Manipulator.set(button, 'data-vg-toggle', 'loadmore');
103
- Manipulator.set(button, 'data-target', '#' + id);
139
+ _initScrollMode() {
140
+ this._setupIntersectionObserver();
141
+ this._observeLastVisibleItem();
142
+ }
104
143
 
105
- if (params) Manipulator.set(button, 'data-autohide', params);
144
+ _setupIntersectionObserver() {
145
+ if (this._observer) return;
146
+
147
+ this._observer = new IntersectionObserver((entries) => {
148
+ entries.forEach(entry => {
149
+ if (entry.isIntersecting) {
150
+ this._params.debug && console.log('[VGLoadMore] Пересечение — вызов toggle');
151
+ this.toggle();
152
+ }
153
+ });
154
+ }, {
155
+ root: null,
156
+ rootMargin: `0px 0px ${this._params.threshold}px 0px`,
157
+ threshold: 0.1
158
+ });
159
+ }
106
160
 
107
- button.innerHTML = buttonParams.text
161
+ _observeLastVisibleItem() {
162
+ if (!this._observer) return;
108
163
 
109
- if ('classes' in buttonParams && buttonParams.classes.length) {
110
- buttonParams.classes.forEach(cl => button.classList.add(cl));
111
- }
164
+ this._observer.disconnect();
112
165
 
113
- el.parentNode.insertBefore(button, el.nextSibling);
166
+ const container = Selectors.find(this._params.target) || this._element.parentNode;
167
+ const items = Selectors.findAll(`.${this._params.elements}`, container);
168
+ const visibleItems = items.filter(item => item.classList.contains(CLASS_NAME_SHOW));
169
+ const lastVisible = visibleItems[visibleItems.length - 1];
114
170
 
115
- execute(callback, [el, button]);
171
+ if (lastVisible instanceof Element) {
172
+ this._observer.observe(lastVisible);
173
+ }
116
174
  }
117
175
 
118
176
  toggle(callback) {
119
- if (this._params.ajax.route) {
120
- this.ajax(callback);
121
- } else {
122
- this.static(callback);
177
+ this._params.debug && console.log('[VGLoadMore] toggle()');
178
+
179
+ if (EventHandler.trigger(this._element, EVENT_KEY_BEFORE_LOAD).defaultPrevented) return;
180
+
181
+ const isButton = this._isToggleElement || (['BUTTON', 'A'].includes(this._element.tagName) && !this._isScrollMode);
182
+ if (isButton) {
183
+ this._element.disabled = true;
184
+ this._element.innerHTML = this._params.ajax.route ? this._params.button.send : this._params.button.show;
123
185
  }
186
+
187
+ this._params.ajax.route ? this.ajax(callback) : this.staticLoad(callback);
124
188
  }
125
189
 
126
190
  ajax(callback) {
127
- this._params.ajax.data = {
128
- limit: this._params.limit,
129
- offset: this._params.offset
191
+ const targetSelector = this._params.ajax.target?.trim();
192
+ let targetEl = null;
193
+
194
+ if (targetSelector) {
195
+ targetEl = Selectors.find(targetSelector);
196
+ } else {
197
+ targetEl = this._element
130
198
  }
131
199
 
132
- if (this._params.button.send) {
133
- this._element.innerHTML = this._params.button.send;
200
+ const originalText = this._params.button.text;
201
+
202
+ if (!targetEl) {
203
+ console.error('[VGLoadMore] target элемент не найден:', this._params.ajax.target);
204
+ return;
134
205
  }
135
206
 
136
- this._route((status, data, target) => {
137
- if ('loader' in this._params.ajax && this._params.ajax.loader) {
138
- let loader = Selectors.find('.vg-loader', target);
139
- if (loader) loader.remove();
140
- }
207
+ this._params.ajax.data = { limit: this._params.limit, offset: this._params.offset };
141
208
 
142
- if ('output' in this._params && this._params.output) {
143
- target.insertAdjacentHTML('beforeend', data.response);
209
+ this._route((status, data, responseTarget) => {
210
+ if (status === 'error' || typeof data?.response !== 'string') return;
211
+
212
+ if (this._params.output) {
213
+ targetEl.insertAdjacentHTML(
214
+ this._params.append === 'after' ? 'beforeend' : 'afterbegin',
215
+ data.response
216
+ );
144
217
  }
145
218
 
146
- this._params.offset = this.counter();
147
- this._element.innerHTML = this._params.button.text;
219
+ this._params.offset += this._params.limit;
220
+ this._restoreElementState(originalText);
221
+ this._observeLastVisibleItem();
148
222
 
149
- if ('autohide' in this._params && this._params.autohide) {
150
- if (!data.response) this._element.remove();
223
+ const noMoreData = !data.response.trim();
224
+ if (this._params.autohide && this._params.detach && noMoreData) {
225
+ this._autohideTrigger();
151
226
  }
152
227
 
153
- EventHandler.trigger(this._element, EVENT_KEY_LOADED, {stats: status, data: data});
154
- execute(callback, [this, data, target, status]);
228
+ EventHandler.trigger(this._element, EVENT_KEY_LOADED, { stats: status, data });
229
+ execute(callback, [this, data, responseTarget, status]);
230
+ }, (error) => {
231
+ this._restoreElementState(originalText);
232
+ console.error('[VGLoadMore] AJAX ошибка', error);
155
233
  });
156
234
  }
157
235
 
158
- static(callback) {
159
- if (!'elements' in this._params && !'target' in this._params) return;
236
+ staticLoad(callback) {
237
+ const container = Selectors.find(this._params.target) || this._element.parentNode;
238
+ const items = Selectors.findAll(`.${this._params.elements}`, container);
239
+ const start = this._params.offset;
240
+ const end = start + this._params.limit;
241
+ const newItems = items.slice(start, end);
160
242
 
161
- let container = Selectors.find(this._params.target),
162
- items = Selectors.findAll('.' + this._params.elements, container);
243
+ if (newItems.length === 0) return;
163
244
 
164
- if (items) {
165
- items.slice(this._params.offset, this._params.offset + this._params.limit).forEach(item => item.classList.add(CLASS_NAME_SHOW));
166
- this._params.offset = this.counter();
245
+ if (this._isToggleElement || (['BUTTON', 'A'].includes(this._element.tagName) && !this._isScrollMode)) {
246
+ this._element.disabled = true;
247
+ this._element.innerHTML = this._params.button.show;
167
248
  }
168
249
 
169
- let itemsHidden = Selectors.findAll('.' + this._params.elements + ':not(.show)', container);
170
-
171
- if (this.remainder(itemsHidden.length)) {
172
- if ('autohide' in this._params && this._params.autohide) {
173
- this._element.remove();
250
+ newItems.forEach(item => {
251
+ item.classList.replace(CLASS_NAME_HIDE, CLASS_NAME_SHOW);
252
+ if (this._params.animate) {
253
+ item.style.opacity = 0;
254
+ requestAnimationFrame(() => {
255
+ item.style.transition = 'opacity 0.3s ease';
256
+ item.style.opacity = 1;
257
+ });
174
258
  }
259
+ });
260
+
261
+ this._params.offset = end;
262
+ this._observeLastVisibleItem();
263
+
264
+ const remaining = items.slice(end);
265
+ if (this._params.autohide && this._params.detach && remaining.length === 0) {
266
+ this._autohideTrigger();
175
267
  }
176
268
 
269
+ this._restoreElementState(this._params.button.text);
177
270
  execute(callback, [this, this._element]);
178
271
  }
179
272
 
180
- counter() {
181
- return this.fOffset + this._params.offset;
273
+ _restoreElementState(text) {
274
+ const isButton = this._isToggleElement || this._element.tagName === 'BUTTON';
275
+ if (isButton && !this._isScrollMode) {
276
+ this._element.disabled = false;
277
+ this._element.innerHTML = text;
278
+ }
279
+ }
280
+
281
+ _autohideTrigger() {
282
+ if (this._isScrollMode) {
283
+ this._observer?.disconnect();
284
+ this._observer = null;
285
+ } else if (this._params.detach && this._element.parentNode) {
286
+ this._element.remove();
287
+ }
182
288
  }
183
289
 
184
- remainder(count) {
185
- return count === 0
290
+ dispose() {
291
+ this._observer?.disconnect();
292
+ super.dispose();
186
293
  }
187
294
  }
188
295
 
189
- EventHandler.on(document, 'DOMContentLoaded', function () {
190
- [... document.querySelectorAll('[data-vgloadmore]')].forEach(el => {
191
- VGLoadMore.init(el);
192
- })
296
+ EventHandler.on(document, 'DOMContentLoaded', () => {
297
+ Selectors.findAll(SELECTOR_DATA_MODULE).forEach(el => {
298
+ !el.dataset.initialized && VGLoadMore.getOrCreateInstance(el);
299
+ el.dataset.initialized = 'true';
300
+ });
193
301
  });
194
302
 
195
- /**
196
- * Data API implementation
197
- */
198
303
  EventHandler.on(document, EVENT_KEY_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
199
- const target = this;
200
-
201
- if (['A', 'AREA'].includes(this.tagName)) event.preventDefault();
202
-
203
- const instance = VGLoadMore.getOrCreateInstance(target);
204
- instance.toggle();
304
+ ['A', 'AREA'].includes(this.tagName) && event.preventDefault();
305
+ VGLoadMore.getOrCreateInstance(this).toggle();
205
306
  });
206
307
 
207
-
208
308
  export default VGLoadMore;
@@ -0,0 +1,145 @@
1
+ # VGLoadMore — Модуль подгрузки контента
2
+
3
+ Модуль **VGLoadMore** позволяет реализовать подгрузку контента по кнопке или при прокрутке (infinite scroll). Поддерживает как статическую подгрузку элементов из DOM, так и AJAX-запросы на сервер. Удобно использовать для лент, списков, галерей и других контейнеров с большим количеством элементов.
4
+
5
+ ---
6
+
7
+ ## ✅ Возможности
8
+
9
+ - 🔘 Подгрузка по **кнопке** или **при прокрутке**
10
+ - 📥 **AJAX-загрузка** с сервера
11
+ - 🧩 **Статическая подгрузка** скрытых элементов из DOM
12
+ - 🎯 Автоматическое скрытие кнопки при достижении конца
13
+ - 🖱 Поддержка `data-vg-toggle` и самостоятельная инициализация
14
+ - 🧪 Гибкая настройка через `data-*` атрибуты или JS
15
+ - 🔄 Анимации показа элементов
16
+ - 🛠 Полная отладка через `data-debug="true"`
17
+ - 🌐 Многоязычная поддержка** (настраиваемые кнопки)
18
+
19
+ ---
20
+
21
+ ## 🌐 Многоязычная поддержка
22
+
23
+ Модуль `VGFiles` полностью поддерживает локализацию интерфейса. Все тексты кнопок.
24
+
25
+ ### ✅ Автоматическое определение языка
26
+
27
+ Язык определяется автоматически:
28
+ - Из атрибута `lang` тега `<html>`: `<html lang="en">`
29
+ - Или через параметр `lang` при инициализации модуля.
30
+
31
+ Если язык не указан — используется **`ru`** (русский).
32
+
33
+ ---
34
+
35
+ ## 🔧 Установка
36
+
37
+ Модуль автоматически инициализируется при наличии атрибутов `data-vgloadmore` или `data-vg-toggle="loadmore"`.
38
+
39
+ ## 🧩 Режимы работы
40
+
41
+ ### 1. Подгрузка по кнопке
42
+ ### 2. Подгрузка при прокрутке
43
+
44
+ → Подгрузка начнётся, когда последний видимый элемент приблизится к нижней границе окна.
45
+
46
+ ---
47
+
48
+ ## ⚙️ Настройки (data-атрибуты)
49
+
50
+ | Атрибут | Описание | По умолчанию |
51
+ |-------|--------|-------------|
52
+ | `data-limit` | Сколько элементов подгружать за раз | `0` |
53
+ | `data-offset` | Начальный сдвиг | `0` |
54
+ | `data-elements` | Класс подгружаемых элементов | — |
55
+ | `data-mode` | `button` или `scroll` | `button` |
56
+ | `data-threshold` | Отступ в px для scroll-режима | `100` |
57
+ | `data-autohide` | Скрывать кнопку при окончании | `true` |
58
+ | `data-detach` | Удалять кнопку из DOM | `false` |
59
+ | `data-animate` | Плавное появление элементов | `false` |
60
+ | `data-append` | `after` или `before` | `after` |
61
+ | `data-debug` | Включить логи в консоль | `false` |
62
+ | `data-button-text` | Текст кнопки | Авто |
63
+
64
+ ---
65
+
66
+ ### AJAX-настройки
67
+
68
+ | Атрибут | Описание | По умолчанию |
69
+ |-------|--------|-------------|
70
+ | `data-ajax-route` | URL для запроса | — |
71
+ | `data-ajax-target` | Куда вставлять ответ | `parent` |
72
+ | `data-ajax-method` | HTTP-метод | `get` |
73
+ | `data-ajax-output` | Вставлять ли ответ | `false` |
74
+
75
+ ---
76
+
77
+ ### Текст кнопки
78
+
79
+ | Состояние | Текст |
80
+ |---------|------|
81
+ | По умолчанию (AJAX) | `Загрузить еще` |
82
+ | По умолчанию (статика) | `Показать еще` |
83
+ | При загрузке (AJAX) | `Загружаю...` |
84
+ | При загрузке (статика) | `Показываю...` |
85
+
86
+ → Настраивается через `data-button='{"send": "...", "show": "..."}'`
87
+
88
+ ---
89
+
90
+ ## 🔌 JavaScript API
91
+
92
+ ### Инициализация
93
+ ```js
94
+ import VGLoadMore from 'app/modules/vgloadmore/js/vgloadmore';
95
+ const instance = VGLoadMore.getOrCreateInstance(element, { limit: 5, ajax: { route: '/api/items' } });
96
+ ```
97
+
98
+ ### Методы
99
+ ```js
100
+ instance.toggle(); // Ручной вызов подгрузки
101
+ instance.dispose(); // Уничтожить инстанс
102
+ ```
103
+ ---
104
+
105
+ ## 📣 События
106
+
107
+ | Событие | Описание |
108
+ |-------|--------|
109
+ | `vg.loadmore.before.load` | Перед подгрузкой |
110
+ | `vg.loadmore.loaded` | После подгрузки |
111
+
112
+ ```js
113
+ element.addEventListener('vg.loadmore.loaded', (e) => { console.log('Загружено:', e.detail.data); });
114
+ ```
115
+
116
+ ---
117
+
118
+ ## 💡 Советы
119
+
120
+ - Используйте класс `vg-collapse`, чтобы изначально скрыть элементы.
121
+ - В `scroll`-режиме кнопка не создаётся.
122
+ - Если `data-ajax-route` указан, модуль всегда использует AJAX.
123
+ - При `data-detach="true"` кнопка удаляется из DOM после окончания.
124
+
125
+ ---
126
+
127
+ ## 🐞 Отладка
128
+
129
+ Включите режим отладки:
130
+ ```html
131
+ data-debug="true"
132
+ ```
133
+ → В консоли появятся логи инициализации, IntersectionObserver и AJAX.
134
+
135
+ ---
136
+
137
+ ## 📝 Лицензия
138
+
139
+ MIT. Свободно использовать и модифицировать.
140
+
141
+ ---
142
+
143
+ 📌 *Разработано в рамках фронтенд-системы VG Modules.*
144
+ > 🚀 Автор: VEGAS STUDIO (vegas-dev.com)
145
+ > 📍 Поддерживается в проектах VEGAS
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vgapp",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "",
5
5
  "author": {
6
6
  "name": "Vegas Studio",