vgapp 1.5.5 → 1.5.7

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.
@@ -3,7 +3,8 @@
3
3
  * Возможности: modal, overlay и dropdown-рендер, компактный размер, Data API, AJAX и Promise-сценарии.
4
4
  */
5
5
  import BaseModule from "../../base-module";
6
- import VGModal from "../../vgmodal";
6
+ import VGModal from "../../vgmodal";
7
+ import VGSidebar from "../../vgsidebar";
7
8
  import VGDropdown from "../../vgdropdown";
8
9
 
9
10
  import { execute, isElement, isVisible, makeRandomString, mergeDeepObject, reflow } from "../../../utils/js/functions";
@@ -450,8 +451,10 @@ class VGAlert {
450
451
  };
451
452
  }
452
453
 
453
- const modal = VGModal.getOrCreateInstance(containerWrap);
454
- const container = Selectors.find('.vg-modal-content', modal._element) || containerWrap;
454
+ const parent = containerWrap.classList.contains('vg-sidebar')
455
+ ? VGSidebar.getOrCreateInstance(containerWrap)
456
+ : VGModal.getOrCreateInstance(containerWrap);
457
+ const container = Selectors.find('.vg-modal-content', containerWrap) || containerWrap;
455
458
 
456
459
  const overlay = document.createElement('div');
457
460
 
@@ -463,7 +466,7 @@ class VGAlert {
463
466
 
464
467
  return {
465
468
  element: overlay,
466
- render: modal,
469
+ render: parent,
467
470
  };
468
471
  }
469
472
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Описание: базовая логика выбора, валидации и отображения файлов VGFiles.
3
- * Возможности: управляет набором файлов, ошибками, списками, кастомными действиями, статистикой и скрытыми полями формы.
3
+ * Возможности: управляет файлами, валидацией, списками, миниатюрами и иконками, действиями, статистикой и скрытыми полями.
4
4
  */
5
5
  import BaseModule from "../../base-module";
6
6
  import {mergeDeepObject} from "../../../utils/js/functions";
@@ -8,7 +8,8 @@ import Html from "../../../utils/js/components/templater";
8
8
  import {lang_messages} from "../../../utils/js/components/lang";
9
9
  import {Classes, Manipulator} from "../../../utils/js/dom/manipulator";
10
10
  import Selectors from "../../../utils/js/dom/selectors";
11
- import {getSVG} from "../../module-fn";
11
+ import {getSVG} from "../../module-fn";
12
+ import {resolveFileIconName} from "../../../utils/js/components/file-icon";
12
13
  import VGFilePreview from "../../vgfilepreview";
13
14
  import {extractAudioMetadata} from "../../../utils/js/components/audio-metadata";
14
15
 
@@ -813,34 +814,28 @@ class VGFilesBase extends BaseModule {
813
814
  }
814
815
  }
815
816
 
816
- _renderUIImage(file) {
817
- const $container = this._tpl.div({ class: 'file-image' });
818
-
819
- const src = file?.src || file?.image;
820
- if (src) {
821
- $container.appendChild(this._tpl.img(src, file.name || '', { class: 'file-preview' }));
822
- return $container;
823
- }
824
-
825
- const customData = this._getFileCustomData(file);
826
- const audioCover = String(customData.audioCover || '').trim();
827
- if (audioCover) {
828
- $container.appendChild(this._tpl.img(audioCover, this._resolveDisplayName(file), { class: 'file-preview' }));
829
- return $container;
830
- }
831
-
832
- if (file?.type && file.type.startsWith('image/')) {
833
- const objectUrl = this._getFileObjectUrl(file);
834
- if (!objectUrl) {
835
- return $container;
836
- }
837
- $container.appendChild(this._tpl.img(objectUrl, file.name, { class: 'file-preview' }));
838
- return $container;
839
- }
840
-
841
- const icon = this._getIconByFileType(file);
842
- $container.appendChild(this._tpl.i({}, icon, { isHTML: true }));
843
- return $container;
817
+ _renderUIImage(file) {
818
+ const $container = this._tpl.div({ class: 'file-image' });
819
+ const renderIcon = () => {
820
+ const icon = this._getIconByFileType(file);
821
+ $container.replaceChildren(this._tpl.i({}, icon, { isHTML: true }));
822
+ };
823
+ const customData = this._getFileCustomData(file);
824
+ let imageSrc = String(file?.image || '').trim() || String(customData.audioCover || '').trim();
825
+
826
+ if (!imageSrc && resolveFileIconName({ type: file?.type, name: file?.name || file?.src }) === 'file-image') {
827
+ imageSrc = String(file?.src || '').trim() || this._getFileObjectUrl(file);
828
+ }
829
+
830
+ if (imageSrc) {
831
+ const image = this._tpl.img(imageSrc, this._resolveDisplayName(file), { class: 'file-preview' });
832
+ image.addEventListener('error', renderIcon, { once: true });
833
+ $container.appendChild(image);
834
+ } else {
835
+ renderIcon();
836
+ }
837
+
838
+ return $container;
844
839
  }
845
840
 
846
841
  _resolveDisplayName(file) {
@@ -1,4 +1,8 @@
1
- import BaseModule from "../../base-module";
1
+ /**
2
+ * Описание: модальные окна VGModal с декларативным и программным управлением.
3
+ * Возможности: AJAX, backdrop, геометрия окна, сворачивание и синхронизация доступности.
4
+ */
5
+ import BaseModule from "../../base-module";
2
6
  import ScrollBarHelper from "../../../utils/js/components/scrollbar";
3
7
  import Backdrop from "../../../utils/js/components/backdrop";
4
8
  import Selectors from "../../../utils/js/dom/selectors";
@@ -346,7 +350,8 @@ class VGModal extends BaseModule {
346
350
  this._saveInteractionState();
347
351
  this._disableInteractionHandlers();
348
352
  this._minimized.reset();
349
- this._element.style.display = 'none';
353
+ this._element.style.display = 'none';
354
+ this._element.setAttribute('aria-hidden', 'true');
350
355
  this._element.removeAttribute('aria-modal');
351
356
  this._element.removeAttribute('role');
352
357
  this._isTransitioning = false;
@@ -395,7 +400,8 @@ class VGModal extends BaseModule {
395
400
  document.body.append(this._element);
396
401
  }
397
402
 
398
- this._element.style.display = 'block';
403
+ this._element.style.display = 'block';
404
+ this._element.removeAttribute('aria-hidden');
399
405
  this._element.setAttribute('aria-modal', true);
400
406
  this._element.setAttribute('role', 'dialog');
401
407
  this._element.scrollTop = 0;
@@ -1,4 +1,8 @@
1
- import BaseModule from "../../base-module";
1
+ /**
2
+ * Описание: адаптивная навигация VGNav с выпадающими пунктами и гамбургером.
3
+ * Возможности: click/hover, вложенные меню, позиционирование, события и интеграция с VGSidebar.
4
+ */
5
+ import BaseModule from "../../base-module";
2
6
  import Selectors from "../../../utils/js/dom/selectors";
3
7
  import {
4
8
  execute,
@@ -29,7 +33,7 @@ const CLASS_NAME_ACTIVE = 'active';
29
33
  /**
30
34
  * Constants toggle
31
35
  */
32
- const SELECTOR_DATA_TOGGLE = '.' + CLASS_NAME + ' a';
36
+ const SELECTOR_DATA_TOGGLE = '.' + CLASS_NAME + ' .dropdown > a';
33
37
 
34
38
  /**
35
39
  * Constants Events
@@ -215,34 +219,43 @@ class VGNav extends BaseModule {
215
219
  }
216
220
  }
217
221
 
218
- show(relatedTarget) {
219
- let target = relatedTarget.relatedTarget;
220
-
221
- if (!target || isDisabled(target)) return;
222
-
223
- if (!target.closest('.dropdown-content')) {
224
- target.classList.add('first');
225
- }
226
-
227
- const showEvent = EventHandler.trigger(target, EVENT_KEY_SHOW, { relatedTarget });
228
- if (showEvent.defaultPrevented) return;
229
-
230
- let drop = Selectors.find('.dropdown-content', target),
231
- link = target.firstElementChild;
222
+ show(relatedTarget) {
223
+ let target = relatedTarget.relatedTarget;
224
+
225
+ if (!target || isDisabled(target)) return;
226
+ const drop = Selectors.find(':scope > .dropdown-content', target);
227
+ if (!drop) return;
228
+
229
+ const showEvent = EventHandler.trigger(target, EVENT_KEY_SHOW, { relatedTarget });
230
+ if (showEvent.defaultPrevented) return;
231
+
232
+ if (!target.closest('.dropdown-content')) {
233
+ target.classList.add('first');
234
+ }
235
+
236
+ const link = target.firstElementChild;
232
237
 
233
238
  if (link) link.setAttribute('aria-expanded', 'true');
234
- drop.classList.add(CLASS_NAME_SHOW);
235
- target.classList.add(CLASS_NAME_ACTIVE);
236
-
237
- const $placement = new Placement({
238
- reference: target,
239
- drop: drop,
240
- placement: 'bottom-start',
241
- fallbackPlacements: ['top-start', 'bottom-end', 'top-end'],
242
- offset: [0, 6],
243
- boundary: 'clippingParents',
244
- autoFlip: true,
245
- overflowProtection: true
239
+ drop.classList.add(CLASS_NAME_SHOW);
240
+ target.classList.add(CLASS_NAME_ACTIVE);
241
+ // Координаты Placement имеют приоритет над старой CSS-схемой top/bottom/right.
242
+ drop.style.bottom = 'auto';
243
+ drop.style.right = 'auto';
244
+ const openToSide = !!target.closest('.dropdown-content') || this._params.placement === 'vertical';
245
+
246
+ const $placement = new Placement({
247
+ reference: target,
248
+ drop: drop,
249
+ placement: openToSide ? 'right-start' : 'bottom-start',
250
+ fallbackPlacements: openToSide
251
+ ? ['left-start', 'right-end', 'left-end', 'bottom-start', 'top-start']
252
+ : ['top-start', 'bottom-end', 'top-end'],
253
+ offset: openToSide ? [6, 0] : [0, 6],
254
+ boundary: 'clippingParents',
255
+ autoFlip: true,
256
+ overflowProtection: true,
257
+ clamp: true,
258
+ isMerge: false
246
259
  });
247
260
 
248
261
  $placement._setPlacement();
@@ -264,13 +277,8 @@ class VGNav extends BaseModule {
264
277
  this._queueCallback(completeCallBack, drop, true, 10);
265
278
  }
266
279
 
267
- hide(relatedTarget) {
268
- const _this = this;
269
- if ('ontouchstart' in document.documentElement) {
270
- for (const element of [].concat(...document.body.children)) {
271
- EventHandler.off(element, 'mouseover', noop);
272
- }
273
- }
280
+ hide(relatedTarget) {
281
+ const _this = this;
274
282
 
275
283
  let element = relatedTarget.relatedTarget;
276
284
 
@@ -288,7 +296,7 @@ class VGNav extends BaseModule {
288
296
  element.classList.remove('first');
289
297
  }
290
298
 
291
- [...Selectors.findAll('.' + CLASS_NAME_SHOW, element)].forEach(function (el, index) {
299
+ [...Selectors.findAll('.dropdown-content.' + CLASS_NAME_SHOW, element)].forEach(function (el) {
292
300
  el.classList.remove(CLASS_NAME_FADE);
293
301
 
294
302
  let parent = el.closest('.dropdown');
@@ -299,21 +307,14 @@ class VGNav extends BaseModule {
299
307
  let link = el.previousElementSibling;
300
308
  if (link) link.setAttribute('aria-expanded', 'false');
301
309
 
302
- if (index === 0) {
303
- const completeCallback = () => {
304
- el.classList.remove(CLASS_NAME_SHOW);
305
- EventHandler.trigger(el, EVENT_KEY_HIDDEN, relatedTarget);
306
- };
307
-
308
- _this._queueCallback(completeCallback, el, true, 500);
309
- }
310
-
311
- const dropData = _this._openDrops.get(el);
312
- if (dropData) {
313
- window.removeEventListener('scroll', dropData.scrollHandler, { capture: true });
314
- window.removeEventListener('resize', dropData.resizeHandler);
315
- _this._openDrops.delete(el);
316
- }
310
+ const completeCallback = () => {
311
+ if (parent.classList.contains(CLASS_NAME_ACTIVE)) return;
312
+ el.classList.remove(CLASS_NAME_SHOW);
313
+ EventHandler.trigger(el, EVENT_KEY_HIDDEN, relatedTarget);
314
+ };
315
+
316
+ _this._queueCallback(completeCallback, el, true, 500);
317
+ _this._cleanupDrop(el);
317
318
  });
318
319
  }
319
320
  }
@@ -341,13 +342,16 @@ class VGNav extends BaseModule {
341
342
  }
342
343
  }
343
344
 
344
- _cleanupDrop(drop) {
345
- const dropData = this._openDrops.get(drop);
346
- if (dropData) {
347
- window.removeEventListener('scroll', dropData.scrollHandler, { capture: true });
348
- window.removeEventListener('resize', dropData.resizeHandler);
349
- this._openDrops.delete(drop);
350
- }
345
+ _cleanupDrop(drop) {
346
+ const dropData = this._openDrops.get(drop);
347
+ if (dropData) {
348
+ this._openDrops.delete(drop);
349
+ // Обработчики общие для экземпляра: другие открытые уровни ещё нуждаются в них.
350
+ if (!this._openDrops.size) {
351
+ window.removeEventListener('scroll', dropData.scrollHandler, { capture: true });
352
+ window.removeEventListener('resize', dropData.resizeHandler);
353
+ }
354
+ }
351
355
  }
352
356
 
353
357
  _isElementInViewport(el) {
@@ -1,4 +1,8 @@
1
- const SKIN_CLASS_DEFAULT = 'is-skin-default';
1
+ /**
2
+ * Описание: визуальные режимы VGRangeSlider — default, ruler и status.
3
+ * Возможности: шкала с адаптивными координатами, активные отметки и пороговые цвета с текстовыми статусами.
4
+ */
5
+ const SKIN_CLASS_DEFAULT = 'is-skin-default';
2
6
  const SKIN_CLASS_RULER = 'is-skin-ruler';
3
7
  const SKIN_CLASS_STATUS = 'is-skin-status';
4
8
  const SKIN_CLASS_RULER_DIM = 'is-ruler-dim-inactive';
@@ -206,8 +210,9 @@ const syncRangeSliderSkin = (skin, state, helpers) => {
206
210
 
207
211
  const isRange = state.from !== state.to || helpers.isRange;
208
212
 
209
- skin.ticks.forEach((tick, index) => {
210
- const value = skin.values[index];
213
+ skin.ticks.forEach((tick, index) => {
214
+ const value = skin.values[index];
215
+ tick.style.left = `${helpers.toPositionPx(value)}px`;
211
216
  const isActive = isRange
212
217
  ? value >= state.from && value <= state.to
213
218
  : value <= state.from;
@@ -1,4 +1,8 @@
1
- import BaseModule from "../../base-module";
1
+ /**
2
+ * Описание: одиночные и диапазонные слайдеры VGRangeSlider на нативных range-input.
3
+ * Возможности: Data API, шкала и статусы, форматирование, синхронизация формы, события и управление значениями.
4
+ */
5
+ import BaseModule from "../../base-module";
2
6
  import EventHandler from "../../../utils/js/dom/event";
3
7
  import Selectors from "../../../utils/js/dom/selectors";
4
8
  import { mergeDeepObject, normalizeData } from "../../../utils/js/functions";
@@ -259,9 +263,10 @@ class VGRangeSlider extends BaseModule {
259
263
  this._dom.labelFrom = Selectors.find('.vg-range-slider__label--from', root);
260
264
  this._dom.labelTo = Selectors.find('.vg-range-slider__label--to', root);
261
265
  this._dom.labelSeparator = Selectors.find('.vg-range-slider__separator', root);
262
- this._dom.hiddenMin = root.querySelector('input[data-vgrangeslider-hidden="min"]');
263
- this._dom.hiddenMax = root.querySelector('input[data-vgrangeslider-hidden="max"]');
264
- this._dom.skin = applyRangeSliderSkin(root, this._params, this._state, {
266
+ this._dom.hiddenMin ||= root.querySelector('input[data-vgrangeslider-hidden="min"]');
267
+ this._dom.hiddenMax ||= root.querySelector('input[data-vgrangeslider-hidden="max"]');
268
+ this._dom.skin = applyRangeSliderSkin(root, this._params, this._state, {
269
+ isRange: this._isRange,
265
270
  formatValue: (value) => this._formatValue(value),
266
271
  toPositionPx: (value) => this._toPositionPx(value),
267
272
  });
@@ -338,11 +343,11 @@ class VGRangeSlider extends BaseModule {
338
343
  root.appendChild(this._createLabels(this._isRange));
339
344
  }
340
345
 
341
- if (this._isRange) {
342
- this._ensureHiddenInput(root, 'min', this._params.input?.min, this._params.name?.min);
343
- this._ensureHiddenInput(root, 'max', this._params.input?.max, this._params.name?.max);
344
- } else if (this._params.name?.min) {
345
- this._ensureHiddenInput(root, 'min', this._params.input?.min, this._params.name.min);
346
+ if (this._isRange) {
347
+ this._dom.hiddenMin = this._ensureHiddenInput(root, 'min', this._params.input?.min, this._params.name?.min);
348
+ this._dom.hiddenMax = this._ensureHiddenInput(root, 'max', this._params.input?.max, this._params.name?.max);
349
+ } else if (this._params.name?.min) {
350
+ this._dom.hiddenMin = this._ensureHiddenInput(root, 'min', this._params.input?.min, this._params.name.min);
346
351
  }
347
352
 
348
353
  return root;
@@ -399,7 +404,8 @@ class VGRangeSlider extends BaseModule {
399
404
  if (name) input.name = name;
400
405
  root.appendChild(input);
401
406
  }
402
- input.setAttribute('data-vgrangeslider-hidden', role);
407
+ input.setAttribute('data-vgrangeslider-hidden', role);
408
+ return input;
403
409
  }
404
410
 
405
411
  _bindEvents() {
@@ -442,7 +448,8 @@ class VGRangeSlider extends BaseModule {
442
448
  window.addEventListener('resize', this._handleResize);
443
449
  }
444
450
 
445
- _syncUI() {
451
+ _syncUI() {
452
+ if (!this._isRange) this._state.to = this._state.from;
446
453
  const fromPercent = this._toPercent(this._state.from);
447
454
  const toPercent = this._toPercent(this._isRange ? this._state.to : this._state.from);
448
455
  const fromPosition = this._toPositionPx(this._state.from);
@@ -467,10 +474,11 @@ class VGRangeSlider extends BaseModule {
467
474
  if (this._dom.labelSeparator) this._dom.labelSeparator.hidden = !this._isRange;
468
475
 
469
476
  this._syncTargets();
470
- syncRangeSliderSkin(this._dom.skin, this._state, {
471
- isRange: this._isRange,
472
- params: this._params,
473
- dom: this._dom,
477
+ syncRangeSliderSkin(this._dom.skin, this._state, {
478
+ isRange: this._isRange,
479
+ params: this._params,
480
+ dom: this._dom,
481
+ toPositionPx: (value) => this._toPositionPx(value),
474
482
  });
475
483
  }
476
484
 
@@ -565,8 +573,12 @@ class VGRangeSlider extends BaseModule {
565
573
  const numeric = Number(normalizeData(value));
566
574
  const safeValue = Number.isFinite(numeric) ? numeric : fallback;
567
575
  const clamped = Math.min(Math.max(safeValue, min), max);
568
- const step = this._state?.step || this._params.step || 1;
569
- const stepped = Math.round((clamped - min) / step) * step + min;
576
+ const configuredStep = Number(this._state?.step ?? this._params.step);
577
+ const step = Number.isFinite(configuredStep) && configuredStep > 0 ? configuredStep : 1;
578
+ // Округление не должно выводить значение за max или с нативной сетки шага.
579
+ const maxSteps = Math.floor((max - min) / step + 1e-9);
580
+ const steps = Math.min(Math.round((clamped - min) / step), maxSteps);
581
+ const stepped = steps * step + min;
570
582
  return Number(stepped.toFixed(5));
571
583
  }
572
584
 
@@ -1,4 +1,8 @@
1
- import BaseModule from "../../base-module";
1
+ /**
2
+ * Описание: сворачивание текста и списков VGRollup.
3
+ * Возможности: ограничение высоты, строк и количества элементов, локализация кнопок, Data API, callbacks и события.
4
+ */
5
+ import BaseModule from "../../base-module";
2
6
  import { execute, isDisabled, mergeDeepObject } from "../../../utils/js/functions";
3
7
  import EventHandler from "../../../utils/js/dom/event";
4
8
  import Selectors from "../../../utils/js/dom/selectors";
@@ -98,7 +102,7 @@ class VGRollup extends BaseModule {
98
102
  }
99
103
  };
100
104
 
101
- let lang = Manipulator.get(element, 'data-lang') || ('lang' in params) ? params.lang : defaultParams.lang;
105
+ const lang = Manipulator.get(element, 'data-lang') || params.lang || defaultParams.lang;
102
106
 
103
107
  // Локализация текстов кнопок
104
108
  defaultParams.button.more = lang_buttons(lang, NAME)['show'];
@@ -288,7 +292,8 @@ class VGRollup extends BaseModule {
288
292
 
289
293
  if (isEllipsis && line) {
290
294
  Classes.add(element, this.classes.ellipsis);
291
- element.style.lineClamp = Number(line);
295
+ element.style.lineClamp = Number(line);
296
+ element.style.setProperty('-webkit-line-clamp', String(Number(line)));
292
297
  } else if (isEllipsis) {
293
298
  console.error("Переменная [data-line] или параметр[line] не должны быть пустыми");
294
299
  }
@@ -377,13 +382,14 @@ class VGRollup extends BaseModule {
377
382
 
378
383
  if (this._params.ellipsis.line) {
379
384
  Classes.add(el, this.classes.ellipsis);
380
- el.style.lineClamp = this._params.ellipsis.line;
385
+ el.style.lineClamp = this._params.ellipsis.line;
386
+ el.style.setProperty('-webkit-line-clamp', String(this._params.ellipsis.line));
381
387
  }
382
388
 
383
389
  if (this._params.fade) Classes.add(el, this.classes.fade);
384
390
  if (this._params.transition) Classes.add(el, this.classes.transition);
385
391
 
386
- execute(this._params.callbacks.expand, [el, this])
392
+ execute(this._params.callbacks.collapse, [el, this])
387
393
  } else if (content === 'elements') {
388
394
  const items = Selectors.findAll('.' + this._params.elements, el);
389
395
  items.forEach((item, index) => {
@@ -399,7 +405,9 @@ class VGRollup extends BaseModule {
399
405
  } else {
400
406
  const { hidden, ellipsis, fade } = this.classes;
401
407
  Classes.remove(el, [hidden, ellipsis, fade]);
402
- Manipulator.remove(el, 'style');
408
+ el.style.removeProperty('height');
409
+ el.style.removeProperty('line-clamp');
410
+ el.style.removeProperty('-webkit-line-clamp');
403
411
 
404
412
  if (this._params.content === 'elements') {
405
413
  const items = Selectors.findAll('.' + this._params.elements, el);
@@ -455,4 +463,4 @@ EventHandler.on(document, EVENT_KEY_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, functi
455
463
  VGRollup.toggle(target, this);
456
464
  });
457
465
 
458
- export default VGRollup;
466
+ export default VGRollup;
@@ -1,8 +1,11 @@
1
- import VGSelect from "./vgselect";
1
+ /**
2
+ * Описание: делегированные обработчики интерфейса VGSelect.
3
+ * Возможности: открытие списка, выбор опций, теги, клавиатура и сброс формы.
4
+ */
5
+ import VGSelect from "./vgselect";
2
6
  import EventHandler from "../../../utils/js/dom/event";
3
7
  import Selectors from "../../../utils/js/dom/selectors";
4
8
  import {Manipulator} from "../../../utils/js/dom/manipulator";
5
- import {transliterate} from "../../../utils/js/functions";
6
9
 
7
10
  const NAME_KEY = 'vg.select';
8
11
 
@@ -100,56 +103,6 @@ const _handlersVGSelect = () => {
100
103
  }
101
104
  });
102
105
 
103
- EventHandler.on(document, EVENT_KEY_UP_DATA_API, SELECTOR_SEARCH_TOGGLE, function(e) {
104
- const input = e.target;
105
- const dropdown = input.closest(SELECTOR_DROPDOWN);
106
- const list = dropdown?.querySelector(`.${CLASS_NAME_LIST}`);
107
- if (!list) return;
108
-
109
- const container = input.closest(`.${CLASS_NAME_CONTAINER}`);
110
- const instance = VGSelect.getInstance(container);
111
-
112
- const options = list.querySelectorAll(`.${CLASS_NAME_OPTION}`);
113
- const groups = list.querySelectorAll(`.${CLASS_NAME_OPTGROUP}`);
114
- const value = input.value.trim().toLowerCase();
115
- const search = [value, transliterate(value), transliterate(value, true)];
116
-
117
- options.forEach(el => { el.hidden = false; el.style.display = ''; });
118
- groups.forEach(el => { el.hidden = false; el.style.display = ''; });
119
-
120
- if (value) {
121
- let visibleCount = 0;
122
- groups.length ? groups.forEach(group => {
123
- const items = group.querySelectorAll(`.${CLASS_NAME_OPTION}`);
124
- const visible = Array.from(items).some(item => {
125
- const t = item.textContent.toLowerCase();
126
- return search.some(s => t.includes(s));
127
- });
128
- group.hidden = !visible;
129
- group.style.display = visible ? '' : 'none';
130
- items.forEach(item => {
131
- const t = item.textContent.toLowerCase();
132
- const match = search.some(s => t.includes(s));
133
- item.hidden = !match;
134
- item.style.display = match ? '' : 'none';
135
- if (match) visibleCount++;
136
- });
137
- }) : options.forEach(option => {
138
- const t = option.textContent.toLowerCase();
139
- const match = search.some(s => t.includes(s));
140
- option.hidden = !match;
141
- option.style.display = match ? '' : 'none';
142
- if (match) visibleCount++;
143
- });
144
-
145
- instance?._triggerEvent(EVENT_KEY_SEARCH, { query: value, results: visibleCount });
146
- instance?._callCallback('onSearch', { query: value, results: visibleCount });
147
- } else {
148
- instance?._triggerEvent(EVENT_KEY_SEARCH, { query: '', results: options.length });
149
- instance?._callCallback('onSearch', { query: '', results: options.length });
150
- }
151
- });
152
-
153
106
  EventHandler.on(document, EVENT_RESET_DATA_API, 'form', function() {
154
107
  Selectors.findAll('select[data-inited="true"]', this).forEach(select => VGSelect.build(select, true));
155
108
  });
@@ -236,4 +189,4 @@ const _handlersVGSelect = () => {
236
189
  });
237
190
  }
238
191
 
239
- export default _handlersVGSelect;
192
+ export default _handlersVGSelect;