vgapp 1.3.8 → 1.4.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 (90) 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 +37 -37
  7. package/app/modules/vgalert/readme.md +105 -105
  8. package/app/modules/vgdynamictable/index.js +4 -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/options.js +8 -4
  13. package/app/modules/vgdynamictable/js/pagination.js +1 -1
  14. package/app/modules/vgdynamictable/js/search.js +12 -12
  15. package/app/modules/vgdynamictable/js/sortable.js +71 -71
  16. package/app/modules/vgdynamictable/js/summary-footer.js +28 -28
  17. package/app/modules/vgdynamictable/js/table-remote.js +79 -79
  18. package/app/modules/vgdynamictable/js/table-url-state.js +31 -31
  19. package/app/modules/vgdynamictable/js/utils/common.js +48 -48
  20. package/app/modules/vgdynamictable/js/vgdynamictable.js +352 -311
  21. package/app/modules/vgdynamictable/readme.md +250 -250
  22. package/app/modules/vgdynamictable/scss/_skeleton.scss +6 -6
  23. package/app/modules/vgdynamictable/scss/_sortable.scss +24 -24
  24. package/app/modules/vgdynamictable/scss/_table.scss +4 -4
  25. package/app/modules/vgdynamictable/scss/_variables.scss +79 -79
  26. package/app/modules/vgdynamictable/scss/vgdynamictable.scss +40 -37
  27. package/app/modules/vgfilepreview/js/i18n.js +56 -56
  28. package/app/modules/vgfilepreview/js/renderers/image-modal.js +145 -145
  29. package/app/modules/vgfilepreview/js/renderers/image.js +92 -92
  30. package/app/modules/vgfilepreview/js/renderers/index.js +19 -19
  31. package/app/modules/vgfilepreview/js/renderers/office-modal.js +168 -168
  32. package/app/modules/vgfilepreview/js/renderers/office.js +79 -79
  33. package/app/modules/vgfilepreview/js/renderers/pdf-modal.js +260 -260
  34. package/app/modules/vgfilepreview/js/renderers/pdf.js +76 -76
  35. package/app/modules/vgfilepreview/js/renderers/playlist.js +71 -71
  36. package/app/modules/vgfilepreview/js/renderers/text-modal.js +343 -343
  37. package/app/modules/vgfilepreview/js/renderers/text.js +83 -83
  38. package/app/modules/vgfilepreview/js/renderers/video-modal.js +272 -272
  39. package/app/modules/vgfilepreview/js/renderers/video.js +80 -80
  40. package/app/modules/vgfilepreview/js/renderers/zip-modal.js +522 -522
  41. package/app/modules/vgfilepreview/js/renderers/zip.js +89 -89
  42. package/app/modules/vgfilepreview/js/vgfilepreview.js +7 -7
  43. package/app/modules/vgfilepreview/readme.md +68 -68
  44. package/app/modules/vgfilepreview/scss/_variables.scss +113 -113
  45. package/app/modules/vgfilepreview/scss/vgfilepreview.scss +464 -464
  46. package/app/modules/vgfiles/js/base.js +26 -26
  47. package/app/modules/vgfiles/js/droppable.js +260 -260
  48. package/app/modules/vgfiles/js/render.js +153 -153
  49. package/app/modules/vgfiles/js/vgfiles.js +104 -104
  50. package/app/modules/vgfiles/readme.md +123 -123
  51. package/app/modules/vgfiles/scss/_variables.scss +18 -18
  52. package/app/modules/vgfiles/scss/vgfiles.scss +148 -148
  53. package/app/modules/vgformsender/js/hideshowpass.js +16 -16
  54. package/app/modules/vgformsender/readme.md +41 -41
  55. package/app/modules/vgformsender/scss/vgformsender.scss +34 -34
  56. package/app/modules/vgmodal/js/vgmodal.drag.js +332 -332
  57. package/app/modules/vgmodal/js/vgmodal.js +188 -188
  58. package/app/modules/vgmodal/js/vgmodal.resize.js +27 -27
  59. package/app/modules/vgmodal/scss/vgmodal.scss +14 -14
  60. package/app/modules/vgnav/js/vgnav.js +135 -135
  61. package/app/modules/vgnav/readme.md +67 -67
  62. package/app/modules/vgnestable/README.md +307 -307
  63. package/app/modules/vgnestable/scss/_variables.scss +60 -60
  64. package/app/modules/vgnestable/scss/vgnestable.scss +163 -163
  65. package/app/modules/vgrangeslider/js/skins.js +222 -222
  66. package/app/modules/vgrangeslider/js/vgrangeslider.js +704 -704
  67. package/app/modules/vgrangeslider/readme.md +523 -523
  68. package/app/modules/vgrangeslider/scss/_variables.scss +53 -53
  69. package/app/modules/vgrangeslider/scss/vgrangeslider.scss +240 -240
  70. package/app/modules/vgselect/js/vgselect.js +39 -39
  71. package/app/modules/vgselect/scss/vgselect.scss +22 -22
  72. package/app/modules/vgspy/readme.md +28 -28
  73. package/app/modules/vgtoast/readme.md +18 -18
  74. package/app/modules/vgtoast/scss/vgtoast.scss +48 -48
  75. package/app/modules/vgtooltip/js/vgtooltip.js +89 -89
  76. package/app/modules/vgtooltip/readme.md +181 -181
  77. package/app/utils/js/components/ajax.js +117 -117
  78. package/app/utils/js/components/audio-metadata.js +240 -240
  79. package/app/utils/js/components/backdrop.js +17 -17
  80. package/app/utils/js/components/file-icon.js +109 -109
  81. package/app/utils/js/components/file-preview.js +304 -304
  82. package/app/utils/js/components/sanitize.js +150 -150
  83. package/app/utils/js/components/video-metadata.js +140 -140
  84. package/app/vgapp.js +295 -130
  85. package/build/vgapp.css +1 -1
  86. package/build/vgapp.css.map +1 -1
  87. package/build/vgapp.js +1 -1
  88. package/build/vgapp.js.map +1 -1
  89. package/index.js +14 -14
  90. package/package.json +1 -1
@@ -17,7 +17,7 @@ import EventHandler from "../../../utils/js/dom/event";
17
17
  import {mergeDeepObject} from "../../../utils/js/functions";
18
18
 
19
19
  /**
20
- * Константы
20
+ * Константы
21
21
  */
22
22
  const NAME = 'dynamicTable';
23
23
  const NAME_KEY = 'vg.' + NAME;
@@ -26,7 +26,7 @@ const MAIN_SELECTOR_CLASS = 'vg-dynamic-table';
26
26
  const SELECTOR_DATA_TOGGLE = '[data-vg-table]';
27
27
 
28
28
  /**
29
- * Карта колбеков
29
+ * Карта колбеков
30
30
  */
31
31
  const ACTION_CALLBACK_MAP = {
32
32
  init: 'onInit',
@@ -131,26 +131,26 @@ class VGDynamicTable extends BaseModule {
131
131
  nextWidth: 0,
132
132
  frame: 0,
133
133
  };
134
- this._virtualState = {
135
- enabled: false,
136
- bound: false,
137
- lastPage: -1,
138
- lastPerPage: -1,
134
+ this._virtualState = {
135
+ enabled: false,
136
+ bound: false,
137
+ lastPage: -1,
138
+ lastPerPage: -1,
139
139
  start: 0,
140
140
  end: 0,
141
141
  page: -1,
142
- perPage: -1,
143
- frame: 0,
144
- lastScrollTop: -1,
145
- rowHeight: 44,
146
- };
147
- this._virtualSpacers = {
148
- top: null,
149
- bottom: null,
150
- };
151
- this._boundVirtualScroll = this._handleVirtualScroll.bind(this);
152
- this._expandable = null;
153
- }
142
+ perPage: -1,
143
+ frame: 0,
144
+ lastScrollTop: -1,
145
+ rowHeight: 44,
146
+ };
147
+ this._virtualSpacers = {
148
+ top: null,
149
+ bottom: null,
150
+ };
151
+ this._boundVirtualScroll = this._handleVirtualScroll.bind(this);
152
+ this._expandable = null;
153
+ }
154
154
 
155
155
  static get NAME() { return NAME; }
156
156
 
@@ -256,7 +256,7 @@ class VGDynamicTable extends BaseModule {
256
256
  });
257
257
  }
258
258
 
259
- setLocale(locale) {
259
+ setLocale(locale) {
260
260
  const normalized = String(locale || '').toLowerCase().trim() || 'ru';
261
261
  this._params.locale = normalized;
262
262
  this._writePersistentState('locale', normalized);
@@ -511,8 +511,8 @@ class VGDynamicTable extends BaseModule {
511
511
 
512
512
  this._ensureTableViewport(directParent);
513
513
  this._bindViewportPan();
514
- this._restorePersistentState();
515
- this._ensureLiveRegion();
514
+ this._restorePersistentState();
515
+ this._ensureLiveRegion();
516
516
  this._ensureSummaryNode();
517
517
  this._ensurePanHintNode();
518
518
  this._applyFooterVisibility();
@@ -554,9 +554,9 @@ class VGDynamicTable extends BaseModule {
554
554
  this._bindPopState();
555
555
  this._refreshStickyAndFixedLayout();
556
556
 
557
- if (paginationEnabled) {
558
- this.setPagination();
559
- this._emitAction('init', {
557
+ if (paginationEnabled) {
558
+ this.setPagination();
559
+ this._emitAction('init', {
560
560
  page: this._pageState.page,
561
561
  perPage: this._pageState.perPage,
562
562
  });
@@ -656,10 +656,10 @@ class VGDynamicTable extends BaseModule {
656
656
  this._renderFooterFromCurrentState();
657
657
  }
658
658
 
659
- _setRemotePagination() {
660
- const page = this._getInitialPage();
661
- const perPage = this._getInitialPerPage();
662
- this._pageState = { page, perPage };
659
+ _setRemotePagination() {
660
+ const page = this._getInitialPage();
661
+ const perPage = this._getInitialPerPage();
662
+ this._pageState = { page, perPage };
663
663
 
664
664
  const userOnChange = this._params.pagination.onChange;
665
665
  const userOnPerPageChange = this._params.pagination.onPerPageChange;
@@ -704,13 +704,13 @@ class VGDynamicTable extends BaseModule {
704
704
  this._loadRemotePage(page, perPage, null, filtersRequestMeta);
705
705
  }
706
706
 
707
- _getTableRows() {
708
- const body = this._element.tBodies && this._element.tBodies[0];
709
- if (!body) {
710
- return [];
711
- }
712
- return Array.from(body.rows).filter((row) => String(row.getAttribute('data-virtual-spacer') || '') !== '1');
713
- }
707
+ _getTableRows() {
708
+ const body = this._element.tBodies && this._element.tBodies[0];
709
+ if (!body) {
710
+ return [];
711
+ }
712
+ return Array.from(body.rows).filter((row) => String(row.getAttribute('data-virtual-spacer') || '') !== '1');
713
+ }
714
714
 
715
715
  _getTotalPages(perPage) {
716
716
  if (!this._rows.length) {
@@ -719,62 +719,62 @@ class VGDynamicTable extends BaseModule {
719
719
  return Math.max(1, Math.ceil(this._rows.length / perPage));
720
720
  }
721
721
 
722
- _renderPageRows(page, perPage) {
723
- if (!this._rows.length) {
724
- return;
725
- }
726
-
727
- const isVirtualEnabled = this._isVirtualEnabled();
728
- const start = (page - 1) * perPage;
729
- const end = start + perPage;
730
-
731
- this._rows.forEach((row, index) => {
732
- row.hidden = index < start || index >= end;
733
- });
734
- if (isVirtualEnabled) {
735
- this._virtualState.lastPage = page;
736
- this._virtualState.lastPerPage = perPage;
737
- this._applyVirtualWindow(page, perPage);
738
- } else {
739
- this._teardownVirtualWindow();
740
- }
741
- this._refreshExpandable();
742
- this._renderFooterFromCurrentState();
743
- this._refreshStickyAndFixedLayout();
744
- }
722
+ _renderPageRows(page, perPage) {
723
+ if (!this._rows.length) {
724
+ return;
725
+ }
745
726
 
746
- _isVirtualEnabled() {
747
- const options = this._params.virtual || {};
748
- const attr = this._element.getAttribute('data-virtual-enable');
749
- const enabled = attr !== null
750
- ? this._isTruthy(attr)
751
- : Boolean(options.enable);
752
- if (!enabled) {
753
- return false;
754
- }
755
- const thresholdRaw = this._element.getAttribute('data-virtual-threshold');
756
- const threshold = this._normalizePositiveInt(
757
- thresholdRaw !== null ? thresholdRaw : options.threshold,
758
- 500
759
- );
760
- return this._rows.length >= threshold;
761
- }
762
-
763
- _initVirtualization() {
764
- if (!this._tableViewport || this._virtualState.bound) {
765
- return;
766
- }
767
- this._tableViewport.addEventListener('scroll', this._boundVirtualScroll, { passive: true });
768
- this._virtualState.bound = true;
769
- }
770
-
771
- _teardownVirtualizationBinding() {
772
- if (!this._tableViewport || !this._virtualState.bound) {
773
- return;
774
- }
775
- this._tableViewport.removeEventListener('scroll', this._boundVirtualScroll);
776
- this._virtualState.bound = false;
777
- }
727
+ const isVirtualEnabled = this._isVirtualEnabled();
728
+ const start = (page - 1) * perPage;
729
+ const end = start + perPage;
730
+
731
+ this._rows.forEach((row, index) => {
732
+ row.hidden = index < start || index >= end;
733
+ });
734
+ if (isVirtualEnabled) {
735
+ this._virtualState.lastPage = page;
736
+ this._virtualState.lastPerPage = perPage;
737
+ this._applyVirtualWindow(page, perPage);
738
+ } else {
739
+ this._teardownVirtualWindow();
740
+ }
741
+ this._refreshExpandable();
742
+ this._renderFooterFromCurrentState();
743
+ this._refreshStickyAndFixedLayout();
744
+ }
745
+
746
+ _isVirtualEnabled() {
747
+ const options = this._params.virtual || {};
748
+ const attr = this._element.getAttribute('data-virtual-enable');
749
+ const enabled = attr !== null
750
+ ? this._isTruthy(attr)
751
+ : Boolean(options.enable);
752
+ if (!enabled) {
753
+ return false;
754
+ }
755
+ const thresholdRaw = this._element.getAttribute('data-virtual-threshold');
756
+ const threshold = this._normalizePositiveInt(
757
+ thresholdRaw !== null ? thresholdRaw : options.threshold,
758
+ 500
759
+ );
760
+ return this._rows.length >= threshold;
761
+ }
762
+
763
+ _initVirtualization() {
764
+ if (!this._tableViewport || this._virtualState.bound) {
765
+ return;
766
+ }
767
+ this._tableViewport.addEventListener('scroll', this._boundVirtualScroll, { passive: true });
768
+ this._virtualState.bound = true;
769
+ }
770
+
771
+ _teardownVirtualizationBinding() {
772
+ if (!this._tableViewport || !this._virtualState.bound) {
773
+ return;
774
+ }
775
+ this._tableViewport.removeEventListener('scroll', this._boundVirtualScroll);
776
+ this._virtualState.bound = false;
777
+ }
778
778
 
779
779
  _handleVirtualScroll() {
780
780
  if (!this._isVirtualEnabled()) {
@@ -801,43 +801,43 @@ class VGDynamicTable extends BaseModule {
801
801
  });
802
802
  }
803
803
 
804
- _applyVirtualWindow(page, perPage) {
805
- if (!this._isVirtualEnabled() || !this._tableViewport) {
806
- this._teardownVirtualWindow();
807
- return;
808
- }
809
-
810
- this._initVirtualization();
811
- const body = this._getBody();
812
- if (!body) {
813
- this._teardownVirtualWindow();
814
- return;
815
- }
816
- const options = this._params.virtual || {};
817
- const rowHeight = this._resolveVirtualRowHeight(page, perPage, options);
818
- const overscanRaw = this._element.getAttribute('data-virtual-overscan');
819
- const overscan = Math.max(0, this._normalizePositiveInt(overscanRaw !== null ? overscanRaw : options.overscan, 8));
820
- const visibleCount = Math.max(1, Math.ceil((this._tableViewport.clientHeight || 0) / rowHeight));
804
+ _applyVirtualWindow(page, perPage) {
805
+ if (!this._isVirtualEnabled() || !this._tableViewport) {
806
+ this._teardownVirtualWindow();
807
+ return;
808
+ }
809
+
810
+ this._initVirtualization();
811
+ const body = this._getBody();
812
+ if (!body) {
813
+ this._teardownVirtualWindow();
814
+ return;
815
+ }
816
+ const options = this._params.virtual || {};
817
+ const rowHeight = this._resolveVirtualRowHeight(page, perPage, options);
818
+ const overscanRaw = this._element.getAttribute('data-virtual-overscan');
819
+ const overscan = Math.max(0, this._normalizePositiveInt(overscanRaw !== null ? overscanRaw : options.overscan, 8));
820
+ const visibleCount = Math.max(1, Math.ceil((this._tableViewport.clientHeight || 0) / rowHeight));
821
821
  const pageStart = (page - 1) * perPage;
822
822
  const pageEnd = Math.min(pageStart + perPage, this._rows.length);
823
823
 
824
- const scrolledRows = Math.floor((this._tableViewport.scrollTop || 0) / rowHeight);
825
- const windowStart = Math.max(pageStart, pageStart + scrolledRows - overscan);
826
- const windowEnd = Math.min(pageEnd, pageStart + scrolledRows + visibleCount + overscan);
827
-
828
- const topSpacerHeight = Math.max(0, (windowStart - pageStart) * rowHeight);
829
- const bottomSpacerHeight = Math.max(0, (pageEnd - windowEnd) * rowHeight);
830
- if (
831
- this._virtualState.start === windowStart
832
- && this._virtualState.end === windowEnd
833
- && this._virtualState.page === page
834
- && this._virtualState.perPage === perPage
835
- ) {
836
- this._syncVirtualSpacers(body, windowStart, windowEnd, topSpacerHeight, bottomSpacerHeight);
837
- return;
838
- }
839
-
840
- const isSameContext = this._virtualState.page === page && this._virtualState.perPage === perPage;
824
+ const scrolledRows = Math.floor((this._tableViewport.scrollTop || 0) / rowHeight);
825
+ const windowStart = Math.max(pageStart, pageStart + scrolledRows - overscan);
826
+ const windowEnd = Math.min(pageEnd, pageStart + scrolledRows + visibleCount + overscan);
827
+
828
+ const topSpacerHeight = Math.max(0, (windowStart - pageStart) * rowHeight);
829
+ const bottomSpacerHeight = Math.max(0, (pageEnd - windowEnd) * rowHeight);
830
+ if (
831
+ this._virtualState.start === windowStart
832
+ && this._virtualState.end === windowEnd
833
+ && this._virtualState.page === page
834
+ && this._virtualState.perPage === perPage
835
+ ) {
836
+ this._syncVirtualSpacers(body, windowStart, windowEnd, topSpacerHeight, bottomSpacerHeight);
837
+ return;
838
+ }
839
+
840
+ const isSameContext = this._virtualState.page === page && this._virtualState.perPage === perPage;
841
841
  const prevStart = this._virtualState.start;
842
842
  const prevEnd = this._virtualState.end;
843
843
  const updateFrom = isSameContext
@@ -846,145 +846,145 @@ class VGDynamicTable extends BaseModule {
846
846
  const updateTo = isSameContext
847
847
  ? Math.min(pageEnd, Math.max(prevEnd, windowEnd))
848
848
  : pageEnd;
849
- for (let index = updateFrom; index < updateTo; index += 1) {
850
- const row = this._rows[index];
851
- if (!row) {
852
- continue;
853
- }
854
- row.hidden = index < windowStart || index >= windowEnd;
855
- }
856
- this._syncVirtualSpacers(body, windowStart, windowEnd, topSpacerHeight, bottomSpacerHeight);
857
- this._virtualState.start = windowStart;
858
- this._virtualState.end = windowEnd;
859
- this._virtualState.page = page;
860
- this._virtualState.perPage = perPage;
861
- this._virtualState.rowHeight = rowHeight;
862
- }
863
-
864
- _resolveVirtualRowHeight(page, perPage, options = {}) {
865
- const attrRowHeight = this._element.getAttribute('data-virtual-row-height');
866
- if (attrRowHeight !== null && String(attrRowHeight).trim() !== '') {
867
- return Math.max(24, this._normalizePositiveInt(attrRowHeight, 44));
868
- }
869
-
870
- const optionRowHeight = this._normalizePositiveInt(options.rowheight, 0);
871
- const pageStart = Math.max(0, (page - 1) * perPage);
872
- const pageEnd = Math.min(pageStart + perPage, this._rows.length);
873
- const measuredHeight = this._measureVirtualRowHeight(pageStart, pageEnd);
874
- if (measuredHeight > 0) {
875
- return measuredHeight;
876
- }
877
- if (optionRowHeight > 0) {
878
- return Math.max(24, optionRowHeight);
879
- }
880
- const cachedHeight = this._normalizePositiveInt(this._virtualState.rowHeight, 44);
881
- return Math.max(24, cachedHeight);
882
- }
883
-
884
- _measureVirtualRowHeight(pageStart, pageEnd) {
885
- const samples = [];
886
- for (let index = pageStart; index < pageEnd; index += 1) {
887
- const row = this._rows[index];
888
- if (!row || row.hidden) {
889
- continue;
890
- }
891
- const rect = row.getBoundingClientRect();
892
- const height = Math.round(rect && rect.height ? rect.height : 0);
893
- if (height > 0) {
894
- samples.push(height);
895
- }
896
- if (samples.length >= 6) {
897
- break;
898
- }
899
- }
900
- if (!samples.length) {
901
- return 0;
902
- }
903
- const total = samples.reduce((sum, value) => sum + value, 0);
904
- return Math.max(24, Math.round(total / samples.length));
905
- }
906
-
907
- _syncVirtualSpacers(body, windowStart, windowEnd, topHeight, bottomHeight) {
908
- const columns = this._getRenderedColumnsCount();
909
- const topSpacer = this._ensureVirtualSpacer('top', columns);
910
- const bottomSpacer = this._ensureVirtualSpacer('bottom', columns);
911
- const firstVisibleRow = this._rows[windowStart] || null;
912
- const afterVisibleRow = this._rows[windowEnd] || null;
913
-
914
- this._updateVirtualSpacer(topSpacer, topHeight, columns);
915
- this._updateVirtualSpacer(bottomSpacer, bottomHeight, columns);
916
-
917
- if (firstVisibleRow) {
918
- body.insertBefore(topSpacer, firstVisibleRow);
919
- } else if (!topSpacer.parentElement) {
920
- body.appendChild(topSpacer);
921
- }
922
-
923
- if (afterVisibleRow) {
924
- body.insertBefore(bottomSpacer, afterVisibleRow);
925
- } else {
926
- body.appendChild(bottomSpacer);
927
- }
928
- }
929
-
930
- _ensureVirtualSpacer(position, columns) {
931
- const current = this._virtualSpacers && this._virtualSpacers[position]
932
- ? this._virtualSpacers[position]
933
- : null;
934
- if (current && current.isConnected) {
935
- this._syncVirtualSpacerColumns(current, columns);
936
- return current;
937
- }
938
-
939
- const spacer = document.createElement('tr');
940
- spacer.setAttribute('data-virtual-spacer', '1');
941
- spacer.setAttribute('data-virtual-spacer-position', position);
942
- spacer.setAttribute('data-group-header', '1');
943
- spacer.setAttribute('aria-hidden', 'true');
944
- const cell = document.createElement('td');
945
- cell.setAttribute('data-virtual-spacer-cell', '1');
946
- cell.colSpan = Math.max(1, columns);
947
- spacer.appendChild(cell);
948
- this._virtualSpacers[position] = spacer;
949
- return spacer;
950
- }
951
-
952
- _syncVirtualSpacerColumns(spacer, columns) {
953
- if (!spacer || !spacer.cells || !spacer.cells[0]) {
954
- return;
955
- }
956
- spacer.cells[0].colSpan = Math.max(1, columns);
957
- }
958
-
959
- _updateVirtualSpacer(spacer, height, columns) {
960
- if (!spacer || !spacer.cells || !spacer.cells[0]) {
961
- return;
962
- }
963
- const safeHeight = Math.max(0, Math.round(height));
964
- this._syncVirtualSpacerColumns(spacer, columns);
965
- const cell = spacer.cells[0];
966
- cell.style.height = `${safeHeight}px`;
967
- spacer.hidden = safeHeight <= 0;
968
- }
969
-
970
- _teardownVirtualWindow() {
971
- this._teardownVirtualizationBinding();
972
- if (this._virtualState.frame && typeof window !== 'undefined' && typeof window.cancelAnimationFrame === 'function') {
973
- window.cancelAnimationFrame(this._virtualState.frame);
974
- }
975
- this._virtualState.start = 0;
976
- this._virtualState.end = 0;
977
- this._virtualState.page = -1;
978
- this._virtualState.perPage = -1;
979
- this._virtualState.frame = 0;
980
- this._virtualState.lastScrollTop = -1;
981
- Object.keys(this._virtualSpacers || {}).forEach((key) => {
982
- const spacer = this._virtualSpacers[key];
983
- if (spacer && spacer.parentElement) {
984
- spacer.parentElement.removeChild(spacer);
985
- }
986
- });
987
- }
849
+ for (let index = updateFrom; index < updateTo; index += 1) {
850
+ const row = this._rows[index];
851
+ if (!row) {
852
+ continue;
853
+ }
854
+ row.hidden = index < windowStart || index >= windowEnd;
855
+ }
856
+ this._syncVirtualSpacers(body, windowStart, windowEnd, topSpacerHeight, bottomSpacerHeight);
857
+ this._virtualState.start = windowStart;
858
+ this._virtualState.end = windowEnd;
859
+ this._virtualState.page = page;
860
+ this._virtualState.perPage = perPage;
861
+ this._virtualState.rowHeight = rowHeight;
862
+ }
863
+
864
+ _resolveVirtualRowHeight(page, perPage, options = {}) {
865
+ const attrRowHeight = this._element.getAttribute('data-virtual-row-height');
866
+ if (attrRowHeight !== null && String(attrRowHeight).trim() !== '') {
867
+ return Math.max(24, this._normalizePositiveInt(attrRowHeight, 44));
868
+ }
869
+
870
+ const optionRowHeight = this._normalizePositiveInt(options.rowheight, 0);
871
+ const pageStart = Math.max(0, (page - 1) * perPage);
872
+ const pageEnd = Math.min(pageStart + perPage, this._rows.length);
873
+ const measuredHeight = this._measureVirtualRowHeight(pageStart, pageEnd);
874
+ if (measuredHeight > 0) {
875
+ return measuredHeight;
876
+ }
877
+ if (optionRowHeight > 0) {
878
+ return Math.max(24, optionRowHeight);
879
+ }
880
+ const cachedHeight = this._normalizePositiveInt(this._virtualState.rowHeight, 44);
881
+ return Math.max(24, cachedHeight);
882
+ }
883
+
884
+ _measureVirtualRowHeight(pageStart, pageEnd) {
885
+ const samples = [];
886
+ for (let index = pageStart; index < pageEnd; index += 1) {
887
+ const row = this._rows[index];
888
+ if (!row || row.hidden) {
889
+ continue;
890
+ }
891
+ const rect = row.getBoundingClientRect();
892
+ const height = Math.round(rect && rect.height ? rect.height : 0);
893
+ if (height > 0) {
894
+ samples.push(height);
895
+ }
896
+ if (samples.length >= 6) {
897
+ break;
898
+ }
899
+ }
900
+ if (!samples.length) {
901
+ return 0;
902
+ }
903
+ const total = samples.reduce((sum, value) => sum + value, 0);
904
+ return Math.max(24, Math.round(total / samples.length));
905
+ }
906
+
907
+ _syncVirtualSpacers(body, windowStart, windowEnd, topHeight, bottomHeight) {
908
+ const columns = this._getRenderedColumnsCount();
909
+ const topSpacer = this._ensureVirtualSpacer('top', columns);
910
+ const bottomSpacer = this._ensureVirtualSpacer('bottom', columns);
911
+ const firstVisibleRow = this._rows[windowStart] || null;
912
+ const afterVisibleRow = this._rows[windowEnd] || null;
913
+
914
+ this._updateVirtualSpacer(topSpacer, topHeight, columns);
915
+ this._updateVirtualSpacer(bottomSpacer, bottomHeight, columns);
916
+
917
+ if (firstVisibleRow) {
918
+ body.insertBefore(topSpacer, firstVisibleRow);
919
+ } else if (!topSpacer.parentElement) {
920
+ body.appendChild(topSpacer);
921
+ }
922
+
923
+ if (afterVisibleRow) {
924
+ body.insertBefore(bottomSpacer, afterVisibleRow);
925
+ } else {
926
+ body.appendChild(bottomSpacer);
927
+ }
928
+ }
929
+
930
+ _ensureVirtualSpacer(position, columns) {
931
+ const current = this._virtualSpacers && this._virtualSpacers[position]
932
+ ? this._virtualSpacers[position]
933
+ : null;
934
+ if (current && current.isConnected) {
935
+ this._syncVirtualSpacerColumns(current, columns);
936
+ return current;
937
+ }
938
+
939
+ const spacer = document.createElement('tr');
940
+ spacer.setAttribute('data-virtual-spacer', '1');
941
+ spacer.setAttribute('data-virtual-spacer-position', position);
942
+ spacer.setAttribute('data-group-header', '1');
943
+ spacer.setAttribute('aria-hidden', 'true');
944
+ const cell = document.createElement('td');
945
+ cell.setAttribute('data-virtual-spacer-cell', '1');
946
+ cell.colSpan = Math.max(1, columns);
947
+ spacer.appendChild(cell);
948
+ this._virtualSpacers[position] = spacer;
949
+ return spacer;
950
+ }
951
+
952
+ _syncVirtualSpacerColumns(spacer, columns) {
953
+ if (!spacer || !spacer.cells || !spacer.cells[0]) {
954
+ return;
955
+ }
956
+ spacer.cells[0].colSpan = Math.max(1, columns);
957
+ }
958
+
959
+ _updateVirtualSpacer(spacer, height, columns) {
960
+ if (!spacer || !spacer.cells || !spacer.cells[0]) {
961
+ return;
962
+ }
963
+ const safeHeight = Math.max(0, Math.round(height));
964
+ this._syncVirtualSpacerColumns(spacer, columns);
965
+ const cell = spacer.cells[0];
966
+ cell.style.height = `${safeHeight}px`;
967
+ spacer.hidden = safeHeight <= 0;
968
+ }
969
+
970
+ _teardownVirtualWindow() {
971
+ this._teardownVirtualizationBinding();
972
+ if (this._virtualState.frame && typeof window !== 'undefined' && typeof window.cancelAnimationFrame === 'function') {
973
+ window.cancelAnimationFrame(this._virtualState.frame);
974
+ }
975
+ this._virtualState.start = 0;
976
+ this._virtualState.end = 0;
977
+ this._virtualState.page = -1;
978
+ this._virtualState.perPage = -1;
979
+ this._virtualState.frame = 0;
980
+ this._virtualState.lastScrollTop = -1;
981
+ Object.keys(this._virtualSpacers || {}).forEach((key) => {
982
+ const spacer = this._virtualSpacers[key];
983
+ if (spacer && spacer.parentElement) {
984
+ spacer.parentElement.removeChild(spacer);
985
+ }
986
+ });
987
+ }
988
988
 
989
989
 
990
990
  _normalizePositiveInt(value, fallback) {
@@ -1003,7 +1003,7 @@ class VGDynamicTable extends BaseModule {
1003
1003
  }
1004
1004
 
1005
1005
  _restorePersistentState() {
1006
- const storedLocale = this._readPersistentState('locale');
1006
+ const storedLocale = this._readPersistentState('locale');
1007
1007
  if (storedLocale) {
1008
1008
  this._params.locale = String(storedLocale).toLowerCase().trim();
1009
1009
  }
@@ -1028,9 +1028,9 @@ class VGDynamicTable extends BaseModule {
1028
1028
  }
1029
1029
 
1030
1030
  const attrNameMap = {
1031
- locale: 'data-persistence-locale',
1032
- filtersApplyMode: 'data-persistence-filters-apply-mode',
1033
- };
1031
+ locale: 'data-persistence-locale',
1032
+ filtersApplyMode: 'data-persistence-filters-apply-mode',
1033
+ };
1034
1034
  const attrName = attrNameMap[key];
1035
1035
  if (attrName) {
1036
1036
  const attrValue = this._element.getAttribute(attrName);
@@ -1279,7 +1279,7 @@ class VGDynamicTable extends BaseModule {
1279
1279
  if (Array.isArray(rows) && rows.length) {
1280
1280
  return false;
1281
1281
  }
1282
- const message = this._getTableMessage('stateEmpty', 'Ничего нет');
1282
+ const message = this._getTableMessage('stateEmpty', 'Ничего нет');
1283
1283
  this._renderStateRow(message, 'empty');
1284
1284
  return true;
1285
1285
  }
@@ -1287,7 +1287,7 @@ class VGDynamicTable extends BaseModule {
1287
1287
  const body = this._getBody();
1288
1288
  body.innerHTML = viewRowsHtml;
1289
1289
  if (!this._hasRenderableRowsInBody(body)) {
1290
- this._renderStateRow(this._getTableMessage('stateEmpty', 'Ничего нет'), 'empty');
1290
+ this._renderStateRow(this._getTableMessage('stateEmpty', 'Ничего нет'), 'empty');
1291
1291
  return true;
1292
1292
  }
1293
1293
  this._setFixedColumnsSuppressed(false);
@@ -3026,12 +3026,34 @@ class VGDynamicTable extends BaseModule {
3026
3026
  return false;
3027
3027
  }
3028
3028
 
3029
- _isCloneStickyHeaderEnabled() {
3030
- if (this._parent && this._parent.classList.contains('table-sticky')) {
3031
- return true;
3032
- }
3033
- return Boolean(this._element && this._element.classList.contains('table-sticky'));
3034
- }
3029
+ _isCloneStickyHeaderEnabled() {
3030
+ const sticky = this._params.stickyHeader || {};
3031
+ const cloneAttr = this._element.getAttribute('data-sticky-header-clone');
3032
+ if (cloneAttr !== null) {
3033
+ const normalized = String(cloneAttr).toLowerCase().trim();
3034
+ return normalized !== 'false' && normalized !== '0';
3035
+ }
3036
+
3037
+ if (sticky.clone !== undefined && sticky.clone !== null) {
3038
+ const normalized = String(sticky.clone).toLowerCase().trim();
3039
+ if (normalized === 'true' || normalized === '1') {
3040
+ return true;
3041
+ }
3042
+ if (normalized === 'false' || normalized === '0') {
3043
+ return false;
3044
+ }
3045
+ return Boolean(sticky.clone);
3046
+ }
3047
+
3048
+ if (this._parent && this._parent.classList.contains('table-sticky')) {
3049
+ return true;
3050
+ }
3051
+ if (this._element && this._element.classList.contains('table-sticky')) {
3052
+ return true;
3053
+ }
3054
+
3055
+ return this._isStickyHeaderEnabled();
3056
+ }
3035
3057
 
3036
3058
  _refreshStickyAndFixedLayout(options = {}) {
3037
3059
  const immediate = Boolean(options && options.immediate);
@@ -3137,12 +3159,13 @@ class VGDynamicTable extends BaseModule {
3137
3159
  };
3138
3160
  }
3139
3161
 
3140
- _getCloneStickyHeaderOffsets() {
3141
- return {
3142
- top: this._readCssSizeVar('--vgdt-sticky-top', 0),
3143
- maxHeight: this._readCssSizeVar('--vgdt-sticky-max-height', 0),
3144
- };
3145
- }
3162
+ _getCloneStickyHeaderOffsets() {
3163
+ const configured = this._getStickyHeaderOffsets();
3164
+ return {
3165
+ top: configured.top || this._readCssSizeVar('--vgdt-sticky-top', 0),
3166
+ maxHeight: configured.maxHeight || this._readCssSizeVar('--vgdt-sticky-max-height', 0),
3167
+ };
3168
+ }
3146
3169
 
3147
3170
  _readCssSizeVar(name, fallback = 0) {
3148
3171
  const sources = [this._parent, this._element, this._tableViewport].filter(Boolean);
@@ -3235,12 +3258,13 @@ class VGDynamicTable extends BaseModule {
3235
3258
  }
3236
3259
  if (state && state.headerWrap && state.headerWrap.parentElement) {
3237
3260
  state.headerWrap.parentElement.removeChild(state.headerWrap);
3238
- }
3239
- this._cloneStickyState = null;
3240
- this._parent.style.removeProperty('--vgdt-sticky-clone-height');
3241
- if (scrollTarget && !this._isStickyHeaderEnabled()) {
3242
- scrollTarget.classList.remove('table-sticky-scroll');
3243
- scrollTarget.style.removeProperty('--vgdt-sticky-max-height');
3261
+ }
3262
+ this._cloneStickyState = null;
3263
+ this._parent.style.removeProperty('--vgdt-sticky-clone-height');
3264
+ this._parent.style.removeProperty('--vgdt-sticky-top');
3265
+ if (scrollTarget && !this._isStickyHeaderEnabled()) {
3266
+ scrollTarget.classList.remove('table-sticky-scroll');
3267
+ scrollTarget.style.removeProperty('--vgdt-sticky-max-height');
3244
3268
  }
3245
3269
  this._updateLayoutResizeBinding();
3246
3270
  return;
@@ -3252,10 +3276,11 @@ class VGDynamicTable extends BaseModule {
3252
3276
 
3253
3277
  const offsets = this._getCloneStickyHeaderOffsets();
3254
3278
 
3255
- if (scrollTarget) {
3256
- if (offsets.maxHeight > 0) {
3257
- scrollTarget.classList.add('table-sticky-scroll');
3258
- scrollTarget.style.setProperty('--vgdt-sticky-max-height', `${offsets.maxHeight}px`);
3279
+ if (scrollTarget) {
3280
+ this._parent.style.setProperty('--vgdt-sticky-top', `${offsets.top}px`);
3281
+ if (offsets.maxHeight > 0) {
3282
+ scrollTarget.classList.add('table-sticky-scroll');
3283
+ scrollTarget.style.setProperty('--vgdt-sticky-max-height', `${offsets.maxHeight}px`);
3259
3284
  } else {
3260
3285
  scrollTarget.classList.remove('table-sticky-scroll');
3261
3286
  scrollTarget.style.removeProperty('--vgdt-sticky-max-height');
@@ -3653,11 +3678,11 @@ class VGDynamicTable extends BaseModule {
3653
3678
  this._syncCloneStickyHeader();
3654
3679
  }
3655
3680
 
3656
- _renderRemoteRows(rows) {
3681
+ _renderRemoteRows(rows) {
3657
3682
  const body = this._getBody();
3658
3683
 
3659
3684
  if (!rows.length) {
3660
- const message = this._getTableMessage('stateEmpty', 'Ничего нет');
3685
+ const message = this._getTableMessage('stateEmpty', 'Ничего нет');
3661
3686
  this._renderStateRow(message, 'empty');
3662
3687
  return;
3663
3688
  }
@@ -3679,7 +3704,7 @@ class VGDynamicTable extends BaseModule {
3679
3704
  const body = this._getBody();
3680
3705
  const columns = this._getRenderedColumnsCount();
3681
3706
  const retryButton = state === 'error'
3682
- ? `<div class="vgdt-state-actions"><button type="button" class="vgdt-state-reset" data-state-retry>${this._escapeHtml(this._getTableMessage('retry', 'Повторить'))}</button></div>`
3707
+ ? `<div class="vgdt-state-actions"><button type="button" class="vgdt-state-reset" data-state-retry>${this._escapeHtml(this._getTableMessage('retry', 'Повторить'))}</button></div>`
3683
3708
  : '';
3684
3709
  const illustration = this._buildStateIllustrationMarkup(state);
3685
3710
  body.innerHTML = `<tr><td colspan="${columns}" data-table-state="${state}"><div class="vgdt-state">${illustration}<div class="vgdt-state-text">${this._escapeHtml(message)}</div>${retryButton}</div></td></tr>`;
@@ -3693,17 +3718,20 @@ class VGDynamicTable extends BaseModule {
3693
3718
  this._refreshStickyAndFixedLayout();
3694
3719
  }
3695
3720
 
3696
- _scrollToPaginationTop() {
3697
- if (!this._isPaginationScrollToTopEnabled()) {
3698
- return;
3699
- }
3700
- if (this._tableViewport) {
3701
- this._tableViewport.scrollTop = 0;
3702
- this._tableViewport.scrollLeft = 0;
3703
- }
3704
- if (typeof window === 'undefined' || !this._parent) {
3705
- return;
3706
- }
3721
+ _scrollToPaginationTop() {
3722
+ if (!this._isPaginationScrollToTopEnabled()) {
3723
+ return;
3724
+ }
3725
+ if (this._tableViewport) {
3726
+ this._tableViewport.scrollTop = 0;
3727
+ this._tableViewport.scrollLeft = 0;
3728
+ }
3729
+ if (this._hasFixedHeightViewport()) {
3730
+ return;
3731
+ }
3732
+ if (typeof window === 'undefined' || !this._parent) {
3733
+ return;
3734
+ }
3707
3735
 
3708
3736
  const targetTop = this._getPaginationScrollTopTarget();
3709
3737
  const currentTop = window.pageYOffset || window.scrollY || 0;
@@ -3720,11 +3748,24 @@ class VGDynamicTable extends BaseModule {
3720
3748
  window.scrollTo({
3721
3749
  top: Math.max(0, Math.round(targetTop)),
3722
3750
  behavior: prefersReducedMotion ? 'auto' : 'smooth',
3723
- });
3724
- }
3725
-
3726
- _getPaginationScrollTopTarget() {
3727
- if (this._isPaginationScrollToWindowTopEnabled()) {
3751
+ });
3752
+ }
3753
+
3754
+ _hasFixedHeightViewport() {
3755
+ if (this._tableViewport && this._tableViewport.classList.contains('table-sticky-scroll')) {
3756
+ return true;
3757
+ }
3758
+
3759
+ const offsets = this._getStickyHeaderOffsets();
3760
+ if (offsets.maxHeight > 0) {
3761
+ return true;
3762
+ }
3763
+
3764
+ return this._readCssSizeVar('--vgdt-sticky-max-height', 0) > 0;
3765
+ }
3766
+
3767
+ _getPaginationScrollTopTarget() {
3768
+ if (this._isPaginationScrollToWindowTopEnabled()) {
3728
3769
  return 0;
3729
3770
  }
3730
3771
  const rect = this._parent.getBoundingClientRect();