vgapp 1.4.8 → 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 +22 -10
  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 +4 -4
  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,134 @@
1
+ /**
2
+ * Описание: отдельное поле поиска VGTable для локальных и Remote-таблиц.
3
+ * Возможности: debounce, немедленный запуск по Enter, очистка, публичное состояние и событие изменения.
4
+ */
5
+ import EventHandler from "../../../utils/js/dom/event";
6
+
7
+
8
+ const EVENT_CHANGE = 'searchchange.vg.table';
9
+
10
+ class _search {
11
+ constructor(table, options = {}, onChange = null) {
12
+ this._table = table;
13
+ this._options = options;
14
+ this._onChange = typeof onChange === 'function' ? onChange : null;
15
+ this._input = null;
16
+ this._resetButtons = [];
17
+ this._timer = null;
18
+ this._boundInput = this._handleInput.bind(this);
19
+ this._boundKeydown = this._handleKeydown.bind(this);
20
+ this._boundReset = this._handleReset.bind(this);
21
+ }
22
+
23
+ init() {
24
+ this._input = this._resolveNode(this._options.input);
25
+ if (!this._input) return this;
26
+ this._resetButtons = this._resolveNodes(this._options.button?.reset || '[data-search-reset]');
27
+ this._input.addEventListener('input', this._boundInput);
28
+ this._input.addEventListener('keydown', this._boundKeydown);
29
+ this._resetButtons.forEach((button) => button.addEventListener('click', this._boundReset));
30
+ return this;
31
+ }
32
+
33
+ dispose() {
34
+ this._input?.removeEventListener('input', this._boundInput);
35
+ this._input?.removeEventListener('keydown', this._boundKeydown);
36
+ this._resetButtons.forEach((button) => button.removeEventListener('click', this._boundReset));
37
+ this._clearTimer();
38
+ this._input = null;
39
+ this._resetButtons = [];
40
+ }
41
+
42
+ getState() {
43
+ return {
44
+ value: this._value(),
45
+ param: String(this._options.param || 'q').trim() || 'q',
46
+ };
47
+ }
48
+
49
+ setValue(value, options = {}) {
50
+ if (!this._input) return this.getState();
51
+ this._input.value = String(value ?? '');
52
+ this._clearTimer();
53
+ if (options.emit === true) this._emit(String(options.source || 'api'));
54
+ return this.getState();
55
+ }
56
+
57
+ reset(options = {}) {
58
+ return this.setValue('', {
59
+ emit: options.emit !== false,
60
+ source: String(options.source || 'reset'),
61
+ });
62
+ }
63
+
64
+ refresh() {
65
+ return this.getState();
66
+ }
67
+
68
+ _handleInput() {
69
+ this._schedule('input');
70
+ }
71
+
72
+ _handleKeydown(event) {
73
+ if (event.key !== 'Enter') return;
74
+ event.preventDefault();
75
+ this._clearTimer();
76
+ this._emit('enter');
77
+ }
78
+
79
+ _handleReset(event) {
80
+ event.preventDefault();
81
+ this.reset({source: 'reset'});
82
+ this._input?.focus?.();
83
+ }
84
+
85
+ _emit(source) {
86
+ const state = Object.assign(this.getState(), {source});
87
+ EventHandler.trigger(this._table, EVENT_CHANGE, state);
88
+ this._onChange?.(state);
89
+ }
90
+
91
+ _schedule(source) {
92
+ this._clearTimer();
93
+ this._timer = window.setTimeout(() => {
94
+ this._timer = null;
95
+ this._emit(source);
96
+ }, this._nonNegativeInt(this._options.debounce, 300));
97
+ }
98
+
99
+ _clearTimer() {
100
+ if (this._timer) window.clearTimeout(this._timer);
101
+ this._timer = null;
102
+ }
103
+
104
+ _value() {
105
+ const value = String(this._input?.value ?? '');
106
+ return this._options.trim === false ? value : value.trim();
107
+ }
108
+
109
+ _resolveNode(value) {
110
+ if (typeof Element !== 'undefined' && value instanceof Element) return value;
111
+ const selector = String(value || '').trim();
112
+ if (!selector) return null;
113
+ try { return document.querySelector(selector); } catch (_) { return null; }
114
+ }
115
+
116
+ _resolveNodes(value) {
117
+ const selector = String(value || '').trim();
118
+ if (!selector) return [];
119
+ try {
120
+ const scope = this._input?.form || this._input?.closest?.('[data-search-controls]') || document;
121
+ const scoped = Array.from(scope.querySelectorAll(selector));
122
+ return scoped.length ? scoped : Array.from(document.querySelectorAll(selector));
123
+ } catch (_) {
124
+ return [];
125
+ }
126
+ }
127
+
128
+ _nonNegativeInt(value, fallback) {
129
+ const parsed = Number.parseInt(value, 10);
130
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
131
+ }
132
+ }
133
+
134
+ export default _search;
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Описание: управление выбором строк базовой таблицы VGTable.
3
+ * Возможности: выбор кликом, одиночный и множественный режимы, публичное управление и событие изменения.
4
+ */
5
+ import EventHandler from "../../../utils/js/dom/event";
6
+
7
+
8
+ const EVENT_SELECTION_CHANGE = 'selectionchange.vg.table';
9
+ const ROW_SELECTOR = 'tbody tr';
10
+ const INTERACTIVE_SELECTOR = 'a, button, input, select, textarea, label, [contenteditable="true"], [role="button"], [data-vg-table-selection-ignore]';
11
+ const SELECTED_CLASS = 'vg-table-row-selected';
12
+ const SELECTED_ATTRIBUTE = 'data-vg-table-selected';
13
+
14
+ class _selection {
15
+ constructor(table, options = {}) {
16
+ this._table = table;
17
+ this._options = Object.assign({click: true, multiple: false}, options);
18
+ this._boundClick = this._handleClick.bind(this);
19
+ }
20
+
21
+ init() {
22
+ this._table.classList.toggle('vg-table-selection-click', this._options.click === true);
23
+ if (this._options.click === true) this._table.addEventListener('click', this._boundClick);
24
+ }
25
+
26
+ dispose() {
27
+ this._table.removeEventListener('click', this._boundClick);
28
+ this._table.classList.remove('vg-table-selection-click');
29
+ this.getSelectedRows().forEach((row) => this._setRowState(row, false));
30
+ }
31
+
32
+ selectRow(row, selected = true, emit = true) {
33
+ const target = this._resolveRow(row);
34
+ if (!target) return false;
35
+
36
+ const nextSelected = selected === true;
37
+ const currentlySelected = target.classList.contains(SELECTED_CLASS);
38
+ if (nextSelected === currentlySelected) return true;
39
+
40
+ if (nextSelected && this._options.multiple !== true) {
41
+ this.getSelectedRows().forEach((selectedRow) => {
42
+ if (selectedRow !== target) this._setRowState(selectedRow, false);
43
+ });
44
+ }
45
+
46
+ this._setRowState(target, nextSelected);
47
+ if (emit) this._emitChange(target, nextSelected);
48
+ return true;
49
+ }
50
+
51
+ toggleRow(row, emit = true) {
52
+ const target = this._resolveRow(row);
53
+ if (!target) return false;
54
+ return this.selectRow(target, !target.classList.contains(SELECTED_CLASS), emit);
55
+ }
56
+
57
+ getSelectedRows() {
58
+ return this._getRows().filter((row) => row.classList.contains(SELECTED_CLASS));
59
+ }
60
+
61
+ clearSelection(emit = true) {
62
+ const selectedRows = this.getSelectedRows();
63
+ if (!selectedRows.length) return false;
64
+
65
+ selectedRows.forEach((row) => this._setRowState(row, false));
66
+ if (emit) this._emitChange(null, false);
67
+ return true;
68
+ }
69
+
70
+ _handleClick(event) {
71
+ if (event.target.closest(INTERACTIVE_SELECTOR)) return;
72
+
73
+ const row = event.target.closest(ROW_SELECTOR);
74
+ if (row && this._table.contains(row)) this.toggleRow(row);
75
+ }
76
+
77
+ _getRows() {
78
+ return Array.from(this._table.tBodies || []).flatMap((body) => Array.from(body.rows || []));
79
+ }
80
+
81
+ _resolveRow(row) {
82
+ if (row instanceof Element) {
83
+ const target = row.matches(ROW_SELECTOR) ? row : row.closest(ROW_SELECTOR);
84
+ return target && this._table.contains(target) ? target : null;
85
+ }
86
+
87
+ const index = Number.parseInt(row, 10);
88
+ return Number.isInteger(index) ? this._getRows()[index] || null : null;
89
+ }
90
+
91
+ _setRowState(row, selected) {
92
+ row.classList.toggle(SELECTED_CLASS, selected);
93
+ if (selected) {
94
+ row.setAttribute(SELECTED_ATTRIBUTE, 'true');
95
+ } else {
96
+ row.removeAttribute(SELECTED_ATTRIBUTE);
97
+ }
98
+ }
99
+
100
+ _emitChange(row, selected) {
101
+ const rows = this.getSelectedRows();
102
+ const tableRows = this._getRows();
103
+ const keys = rows.map((selectedRow) => selectedRow.getAttribute('data-row-key') || String(tableRows.indexOf(selectedRow)));
104
+ EventHandler.trigger(this._table, EVENT_SELECTION_CHANGE, {
105
+ row,
106
+ selected,
107
+ rows,
108
+ keys,
109
+ });
110
+ }
111
+ }
112
+
113
+ export default _selection;
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Описание: skeleton-состояние загрузки строк VGTable.
3
+ * Возможности: сохраняет количество, высоту и ширины текущих строк, поддерживает пустую таблицу и пагинацию.
4
+ */
5
+
6
+ const SKELETON_ROW_SELECTOR = 'tr[data-vg-table-skeleton]';
7
+
8
+ class _skeleton {
9
+ constructor(table, options = {}, hooks = {}) {
10
+ this._table = table;
11
+ this._options = options;
12
+ this._hooks = hooks;
13
+ }
14
+
15
+ render() {
16
+ if (this._options.enabled === false) return 0;
17
+ const body = this._body();
18
+ const rows = this._dataRows();
19
+ const widths = this._columnWidths(rows[0]);
20
+ const preserveColumnWidths = this._hasHorizontalOverflow();
21
+ const columns = Math.max(1, widths.length, this._headerCells().length);
22
+ const rowCount = this._rowsCount(rows);
23
+ const rowHeight = this._rowHeight(rows[0]);
24
+ const fragment = this._table.ownerDocument.createDocumentFragment();
25
+
26
+ for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {
27
+ const row = this._table.ownerDocument.createElement('tr');
28
+ row.className = 'vg-table-skeleton-row';
29
+ row.setAttribute('data-vg-table-skeleton', '');
30
+ row.setAttribute('aria-hidden', 'true');
31
+ row.style.setProperty('--vg-table-skeleton-delay', `${(rowIndex * 0.08).toFixed(2)}s`);
32
+ if (rowHeight > 0) row.style.setProperty('--vg-table-skeleton-row-height', `${rowHeight}px`);
33
+
34
+ for (let columnIndex = 0; columnIndex < columns; columnIndex += 1) {
35
+ const cell = this._table.ownerDocument.createElement('td');
36
+ if (rowHeight > 0) cell.style.height = `${rowHeight}px`;
37
+ const width = Number.isFinite(widths[columnIndex]) ? Math.round(widths[columnIndex]) : 0;
38
+ if (preserveColumnWidths && width > 0) {
39
+ cell.style.width = `${width}px`;
40
+ cell.style.minWidth = `${width}px`;
41
+ cell.style.maxWidth = `${width}px`;
42
+ }
43
+ const content = this._table.ownerDocument.createElement('span');
44
+ content.className = 'vg-table-skeleton-cell';
45
+ const line = this._table.ownerDocument.createElement('span');
46
+ line.className = 'vg-table-skeleton-line';
47
+ content.append(line);
48
+ cell.append(content);
49
+ row.append(cell);
50
+ }
51
+ fragment.append(row);
52
+ }
53
+
54
+ body.replaceChildren(fragment);
55
+ this._hooks.afterRender?.();
56
+ return rowCount;
57
+ }
58
+
59
+ dispose() {
60
+ this._body().querySelectorAll(SKELETON_ROW_SELECTOR).forEach((row) => row.remove());
61
+ }
62
+
63
+ _rowsCount(rows) {
64
+ if (rows.length) return rows.length;
65
+ const current = this._body().querySelectorAll(SKELETON_ROW_SELECTOR).length;
66
+ if (current) return current;
67
+ const explicit = Number.parseInt(this._options.skeleton ?? this._options.rows, 10);
68
+ if (Number.isFinite(explicit) && explicit > 0) return explicit;
69
+ const perPage = Number.parseInt(this._hooks.getPagination?.()?.perPage, 10);
70
+ return Number.isFinite(perPage) && perPage > 0 ? perPage : 5;
71
+ }
72
+
73
+ _columnWidths(firstRow) {
74
+ const headerWidths = this._headerCells().map((cell) => this._width(cell));
75
+ if (headerWidths.some((width) => width > 0)) return headerWidths;
76
+ return firstRow ? Array.from(firstRow.cells || [], (cell) => this._width(cell)) : [];
77
+ }
78
+
79
+ _headerCells() {
80
+ const headerTable = this._hooks.getHeaderTable?.() || this._table;
81
+ const head = headerTable.tHead || headerTable.querySelector('thead');
82
+ return head?.rows.length ? Array.from(head.rows[head.rows.length - 1].cells) : [];
83
+ }
84
+
85
+ _dataRows() {
86
+ return Array.from(this._body().rows || []).filter((row) => (
87
+ !row.matches(SKELETON_ROW_SELECTOR) && !row.querySelector('[data-vg-table-state]')
88
+ ));
89
+ }
90
+
91
+ _rowHeight(row) {
92
+ const rowHeight = row ? Math.round(this._height(row)) : 0;
93
+ if (rowHeight > 0) return rowHeight;
94
+ const headerTable = this._hooks.getHeaderTable?.() || this._table;
95
+ const head = headerTable.tHead || headerTable.querySelector('thead');
96
+ const headerRow = head?.rows.length ? head.rows[head.rows.length - 1] : null;
97
+ return headerRow ? Math.round(this._height(headerRow)) : 0;
98
+ }
99
+
100
+ _width(element) {
101
+ const width = element?.getBoundingClientRect?.().width;
102
+ return Number.isFinite(width) && width > 0 ? width : 0;
103
+ }
104
+
105
+ _height(element) {
106
+ const height = element?.getBoundingClientRect?.().height;
107
+ return Number.isFinite(height) && height > 0 ? height : 0;
108
+ }
109
+
110
+ _hasHorizontalOverflow() {
111
+ const host = this._hooks.getScrollHost?.()
112
+ || this._table.closest('.vg-table-body')
113
+ || this._table.closest('.vg-table-container');
114
+ if (!host) return false;
115
+ return host.scrollWidth - host.clientWidth > 1;
116
+ }
117
+
118
+ _body() {
119
+ return this._table.tBodies[0] || this._table.createTBody();
120
+ }
121
+ }
122
+
123
+ export default _skeleton;