vgapp 1.3.4 → 1.3.6

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.
Files changed (88) hide show
  1. package/app/langs/en/buttons.json +17 -17
  2. package/app/langs/en/messages.json +36 -36
  3. package/app/langs/ru/buttons.json +17 -17
  4. package/app/langs/ru/messages.json +36 -36
  5. package/app/modules/base-module.js +111 -111
  6. package/app/modules/vgalert/js/vgalert.js +52 -52
  7. package/app/modules/vgalert/readme.md +105 -105
  8. package/app/modules/vgdynamictable/index.js +5 -5
  9. package/app/modules/vgdynamictable/js/editable.js +19 -19
  10. package/app/modules/vgdynamictable/js/filters.js +21 -21
  11. package/app/modules/vgdynamictable/js/fixed.js +179 -179
  12. package/app/modules/vgdynamictable/js/pagination.js +1 -1
  13. package/app/modules/vgdynamictable/js/search.js +12 -12
  14. package/app/modules/vgdynamictable/js/sortable.js +71 -71
  15. package/app/modules/vgdynamictable/js/summary-footer.js +28 -28
  16. package/app/modules/vgdynamictable/js/table-remote.js +79 -79
  17. package/app/modules/vgdynamictable/js/table-url-state.js +31 -31
  18. package/app/modules/vgdynamictable/js/utils/common.js +48 -48
  19. package/app/modules/vgdynamictable/js/vgdynamictable.js +268 -268
  20. package/app/modules/vgdynamictable/readme.md +250 -250
  21. package/app/modules/vgdynamictable/scss/_skeleton.scss +6 -6
  22. package/app/modules/vgdynamictable/scss/_sortable.scss +24 -24
  23. package/app/modules/vgdynamictable/scss/_table.scss +4 -4
  24. package/app/modules/vgdynamictable/scss/_variables.scss +79 -79
  25. package/app/modules/vgdynamictable/scss/vgdynamictable.scss +27 -27
  26. package/app/modules/vgfilepreview/js/i18n.js +56 -56
  27. package/app/modules/vgfilepreview/js/renderers/image-modal.js +145 -145
  28. package/app/modules/vgfilepreview/js/renderers/image.js +92 -92
  29. package/app/modules/vgfilepreview/js/renderers/index.js +19 -19
  30. package/app/modules/vgfilepreview/js/renderers/office-modal.js +168 -168
  31. package/app/modules/vgfilepreview/js/renderers/office.js +79 -79
  32. package/app/modules/vgfilepreview/js/renderers/pdf-modal.js +260 -260
  33. package/app/modules/vgfilepreview/js/renderers/pdf.js +76 -76
  34. package/app/modules/vgfilepreview/js/renderers/playlist.js +71 -71
  35. package/app/modules/vgfilepreview/js/renderers/text-modal.js +343 -343
  36. package/app/modules/vgfilepreview/js/renderers/text.js +83 -83
  37. package/app/modules/vgfilepreview/js/renderers/video-modal.js +272 -272
  38. package/app/modules/vgfilepreview/js/renderers/video.js +80 -80
  39. package/app/modules/vgfilepreview/js/renderers/zip-modal.js +522 -522
  40. package/app/modules/vgfilepreview/js/renderers/zip.js +89 -89
  41. package/app/modules/vgfilepreview/js/vgfilepreview.js +7 -7
  42. package/app/modules/vgfilepreview/readme.md +68 -68
  43. package/app/modules/vgfilepreview/scss/_variables.scss +113 -113
  44. package/app/modules/vgfilepreview/scss/vgfilepreview.scss +464 -464
  45. package/app/modules/vgfiles/js/base.js +26 -26
  46. package/app/modules/vgfiles/js/droppable.js +260 -260
  47. package/app/modules/vgfiles/js/render.js +153 -153
  48. package/app/modules/vgfiles/js/vgfiles.js +104 -104
  49. package/app/modules/vgfiles/readme.md +123 -123
  50. package/app/modules/vgfiles/scss/_variables.scss +18 -18
  51. package/app/modules/vgfiles/scss/vgfiles.scss +148 -148
  52. package/app/modules/vgformsender/js/hideshowpass.js +16 -16
  53. package/app/modules/vgformsender/scss/vgformsender.scss +34 -34
  54. package/app/modules/vgmodal/js/vgmodal.drag.js +332 -332
  55. package/app/modules/vgmodal/js/vgmodal.js +188 -188
  56. package/app/modules/vgmodal/js/vgmodal.resize.js +27 -27
  57. package/app/modules/vgmodal/scss/vgmodal.scss +14 -14
  58. package/app/modules/vgnav/js/vgnav.js +135 -135
  59. package/app/modules/vgnav/readme.md +67 -67
  60. package/app/modules/vgnestable/README.md +307 -307
  61. package/app/modules/vgnestable/scss/_variables.scss +60 -60
  62. package/app/modules/vgnestable/scss/vgnestable.scss +163 -163
  63. package/app/modules/vgrangeslider/js/skins.js +222 -222
  64. package/app/modules/vgrangeslider/js/vgrangeslider.js +704 -704
  65. package/app/modules/vgrangeslider/readme.md +523 -523
  66. package/app/modules/vgrangeslider/scss/_variables.scss +53 -53
  67. package/app/modules/vgrangeslider/scss/vgrangeslider.scss +240 -240
  68. package/app/modules/vgselect/js/vgselect.js +39 -39
  69. package/app/modules/vgselect/scss/vgselect.scss +22 -22
  70. package/app/modules/vgspy/readme.md +28 -28
  71. package/app/modules/vgtoast/readme.md +18 -18
  72. package/app/modules/vgtoast/scss/vgtoast.scss +48 -48
  73. package/app/modules/vgtooltip/js/vgtooltip.js +89 -89
  74. package/app/modules/vgtooltip/readme.md +181 -181
  75. package/app/utils/js/components/ajax.js +117 -117
  76. package/app/utils/js/components/audio-metadata.js +240 -240
  77. package/app/utils/js/components/backdrop.js +17 -17
  78. package/app/utils/js/components/file-icon.js +109 -109
  79. package/app/utils/js/components/file-preview.js +304 -304
  80. package/app/utils/js/components/sanitize.js +150 -150
  81. package/app/utils/js/components/video-metadata.js +140 -140
  82. package/app/vgapp.js +46 -46
  83. package/build/vgapp.css +1 -1
  84. package/build/vgapp.css.map +1 -1
  85. package/build/vgapp.js +1 -1
  86. package/build/vgapp.js.map +1 -1
  87. package/index.js +14 -14
  88. package/package.json +3 -1
@@ -1,181 +1,181 @@
1
- # VGTooltip — модуль подсказок и popover
2
-
3
- `VGTooltip` — модуль всплывающих подсказок для Vegas App. Поддерживает tooltip и popover режимы, Data API и JS API, автопозиционирование, анимации и управление через события.
4
-
5
- ---
6
-
7
- ## ✅ Возможности
8
-
9
- - 📦 **Легковесный и автономный** — работает как модуль без внешнего UI-фреймворка.
10
- - 🎯 **Два режима работы** — обычный tooltip и `popover` с заголовком и контентом.
11
- - 🧭 **Умное позиционирование** — поддерживает `top`, `bottom`, `left`, `right` и авто-переворот.
12
- - 🖱 **Гибкие триггеры** — `hover`, `click` или их комбинация.
13
- - 🔁 **Автозакрытие других подсказок** — при `closeOther: true`.
14
- - ⌨ **Поддержка `Esc`** и закрытия по клику вне элемента.
15
- - 🎬 **Анимации показа и скрытия** через общий animation API.
16
- - 📱 **Адаптивное поведение** — перерасчёт позиции при `resize` и `scroll`.
17
-
18
- ---
19
-
20
- ## 📦 Установка
21
-
22
- Модуль подключается как часть системы модулей. Для работы должны быть доступны:
23
- - `BaseModule`
24
- - `EventHandler`
25
- - `Selectors`
26
- - `Placement`
27
-
28
- ---
29
-
30
- ## 🧰 Быстрое использование
31
-
32
- ### Через JavaScript
33
- ```js
34
- const button = document.querySelector('#tooltip-demo');
35
-
36
- const tooltip = new VGTooltip(button, {
37
- title: 'Подсказка',
38
- placement: 'right'
39
- });
40
-
41
- tooltip.show();
42
- ```
43
-
44
- ### Через Data API
45
- ```html
46
- <button
47
- data-vg-toggle="tooltip"
48
- data-vg-title="Текст подсказки">
49
- Наведи на меня
50
- </button>
51
- ```
52
-
53
- ### Popover
54
- ```html
55
- <button
56
- data-vg-toggle="popover"
57
- data-vg-title="Заголовок"
58
- data-vg-content="Подробный текст">
59
- Открыть popover
60
- </button>
61
- ```
62
-
63
- ---
64
-
65
- ## ⚙️ Параметры
66
-
67
- | Параметр | Тип | По умолчанию | Описание |
68
- |--------|------|-------------|--------|
69
- | `title` | `string` | `''` | Заголовок tooltip/popover |
70
- | `content` | `string` | `''` | Контент для режима `popover` |
71
- | `trigger` | `string` | `'hover'` | Триггер показа: `hover`, `click` или комбинация |
72
- | `placement` | `string` | `'top'` | Позиция: `top`, `bottom`, `left`, `right` и варианты |
73
- | `container` | `string` | `'body'` | Контейнер, в который рендерится tooltip |
74
- | `popover` | `boolean` | `false` | Режим popover |
75
- | `html` | `boolean` | `false` | Разрешить HTML в `title` и `content` |
76
- | `offset` | `array` | `[8, 8]` | Смещение относительно элемента |
77
- | `autoFlip` | `boolean` | `true` | Автоматически менять сторону при нехватке места |
78
- | `overflowProtection` | `boolean` | `true` | Защита от выхода за границы окна |
79
- | `fallbackPlacements` | `array` | `['bottom', 'right', 'left']` | Порядок запасных позиций |
80
- | `closeOnOutsideClick` | `boolean` | `true` | Закрывать по клику вне tooltip |
81
- | `keyboard` | `boolean` | `true` | Закрывать по `Esc` |
82
- | `closeOther` | `boolean` | `true` | Скрывать другие открытые tooltip |
83
- | `delay.show` | `number` | `100` | Задержка перед показом, мс |
84
- | `delay.hide` | `number` | `100` | Задержка перед скрытием, мс |
85
- | `arrow.padding` | `number` | `8` | Отступ стрелки от краёв |
86
- | `custom.class` | `string` | `''` | Дополнительный CSS-класс |
87
- | `animation.enable` | `boolean` | `true` | Включить анимации |
88
- | `animation.in` | `string` | `'animate__backInUp'` | Анимация появления |
89
- | `animation.out` | `string` | `'animate__backOutDown'` | Анимация скрытия |
90
- | `animation.delay` | `number` | `300` | Длительность анимации, мс |
91
- | `animation.effect` | `string` | `'none'` | Дополнительный CSS-эффект |
92
-
93
- ---
94
-
95
- ## 💡 Расширенные примеры
96
-
97
- ### 1. Tooltip по клику
98
- ```js
99
- new VGTooltip(document.querySelector('#help'), {
100
- title: 'Подсказка по клику',
101
- trigger: 'click',
102
- placement: 'bottom'
103
- });
104
- ```
105
-
106
- ### 2. HTML-контент
107
- ```js
108
- new VGTooltip(document.querySelector('#html-tooltip'), {
109
- title: '<strong>Важно</strong>',
110
- content: 'Можно передавать <em>HTML</em>',
111
- html: true,
112
- popover: true,
113
- trigger: 'click'
114
- });
115
- ```
116
-
117
- ### 3. Кастомный контейнер и класс
118
- ```js
119
- new VGTooltip(document.querySelector('#custom-tooltip'), {
120
- title: 'Подсказка внутри контейнера',
121
- container: '.demo-area',
122
- custom: {
123
- class: 'tooltip-accent'
124
- }
125
- });
126
- ```
127
-
128
- ---
129
-
130
- ## 🧩 Data API
131
-
132
- - `data-vg-toggle="tooltip"` — обычная подсказка.
133
- - `data-vg-toggle="popover"` — popover-режим.
134
- - `data-vg-title` — текст заголовка.
135
- - `data-vg-content` — текст контента.
136
- - Если у элемента есть `title`, модуль переносит его в `data-vg-original-title` и использует как источник текста.
137
-
138
- ---
139
-
140
- ## 🧪 События
141
-
142
- | Событие | Описание |
143
- |-------|--------|
144
- | `vg.tooltip.show` | Перед показом |
145
- | `vg.tooltip.shown` | После показа |
146
- | `vg.tooltip.hide` | Перед скрытием |
147
- | `vg.tooltip.hidden` | После скрытия |
148
-
149
- ---
150
-
151
- ## 🔄 Методы
152
-
153
- | Метод | Описание |
154
- |------|--------|
155
- | `VGTooltip.getInstance(element)` | Получить существующий экземпляр |
156
- | `VGTooltip.getOrCreateInstance(element, params)` | Получить или создать экземпляр |
157
- | `tooltip.show(relatedTarget)` | Показать tooltip |
158
- | `tooltip.hide()` | Скрыть tooltip |
159
- | `tooltip.toggle(relatedTarget)` | Переключить состояние |
160
- | `tooltip.dispose()` | Удалить tooltip и очистить обработчики |
161
-
162
- ---
163
-
164
- ## 🌐 Глобальные классы
165
-
166
- - `.vg-tooltip-open` — добавляется к `<body>`, пока открыт хотя бы один tooltip.
167
- - `.vg-tooltip.show` — tooltip находится в DOM и видим.
168
- - `.vg-tooltip.shown` — tooltip завершил фазу показа.
169
- - `.vg-tooltip-popover` — оформление для режима popover.
170
-
171
- ---
172
-
173
- ## 📝 Лицензия
174
-
175
- MIT. Свободно использовать и модифицировать.
176
-
177
- ---
178
-
179
- 📌 *Разработано в рамках фронтенд-системы VG Modules.*
180
- > 🚀 Автор: VEGAS STUDIO (vegas-dev.com)
181
- > 📍 Поддерживается в проектах VEGAS
1
+ # VGTooltip — модуль подсказок и popover
2
+
3
+ `VGTooltip` — модуль всплывающих подсказок для Vegas App. Поддерживает tooltip и popover режимы, Data API и JS API, автопозиционирование, анимации и управление через события.
4
+
5
+ ---
6
+
7
+ ## ✅ Возможности
8
+
9
+ - 📦 **Легковесный и автономный** — работает как модуль без внешнего UI-фреймворка.
10
+ - 🎯 **Два режима работы** — обычный tooltip и `popover` с заголовком и контентом.
11
+ - 🧭 **Умное позиционирование** — поддерживает `top`, `bottom`, `left`, `right` и авто-переворот.
12
+ - 🖱 **Гибкие триггеры** — `hover`, `click` или их комбинация.
13
+ - 🔁 **Автозакрытие других подсказок** — при `closeOther: true`.
14
+ - ⌨ **Поддержка `Esc`** и закрытия по клику вне элемента.
15
+ - 🎬 **Анимации показа и скрытия** через общий animation API.
16
+ - 📱 **Адаптивное поведение** — перерасчёт позиции при `resize` и `scroll`.
17
+
18
+ ---
19
+
20
+ ## 📦 Установка
21
+
22
+ Модуль подключается как часть системы модулей. Для работы должны быть доступны:
23
+ - `BaseModule`
24
+ - `EventHandler`
25
+ - `Selectors`
26
+ - `Placement`
27
+
28
+ ---
29
+
30
+ ## 🧰 Быстрое использование
31
+
32
+ ### Через JavaScript
33
+ ```js
34
+ const button = document.querySelector('#tooltip-demo');
35
+
36
+ const tooltip = new VGTooltip(button, {
37
+ title: 'Подсказка',
38
+ placement: 'right'
39
+ });
40
+
41
+ tooltip.show();
42
+ ```
43
+
44
+ ### Через Data API
45
+ ```html
46
+ <button
47
+ data-vg-toggle="tooltip"
48
+ data-vg-title="Текст подсказки">
49
+ Наведи на меня
50
+ </button>
51
+ ```
52
+
53
+ ### Popover
54
+ ```html
55
+ <button
56
+ data-vg-toggle="popover"
57
+ data-vg-title="Заголовок"
58
+ data-vg-content="Подробный текст">
59
+ Открыть popover
60
+ </button>
61
+ ```
62
+
63
+ ---
64
+
65
+ ## ⚙️ Параметры
66
+
67
+ | Параметр | Тип | По умолчанию | Описание |
68
+ |--------|------|-------------|--------|
69
+ | `title` | `string` | `''` | Заголовок tooltip/popover |
70
+ | `content` | `string` | `''` | Контент для режима `popover` |
71
+ | `trigger` | `string` | `'hover'` | Триггер показа: `hover`, `click` или комбинация |
72
+ | `placement` | `string` | `'top'` | Позиция: `top`, `bottom`, `left`, `right` и варианты |
73
+ | `container` | `string` | `'body'` | Контейнер, в который рендерится tooltip |
74
+ | `popover` | `boolean` | `false` | Режим popover |
75
+ | `html` | `boolean` | `false` | Разрешить HTML в `title` и `content` |
76
+ | `offset` | `array` | `[8, 8]` | Смещение относительно элемента |
77
+ | `autoFlip` | `boolean` | `true` | Автоматически менять сторону при нехватке места |
78
+ | `overflowProtection` | `boolean` | `true` | Защита от выхода за границы окна |
79
+ | `fallbackPlacements` | `array` | `['bottom', 'right', 'left']` | Порядок запасных позиций |
80
+ | `closeOnOutsideClick` | `boolean` | `true` | Закрывать по клику вне tooltip |
81
+ | `keyboard` | `boolean` | `true` | Закрывать по `Esc` |
82
+ | `closeOther` | `boolean` | `true` | Скрывать другие открытые tooltip |
83
+ | `delay.show` | `number` | `100` | Задержка перед показом, мс |
84
+ | `delay.hide` | `number` | `100` | Задержка перед скрытием, мс |
85
+ | `arrow.padding` | `number` | `8` | Отступ стрелки от краёв |
86
+ | `custom.class` | `string` | `''` | Дополнительный CSS-класс |
87
+ | `animation.enable` | `boolean` | `true` | Включить анимации |
88
+ | `animation.in` | `string` | `'animate__backInUp'` | Анимация появления |
89
+ | `animation.out` | `string` | `'animate__backOutDown'` | Анимация скрытия |
90
+ | `animation.delay` | `number` | `300` | Длительность анимации, мс |
91
+ | `animation.effect` | `string` | `'none'` | Дополнительный CSS-эффект |
92
+
93
+ ---
94
+
95
+ ## 💡 Расширенные примеры
96
+
97
+ ### 1. Tooltip по клику
98
+ ```js
99
+ new VGTooltip(document.querySelector('#help'), {
100
+ title: 'Подсказка по клику',
101
+ trigger: 'click',
102
+ placement: 'bottom'
103
+ });
104
+ ```
105
+
106
+ ### 2. HTML-контент
107
+ ```js
108
+ new VGTooltip(document.querySelector('#html-tooltip'), {
109
+ title: '<strong>Важно</strong>',
110
+ content: 'Можно передавать <em>HTML</em>',
111
+ html: true,
112
+ popover: true,
113
+ trigger: 'click'
114
+ });
115
+ ```
116
+
117
+ ### 3. Кастомный контейнер и класс
118
+ ```js
119
+ new VGTooltip(document.querySelector('#custom-tooltip'), {
120
+ title: 'Подсказка внутри контейнера',
121
+ container: '.demo-area',
122
+ custom: {
123
+ class: 'tooltip-accent'
124
+ }
125
+ });
126
+ ```
127
+
128
+ ---
129
+
130
+ ## 🧩 Data API
131
+
132
+ - `data-vg-toggle="tooltip"` — обычная подсказка.
133
+ - `data-vg-toggle="popover"` — popover-режим.
134
+ - `data-vg-title` — текст заголовка.
135
+ - `data-vg-content` — текст контента.
136
+ - Если у элемента есть `title`, модуль переносит его в `data-vg-original-title` и использует как источник текста.
137
+
138
+ ---
139
+
140
+ ## 🧪 События
141
+
142
+ | Событие | Описание |
143
+ |-------|--------|
144
+ | `vg.tooltip.show` | Перед показом |
145
+ | `vg.tooltip.shown` | После показа |
146
+ | `vg.tooltip.hide` | Перед скрытием |
147
+ | `vg.tooltip.hidden` | После скрытия |
148
+
149
+ ---
150
+
151
+ ## 🔄 Методы
152
+
153
+ | Метод | Описание |
154
+ |------|--------|
155
+ | `VGTooltip.getInstance(element)` | Получить существующий экземпляр |
156
+ | `VGTooltip.getOrCreateInstance(element, params)` | Получить или создать экземпляр |
157
+ | `tooltip.show(relatedTarget)` | Показать tooltip |
158
+ | `tooltip.hide()` | Скрыть tooltip |
159
+ | `tooltip.toggle(relatedTarget)` | Переключить состояние |
160
+ | `tooltip.dispose()` | Удалить tooltip и очистить обработчики |
161
+
162
+ ---
163
+
164
+ ## 🌐 Глобальные классы
165
+
166
+ - `.vg-tooltip-open` — добавляется к `<body>`, пока открыт хотя бы один tooltip.
167
+ - `.vg-tooltip.show` — tooltip находится в DOM и видим.
168
+ - `.vg-tooltip.shown` — tooltip завершил фазу показа.
169
+ - `.vg-tooltip-popover` — оформление для режима popover.
170
+
171
+ ---
172
+
173
+ ## 📝 Лицензия
174
+
175
+ MIT. Свободно использовать и модифицировать.
176
+
177
+ ---
178
+
179
+ 📌 *Разработано в рамках фронтенд-системы VG Modules.*
180
+ > 🚀 Автор: VEGAS STUDIO (vegas-dev.com)
181
+ > 📍 Поддерживается в проектах VEGAS
@@ -1,117 +1,117 @@
1
- import { isObject, noop, normalizeData } from "../functions";
2
-
3
- class Ajax {
4
- static _toRouteQueryParamValue(value) {
5
- if (!Array.isArray(value)) {
6
- return value;
7
- }
8
-
9
- return value
10
- .map((item) => String(item || '').trim())
11
- .filter((item) => item !== '')
12
- .join(',');
13
- }
14
-
15
- static buildRouteUrl(route, options = {}) {
16
- const target = String(route || '').trim();
17
- if (!target) {
18
- throw new Error('Request endpoint is empty');
19
- }
20
-
21
- const baseParams = isObject(options.baseParams) ? options.baseParams : {};
22
- const params = isObject(options.params) ? options.params : {};
23
- const url = new URL(target, window.location.origin);
24
- const merged = { ...baseParams, ...params };
25
-
26
- Object.keys(merged).forEach((key) => {
27
- const value = this._toRouteQueryParamValue(merged[key]);
28
- if (value === undefined || value === null || value === '') {
29
- return;
30
- }
31
-
32
- url.searchParams.set(key, String(value));
33
- });
34
-
35
- return url;
36
- }
37
-
38
- static async requestRoute(options = {}) {
39
- const method = String(options.method || 'GET').toUpperCase().trim() || 'GET';
40
- const headers = isObject(options.headers) ? { ...options.headers } : {};
41
- const credentials = String(options.credentials || 'same-origin').trim() || 'same-origin';
42
- const baseParams = isObject(options.baseParams) ? options.baseParams : {};
43
- const params = isObject(options.params) ? options.params : {};
44
- const hasBody = options.body !== undefined && options.body !== null;
45
- const isGetLike = method === 'GET' || method === 'HEAD';
46
- const route = String(options.route || options.endpoint || '').trim();
47
- const url = this.buildRouteUrl(route, {
48
- baseParams,
49
- params: isGetLike ? params : {},
50
- });
51
- const requestOptions = {
52
- method,
53
- credentials,
54
- headers,
55
- signal: options.signal || undefined,
56
- };
57
-
58
- if (!isGetLike && hasBody) {
59
- const body = options.body;
60
- const isFormData = typeof FormData !== 'undefined' && body instanceof FormData;
61
-
62
- if (isFormData) {
63
- delete requestOptions.headers['Content-Type'];
64
- requestOptions.body = body;
65
- } else if (typeof body === 'string') {
66
- if (!requestOptions.headers['Content-Type']) {
67
- requestOptions.headers['Content-Type'] = 'application/json';
68
- }
69
- requestOptions.body = body;
70
- } else {
71
- if (!requestOptions.headers['Content-Type']) {
72
- requestOptions.headers['Content-Type'] = 'application/json';
73
- }
74
- requestOptions.body = JSON.stringify(body);
75
- }
76
- }
77
-
78
- const response = await fetch(url.toString(), requestOptions);
79
- const contentType = String(response.headers.get('content-type') || '').toLowerCase();
80
- const expectsJson = options.responseType === 'json'
81
- || (options.responseType !== 'text' && contentType.includes('application/json'));
82
- const payload = await (async () => {
83
- if (expectsJson) {
84
- try {
85
- return await response.json();
86
- } catch (error) {
87
- return await response.text();
88
- }
89
- }
90
-
91
- return response.text();
92
- })();
93
-
94
- if (!response.ok) {
95
- const apiError = payload && typeof payload === 'object' && payload.error && typeof payload.error === 'object'
96
- ? payload.error
97
- : {};
98
- const fallbackMessage = typeof payload === 'string' && payload.trim()
99
- ? payload
100
- : response.statusText;
101
- const message = String(apiError.message || fallbackMessage || 'Request failed');
102
- const error = new Error(message);
103
- error.status = response.status;
104
- error.code = apiError.code || 'REQUEST_FAILED';
105
- error.details = Array.isArray(apiError.details) ? apiError.details : [];
106
- error.requestId = apiError.request_id || response.headers.get('X-Request-Id') || '';
107
- error.response = payload;
108
- throw error;
109
- }
110
-
111
- return payload;
112
- }
113
-
114
- /**
1
+ import { isObject, noop, normalizeData } from "../functions";
2
+
3
+ class Ajax {
4
+ static _toRouteQueryParamValue(value) {
5
+ if (!Array.isArray(value)) {
6
+ return value;
7
+ }
8
+
9
+ return value
10
+ .map((item) => String(item || '').trim())
11
+ .filter((item) => item !== '')
12
+ .join(',');
13
+ }
14
+
15
+ static buildRouteUrl(route, options = {}) {
16
+ const target = String(route || '').trim();
17
+ if (!target) {
18
+ throw new Error('Request endpoint is empty');
19
+ }
20
+
21
+ const baseParams = isObject(options.baseParams) ? options.baseParams : {};
22
+ const params = isObject(options.params) ? options.params : {};
23
+ const url = new URL(target, window.location.origin);
24
+ const merged = { ...baseParams, ...params };
25
+
26
+ Object.keys(merged).forEach((key) => {
27
+ const value = this._toRouteQueryParamValue(merged[key]);
28
+ if (value === undefined || value === null || value === '') {
29
+ return;
30
+ }
31
+
32
+ url.searchParams.set(key, String(value));
33
+ });
34
+
35
+ return url;
36
+ }
37
+
38
+ static async requestRoute(options = {}) {
39
+ const method = String(options.method || 'GET').toUpperCase().trim() || 'GET';
40
+ const headers = isObject(options.headers) ? { ...options.headers } : {};
41
+ const credentials = String(options.credentials || 'same-origin').trim() || 'same-origin';
42
+ const baseParams = isObject(options.baseParams) ? options.baseParams : {};
43
+ const params = isObject(options.params) ? options.params : {};
44
+ const hasBody = options.body !== undefined && options.body !== null;
45
+ const isGetLike = method === 'GET' || method === 'HEAD';
46
+ const route = String(options.route || options.endpoint || '').trim();
47
+ const url = this.buildRouteUrl(route, {
48
+ baseParams,
49
+ params: isGetLike ? params : {},
50
+ });
51
+ const requestOptions = {
52
+ method,
53
+ credentials,
54
+ headers,
55
+ signal: options.signal || undefined,
56
+ };
57
+
58
+ if (!isGetLike && hasBody) {
59
+ const body = options.body;
60
+ const isFormData = typeof FormData !== 'undefined' && body instanceof FormData;
61
+
62
+ if (isFormData) {
63
+ delete requestOptions.headers['Content-Type'];
64
+ requestOptions.body = body;
65
+ } else if (typeof body === 'string') {
66
+ if (!requestOptions.headers['Content-Type']) {
67
+ requestOptions.headers['Content-Type'] = 'application/json';
68
+ }
69
+ requestOptions.body = body;
70
+ } else {
71
+ if (!requestOptions.headers['Content-Type']) {
72
+ requestOptions.headers['Content-Type'] = 'application/json';
73
+ }
74
+ requestOptions.body = JSON.stringify(body);
75
+ }
76
+ }
77
+
78
+ const response = await fetch(url.toString(), requestOptions);
79
+ const contentType = String(response.headers.get('content-type') || '').toLowerCase();
80
+ const expectsJson = options.responseType === 'json'
81
+ || (options.responseType !== 'text' && contentType.includes('application/json'));
82
+ const payload = await (async () => {
83
+ if (expectsJson) {
84
+ try {
85
+ return await response.json();
86
+ } catch (error) {
87
+ return await response.text();
88
+ }
89
+ }
90
+
91
+ return response.text();
92
+ })();
93
+
94
+ if (!response.ok) {
95
+ const apiError = payload && typeof payload === 'object' && payload.error && typeof payload.error === 'object'
96
+ ? payload.error
97
+ : {};
98
+ const fallbackMessage = typeof payload === 'string' && payload.trim()
99
+ ? payload
100
+ : response.statusText;
101
+ const message = String(apiError.message || fallbackMessage || 'Request failed');
102
+ const error = new Error(message);
103
+ error.status = response.status;
104
+ error.code = apiError.code || 'REQUEST_FAILED';
105
+ error.details = Array.isArray(apiError.details) ? apiError.details : [];
106
+ error.requestId = apiError.request_id || response.headers.get('X-Request-Id') || '';
107
+ error.response = payload;
108
+ throw error;
109
+ }
110
+
111
+ return payload;
112
+ }
113
+
114
+ /**
115
115
  * Конфигурация запроса
116
116
  * @param {Object} options
117
117
  * @param {string} options.baseUrl - Базовый URL API (опционально)
@@ -231,7 +231,7 @@ class Ajax {
231
231
  method,
232
232
  headers,
233
233
  signal,
234
- credentials: withCredentials ? 'include' : 'same-origin',
234
+ credentials: withCredentials ? 'include' : 'same-origin',
235
235
  ...(body !== undefined && { body }),
236
236
  };
237
237
 
@@ -267,7 +267,7 @@ class Ajax {
267
267
  () => {}
268
268
  );
269
269
  } else {
270
- onSuccess(data);
270
+ onSuccess(data);
271
271
  }
272
272
  })
273
273
  .catch((error) => {
@@ -393,4 +393,4 @@ class Ajax {
393
393
  }
394
394
  }
395
395
 
396
- export default Ajax;
396
+ export default Ajax;