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.
@@ -28,7 +28,8 @@
28
28
  &.pending,
29
29
  &.loading,
30
30
  &.failing {
31
- .file-actions {
31
+ .file-actions,
32
+ .file-custom > :not(.file-remove):not(.file-info) {
32
33
  display: none;
33
34
  }
34
35
  }
@@ -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
- const weights = flexible.map((index) => {
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._isRemote = Boolean(String(this._params.request.route || '').trim());
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._element.classList.remove('vg-table-complex');
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();