vgapp 1.1.0 → 1.1.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.
@@ -88,7 +88,13 @@ class VGRollup extends BaseModule {
88
88
  button: {
89
89
  enabled: true,
90
90
  more: "Показать",
91
- less: "Свернуть"
91
+ less: "Свернуть",
92
+ classes: ''
93
+ },
94
+ callbacks: {
95
+ init: () => {},
96
+ expand: () => {},
97
+ collapse: () => {}
92
98
  }
93
99
  }, params));
94
100
 
@@ -255,6 +261,8 @@ class VGRollup extends BaseModule {
255
261
  } else if (content === 'elements') {
256
262
  this._setupElementsContent(element, elementClass, cnt, fade, transition, isEllipsis, isButton, showNum);
257
263
  }
264
+
265
+ execute(this._params.callbacks.init, [element, this])
258
266
  }
259
267
 
260
268
  /**
@@ -336,7 +344,7 @@ class VGRollup extends BaseModule {
336
344
 
337
345
  const btnTextMore = this._params.button.more;
338
346
  const btnHTML = `<div class="${this.classes.button}">
339
- <a href="#" aria-expanded="false" data-vg-toggle="rollup" data-vg-target="#${element.id}">
347
+ <a href="#" aria-expanded="false" data-vg-toggle="rollup" class="${this._params.button.classes}" data-vg-target="#${element.id}">
340
348
  ${btnTextMore}${textNum}
341
349
  </a>
342
350
  </div>`;
@@ -369,6 +377,8 @@ class VGRollup extends BaseModule {
369
377
 
370
378
  if (this._params.fade) Classes.add(el, this.classes.fade);
371
379
  if (this._params.transition) Classes.add(el, this.classes.transition);
380
+
381
+ execute(this._params.callbacks.expand, [el, this])
372
382
  } else if (content === 'elements') {
373
383
  const items = Selectors.findAll('.' + this._params.elements, el);
374
384
  items.forEach((item, index) => {
@@ -376,6 +386,8 @@ class VGRollup extends BaseModule {
376
386
  Classes.add(item, CLASS_NAME_HIDE);
377
387
  }
378
388
  });
389
+
390
+ execute(this._params.callbacks.collapse, [el, items, this])
379
391
  }
380
392
 
381
393
  Classes.add(el, this.classes.container);
@@ -388,6 +400,8 @@ class VGRollup extends BaseModule {
388
400
  const items = Selectors.findAll('.' + this._params.elements, el);
389
401
  items.forEach(item => Classes.remove(item, CLASS_NAME_HIDE));
390
402
  }
403
+
404
+ execute(this._params.callbacks.expand, [el, this])
391
405
  }
392
406
  }
393
407
 
@@ -46,12 +46,13 @@ const EVENT_KEY_CLEAR = `${NAME_KEY}.clear`;
46
46
  const EVENT_KEY_ERROR = `${NAME_KEY}.error`;
47
47
  const EVENT_KEY_LOAD_NEXT = `${NAME_KEY}.loadNext`;
48
48
 
49
- const SELECTOR_DATA_TOGGLE = '[data-vg-toggle="select"]';
50
- const SELECTOR_CURRENT = `.${CLASS_NAME_CURRENT}`;
51
- const SELECTOR_DROPDOWN = `.${CLASS_NAME_DROPDOWN}`;
52
- const SELECTOR_SEARCH_INPUT = `.${CLASS_NAME_SEARCH} input`;
53
- const SELECTOR_LIST = `.${CLASS_NAME_LIST}`;
54
- const SELECTOR_LOAD_MORE_BTN = `.${CLASS_NAME_LOAD_MORE}`;
49
+ const SELECTOR_DATA_TOGGLE = '[data-vg-toggle="select"]';
50
+ const SELECTOR_CURRENT = `.${CLASS_NAME_CURRENT}`;
51
+ const SELECTOR_DROPDOWN = `.${CLASS_NAME_DROPDOWN}`;
52
+ const SELECTOR_SEARCH_INPUT = `.${CLASS_NAME_SEARCH} input`;
53
+ const SELECTOR_LIST = `.${CLASS_NAME_LIST}`;
54
+ const SELECTOR_LOAD_MORE_BTN = `.${CLASS_NAME_LOAD_MORE}`;
55
+ const DATA_ATTR_COPY_EXCLUDE_DEFAULT = ['inited', 'updating', 'exclude'];
55
56
 
56
57
  /**
57
58
  * Класс VGSelect
@@ -89,6 +90,7 @@ class VGSelect extends BaseModule {
89
90
  },
90
91
  close: true,
91
92
  tree: false,
93
+ exclude: '',
92
94
  placeholder: '',
93
95
  onInit: null,
94
96
  onShow: null,
@@ -147,46 +149,46 @@ class VGSelect extends BaseModule {
147
149
  * @param {HTMLElement} drop - Контейнер выпадающего списка
148
150
  * @returns {HTMLElement} - Обновлённый список
149
151
  */
150
- static buildListOptions(selector, drop, params = {}) {
151
- let list = drop.querySelector(`.${CLASS_NAME_LIST}`);
152
- if (!list) {
153
- list = document.createElement('ul');
152
+ static buildListOptions(selector, drop, params = {}) {
153
+ let list = drop.querySelector(`.${CLASS_NAME_LIST}`);
154
+ if (!list) {
155
+ list = document.createElement('ul');
154
156
  Classes.add(list, CLASS_NAME_LIST);
155
157
  drop.appendChild(list);
156
158
  } else {
157
159
  list.innerHTML = '';
158
160
  }
159
161
 
160
- const optGroups = Selectors.findAll('optgroup', selector);
161
- const fragment = document.createDocumentFragment();
162
- const isTree = this._isTreeEnabled(selector, params);
163
-
164
- if (optGroups.length > 0) {
165
- optGroups.forEach(optGroup => {
166
- const ol = document.createElement('ol');
167
- Classes.add(ol, CLASS_NAME_OPTGROUP);
162
+ const optGroups = Selectors.findAll('optgroup', selector);
163
+ const fragment = document.createDocumentFragment();
164
+ const isTree = this._isTreeEnabled(selector, params);
165
+
166
+ if (optGroups.length > 0) {
167
+ optGroups.forEach(optGroup => {
168
+ const ol = document.createElement('ol');
169
+ Classes.add(ol, CLASS_NAME_OPTGROUP);
168
170
 
169
171
  const label = document.createElement('li');
170
- label.textContent = optGroup.label.trim();
171
- Classes.add(label, CLASS_NAME_OPTGROUP_TITLE);
172
- ol.appendChild(label);
173
-
174
- VGSelect._createListItems(Selectors.findAll('option', optGroup), ol, selector, {
175
- tree: isTree,
176
- depth: isTree ? 1 : 0,
177
- });
178
- fragment.appendChild(ol);
179
- });
180
- list.appendChild(fragment);
181
- } else {
182
- VGSelect._createListItems(selector.options, list, selector, {
183
- tree: isTree,
184
- depth: 0,
185
- });
186
- }
187
-
188
- return list;
189
- }
172
+ label.textContent = optGroup.label.trim();
173
+ Classes.add(label, CLASS_NAME_OPTGROUP_TITLE);
174
+ ol.appendChild(label);
175
+
176
+ VGSelect._createListItems(Selectors.findAll('option', optGroup), ol, selector, {
177
+ tree: isTree,
178
+ depth: isTree ? 1 : 0,
179
+ });
180
+ fragment.appendChild(ol);
181
+ });
182
+ list.appendChild(fragment);
183
+ } else {
184
+ VGSelect._createListItems(selector.options, list, selector, {
185
+ tree: isTree,
186
+ depth: 0,
187
+ });
188
+ }
189
+
190
+ return list;
191
+ }
190
192
 
191
193
  /**
192
194
  * Создаёт <li> элементы из списка <option>
@@ -195,13 +197,13 @@ class VGSelect extends BaseModule {
195
197
  * @param {HTMLSelectElement} selector - Исходный <select>
196
198
  * @private
197
199
  */
198
- static _createListItems(options, parent, selector, config = {}) {
199
- const frag = document.createDocumentFragment();
200
- const baseDepth = Number.isInteger(config.depth) ? config.depth : 0;
201
- const treeEnabled = !!config.tree;
202
-
203
- [...options].forEach((option) => {
204
- if (option.hidden) return;
200
+ static _createListItems(options, parent, selector, config = {}) {
201
+ const frag = document.createDocumentFragment();
202
+ const baseDepth = Number.isInteger(config.depth) ? config.depth : 0;
203
+ const treeEnabled = !!config.tree;
204
+
205
+ [...options].forEach((option) => {
206
+ if (option.hidden) return;
205
207
 
206
208
  // value атрибута может не быть или он может быть пустым -> для маппинга используем индекс
207
209
  const rawValueAttr = option.getAttribute('value'); // null если атрибута нет
@@ -212,16 +214,16 @@ class VGSelect extends BaseModule {
212
214
  const li = document.createElement('li');
213
215
  li.textContent = text;
214
216
  li.dataset.index = String(option.index);
215
- li.dataset.value = value;
216
- li.classList.add(CLASS_NAME_OPTION);
217
- Manipulator.set(li, 'data-vg-toggle', 'select-option');
218
- if (treeEnabled) {
219
- const optionLevelRaw = option.dataset.level;
220
- const optionLevel = Number.isFinite(Number(optionLevelRaw)) ? parseInt(optionLevelRaw, 10) : null;
221
- const level = Math.max(0, optionLevel == null ? baseDepth : optionLevel);
222
- li.dataset.level = String(level);
223
- li.style.paddingLeft = `${16 + (level * 16)}px`;
224
- }
217
+ li.dataset.value = value;
218
+ li.classList.add(CLASS_NAME_OPTION);
219
+ Manipulator.set(li, 'data-vg-toggle', 'select-option');
220
+ if (treeEnabled) {
221
+ const optionLevelRaw = option.dataset.level;
222
+ const optionLevel = Number.isFinite(Number(optionLevelRaw)) ? parseInt(optionLevelRaw, 10) : null;
223
+ const level = Math.max(0, optionLevel == null ? baseDepth : optionLevel);
224
+ li.dataset.level = String(level);
225
+ li.style.paddingLeft = `${16 + (level * 16)}px`;
226
+ }
225
227
 
226
228
  // Раньше подсветка зависела от "явно выбранных" option (атрибут selected),
227
229
  // из-за этого UI мог показывать placeholder, но DOM считал, что выбрана 1-я опция.
@@ -249,9 +251,9 @@ class VGSelect extends BaseModule {
249
251
  frag.appendChild(li);
250
252
  });
251
253
 
252
- parent.appendChild(frag);
253
- }
254
-
254
+ parent.appendChild(frag);
255
+ }
256
+
255
257
  static _isTreeEnabled(selector, params = {}) {
256
258
  if (typeof params.tree === 'boolean') return params.tree;
257
259
  if (selector?.dataset && typeof selector.dataset.tree !== 'undefined') {
@@ -260,6 +262,26 @@ class VGSelect extends BaseModule {
260
262
 
261
263
  return false;
262
264
  }
265
+
266
+ static _getDataAttrCopyExclusions(selector, params = {}) {
267
+ const rawExclude = typeof params.exclude === 'string'
268
+ ? params.exclude
269
+ : (selector.dataset.exclude || '');
270
+ const customExcluded = rawExclude
271
+ .split(',')
272
+ .map(item => this._normalizeDataAttrKey(item))
273
+ .filter(Boolean);
274
+
275
+ return new Set([...DATA_ATTR_COPY_EXCLUDE_DEFAULT, ...customExcluded]);
276
+ }
277
+
278
+ static _normalizeDataAttrKey(value) {
279
+ return String(value || '')
280
+ .trim()
281
+ .replace(/^data-/, '')
282
+ .toLowerCase()
283
+ .replace(/-([a-z0-9])/g, (_, chr) => chr.toUpperCase());
284
+ }
263
285
 
264
286
  /**
265
287
  * Проверяет, является ли значение "пустым" (соответствует placeholder)
@@ -317,12 +339,14 @@ class VGSelect extends BaseModule {
317
339
  container.classList.add('disabled');
318
340
  }
319
341
 
320
- const elData = Manipulator.get(selector);
321
- if (!isEmptyObj(elData)) {
322
- Object.keys(elData).forEach(key => {
323
- Manipulator.set(container, `data-${key}`, elData[key]);
324
- });
325
- }
342
+ const elData = Manipulator.get(selector);
343
+ if (!isEmptyObj(elData)) {
344
+ const excludeDataAttrs = this._getDataAttrCopyExclusions(selector, params);
345
+ Object.keys(elData).forEach(key => {
346
+ if (excludeDataAttrs.has(key)) return;
347
+ Manipulator.set(container, `data-${key}`, elData[key]);
348
+ });
349
+ }
326
350
 
327
351
  const placeholder = selector.dataset.placeholder || '';
328
352
  const isMultiple = selector.multiple;
@@ -339,11 +363,11 @@ class VGSelect extends BaseModule {
339
363
  tags.classList.add(CLASS_NAME_TAGS);
340
364
  current.appendChild(tags);
341
365
 
342
- const input = document.createElement('input');
343
- input.type = 'text';
344
- input.className = 'vg-select-multiple-input';
345
- input.style.cssText = 'border:0;outline:none;background:transparent;padding:0;margin:0;min-width:1px;width:1px;height:1px;line-height:1;font:inherit;opacity:0;';
346
- tags.appendChild(input);
366
+ const input = document.createElement('input');
367
+ input.type = 'text';
368
+ input.className = 'vg-select-multiple-input';
369
+ input.style.cssText = 'border:0;outline:none;background:transparent;padding:0;margin:0;min-width:1px;width:1px;height:1px;line-height:1;font:inherit;opacity:0;';
370
+ tags.appendChild(input);
347
371
 
348
372
  input.addEventListener('focus', () => {
349
373
  const inst = VGSelect.getInstance(input.closest(`.${CLASS_NAME_CONTAINER}`));
@@ -368,7 +392,7 @@ class VGSelect extends BaseModule {
368
392
  dropdown.classList.add(CLASS_NAME_DROPDOWN);
369
393
  container.appendChild(dropdown);
370
394
 
371
- this.buildListOptions(selector, dropdown, params);
395
+ this.buildListOptions(selector, dropdown, params);
372
396
 
373
397
  selector.insertAdjacentElement('afterend', container);
374
398
  selector.dataset.inited = 'true';
@@ -377,6 +401,8 @@ class VGSelect extends BaseModule {
377
401
  this.updateUI(selector);
378
402
  const instance = VGSelect.getInstance(container);
379
403
 
404
+ console.log(instance);
405
+
380
406
  let searchInput = null;
381
407
  if (Manipulator.has(selector, 'data-search-enabled')) {
382
408
  const search = document.createElement('div');
@@ -571,7 +597,7 @@ class VGSelect extends BaseModule {
571
597
  }
572
598
 
573
599
  const drop = this._element.querySelector(SELECTOR_DROPDOWN);
574
- VGSelect.buildListOptions(select, drop, this._params);
600
+ VGSelect.buildListOptions(select, drop, this._params);
575
601
  VGSelect.updateUI(select);
576
602
  }
577
603
 
@@ -1131,13 +1157,13 @@ class VGSelect extends BaseModule {
1131
1157
  optionsData = data.results;
1132
1158
  }
1133
1159
 
1134
- if (!Array.isArray(optionsData)) {
1135
- instance?._triggerEvent(EVENT_KEY_ERROR, { error: 'Invalid data format: expected array' });
1136
- return;
1137
- }
1138
- const treeEnabled = this._isTreeEnabled(select, instance?._params || {});
1139
-
1140
- if (!preserve) {
1160
+ if (!Array.isArray(optionsData)) {
1161
+ instance?._triggerEvent(EVENT_KEY_ERROR, { error: 'Invalid data format: expected array' });
1162
+ return;
1163
+ }
1164
+ const treeEnabled = this._isTreeEnabled(select, instance?._params || {});
1165
+
1166
+ if (!preserve) {
1141
1167
  // Удаление только не помеченных как data-preserve
1142
1168
  [...select.querySelectorAll('option')].forEach(option => {
1143
1169
  const parentOptGroup = option.closest('optgroup');
@@ -1151,62 +1177,62 @@ class VGSelect extends BaseModule {
1151
1177
  if (og.children.length === 0 && !og.hasAttribute('data-preserve')) {
1152
1178
  og.remove();
1153
1179
  }
1154
- });
1155
- }
1156
-
1157
- const appendOption = (item, parent, level = null) => {
1158
- const option = document.createElement('option');
1159
- const hasChildren = Array.isArray(item.children) && item.children.length > 0;
1160
-
1161
- option.value = item.id || '';
1162
- option.textContent = item.text || '';
1163
- if (item.selected) option.selected = true;
1164
- if (item.disabled) option.disabled = true;
1165
- if (treeEnabled && Number.isInteger(level)) {
1166
- option.setAttribute('data-level', String(level));
1167
- if (hasChildren && !option.value) {
1168
- option.disabled = true;
1169
- }
1170
- }
1171
-
1172
- const dataAttrs = Object.keys(item).filter(k => !['id', 'text', 'selected', 'disabled', 'children'].includes(k));
1173
- dataAttrs.forEach(key => {
1174
- option.setAttribute(`data-${key}`, item[key]);
1175
- });
1176
-
1177
- parent.appendChild(option);
1178
- };
1179
-
1180
- const appendTreeOptions = (items, parent, level = 0) => {
1181
- items.forEach(item => {
1182
- appendOption(item, parent, level);
1183
- if (Array.isArray(item.children) && item.children.length > 0) {
1184
- appendTreeOptions(item.children, parent, level + 1);
1185
- }
1186
- });
1187
- };
1188
-
1189
- if (treeEnabled) {
1190
- appendTreeOptions(optionsData, select, 0);
1191
- } else {
1192
- optionsData.forEach(item => {
1193
- if (item.children && Array.isArray(item.children)) {
1194
- const optgroup = document.createElement('optgroup');
1195
- optgroup.label = item.text || '';
1196
- if (item.disabled) optgroup.disabled = true;
1197
-
1198
- item.children.forEach(child => appendOption(child, optgroup));
1199
-
1200
- select.appendChild(optgroup);
1201
- } else {
1202
- appendOption(item, select);
1203
- }
1204
- });
1205
- }
1180
+ });
1181
+ }
1182
+
1183
+ const appendOption = (item, parent, level = null) => {
1184
+ const option = document.createElement('option');
1185
+ const hasChildren = Array.isArray(item.children) && item.children.length > 0;
1186
+
1187
+ option.value = item.id || '';
1188
+ option.textContent = item.text || '';
1189
+ if (item.selected) option.selected = true;
1190
+ if (item.disabled) option.disabled = true;
1191
+ if (treeEnabled && Number.isInteger(level)) {
1192
+ option.setAttribute('data-level', String(level));
1193
+ if (hasChildren && !option.value) {
1194
+ option.disabled = true;
1195
+ }
1196
+ }
1197
+
1198
+ const dataAttrs = Object.keys(item).filter(k => !['id', 'text', 'selected', 'disabled', 'children'].includes(k));
1199
+ dataAttrs.forEach(key => {
1200
+ option.setAttribute(`data-${key}`, item[key]);
1201
+ });
1202
+
1203
+ parent.appendChild(option);
1204
+ };
1205
+
1206
+ const appendTreeOptions = (items, parent, level = 0) => {
1207
+ items.forEach(item => {
1208
+ appendOption(item, parent, level);
1209
+ if (Array.isArray(item.children) && item.children.length > 0) {
1210
+ appendTreeOptions(item.children, parent, level + 1);
1211
+ }
1212
+ });
1213
+ };
1214
+
1215
+ if (treeEnabled) {
1216
+ appendTreeOptions(optionsData, select, 0);
1217
+ } else {
1218
+ optionsData.forEach(item => {
1219
+ if (item.children && Array.isArray(item.children)) {
1220
+ const optgroup = document.createElement('optgroup');
1221
+ optgroup.label = item.text || '';
1222
+ if (item.disabled) optgroup.disabled = true;
1223
+
1224
+ item.children.forEach(child => appendOption(child, optgroup));
1225
+
1226
+ select.appendChild(optgroup);
1227
+ } else {
1228
+ appendOption(item, select);
1229
+ }
1230
+ });
1231
+ }
1206
1232
 
1207
1233
  if (isRebuild) {
1208
1234
  const drop = container.querySelector(`.${CLASS_NAME_DROPDOWN}`);
1209
- VGSelect.buildListOptions(select, drop, instance?._params || {});
1235
+ VGSelect.buildListOptions(select, drop, instance?._params || {});
1210
1236
  instance?._triggerEvent(EVENT_KEY_REBUILD);
1211
1237
  } else {
1212
1238
  this.updateUI(select);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vgapp",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "",
5
5
  "author": {
6
6
  "name": "Vegas Studio",