vgapp 1.4.8 → 1.5.0

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 (86) 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 +29 -15
  11. package/app/modules/vgfiles/js/droppable.js +37 -31
  12. package/app/modules/vgfiles/js/vgfiles.js +10 -4
  13. package/app/modules/vgfiles/scss/_mixins.scss +12 -2
  14. package/app/modules/vgfiles/scss/_variables.scss +3 -1
  15. package/app/modules/vgfiles/scss/vgfiles.scss +55 -10
  16. package/app/modules/vgformsender/js/vgformsender.js +5 -2
  17. package/app/modules/vgloadmore/js/vgloadmore.js +35 -7
  18. package/app/modules/vgnestable/js/vgnestable.js +140 -0
  19. package/app/modules/vgnestable/scss/_variables.scss +16 -9
  20. package/app/modules/vgnestable/scss/vgnestable.scss +35 -2
  21. package/app/modules/vgtable/index.js +3 -0
  22. package/app/modules/vgtable/js/_columns.js +567 -0
  23. package/app/modules/vgtable/js/_expandable.js +242 -0
  24. package/app/modules/vgtable/js/_filters.js +264 -0
  25. package/app/modules/vgtable/js/_fixed-columns.js +249 -0
  26. package/app/modules/vgtable/js/_i18n.js +75 -0
  27. package/app/modules/vgtable/js/_options.js +428 -0
  28. package/app/modules/vgtable/js/_pagination.js +491 -0
  29. package/app/modules/vgtable/js/_panning.js +124 -0
  30. package/app/modules/vgtable/js/_params-groups.js +21 -0
  31. package/app/modules/vgtable/js/_remote.js +395 -0
  32. package/app/modules/vgtable/js/_row-reorder.js +268 -0
  33. package/app/modules/vgtable/js/_search.js +134 -0
  34. package/app/modules/vgtable/js/_selection.js +113 -0
  35. package/app/modules/vgtable/js/_skeleton.js +123 -0
  36. package/app/modules/vgtable/js/_sorting.js +340 -0
  37. package/app/modules/vgtable/js/_states.js +143 -0
  38. package/app/modules/vgtable/js/_sticky-header.js +218 -0
  39. package/app/modules/vgtable/js/_url-state.js +178 -0
  40. package/app/modules/vgtable/js/vgtable.js +1444 -0
  41. package/app/modules/vgtable/readme.md +922 -0
  42. package/app/modules/vgtable/scss/_columns.scss +52 -0
  43. package/app/modules/vgtable/scss/_expandable.scss +54 -0
  44. package/app/modules/vgtable/scss/_fixed-columns.scss +117 -0
  45. package/app/modules/vgtable/scss/_pagination.scss +191 -0
  46. package/app/modules/vgtable/scss/_row-reorder.scss +45 -0
  47. package/app/modules/vgtable/scss/_skeleton.scss +65 -0
  48. package/app/modules/vgtable/scss/_sorting.scss +89 -0
  49. package/app/modules/vgtable/scss/_states.scss +70 -0
  50. package/app/modules/vgtable/scss/_sticky-header.scss +127 -0
  51. package/app/modules/vgtable/scss/_variables.scss +296 -0
  52. package/app/modules/vgtable/scss/vgtable.scss +206 -0
  53. package/app/utils/js/components/placement.js +64 -10
  54. package/app/vgapp.js +3 -3
  55. package/build/vgapp.css +1 -1
  56. package/build/vgapp.css.map +1 -1
  57. package/build/vgapp.js +1 -1
  58. package/build/vgapp.js.map +1 -1
  59. package/index.js +1 -1
  60. package/index.scss +3 -3
  61. package/package.json +4 -4
  62. package/app/modules/vgdynamictable/index.js +0 -4
  63. package/app/modules/vgdynamictable/js/editable.js +0 -438
  64. package/app/modules/vgdynamictable/js/expandable.js +0 -248
  65. package/app/modules/vgdynamictable/js/filters.js +0 -450
  66. package/app/modules/vgdynamictable/js/fixed.js +0 -566
  67. package/app/modules/vgdynamictable/js/options.js +0 -650
  68. package/app/modules/vgdynamictable/js/pagination.js +0 -623
  69. package/app/modules/vgdynamictable/js/search.js +0 -82
  70. package/app/modules/vgdynamictable/js/skeleton.js +0 -144
  71. package/app/modules/vgdynamictable/js/sortable.js +0 -442
  72. package/app/modules/vgdynamictable/js/summary-footer.js +0 -284
  73. package/app/modules/vgdynamictable/js/table-remote.js +0 -821
  74. package/app/modules/vgdynamictable/js/table-state.js +0 -243
  75. package/app/modules/vgdynamictable/js/table-url-state.js +0 -444
  76. package/app/modules/vgdynamictable/js/utils/common.js +0 -48
  77. package/app/modules/vgdynamictable/js/vgdynamictable.js +0 -3870
  78. package/app/modules/vgdynamictable/js/viewport.js +0 -322
  79. package/app/modules/vgdynamictable/readme.md +0 -251
  80. package/app/modules/vgdynamictable/scss/_actions.scss +0 -196
  81. package/app/modules/vgdynamictable/scss/_pagination.scss +0 -186
  82. package/app/modules/vgdynamictable/scss/_skeleton.scss +0 -63
  83. package/app/modules/vgdynamictable/scss/_sortable.scss +0 -66
  84. package/app/modules/vgdynamictable/scss/_table.scss +0 -137
  85. package/app/modules/vgdynamictable/scss/_variables.scss +0 -138
  86. package/app/modules/vgdynamictable/scss/vgdynamictable.scss +0 -264
@@ -0,0 +1,249 @@
1
+ /**
2
+ * Описание: нативная фиксация колонок VGTable при горизонтальной прокрутке.
3
+ * Возможности: left/right sticky-колонки, последовательный stack-режим, синхронные тени и совместимость с Fixed Header.
4
+ */
5
+
6
+ const CELL_ATTRIBUTE = 'data-vg-table-fixed-side';
7
+ const EDGE_ATTRIBUTE = 'data-vg-table-fixed-edge';
8
+ const OFFSET_PROPERTY = '--vg-table-fixed-offset';
9
+ const WRAPPER_CLASSES = [
10
+ 'vg-table-wrapper--fixed-columns',
11
+ 'vg-table-wrapper--fixed-columns-stack',
12
+ 'is-vg-table-fixed-scrolled-start',
13
+ 'is-vg-table-fixed-scrolled-end',
14
+ ];
15
+
16
+ class _fixedColumns {
17
+ constructor(element, wrapper, params = {}, stickyHeader = null) {
18
+ this._element = element;
19
+ this._wrapper = wrapper;
20
+ this._params = params;
21
+ this._stickyHeader = stickyHeader;
22
+ this._headerTable = stickyHeader?.getHeaderTable?.() || element;
23
+ this._scrollHost = stickyHeader?.getBody?.() || element.closest('.vg-table-container') || wrapper;
24
+ this._columns = {left: [], right: []};
25
+ this._widths = [];
26
+ this._previousClasses = new Map();
27
+ this._activeEdges = {left: null, right: null};
28
+ this._resizeObserver = null;
29
+ this._boundScroll = this._syncScrollState.bind(this);
30
+ this._boundResize = this.refresh.bind(this);
31
+ }
32
+
33
+ init() {
34
+ if (!this._wrapper || !this._scrollHost || !this._headerTable?.tHead) return this;
35
+
36
+ WRAPPER_CLASSES.forEach((className) => {
37
+ this._previousClasses.set(className, this._wrapper.classList.contains(className));
38
+ });
39
+ this._wrapper.classList.add('vg-table-wrapper--fixed-columns');
40
+ this._wrapper.classList.toggle('vg-table-wrapper--fixed-columns-stack', this._params.mode === 'stack');
41
+ this._element.setAttribute('data-vg-table-fixed-columns', this._params.mode);
42
+ this._scrollHost.addEventListener('scroll', this._boundScroll, {passive: true});
43
+
44
+ if (typeof ResizeObserver !== 'undefined') {
45
+ this._resizeObserver = new ResizeObserver(this._boundResize);
46
+ this._resizeObserver.observe(this._scrollHost);
47
+ this._resizeObserver.observe(this._element);
48
+ } else if (typeof window !== 'undefined') {
49
+ window.addEventListener('resize', this._boundResize);
50
+ }
51
+
52
+ this.refresh();
53
+ return this;
54
+ }
55
+
56
+ refresh() {
57
+ this._headerTable = this._stickyHeader?.getHeaderTable?.() || this._element;
58
+ this._scrollHost = this._stickyHeader?.getBody?.() || this._element.closest('.vg-table-container') || this._wrapper;
59
+ const headers = this._getHeaders();
60
+ if (!headers.length) return this;
61
+
62
+ this._clearCells();
63
+ this._columns = this._resolveColumns(headers);
64
+ this._widths = headers.map((header, index) => this._measureColumn(header, index));
65
+ this._applySide('left', this._columns.left, headers);
66
+ this._applySide('right', [...this._columns.right].reverse(), headers);
67
+ this._syncScrollState(true);
68
+ return this;
69
+ }
70
+
71
+ getState() {
72
+ return {
73
+ mode: this._params.mode,
74
+ left: [...this._columns.left],
75
+ right: [...this._columns.right],
76
+ };
77
+ }
78
+
79
+ syncScroll() {
80
+ this._syncScrollState();
81
+ return this;
82
+ }
83
+
84
+ dispose() {
85
+ this._scrollHost?.removeEventListener('scroll', this._boundScroll);
86
+ this._resizeObserver?.disconnect();
87
+ if (!this._resizeObserver && typeof window !== 'undefined') {
88
+ window.removeEventListener('resize', this._boundResize);
89
+ }
90
+ this._clearCells();
91
+ WRAPPER_CLASSES.forEach((className) => {
92
+ this._wrapper.classList.toggle(className, this._previousClasses.get(className) === true);
93
+ });
94
+ this._element.removeAttribute('data-vg-table-fixed-columns');
95
+ }
96
+
97
+ _getHeaders() {
98
+ const rows = Array.from(this._headerTable.tHead?.rows || []);
99
+ return rows.length ? Array.from(rows[rows.length - 1].cells) : [];
100
+ }
101
+
102
+ _resolveColumns(headers) {
103
+ const result = {left: [], right: []};
104
+ const add = (side, value) => {
105
+ const index = this._resolveIndex(value, headers);
106
+ if (index >= 0 && !result[side].includes(index)) result[side].push(index);
107
+ };
108
+ const configured = this._params.columns;
109
+
110
+ if (configured && typeof configured === 'object' && !Array.isArray(configured)) {
111
+ ['left', 'right'].forEach((side) => {
112
+ const values = Array.isArray(configured[side]) ? configured[side] : String(configured[side] || '').split(',');
113
+ values.forEach((value) => add(side, value));
114
+ });
115
+ } else {
116
+ String(configured || '').split(';').forEach((group) => {
117
+ const [rawSide, rawValues = ''] = group.split(':');
118
+ const side = String(rawSide || '').trim().toLowerCase();
119
+ if (!['left', 'right'].includes(side)) return;
120
+ rawValues.split(',').forEach((value) => add(side, value));
121
+ });
122
+ }
123
+
124
+ headers.forEach((header, index) => {
125
+ const side = String(header.getAttribute('data-fixed') || header.getAttribute('data-fixed-column') || '').toLowerCase();
126
+ if (['left', 'right'].includes(side)) add(side, index);
127
+ });
128
+
129
+ result.left.sort((a, b) => a - b);
130
+ result.right.sort((a, b) => a - b);
131
+ return result;
132
+ }
133
+
134
+ _resolveIndex(value, headers) {
135
+ const normalized = String(value ?? '').trim();
136
+ if (!normalized) return -1;
137
+ if (/^\d+$/.test(normalized)) {
138
+ const numeric = Number.parseInt(normalized, 10);
139
+ return numeric >= 0 && numeric < headers.length ? numeric : -1;
140
+ }
141
+ return headers.findIndex((header) => String(header.dataset.field || '').trim() === normalized);
142
+ }
143
+
144
+ _measureColumn(header, index) {
145
+ const bodyRow = Array.from(this._element.tBodies[0]?.rows || [])
146
+ .find((row) => !row.hasAttribute('data-vg-table-state-row'));
147
+ const bodyCell = bodyRow?.cells[index];
148
+ const col = this._element.querySelector(`:scope > colgroup > col:nth-child(${index + 1})`);
149
+ const measured = bodyCell?.getBoundingClientRect?.().width
150
+ || header.getBoundingClientRect?.().width
151
+ || bodyCell?.offsetWidth
152
+ || header.offsetWidth;
153
+ if (measured > 0) return measured;
154
+ const declared = Number.parseFloat(col?.style.width || col?.getAttribute('width') || '0');
155
+ return Number.isFinite(declared) ? declared : 0;
156
+ }
157
+
158
+ _applySide(side, indices, headers) {
159
+ let offset = 0;
160
+ indices.forEach((index) => {
161
+ this._cellsAt(index, headers).forEach((cell) => {
162
+ cell.setAttribute(CELL_ATTRIBUTE, side);
163
+ cell.style.setProperty(OFFSET_PROPERTY, `${offset}px`);
164
+ });
165
+ offset += this._widths[index] || 0;
166
+ if (this._params.mode === 'stack') offset += this._params.stackGap;
167
+ });
168
+ }
169
+
170
+ _cellsAt(index, headers = this._getHeaders()) {
171
+ const cells = [];
172
+ if (headers[index]) cells.push(headers[index]);
173
+ Array.from(this._element.tBodies || []).forEach((tbody) => {
174
+ Array.from(tbody.rows || []).forEach((row) => {
175
+ if (row.hasAttribute('data-vg-table-state-row')) return;
176
+ if (row.cells[index]) cells.push(row.cells[index]);
177
+ });
178
+ });
179
+ Array.from(this._element.tFoot?.rows || []).forEach((row) => {
180
+ if (row.cells[index]) cells.push(row.cells[index]);
181
+ });
182
+ return cells;
183
+ }
184
+
185
+ _syncScrollState(force = false) {
186
+ if (!this._scrollHost) return;
187
+ const max = Math.max(0, this._scrollHost.scrollWidth - this._scrollHost.clientWidth);
188
+ const position = Math.max(0, Math.min(max, this._scrollHost.scrollLeft));
189
+ this._wrapper.classList.toggle('is-vg-table-fixed-scrolled-start', position > 0.5);
190
+ this._wrapper.classList.toggle('is-vg-table-fixed-scrolled-end', position < max - 0.5);
191
+
192
+ if (this._params.mode !== 'stack') {
193
+ this._setEdge('left', this._columns.left.at(-1) ?? null, force);
194
+ this._setEdge('right', this._columns.right[0] ?? null, force);
195
+ return;
196
+ }
197
+
198
+ const leftActive = this._columns.left.filter((index) => {
199
+ const naturalStart = this._widths.slice(0, index).reduce((sum, width) => sum + width, 0);
200
+ const offset = this._stackOffset('left', index);
201
+ return position >= naturalStart - offset - 0.5;
202
+ });
203
+ const totalWidth = this._widths.reduce((sum, width) => sum + width, 0);
204
+ const remaining = max - position;
205
+ const rightActive = this._columns.right.filter((index) => {
206
+ const naturalEnd = this._widths.slice(0, index + 1).reduce((sum, width) => sum + width, 0);
207
+ const distance = totalWidth - naturalEnd;
208
+ return remaining >= distance - this._stackOffset('right', index) - 0.5;
209
+ });
210
+ this._setEdge('left', leftActive.at(-1) ?? null, force);
211
+ this._setEdge('right', rightActive[0] ?? null, force);
212
+ }
213
+
214
+ _stackOffset(side, index) {
215
+ const ordered = side === 'left' ? this._columns.left : [...this._columns.right].reverse();
216
+ let offset = 0;
217
+ for (const current of ordered) {
218
+ if (current === index) return offset;
219
+ offset += (this._widths[current] || 0) + this._params.stackGap;
220
+ }
221
+ return offset;
222
+ }
223
+
224
+ _setEdge(side, index, force = false) {
225
+ if (!force && this._activeEdges[side] === index) return;
226
+ this._allManagedCells().forEach((cell) => {
227
+ if (cell.getAttribute(EDGE_ATTRIBUTE) === side) cell.removeAttribute(EDGE_ATTRIBUTE);
228
+ });
229
+ if (index !== null) {
230
+ this._cellsAt(index).forEach((cell) => cell.setAttribute(EDGE_ATTRIBUTE, side));
231
+ }
232
+ this._activeEdges[side] = index;
233
+ }
234
+
235
+ _allManagedCells() {
236
+ return Array.from(this._wrapper.querySelectorAll(`[${CELL_ATTRIBUTE}], [${EDGE_ATTRIBUTE}]`));
237
+ }
238
+
239
+ _clearCells() {
240
+ this._allManagedCells().forEach((cell) => {
241
+ cell.removeAttribute(CELL_ATTRIBUTE);
242
+ cell.removeAttribute(EDGE_ATTRIBUTE);
243
+ cell.style.removeProperty(OFFSET_PROPERTY);
244
+ });
245
+ this._activeEdges = {left: null, right: null};
246
+ }
247
+ }
248
+
249
+ export default _fixedColumns;
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Описание: словари и разрешение локализованных подписей VGTable.
3
+ * Возможности: встроенные ru/en локали, региональные коды, пользовательские словари и подстановка переменных.
4
+ */
5
+
6
+ const DEFAULT_LOCALE = 'ru';
7
+
8
+ const DEFAULT_I18N = {
9
+ ru: {
10
+ pagination: {
11
+ size: {label: 'Строк на странице', suffix: 'на страницу'},
12
+ quick: {label: 'Перейти к странице', button: 'Перейти'},
13
+ labels: {
14
+ aria: 'Пагинация', prev: 'Назад', next: 'Вперёд',
15
+ ellipsisPrev: 'Перейти к предыдущим страницам', ellipsisNext: 'Перейти к следующим страницам',
16
+ },
17
+ },
18
+ expandable: {labels: {expand: 'Развернуть дочерние строки', collapse: 'Свернуть дочерние строки'}},
19
+ state: {labels: {
20
+ empty: 'Нет данных', 'filtered-empty': 'По вашему запросу ничего не найдено',
21
+ error: 'Не удалось загрузить данные', retry: 'Повторить', reset: 'Сбросить фильтры',
22
+ }},
23
+ columnResize: {label: 'Изменить ширину колонки {column}'},
24
+ rowReorder: {labels: {
25
+ handle: 'Изменить позицию строки {row}',
26
+ }},
27
+ remote: {loading: 'Загрузка…', empty: 'Ничего не найдено', error: 'Не удалось загрузить данные', retry: 'Повторить'},
28
+ },
29
+ en: {
30
+ pagination: {
31
+ size: {label: 'Rows per page', suffix: 'per page'},
32
+ quick: {label: 'Go to page', button: 'Go'},
33
+ labels: {
34
+ aria: 'Pagination', prev: 'Previous', next: 'Next',
35
+ ellipsisPrev: 'Jump to previous pages', ellipsisNext: 'Jump to next pages',
36
+ },
37
+ },
38
+ expandable: {labels: {expand: 'Expand child rows', collapse: 'Collapse child rows'}},
39
+ state: {labels: {
40
+ empty: 'No data', 'filtered-empty': 'No results found', error: 'Failed to load data', retry: 'Retry', reset: 'Reset filters',
41
+ }},
42
+ columnResize: {label: 'Resize column {column}'},
43
+ rowReorder: {labels: {
44
+ handle: 'Change position of row {row}',
45
+ }},
46
+ remote: {loading: 'Loading…', empty: 'Nothing found', error: 'Failed to load data', retry: 'Retry'},
47
+ },
48
+ };
49
+
50
+ const isObject = value => value && typeof value === 'object' && !Array.isArray(value);
51
+
52
+ const merge = (...sources) => sources.reduce((target, source) => {
53
+ if (!isObject(source)) return target;
54
+ Object.entries(source).forEach(([key, value]) => {
55
+ target[key] = isObject(value) ? merge(isObject(target[key]) ? target[key] : {}, value) : value;
56
+ });
57
+ return target;
58
+ }, {});
59
+
60
+ const normalizeLocale = locale => String(locale || DEFAULT_LOCALE).trim().toLowerCase().replace('_', '-') || DEFAULT_LOCALE;
61
+
62
+ const localeCandidates = locale => {
63
+ const normalized = normalizeLocale(locale);
64
+ const base = normalized.split('-')[0];
65
+ return normalized === base ? [normalized] : [normalized, base];
66
+ };
67
+
68
+ const resolveDictionary = (locale, dictionaries = {}) => {
69
+ const all = merge({}, DEFAULT_I18N, dictionaries);
70
+ const fallback = all[DEFAULT_LOCALE] || {};
71
+ const localized = localeCandidates(locale).map(key => all[key]).find(Boolean) || fallback;
72
+ return merge({}, fallback, localized);
73
+ };
74
+
75
+ export {normalizeLocale, resolveDictionary};