vgapp 1.4.7 → 1.4.9

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 (85) hide show
  1. package/README.md +24 -2
  2. package/app/modules/base-module.js +14 -2
  3. package/app/modules/module-fn.js +5 -4
  4. package/app/modules/vgalert/js/vgalert.js +21 -1
  5. package/app/modules/vgalert/scss/vgalert.scss +48 -0
  6. package/app/modules/vgdropdown/js/vgdropdown.js +58 -27
  7. package/app/modules/vgfilepreview/js/renderers/video-modal.js +5 -1
  8. package/app/modules/vgfilepreview/js/renderers/video.js +6 -1
  9. package/app/modules/vgfilepreview/js/vgfilepreview.js +5 -1
  10. package/app/modules/vgfiles/js/base.js +14 -5
  11. package/app/modules/vgfiles/js/droppable.js +37 -31
  12. package/app/modules/vgfiles/js/vgfiles.js +5 -2
  13. package/app/modules/vgfiles/scss/_variables.scss +3 -1
  14. package/app/modules/vgfiles/scss/vgfiles.scss +55 -10
  15. package/app/modules/vgformsender/js/vgformsender.js +5 -2
  16. package/app/modules/vgloadmore/js/vgloadmore.js +35 -7
  17. package/app/modules/vgnestable/js/vgnestable.js +140 -0
  18. package/app/modules/vgnestable/scss/_variables.scss +16 -9
  19. package/app/modules/vgnestable/scss/vgnestable.scss +35 -2
  20. package/app/modules/vgtable/index.js +3 -0
  21. package/app/modules/vgtable/js/_columns.js +567 -0
  22. package/app/modules/vgtable/js/_expandable.js +242 -0
  23. package/app/modules/vgtable/js/_filters.js +264 -0
  24. package/app/modules/vgtable/js/_fixed-columns.js +249 -0
  25. package/app/modules/vgtable/js/_i18n.js +75 -0
  26. package/app/modules/vgtable/js/_options.js +428 -0
  27. package/app/modules/vgtable/js/_pagination.js +491 -0
  28. package/app/modules/vgtable/js/_panning.js +124 -0
  29. package/app/modules/vgtable/js/_params-groups.js +21 -0
  30. package/app/modules/vgtable/js/_remote.js +395 -0
  31. package/app/modules/vgtable/js/_row-reorder.js +268 -0
  32. package/app/modules/vgtable/js/_search.js +134 -0
  33. package/app/modules/vgtable/js/_selection.js +113 -0
  34. package/app/modules/vgtable/js/_skeleton.js +123 -0
  35. package/app/modules/vgtable/js/_sorting.js +340 -0
  36. package/app/modules/vgtable/js/_states.js +143 -0
  37. package/app/modules/vgtable/js/_sticky-header.js +193 -0
  38. package/app/modules/vgtable/js/_url-state.js +178 -0
  39. package/app/modules/vgtable/js/vgtable.js +1444 -0
  40. package/app/modules/vgtable/readme.md +922 -0
  41. package/app/modules/vgtable/scss/_columns.scss +52 -0
  42. package/app/modules/vgtable/scss/_expandable.scss +54 -0
  43. package/app/modules/vgtable/scss/_fixed-columns.scss +117 -0
  44. package/app/modules/vgtable/scss/_pagination.scss +191 -0
  45. package/app/modules/vgtable/scss/_row-reorder.scss +45 -0
  46. package/app/modules/vgtable/scss/_skeleton.scss +65 -0
  47. package/app/modules/vgtable/scss/_sorting.scss +89 -0
  48. package/app/modules/vgtable/scss/_states.scss +70 -0
  49. package/app/modules/vgtable/scss/_sticky-header.scss +121 -0
  50. package/app/modules/vgtable/scss/_variables.scss +296 -0
  51. package/app/modules/vgtable/scss/vgtable.scss +206 -0
  52. package/app/utils/js/components/placement.js +64 -10
  53. package/app/vgapp.js +3 -3
  54. package/build/vgapp.css +1 -1
  55. package/build/vgapp.css.map +1 -1
  56. package/build/vgapp.js +1 -1
  57. package/build/vgapp.js.map +1 -1
  58. package/index.js +1 -1
  59. package/index.scss +3 -3
  60. package/package.json +5 -5
  61. package/app/modules/vgdynamictable/index.js +0 -4
  62. package/app/modules/vgdynamictable/js/editable.js +0 -438
  63. package/app/modules/vgdynamictable/js/expandable.js +0 -248
  64. package/app/modules/vgdynamictable/js/filters.js +0 -450
  65. package/app/modules/vgdynamictable/js/fixed.js +0 -566
  66. package/app/modules/vgdynamictable/js/options.js +0 -650
  67. package/app/modules/vgdynamictable/js/pagination.js +0 -623
  68. package/app/modules/vgdynamictable/js/search.js +0 -82
  69. package/app/modules/vgdynamictable/js/skeleton.js +0 -144
  70. package/app/modules/vgdynamictable/js/sortable.js +0 -442
  71. package/app/modules/vgdynamictable/js/summary-footer.js +0 -284
  72. package/app/modules/vgdynamictable/js/table-remote.js +0 -821
  73. package/app/modules/vgdynamictable/js/table-state.js +0 -243
  74. package/app/modules/vgdynamictable/js/table-url-state.js +0 -444
  75. package/app/modules/vgdynamictable/js/utils/common.js +0 -48
  76. package/app/modules/vgdynamictable/js/vgdynamictable.js +0 -3870
  77. package/app/modules/vgdynamictable/js/viewport.js +0 -322
  78. package/app/modules/vgdynamictable/readme.md +0 -251
  79. package/app/modules/vgdynamictable/scss/_actions.scss +0 -196
  80. package/app/modules/vgdynamictable/scss/_pagination.scss +0 -186
  81. package/app/modules/vgdynamictable/scss/_skeleton.scss +0 -63
  82. package/app/modules/vgdynamictable/scss/_sortable.scss +0 -66
  83. package/app/modules/vgdynamictable/scss/_table.scss +0 -137
  84. package/app/modules/vgdynamictable/scss/_variables.scss +0 -138
  85. package/app/modules/vgdynamictable/scss/vgdynamictable.scss +0 -264
@@ -0,0 +1,922 @@
1
+ # VGTable
2
+
3
+ `VGTable` — базовый модуль таблиц VGApp. Компонент работает с обычной HTML-таблицей, не заменяет её собственной моделью данных и добавляет интерактивность только для включённых возможностей.
4
+
5
+ ## Возможности
6
+
7
+ - локальная и серверная сортировка, включая мультисортировку;
8
+ - локальная и Remote-пагинация;
9
+ - поиск, фильтры и синхронизация состояния с URL;
10
+ - состояния `empty`, `filtered-empty`, `error`, повтор запроса и сброс фильтров;
11
+ - фиксированный заголовок при прокрутке контейнера или страницы;
12
+ - фиксированные и последовательно складывающиеся колонки;
13
+ - изменение ширины, перестановка, скрытие и отображение колонок;
14
+ - самостоятельная перестановка строк;
15
+ - выбор строк и многоуровневые раскрываемые строки;
16
+ - Remote-режим с JSON-данными или готовой серверной разметкой строк;
17
+ - skeleton загрузки без схлопывания таблицы;
18
+ - встроенные локали `ru` и `en`, пользовательские словари;
19
+ - сохранение пользовательских настроек в `localStorage`;
20
+ - управление через Data API, JavaScript API и DOM-события.
21
+
22
+ ## Подключение
23
+
24
+ Модуль экспортируется из основной точки входа пакета:
25
+
26
+ ```js
27
+ import {VGTable} from 'vgapp';
28
+ ```
29
+
30
+ Стили подключаются общей SCSS-точкой входа VGApp.
31
+
32
+ ## Разметка
33
+
34
+ Рекомендуется явно использовать два контейнера:
35
+
36
+ ```html
37
+ <div class="vg-table-wrapper">
38
+ <div class="vg-table-container">
39
+ <table class="vg-table" data-vg-table>
40
+ <thead>
41
+ <tr>
42
+ <th data-field="id" data-sort-type="number">ID</th>
43
+ <th data-field="name">Название</th>
44
+ <th data-field="status">Статус</th>
45
+ </tr>
46
+ </thead>
47
+ <tbody>
48
+ <tr data-row-key="1">
49
+ <td>1</td>
50
+ <td>Первая запись</td>
51
+ <td>Активна</td>
52
+ </tr>
53
+ </tbody>
54
+ </table>
55
+ </div>
56
+ </div>
57
+ ```
58
+
59
+ Назначение элементов:
60
+
61
+ - `.vg-table-wrapper` — внешний контейнер таблицы и панелей управления;
62
+ - `.vg-table-container` — внутренняя область прокрутки непосредственно таблицы;
63
+ - `.vg-table` — сама таблица;
64
+ - `data-vg-table` — маркер автоматической инициализации;
65
+ - `data-field` — стабильный идентификатор колонки для сортировки, Remote-данных и управления колонками;
66
+ - `data-row-key` — стабильный идентификатор строки для перестановки и связанных сценариев.
67
+
68
+ Если wrapper или container отсутствуют, компонент создаст недостающую обёртку самостоятельно и корректно удалит её при `dispose()`. Явная разметка предпочтительна: она заранее разделяет прокрутку таблицы и внешнюю пагинацию.
69
+
70
+ ## Инициализация
71
+
72
+ ### Автоматическая
73
+
74
+ Все элементы с `data-vg-table` инициализируются после `DOMContentLoaded`:
75
+
76
+ ```html
77
+ <table class="vg-table" data-vg-table>
78
+ <!-- ... -->
79
+ </table>
80
+ ```
81
+
82
+ ### Через JavaScript
83
+
84
+ ```js
85
+ const element = document.querySelector('#orders-table');
86
+ const table = VGTable.getOrCreateInstance(element, {
87
+ pagination: {
88
+ enabled: true,
89
+ per: 10,
90
+ size: {
91
+ enabled: true,
92
+ options: [10, 25, 50, 100],
93
+ },
94
+ },
95
+ });
96
+
97
+ table.init();
98
+ ```
99
+
100
+ Повторный вызов `getOrCreateInstance()` возвращает существующий экземпляр.
101
+
102
+ ## Data API
103
+
104
+ Основные возможности можно включить атрибутами таблицы:
105
+
106
+ ```html
107
+ <table
108
+ id="orders-table"
109
+ class="vg-table"
110
+ data-vg-table
111
+ data-locale="ru"
112
+ data-pagination-enabled="true"
113
+ data-pagination-per-page="10"
114
+ data-pagination-show-per-page="true"
115
+ data-pagination-per-page-options="10,25,50,100"
116
+ data-sort-multiple="true"
117
+ data-sticky-header-enabled="true"
118
+ data-sticky-header-mode="container"
119
+ data-sticky-header-max-height="32rem"
120
+ >
121
+ <!-- ... -->
122
+ </table>
123
+ ```
124
+
125
+ Имена атрибутов обычно повторяют группы JavaScript-настроек: например, `stickyHeader.maxHeight` соответствует `data-sticky-header-max-height`. Для совместимости с VGDynamicTable некоторые параметры также имеют короткие aliases, например `data-pagination-show-per-page`.
126
+
127
+ Полный актуальный список параметров, допустимых значений и соответствующих data-атрибутов находится в `js/_options.js`.
128
+
129
+ ## Настройки
130
+
131
+ Ниже приведена структура основных параметров. Неуказанные значения дополняются настройками по умолчанию.
132
+
133
+ ```js
134
+ const table = new VGTable(element, {
135
+ locale: 'ru',
136
+ i18n: {},
137
+
138
+ sort: {
139
+ enabled: true,
140
+ hover: true,
141
+ multiple: false,
142
+ multipleWithShift: true,
143
+ },
144
+
145
+ pan: {
146
+ enabled: true,
147
+ },
148
+
149
+ pagination: {
150
+ enabled: false,
151
+ page: 1,
152
+ per: 10,
153
+ max: 100,
154
+ position: 'bottom', // top | bottom | both
155
+ align: 'right', // left | center | right | between
156
+ ellipsis: true,
157
+ ellipsisHover: true,
158
+ threshold: 5,
159
+ visible: 5,
160
+ size: {
161
+ enabled: false,
162
+ options: [10, 25, 50, 100],
163
+ label: 'Строк на странице',
164
+ suffix: 'на страницу',
165
+ },
166
+ quick: {
167
+ enabled: false, // true | false | auto
168
+ label: 'Перейти к странице',
169
+ button: 'Перейти',
170
+ },
171
+ persist: {
172
+ page: true,
173
+ per: true,
174
+ },
175
+ storage: {
176
+ key: '',
177
+ },
178
+ scroll: false, // false | table | window
179
+ },
180
+
181
+ stickyHeader: {
182
+ enabled: false,
183
+ mode: 'container', // container | page
184
+ top: null,
185
+ maxHeight: '24rem',
186
+ },
187
+
188
+ fixedColumns: {
189
+ enabled: false,
190
+ columns: '',
191
+ mode: 'fixed', // fixed | stack
192
+ stackGap: 0,
193
+ },
194
+
195
+ columnResize: {
196
+ enabled: false,
197
+ minWidth: 80,
198
+ maxWidth: 600,
199
+ persist: false,
200
+ storageKey: '',
201
+ },
202
+
203
+ columnReorder: {
204
+ enabled: false,
205
+ persist: false,
206
+ storageKey: '',
207
+ },
208
+
209
+ columnVisibility: {
210
+ enabled: false,
211
+ controls: '',
212
+ minVisible: 1,
213
+ persist: false,
214
+ storageKey: '',
215
+ },
216
+
217
+ rowReorder: {
218
+ enabled: false,
219
+ mode: 'handle', // handle | row
220
+ handleSelector: '[data-row-reorder-handle]',
221
+ keyAttr: 'data-row-key',
222
+ persist: false,
223
+ storageKey: '',
224
+ },
225
+
226
+ selection: {
227
+ enabled: false,
228
+ click: true,
229
+ multiple: false,
230
+ },
231
+
232
+ expandable: {
233
+ enabled: false,
234
+ idAttr: 'data-expand-id',
235
+ parentAttr: 'data-expand-parent-id',
236
+ toggleSelector: '[data-expand-toggle]',
237
+ collapsed: true,
238
+ },
239
+
240
+ search: {
241
+ enabled: false,
242
+ input: '',
243
+ param: 'q',
244
+ debounce: 300,
245
+ resetPage: true,
246
+ trim: true,
247
+ fields: [],
248
+ },
249
+
250
+ filters: {
251
+ enabled: false,
252
+ form: '',
253
+ debounce: 300,
254
+ apply: 'auto', // auto | manual
255
+ resetPage: true,
256
+ skipEmpty: true,
257
+ trim: true,
258
+ defaultOperator: 'eq',
259
+ },
260
+
261
+ state: {
262
+ enabled: true,
263
+ },
264
+
265
+ urlState: {
266
+ enabled: false,
267
+ read: true,
268
+ write: true,
269
+ listen: true,
270
+ mode: 'replace', // replace | push
271
+ prefix: '',
272
+ },
273
+
274
+ loading: {
275
+ enabled: true,
276
+ minDelay: 500,
277
+ skeleton: 5,
278
+ },
279
+
280
+ request: {
281
+ route: '',
282
+ method: 'GET',
283
+ credentials: 'same-origin',
284
+ headers: {'Accept': 'application/json'},
285
+ params: {},
286
+ parammap: {},
287
+ responsemode: 'data', // data | view | auto
288
+ datapath: 'data',
289
+ metapath: 'meta',
290
+ viewpath: 'view.tbody',
291
+ viewparam: 'view',
292
+ viewvalue: 'rows',
293
+ fieldsparam: 'fields',
294
+ cache: {
295
+ enable: true,
296
+ ttl: 30000,
297
+ max: 30,
298
+ },
299
+ export: {
300
+ route: '',
301
+ },
302
+ },
303
+ });
304
+ ```
305
+
306
+ ## Сортировка
307
+
308
+ По умолчанию сортировка включена для обычных таблиц. Поле определяется по `data-field`, тип — автоматически или через `data-sort-type`.
309
+
310
+ ```html
311
+ <th data-field="created_at" data-sort-type="date">Создано</th>
312
+ <th data-field="total" data-sort-type="number">Сумма</th>
313
+ <th data-field="actions" data-sort-enabled="false">Действия</th>
314
+ ```
315
+
316
+ Для значения, отличного от отображаемого текста, используется `data-sort-value`:
317
+
318
+ ```html
319
+ <td data-sort-value="2026-08-22">22 августа 2026</td>
320
+ ```
321
+
322
+ При `sort.multiple: true` следующая колонка добавляется с `Shift`. Если `multipleWithShift` равен `false`, мультисортировка действует постоянно.
323
+
324
+ Таблица с `colspan` или `rowspan` считается сложной. Для неё сортировка и операции, меняющие структуру колонок, отключаются автоматически, чтобы не нарушить сетку.
325
+
326
+ ## Пагинация
327
+
328
+ Панель пагинации создаётся вне `.vg-table-container`, поэтому не попадает в горизонтальную прокрутку таблицы.
329
+
330
+ ```html
331
+ <table
332
+ class="vg-table"
333
+ data-vg-table
334
+ data-pagination-enabled="true"
335
+ data-pagination-per-page="10"
336
+ data-pagination-position="bottom"
337
+ data-pagination-align="right"
338
+ data-pagination-show-per-page="true"
339
+ data-pagination-per-page-options="10,25,50,100"
340
+ ></table>
341
+ ```
342
+
343
+ Выбор количества строк использует `VGDropdown`, но поле также принимает произвольное число от `1` до `pagination.max`. Текущая страница и размер страницы могут сохраняться в `localStorage`.
344
+
345
+ ## Поиск, фильтры и состояния
346
+
347
+ ### Поиск
348
+
349
+ ```html
350
+ <input id="orders-search" class="form-control" type="search">
351
+ <button type="button" data-search-reset>Очистить</button>
352
+
353
+ <table
354
+ class="vg-table"
355
+ data-vg-table
356
+ data-search-enable="true"
357
+ data-search-input="#orders-search"
358
+ data-search-fields="name,status"
359
+ ></table>
360
+ ```
361
+
362
+ В локальном режиме поиск фильтрует существующие строки, в Remote-режиме передаёт значение на сервер параметром `q` или именем из `search.param`.
363
+
364
+ ### Фильтры
365
+
366
+ ```html
367
+ <form id="orders-filters">
368
+ <select data-filter-field="status">
369
+ <option value="">Все статусы</option>
370
+ <option value="active">Активные</option>
371
+ </select>
372
+
373
+ <input
374
+ type="number"
375
+ data-filter-field="total"
376
+ data-filter-operator="gte"
377
+ placeholder="Сумма от"
378
+ >
379
+
380
+ <button type="button" data-filter-reset>Сбросить фильтры</button>
381
+ </form>
382
+
383
+ <table
384
+ class="vg-table"
385
+ data-vg-table
386
+ data-filters-enable="true"
387
+ data-filters-form="#orders-filters"
388
+ data-filters-apply="auto"
389
+ ></table>
390
+ ```
391
+
392
+ Доступные операторы: `eq`, `neq`, `contains`, `starts`, `ends`, `gt`, `gte`, `lt`, `lte`, `in`, `notin`.
393
+
394
+ Кнопка сброса очищает фильтры и глобальный поиск. При отсутствии результата компонент различает обычное пустое состояние и `filtered-empty`.
395
+
396
+ ### Состояния
397
+
398
+ Встроенный state-layer поддерживает:
399
+
400
+ - `empty` — исходных строк нет;
401
+ - `filtered-empty` — строки не найдены после поиска или фильтрации;
402
+ - `error` — Remote-запрос завершился ошибкой;
403
+ - повтор запроса из состояния ошибки;
404
+ - сброс поиска и фильтров из `filtered-empty`.
405
+
406
+ Тексты задаются через `state.labels` или соответствующие `data-state-*-label`.
407
+
408
+ ## Состояние в URL
409
+
410
+ Общий URL state синхронизирует `page`, `perPage`, `sort`, `dir`, `search` и фильтры:
411
+
412
+ ```html
413
+ <table
414
+ class="vg-table"
415
+ data-vg-table
416
+ data-url-state-enable="true"
417
+ data-url-state-mode="replace"
418
+ data-url-state-prefix="orders-"
419
+ ></table>
420
+ ```
421
+
422
+ Для нескольких таблиц на одной странице обязательно задавайте разные `urlState.prefix`. Компонент умеет читать исходный query string, записывать изменения и обрабатывать переходы Back/Forward.
423
+
424
+ ## Фиксированный заголовок
425
+
426
+ VGTable использует нативный `position: sticky` и отдельный header-слой без клонирования строк и обработчиков.
427
+
428
+ ### Прокрутка внутри таблицы
429
+
430
+ ```html
431
+ <table
432
+ class="vg-table"
433
+ data-vg-table
434
+ data-sticky-header-enabled="true"
435
+ data-sticky-header-mode="container"
436
+ data-sticky-header-max-height="28rem"
437
+ ></table>
438
+ ```
439
+
440
+ ### Прокрутка страницы
441
+
442
+ ```html
443
+ <table
444
+ class="vg-table"
445
+ data-vg-table
446
+ data-sticky-header-enabled="true"
447
+ data-sticky-header-mode="page"
448
+ data-sticky-header-top="72"
449
+ ></table>
450
+ ```
451
+
452
+ `stickyHeader.top` принимает число пикселей или CSS-значение. Если параметр равен `null`, отступом можно управлять публичной переменной `--vg-table-sticky-top`, например с учётом фиксированного header сайта.
453
+
454
+ ## Фиксированные колонки
455
+
456
+ Колонки задаются по `data-field` или zero-based индексу:
457
+
458
+ ```html
459
+ <table
460
+ class="vg-table"
461
+ data-vg-table
462
+ data-fixed-columns-enabled="true"
463
+ data-fixed-columns="left:id,name;right:actions"
464
+ data-fixed-columns-mode="fixed"
465
+ ></table>
466
+ ```
467
+
468
+ Поддерживаются режимы:
469
+
470
+ - `fixed` — колонки постоянно закреплены у соответствующего края;
471
+ - `stack` — колонки последовательно фиксируются при прокрутке.
472
+
473
+ Отдельную колонку также можно пометить непосредственно в заголовке. Фиксированные ячейки получают непрозрачный фон и повышенный `z-index`, чтобы соседние колонки не просвечивали при прокрутке.
474
+
475
+ ## Управление колонками
476
+
477
+ Для изменения ширины, перестановки и видимости каждой колонке нужен стабильный `data-field`.
478
+
479
+ ```html
480
+ <div id="column-controls">
481
+ <label><input type="checkbox" data-vg-table-column="email" checked> Почта</label>
482
+ <label><input type="checkbox" data-vg-table-column="city" checked> Город</label>
483
+ </div>
484
+
485
+ <table
486
+ class="vg-table"
487
+ data-vg-table
488
+ data-column-resize-enable="true"
489
+ data-column-reorder-enable="true"
490
+ data-column-visibility-enable="true"
491
+ data-column-visibility-controls="#column-controls"
492
+ ></table>
493
+ ```
494
+
495
+ Все три режима могут независимо сохранять состояние в `localStorage`. Фиксированные колонки разрешено изменять по ширине, но нельзя скрывать или переносить через границу фиксированной области.
496
+
497
+ ## Перестановка строк
498
+
499
+ ```html
500
+ <table
501
+ class="vg-table"
502
+ data-vg-table
503
+ data-row-reorder-enable="true"
504
+ data-row-reorder-mode="handle"
505
+ >
506
+ <tbody>
507
+ <tr data-row-key="order-1">
508
+ <td><button type="button" data-row-reorder-handle>Переместить</button></td>
509
+ <td>Заказ №1</td>
510
+ </tr>
511
+ </tbody>
512
+ </table>
513
+ ```
514
+
515
+ Режим `handle` начинает перенос только с указанного элемента, `row` — с любой неинтерактивной области строки. При включённой перестановке строк сортировка не инициализируется: ручной порядок и сортировка являются взаимоисключающими режимами.
516
+
517
+ ## Выбор строк
518
+
519
+ ```js
520
+ const table = VGTable.getOrCreateInstance(element, {
521
+ selection: {
522
+ enabled: true,
523
+ click: true,
524
+ multiple: true,
525
+ },
526
+ });
527
+ ```
528
+
529
+ Клики по ссылкам, кнопкам и полям формы внутри строки не изменяют выбор. Текущее состояние доступно через `getSelectedRows()`.
530
+
531
+ ## Раскрываемые строки
532
+
533
+ Связи дерева задаются ID строки и ID родителя. Уровень вложенности не ограничен разметкой одного уровня.
534
+
535
+ ```html
536
+ <table
537
+ class="vg-table"
538
+ data-vg-table
539
+ data-expandable-enabled="true"
540
+ data-expandable-collapsed="true"
541
+ >
542
+ <tbody>
543
+ <tr data-expand-id="root">
544
+ <td><button type="button" data-expand-toggle></button> Родитель</td>
545
+ </tr>
546
+ <tr data-expand-id="child" data-expand-parent-id="root">
547
+ <td>Дочерняя строка</td>
548
+ </tr>
549
+ <tr data-expand-id="leaf" data-expand-parent-id="child">
550
+ <td>Третий уровень</td>
551
+ </tr>
552
+ </tbody>
553
+ </table>
554
+ ```
555
+
556
+ Если кнопка управления не добавлена вручную, компонент создаёт её для родительской строки автоматически.
557
+
558
+ ## Remote-режим
559
+
560
+ Непустой `request.route` автоматически переводит таблицу в Remote-режим. Запросы выполняются через общий Ajax-компонент VGApp.
561
+
562
+ ```html
563
+ <table
564
+ id="remote-orders"
565
+ class="vg-table"
566
+ data-vg-table
567
+ data-request-route="/api/orders"
568
+ data-request-method="GET"
569
+ data-request-responsemode="data"
570
+ data-request-datapath="data"
571
+ data-request-metapath="meta"
572
+ data-request-cache-enable="true"
573
+ data-pagination-enabled="true"
574
+ data-pagination-show-per-page="true"
575
+ ></table>
576
+ ```
577
+
578
+ Компонент передаёт серверу:
579
+
580
+ - `page` — текущую страницу;
581
+ - `per_page` — количество строк;
582
+ - `sort` — поле или список полей через запятую;
583
+ - `dir` — направление или список направлений через запятую;
584
+ - параметр поиска, по умолчанию `q`;
585
+ - параметры активных фильтров;
586
+ - дополнительные значения из `request.params`.
587
+
588
+ Имена можно переопределить через `request.parammap`:
589
+
590
+ ```js
591
+ request: {
592
+ route: '/api/orders',
593
+ parammap: {
594
+ q: 'search',
595
+ per_page: 'limit',
596
+ },
597
+ }
598
+ ```
599
+
600
+ ### Ответ с данными
601
+
602
+ ```json
603
+ {
604
+ "data": [
605
+ {
606
+ "id": 1,
607
+ "name": "Заказ №1",
608
+ "status": "active"
609
+ }
610
+ ],
611
+ "meta": {
612
+ "current_page": 1,
613
+ "per_page": 10,
614
+ "total": 100,
615
+ "last_page": 10
616
+ }
617
+ }
618
+ ```
619
+
620
+ Ключи объектов сопоставляются с заголовками `th[data-field]`. Путь к массиву и meta можно изменить через `datapath` и `metapath`.
621
+
622
+ ### Ответ с готовой разметкой
623
+
624
+ Для `responsemode: 'view'` сервер возвращает HTML строк:
625
+
626
+ ```json
627
+ {
628
+ "view": {
629
+ "tbody": "<tr data-row-key=\"1\"><td>1</td><td>Заказ №1</td></tr>"
630
+ },
631
+ "meta": {
632
+ "page": 1,
633
+ "per_page": 10,
634
+ "total": 100,
635
+ "pages": 10
636
+ }
637
+ }
638
+ ```
639
+
640
+ В режиме `auto` компонент сначала ищет server-rendered view, затем массив данных. Для view-запроса автоматически передаются имя представления и список полей, если настроены `viewparam`, `viewvalue` и `fieldsparam`.
641
+
642
+ Повторяющиеся запросы кратковременно кешируются в памяти экземпляра. Для выгрузки текущего серверного состояния используется отдельный `request.export.route` или основной route.
643
+
644
+ ## Группы параметров
645
+
646
+ Повторяющуюся конфигурацию можно зарегистрировать один раз и подключать одним атрибутом:
647
+
648
+ ```js
649
+ VGTable.registerParamsGroup('remote-registry', {
650
+ pagination: {
651
+ enabled: true,
652
+ size: {enabled: true},
653
+ },
654
+ loading: {
655
+ enabled: true,
656
+ skeleton: 10,
657
+ },
658
+ urlState: {
659
+ enabled: true,
660
+ },
661
+ });
662
+ ```
663
+
664
+ ```html
665
+ <table
666
+ class="vg-table"
667
+ data-vg-table
668
+ data-group-params="remote-registry"
669
+ data-request-route="/api/orders"
670
+ ></table>
671
+ ```
672
+
673
+ Параметры конкретного экземпляра и его Data API дополняют или переопределяют зарегистрированную группу.
674
+
675
+ Доступные методы:
676
+
677
+ ```js
678
+ VGTable.registerParamsGroup(name, params);
679
+ VGTable.getParamsGroup(name);
680
+ VGTable.unregisterParamsGroup(name);
681
+ ```
682
+
683
+ ## Интернационализация
684
+
685
+ Встроены локали `ru` и `en`. Региональный код, например `ru-RU`, использует точное совпадение, а затем базовую локаль `ru`.
686
+
687
+ ```js
688
+ const table = VGTable.getOrCreateInstance(element, {
689
+ locale: 'en',
690
+ i18n: {
691
+ en: {
692
+ pagination: {
693
+ size: {
694
+ label: 'Rows per page',
695
+ },
696
+ },
697
+ },
698
+ },
699
+ });
700
+
701
+ table.init();
702
+ table.setLocale('ru');
703
+ ```
704
+
705
+ Пользовательский словарь объединяется со встроенным, поэтому достаточно указать только изменяемые строки.
706
+
707
+ ## Публичные методы
708
+
709
+ ### Экземпляр и жизненный цикл
710
+
711
+ ```js
712
+ VGTable.getInstance(element);
713
+ VGTable.getOrCreateInstance(element, params);
714
+
715
+ table.init();
716
+ table.dispose();
717
+ table.isRemote();
718
+ table.isComplex();
719
+ ```
720
+
721
+ ### Локаль
722
+
723
+ ```js
724
+ table.getLocale();
725
+ table.setLocale(locale);
726
+ ```
727
+
728
+ ### Сортировка
729
+
730
+ ```js
731
+ table.setSort(field, direction);
732
+ table.setSorts(sorts);
733
+ table.getSort();
734
+ table.getSorts();
735
+ table.clearSort();
736
+ ```
737
+
738
+ ### Пагинация
739
+
740
+ ```js
741
+ table.setPage(page);
742
+ table.setPerPage(perPage);
743
+ table.getPagination();
744
+ table.refreshPagination();
745
+ ```
746
+
747
+ ### Поиск, фильтры и URL
748
+
749
+ ```js
750
+ table.getSearch();
751
+ table.setSearch(value);
752
+ table.resetSearch();
753
+ table.refreshSearch();
754
+
755
+ table.getFilters();
756
+ table.setFilters(filters);
757
+ table.resetFilters();
758
+ table.refreshFilters();
759
+
760
+ table.getUrlState();
761
+ table.refreshUrlState();
762
+ ```
763
+
764
+ ### Состояния и Remote
765
+
766
+ ```js
767
+ table.getTableState();
768
+ table.showTableState(state, message);
769
+ table.clearTableState();
770
+
771
+ table.reload();
772
+ table.setRequestParams(params);
773
+ table.getRequestState();
774
+ table.clearRequestCache();
775
+ table.exportRemote(format, options);
776
+ ```
777
+
778
+ ### Заголовок и колонки
779
+
780
+ ```js
781
+ table.refreshStickyHeader();
782
+ table.refreshFixedColumns();
783
+ table.getFixedColumns();
784
+
785
+ table.getColumns();
786
+ table.setColumnWidth(field, width);
787
+ table.moveColumn(field, target);
788
+ table.setColumnVisible(field, visible);
789
+ table.resetColumns();
790
+ table.refreshColumns();
791
+ ```
792
+
793
+ ### Строки
794
+
795
+ ```js
796
+ table.selectRow(row, selected);
797
+ table.toggleRow(row);
798
+ table.getSelectedRows();
799
+ table.clearSelection();
800
+
801
+ table.toggleExpanded(id);
802
+ table.expandRow(id);
803
+ table.collapseRow(id);
804
+ table.getExpandable();
805
+ table.refreshExpandable();
806
+
807
+ table.getRowOrder();
808
+ table.moveRow(row, target, position, emit);
809
+ table.resetRows();
810
+ table.refreshRowReorder();
811
+ ```
812
+
813
+ ## События
814
+
815
+ Все события всплывают от элемента таблицы и доступны через `event.detail`.
816
+
817
+ | Событие | Назначение |
818
+ | --- | --- |
819
+ | `sortchange.vg.table` | Изменилось поле, направление или порядок мультисортировки |
820
+ | `pagechange.vg.table` | Изменилась текущая страница |
821
+ | `perpagechange.vg.table` | Изменилось количество строк на странице |
822
+ | `searchchange.vg.table` | Изменился глобальный поиск |
823
+ | `filterschange.vg.table` | Изменились активные фильтры |
824
+ | `selectionchange.vg.table` | Изменился выбор строк |
825
+ | `rowtoggle.vg.table` | Изменилось состояние раскрываемой ветви |
826
+ | `rowexpand.vg.table` | Ветвь развёрнута |
827
+ | `rowcollapse.vg.table` | Ветвь свёрнута |
828
+ | `rowreorder.vg.table` | Изменён порядок строк |
829
+ | `columnresize.vg.table` | Изменена ширина колонки |
830
+ | `columnreorder.vg.table` | Изменён порядок колонок |
831
+ | `columnvisibilitychange.vg.table` | Изменена видимость колонки |
832
+ | `beforeload.vg.table` | Remote-запрос подготовлен к отправке |
833
+ | `requestsuccess.vg.table` | Получен успешный Remote-ответ |
834
+ | `requesterror.vg.table` | Remote-запрос завершился ошибкой |
835
+ | `dataloaded.vg.table` | Данные ответа прочитаны |
836
+ | `afterrender.vg.table` | Строки ответа отрисованы |
837
+ | `statechange.vg.table` | Изменилось состояние результата |
838
+ | `urlstateread.vg.table` | Состояние прочитано из URL |
839
+ | `urlstatewrite.vg.table` | Состояние записано в URL |
840
+ | `urlstateerror.vg.table` | Возникла ошибка разбора URL state |
841
+ | `localechange.vg.table` | Изменилась активная локаль |
842
+
843
+ ```js
844
+ element.addEventListener('sortchange.vg.table', (event) => {
845
+ console.log(event.detail.sorts);
846
+ });
847
+
848
+ element.addEventListener('requesterror.vg.table', (event) => {
849
+ console.error(event.detail.error);
850
+ });
851
+ ```
852
+
853
+ ## Размеры и оформление
854
+
855
+ Доступные классы размеров:
856
+
857
+ ```html
858
+ <table class="vg-table vg-table-xs"></table>
859
+ <table class="vg-table vg-table-sm"></table>
860
+ <table class="vg-table"></table>
861
+ <table class="vg-table vg-table-lg"></table>
862
+ <table class="vg-table vg-table-xl"></table>
863
+ ```
864
+
865
+ Средний размер является значением по умолчанию и не требует класса `vg-table-md`.
866
+
867
+ Дополнительные классы:
868
+
869
+ - `.vg-table-striped` — чередование фона строк;
870
+ - `.vg-table-hovered` — подсветка строки при наведении.
871
+
872
+ Для темизации доступны группы CSS-переменных:
873
+
874
+ - `--vg-table-*` — таблица и ячейки;
875
+ - `--vg-table-wrapper-*` — внешний wrapper;
876
+ - `--vg-table-container-*` — scroll-контейнер;
877
+ - `--vg-table-sort-*` — сортировка;
878
+ - `--vg-table-columns-*` — управление колонками;
879
+ - `--vg-table-row-reorder-*` — перестановка строк;
880
+ - `--vg-table-pan-*` — горизонтальное перемещение;
881
+ - `--vg-table-selection-*` — выбранные строки;
882
+ - `--vg-table-expandable-*` — раскрываемое дерево;
883
+ - `--vg-table-pagination-*` — пагинация;
884
+ - `--vg-table-sticky-*` — фиксированный заголовок;
885
+ - `--vg-table-fixed-*` — фиксированные колонки;
886
+ - `--vg-table-state-*` — состояния результата;
887
+ - `--vg-table-skeleton-*` — skeleton загрузки.
888
+
889
+ ```css
890
+ .orders-table {
891
+ --vg-table-sticky-top: 4.5rem;
892
+ --vg-table-pagination-gap: 0.75rem;
893
+ }
894
+ ```
895
+
896
+ ## Доступность и клавиатура
897
+
898
+ - сортируемые заголовки поддерживают `Enter` и `Space`;
899
+ - resize-handle колонок поддерживает стрелки, `Home` и `End`;
900
+ - handle перестановки строк поддерживает стрелки вверх/вниз, `Home` и `End`;
901
+ - кнопки раскрываемых ветвей получают `aria-expanded` и локализованную подпись;
902
+ - активная страница отмечается `aria-current="page"`;
903
+ - шевроны пагинации имеют текстовые `aria-label`;
904
+ - состояние таблицы и интерактивные контролы обновляются без удаления исходной семантики `<table>`.
905
+
906
+ ## Важные сочетания режимов
907
+
908
+ - `colspan` и `rowspan` автоматически помечают таблицу как сложную и отключают сортировку, фиксированные колонки и структурное управление колонками;
909
+ - самостоятельная перестановка строк отключает сортировку;
910
+ - внешний wrapper отвечает за панели, внутренний container — только за прокрутку таблицы;
911
+ - для Remote-таблицы `data-field` должны совпадать с ключами данных или сервер должен возвращать готовый view;
912
+ - для сохранения колонок и строк нужны стабильные `data-field` и `data-row-key`;
913
+ - после ручного изменения DOM вызывайте соответствующий метод `refresh*()`.
914
+
915
+ ## Удаление экземпляра
916
+
917
+ ```js
918
+ const table = VGTable.getInstance(element);
919
+ table?.dispose();
920
+ ```
921
+
922
+ `dispose()` удаляет созданные компонентом обработчики и служебные элементы, восстанавливает исходную структуру таблицы и удаляет только те wrapper/container, которые VGTable создал самостоятельно.