vgapp 1.5.2 → 1.5.3
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.
- package/app/modules/vgfiles/scss/_mixins.scss +2 -1
- package/app/modules/vgtable/js/_sticky-header.js +58 -13
- package/app/modules/vgtable/js/vgtable.js +45 -10
- package/build/vgapp.css +1 -1
- package/build/vgapp.css.map +1 -1
- package/build/vgapp.js +1 -1
- package/build/vgapp.js.map +1 -1
- package/package.json +1 -1
|
@@ -23,6 +23,7 @@ class _stickyHeader {
|
|
|
23
23
|
this._previousStyles = new Map();
|
|
24
24
|
this._columnWeights = new Map();
|
|
25
25
|
this._hardColumnWidths = new Map();
|
|
26
|
+
this._columnMinimumWidths = new Map();
|
|
26
27
|
this._hasSourceColgroup = false;
|
|
27
28
|
this._resizeObserver = null;
|
|
28
29
|
this._resizeFrame = null;
|
|
@@ -215,6 +216,8 @@ class _stickyHeader {
|
|
|
215
216
|
this._columnWeights.set(key, widths[index] > 0 ? widths[index] : 1);
|
|
216
217
|
if (this._hasDeclaredWidth(header) || this._hasDeclaredWidth(sourceCols[index])) {
|
|
217
218
|
this._hardColumnWidths.set(key, widths[index] > 0 ? widths[index] : 0);
|
|
219
|
+
} else if (this._isEmptyHeader(header) && widths[index] > 0) {
|
|
220
|
+
this._columnMinimumWidths.set(key, widths[index]);
|
|
218
221
|
}
|
|
219
222
|
});
|
|
220
223
|
}
|
|
@@ -252,22 +255,60 @@ class _stickyHeader {
|
|
|
252
255
|
hard.forEach((width, index) => { widths[index] = width; });
|
|
253
256
|
|
|
254
257
|
if (!flexible.length) return widths;
|
|
255
|
-
|
|
256
|
-
const key = this._columnKey(headers[index], index);
|
|
257
|
-
return this._columnWeights.get(key) || measured[index] || 1;
|
|
258
|
-
});
|
|
259
|
-
const weightTotal = weights.reduce((total, weight) => total + weight, 0) || flexible.length;
|
|
260
|
-
let distributed = 0;
|
|
261
|
-
flexible.forEach((index, position) => {
|
|
262
|
-
const width = position === flexible.length - 1
|
|
263
|
-
? Math.max(0, available - distributed)
|
|
264
|
-
: available * weights[position] / weightTotal;
|
|
265
|
-
widths[index] = width;
|
|
266
|
-
distributed += width;
|
|
267
|
-
});
|
|
258
|
+
this._distributeFlexibleWidths(widths, flexible, available, headers, measured);
|
|
268
259
|
return widths;
|
|
269
260
|
}
|
|
270
261
|
|
|
262
|
+
_distributeFlexibleWidths(widths, indexes, available, headers, measured) {
|
|
263
|
+
let pending = indexes.slice();
|
|
264
|
+
let remaining = available;
|
|
265
|
+
const minimumTotal = pending.reduce((total, index) => {
|
|
266
|
+
const key = this._columnKey(headers[index], index);
|
|
267
|
+
return total + (this._columnMinimumWidths.get(key) || 0);
|
|
268
|
+
}, 0);
|
|
269
|
+
|
|
270
|
+
if (minimumTotal > remaining) {
|
|
271
|
+
pending.forEach((index) => {
|
|
272
|
+
const key = this._columnKey(headers[index], index);
|
|
273
|
+
widths[index] = this._columnMinimumWidths.get(key) || 0;
|
|
274
|
+
});
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
while (pending.length) {
|
|
279
|
+
const weights = pending.map((index) => {
|
|
280
|
+
const key = this._columnKey(headers[index], index);
|
|
281
|
+
return this._columnWeights.get(key) || measured[index] || 1;
|
|
282
|
+
});
|
|
283
|
+
const weightTotal = weights.reduce((total, weight) => total + weight, 0) || pending.length;
|
|
284
|
+
const constrained = pending.filter((index, position) => {
|
|
285
|
+
const key = this._columnKey(headers[index], index);
|
|
286
|
+
const minimum = this._columnMinimumWidths.get(key) || 0;
|
|
287
|
+
return minimum > remaining * weights[position] / weightTotal;
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
if (!constrained.length) {
|
|
291
|
+
let distributed = 0;
|
|
292
|
+
pending.forEach((index, position) => {
|
|
293
|
+
const width = position === pending.length - 1
|
|
294
|
+
? Math.max(0, remaining - distributed)
|
|
295
|
+
: remaining * weights[position] / weightTotal;
|
|
296
|
+
widths[index] = width;
|
|
297
|
+
distributed += width;
|
|
298
|
+
});
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
constrained.forEach((index) => {
|
|
303
|
+
const key = this._columnKey(headers[index], index);
|
|
304
|
+
const minimum = this._columnMinimumWidths.get(key) || 0;
|
|
305
|
+
widths[index] = minimum;
|
|
306
|
+
remaining -= minimum;
|
|
307
|
+
});
|
|
308
|
+
pending = pending.filter((index) => !constrained.includes(index));
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
271
312
|
_getHeaders() {
|
|
272
313
|
const rows = Array.from(this._head?.rows || []);
|
|
273
314
|
return rows.length ? Array.from(rows.at(-1).cells || []) : [];
|
|
@@ -277,6 +318,10 @@ class _stickyHeader {
|
|
|
277
318
|
return String(header?.getAttribute('data-field') || index).trim();
|
|
278
319
|
}
|
|
279
320
|
|
|
321
|
+
_isEmptyHeader(header) {
|
|
322
|
+
return Boolean(header) && header.childElementCount === 0 && String(header.textContent || '').trim() === '';
|
|
323
|
+
}
|
|
324
|
+
|
|
280
325
|
_hasDeclaredWidth(element) {
|
|
281
326
|
if (!element) return false;
|
|
282
327
|
return element.hasAttribute('width')
|
|
@@ -34,7 +34,8 @@ import {
|
|
|
34
34
|
WRAPPER_SELECTOR,
|
|
35
35
|
} from "./_options.js";
|
|
36
36
|
|
|
37
|
-
const FILTER_HIDDEN_ATTRIBUTE = 'data-vg-table-filter-hidden';
|
|
37
|
+
const FILTER_HIDDEN_ATTRIBUTE = 'data-vg-table-filter-hidden';
|
|
38
|
+
const NOT_SPLITTER_CLASS = 'not-splitter';
|
|
38
39
|
|
|
39
40
|
class VGTable extends BaseModule {
|
|
40
41
|
constructor(element, params = {}) {
|
|
@@ -77,8 +78,11 @@ class VGTable extends BaseModule {
|
|
|
77
78
|
this._fixedColumns = null;
|
|
78
79
|
this._columns = null;
|
|
79
80
|
this._rowReorder = null;
|
|
80
|
-
this._remote = null;
|
|
81
|
-
this.
|
|
81
|
+
this._remote = null;
|
|
82
|
+
this._autoNotSplitterHeaders = new Set();
|
|
83
|
+
this._emptyHeaderSortOptions = new Map();
|
|
84
|
+
this._emptyHeaderSplittersSynced = false;
|
|
85
|
+
this._isRemote = Boolean(String(this._params.request.route || '').trim());
|
|
82
86
|
this._complex = false;
|
|
83
87
|
this._wrapper = null;
|
|
84
88
|
this._ownsWrapper = false;
|
|
@@ -108,8 +112,9 @@ class VGTable extends BaseModule {
|
|
|
108
112
|
*/
|
|
109
113
|
init() {
|
|
110
114
|
this._ensureWrapper();
|
|
111
|
-
this._ensureTableContainer();
|
|
112
|
-
this._syncComplexState();
|
|
115
|
+
this._ensureTableContainer();
|
|
116
|
+
this._syncComplexState();
|
|
117
|
+
this._syncEmptyHeaderSplitters();
|
|
113
118
|
|
|
114
119
|
// Включаем нативный sticky-заголовок без клонирования DOM
|
|
115
120
|
if (!this._stickyHeader && this._params.stickyHeader.enabled === true) {
|
|
@@ -445,15 +450,38 @@ class VGTable extends BaseModule {
|
|
|
445
450
|
* Определяет таблицу с многострочным thead, colspan или rowspan.
|
|
446
451
|
* @private
|
|
447
452
|
*/
|
|
448
|
-
_syncComplexState() {
|
|
453
|
+
_syncComplexState() {
|
|
449
454
|
const rows = this._isRemote
|
|
450
455
|
? Array.from(this._element.tHead?.rows || [])
|
|
451
456
|
: Array.from(this._element.rows || []);
|
|
452
457
|
const hasSpans = rows.some((row) => Array.from(row.cells || []).some((cell) => cell.colSpan > 1 || cell.rowSpan > 1));
|
|
453
458
|
this._complex = Boolean((this._element.tHead?.rows.length || 0) > 1 || hasSpans);
|
|
454
459
|
this._element.classList.toggle('vg-table-complex', this._complex);
|
|
455
|
-
this._element.toggleAttribute('data-vg-table-complex', this._complex);
|
|
456
|
-
}
|
|
460
|
+
this._element.toggleAttribute('data-vg-table-complex', this._complex);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Скрывает разделитель и отключает сортировку у пустых заголовочных ячеек.
|
|
465
|
+
* @private
|
|
466
|
+
*/
|
|
467
|
+
_syncEmptyHeaderSplitters() {
|
|
468
|
+
if (this._emptyHeaderSplittersSynced) return;
|
|
469
|
+
|
|
470
|
+
Array.from(this._element.tHead?.querySelectorAll('th') || []).forEach((header) => {
|
|
471
|
+
const isEmpty = header.childElementCount === 0 && String(header.textContent || '').trim() === '';
|
|
472
|
+
if (!isEmpty) return;
|
|
473
|
+
|
|
474
|
+
if (!header.classList.contains(NOT_SPLITTER_CLASS)) {
|
|
475
|
+
header.classList.add(NOT_SPLITTER_CLASS);
|
|
476
|
+
this._autoNotSplitterHeaders.add(header);
|
|
477
|
+
}
|
|
478
|
+
if (String(header.getAttribute('data-sort-enabled')).toLowerCase() !== 'false') {
|
|
479
|
+
this._emptyHeaderSortOptions.set(header, header.getAttribute('data-sort-enabled'));
|
|
480
|
+
header.setAttribute('data-sort-enabled', 'false');
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
this._emptyHeaderSplittersSynced = true;
|
|
484
|
+
}
|
|
457
485
|
|
|
458
486
|
/**
|
|
459
487
|
* Подготовка переменных для пагинации
|
|
@@ -1426,8 +1454,15 @@ class VGTable extends BaseModule {
|
|
|
1426
1454
|
this._element.removeEventListener('sortchange.vg.table', this._boundPaginationRefresh);
|
|
1427
1455
|
this._element.removeEventListener('sortchange.vg.table', this._boundLocalSortChange);
|
|
1428
1456
|
this._element.removeEventListener('sortchange.vg.table', this._boundRemoteSortChange);
|
|
1429
|
-
if (this._pagination) this._pagination.dispose();
|
|
1430
|
-
this.
|
|
1457
|
+
if (this._pagination) this._pagination.dispose();
|
|
1458
|
+
this._autoNotSplitterHeaders.forEach((header) => header.classList.remove(NOT_SPLITTER_CLASS));
|
|
1459
|
+
this._autoNotSplitterHeaders.clear();
|
|
1460
|
+
this._emptyHeaderSortOptions.forEach((value, header) => {
|
|
1461
|
+
if (value === null) header.removeAttribute('data-sort-enabled');
|
|
1462
|
+
else header.setAttribute('data-sort-enabled', value);
|
|
1463
|
+
});
|
|
1464
|
+
this._emptyHeaderSortOptions.clear();
|
|
1465
|
+
this._element.classList.remove('vg-table-complex');
|
|
1431
1466
|
this._element.removeAttribute('data-vg-table-complex');
|
|
1432
1467
|
this._removeGeneratedTableContainer();
|
|
1433
1468
|
this._removeGeneratedWrapper();
|