vgapp 1.2.4 → 1.2.6

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 (51) hide show
  1. package/CHANGELOG.md +66 -7
  2. package/README.md +2 -1
  3. package/agents.md +2 -2
  4. package/app/modules/base-module.js +87 -10
  5. package/app/modules/vgalert/js/vgalert.js +133 -202
  6. package/app/modules/vgalert/readme.md +105 -46
  7. package/app/modules/vgalert/scss/vgalert.scss +18 -0
  8. package/app/modules/vgdynamictable/index.js +5 -0
  9. package/app/modules/vgdynamictable/js/editable.js +438 -0
  10. package/app/modules/vgdynamictable/js/expandable.js +248 -0
  11. package/app/modules/vgdynamictable/js/filters.js +450 -0
  12. package/app/modules/vgdynamictable/js/fixed.js +566 -0
  13. package/app/modules/vgdynamictable/js/options.js +646 -0
  14. package/app/modules/vgdynamictable/js/pagination.js +623 -0
  15. package/app/modules/vgdynamictable/js/search.js +82 -0
  16. package/app/modules/vgdynamictable/js/skeleton.js +136 -0
  17. package/app/modules/vgdynamictable/js/sortable.js +442 -0
  18. package/app/modules/vgdynamictable/js/summary-footer.js +284 -0
  19. package/app/modules/vgdynamictable/js/table-remote.js +821 -0
  20. package/app/modules/vgdynamictable/js/table-state.js +243 -0
  21. package/app/modules/vgdynamictable/js/table-url-state.js +444 -0
  22. package/app/modules/vgdynamictable/js/utils/common.js +48 -0
  23. package/app/modules/vgdynamictable/js/vgdynamictable.js +3829 -0
  24. package/app/modules/vgdynamictable/js/viewport.js +322 -0
  25. package/app/modules/vgdynamictable/readme.md +251 -0
  26. package/app/modules/vgdynamictable/scss/_actions.scss +193 -0
  27. package/app/modules/vgdynamictable/scss/_pagination.scss +183 -0
  28. package/app/modules/vgdynamictable/scss/_skeleton.scss +60 -0
  29. package/app/modules/vgdynamictable/scss/_sortable.scss +63 -0
  30. package/app/modules/vgdynamictable/scss/_table.scss +157 -0
  31. package/app/modules/vgdynamictable/scss/_variables.scss +130 -0
  32. package/app/modules/vgdynamictable/scss/vgdynamictable.scss +267 -0
  33. package/app/modules/vgrangeslider/index.js +3 -0
  34. package/app/modules/vgrangeslider/js/skins.js +222 -0
  35. package/app/modules/vgrangeslider/js/vgrangeslider.js +704 -0
  36. package/app/modules/vgrangeslider/readme.md +523 -0
  37. package/app/modules/vgrangeslider/scss/_variables.scss +53 -0
  38. package/app/modules/vgrangeslider/scss/vgrangeslider.scss +240 -0
  39. package/app/modules/vgtoast/js/vgtoast.js +111 -111
  40. package/app/modules/vgtooltip/index.js +3 -0
  41. package/app/modules/vgtooltip/js/vgtooltip.js +493 -0
  42. package/app/modules/vgtooltip/readme.md +181 -0
  43. package/app/modules/vgtooltip/scss/_variables.scss +15 -0
  44. package/app/modules/vgtooltip/scss/vgtooltip.scss +64 -0
  45. package/app/utils/js/components/ajax.js +116 -7
  46. package/app/utils/js/components/placement.js +112 -41
  47. package/build/vgapp.css +1 -1
  48. package/build/vgapp.css.map +1 -1
  49. package/index.js +20 -17
  50. package/index.scss +9 -0
  51. package/package.json +1 -1
@@ -0,0 +1,438 @@
1
+ import EventHandler from "../../../utils/js/dom/event";
2
+ import Data from "../../../utils/js/dom/data";
3
+
4
+ const TABLE_SELECTOR = '[data-vg-table]';
5
+ const ROW_SELECTOR = 'tbody tr:not([data-group-header="1"])';
6
+ const ROW_CHECK_SELECTOR = '[data-row-select]';
7
+ const CHECK_ALL_SELECTOR = '[data-select-all]';
8
+ const DEFAULT_ROW_REORDER_HANDLE_SELECTOR = '[data-row-reorder-handle]';
9
+ const ROW_REORDER_IGNORE_SELECTOR = 'input, button, a, select, textarea, [contenteditable="true"]';
10
+ const TABLE_DATA_KEY = 'vg.dynamicTable';
11
+ const ACTION_CALLBACK_MAP = {
12
+ rowcheck: 'onRowCheck',
13
+ checkall: 'onCheckAll',
14
+ rowreorder: 'onRowReorder',
15
+ };
16
+
17
+ class Editable {
18
+ static _getInitStore() {
19
+ if (!this.__initStore) {
20
+ this.__initStore = new WeakSet();
21
+ }
22
+ return this.__initStore;
23
+ }
24
+
25
+ static _getDragStore() {
26
+ if (!this.__dragStore) {
27
+ this.__dragStore = new WeakMap();
28
+ }
29
+ return this.__dragStore;
30
+ }
31
+
32
+ static initAll() {
33
+ const tables = Array.from(document.querySelectorAll(TABLE_SELECTOR));
34
+ tables.forEach((table) => {
35
+ if (!this._isEditableEnabled(table)) {
36
+ return;
37
+ }
38
+
39
+ const initStore = this._getInitStore();
40
+ if (initStore.has(table)) {
41
+ this._syncSelectAll(table);
42
+ this._syncRowReorder(table);
43
+ return;
44
+ }
45
+
46
+ initStore.add(table);
47
+ this._bind(table);
48
+ this._syncSelectAll(table);
49
+ this._syncRowReorder(table);
50
+ });
51
+ }
52
+
53
+ static _bind(table) {
54
+ table.addEventListener('change', (event) => {
55
+ const checkAll = event.target.closest(CHECK_ALL_SELECTOR);
56
+ if (checkAll) {
57
+ this._toggleAll(table, Boolean(checkAll.checked));
58
+ this._syncSelectAll(table);
59
+ this._emit(table, 'checkall', this._getSelectionPayload(table, {
60
+ checked: Boolean(checkAll.checked),
61
+ }));
62
+ return;
63
+ }
64
+
65
+ const rowCheckbox = event.target.closest(ROW_CHECK_SELECTOR);
66
+ if (!rowCheckbox) {
67
+ return;
68
+ }
69
+
70
+ this._syncSelectAll(table);
71
+ const row = rowCheckbox.closest('tr');
72
+ this._emit(table, 'rowcheck', this._getSelectionPayload(table, {
73
+ checked: Boolean(rowCheckbox.checked),
74
+ row: this._getRowPayload(table, row),
75
+ }));
76
+ });
77
+
78
+ table.addEventListener('dragstart', (event) => {
79
+ this._handleRowDragStart(table, event);
80
+ });
81
+ table.addEventListener('dragover', (event) => {
82
+ this._handleRowDragOver(table, event);
83
+ });
84
+ table.addEventListener('drop', (event) => {
85
+ this._handleRowDrop(table, event);
86
+ });
87
+ table.addEventListener('dragend', () => {
88
+ this._clearRowDragState(table);
89
+ });
90
+
91
+ const eventPrefix = this._getEventPrefix(table);
92
+ table.addEventListener(`${eventPrefix}:dataloaded`, () => {
93
+ this._syncSelectAll(table);
94
+ this._syncRowReorder(table);
95
+ });
96
+ }
97
+
98
+ static _toggleAll(table, checked) {
99
+ Array.from(table.querySelectorAll(`${ROW_SELECTOR} ${ROW_CHECK_SELECTOR}`)).forEach((node) => {
100
+ node.checked = checked;
101
+ });
102
+ }
103
+
104
+ static _syncSelectAll(table) {
105
+ const rowCheckboxes = Array.from(table.querySelectorAll(`${ROW_SELECTOR} ${ROW_CHECK_SELECTOR}`));
106
+ const total = rowCheckboxes.length;
107
+ const selected = rowCheckboxes.filter((node) => node.checked).length;
108
+ const checkAll = table.querySelector(CHECK_ALL_SELECTOR);
109
+ if (!checkAll) {
110
+ return;
111
+ }
112
+ checkAll.checked = total > 0 && selected === total;
113
+ checkAll.indeterminate = selected > 0 && selected < total;
114
+ }
115
+
116
+ static _getSelectionPayload(table, extra = {}) {
117
+ const rowCheckboxes = Array.from(table.querySelectorAll(`${ROW_SELECTOR} ${ROW_CHECK_SELECTOR}`));
118
+ const total = rowCheckboxes.length;
119
+ const selected = rowCheckboxes.filter((node) => node.checked).length;
120
+ return Object.assign({ total, selected }, extra);
121
+ }
122
+
123
+ static _isEditableEnabled(table) {
124
+ const editableOptions = this._getEditableOptions(this._getTableInstance(table));
125
+ if (editableOptions.enable !== undefined) {
126
+ return Boolean(editableOptions.enable);
127
+ }
128
+ const attrEnabled = table.getAttribute('data-editable-enable');
129
+ if (attrEnabled === null) {
130
+ return false;
131
+ }
132
+ return this._parseBool(attrEnabled, false);
133
+ }
134
+
135
+ static _isRowReorderEnabled(table) {
136
+ const editableOptions = this._getEditableOptions(this._getTableInstance(table));
137
+ const attrEnabled = table.getAttribute('data-row-reorder-enable');
138
+ if (attrEnabled !== null) {
139
+ return this._parseBool(attrEnabled, false);
140
+ }
141
+ if (editableOptions.rowReorder !== undefined) {
142
+ return Boolean(editableOptions.rowReorder);
143
+ }
144
+ return true;
145
+ }
146
+
147
+ static _getRowReorderMode(table) {
148
+ const editableOptions = this._getEditableOptions(this._getTableInstance(table));
149
+ const attrMode = table.getAttribute('data-row-reorder-mode');
150
+ const mode = String(attrMode !== null ? attrMode : editableOptions.rowReorderMode || 'row').trim().toLowerCase();
151
+ return mode === 'handle' ? 'handle' : 'row';
152
+ }
153
+
154
+ static _getRowReorderHandleSelector(table) {
155
+ const editableOptions = this._getEditableOptions(this._getTableInstance(table));
156
+ const attrSelector = String(table.getAttribute('data-row-reorder-handle-selector') || '').trim();
157
+ const optionSelector = String(editableOptions.rowReorderHandleSelector || '').trim();
158
+ return attrSelector || optionSelector || DEFAULT_ROW_REORDER_HANDLE_SELECTOR;
159
+ }
160
+
161
+ static _syncRowReorder(table) {
162
+ const enabled = this._isRowReorderEnabled(table);
163
+ const mode = this._getRowReorderMode(table);
164
+ const handleSelector = this._getRowReorderHandleSelector(table);
165
+ const rows = Array.from(table.querySelectorAll(ROW_SELECTOR));
166
+
167
+ rows.forEach((row) => {
168
+ const handles = Array.from(row.querySelectorAll(handleSelector));
169
+ if (!enabled) {
170
+ row.removeAttribute('data-row-reorder');
171
+ row.removeAttribute('data-row-reorder-mode');
172
+ row.setAttribute('draggable', 'false');
173
+ handles.forEach((handle) => handle.setAttribute('draggable', 'false'));
174
+ return;
175
+ }
176
+
177
+ row.setAttribute('data-row-reorder', '1');
178
+ row.setAttribute('data-row-reorder-mode', mode);
179
+ row.setAttribute('draggable', mode === 'row' ? 'true' : 'false');
180
+ handles.forEach((handle) => handle.setAttribute('draggable', mode === 'handle' ? 'true' : 'false'));
181
+ });
182
+
183
+ if (!enabled) {
184
+ this._clearRowDragState(table);
185
+ }
186
+ }
187
+
188
+ static _getRowDragState(table) {
189
+ const store = this._getDragStore();
190
+ if (!store.has(table)) {
191
+ store.set(table, {
192
+ fromRow: null,
193
+ fromIndex: -1,
194
+ overRow: null,
195
+ overAfter: false,
196
+ });
197
+ }
198
+ return store.get(table);
199
+ }
200
+
201
+ static _clearRowDragState(table) {
202
+ const state = this._getRowDragState(table);
203
+ Array.from(table.querySelectorAll(`${ROW_SELECTOR}[data-row-dragging], ${ROW_SELECTOR}[data-row-drag-over]`)).forEach((row) => {
204
+ row.removeAttribute('data-row-dragging');
205
+ row.removeAttribute('data-row-drag-over');
206
+ });
207
+ state.fromRow = null;
208
+ state.fromIndex = -1;
209
+ state.overRow = null;
210
+ state.overAfter = false;
211
+ }
212
+
213
+ static _handleRowDragStart(table, event) {
214
+ if (!this._isRowReorderEnabled(table)) {
215
+ return;
216
+ }
217
+
218
+ const row = event.target && event.target.closest ? event.target.closest(`${ROW_SELECTOR}[data-row-reorder="1"]`) : null;
219
+ if (!row || !table.contains(row)) {
220
+ return;
221
+ }
222
+
223
+ const mode = this._getRowReorderMode(table);
224
+ if (mode === 'handle') {
225
+ const handleSelector = this._getRowReorderHandleSelector(table);
226
+ const isFromHandle = Boolean(event.target && event.target.closest && event.target.closest(handleSelector));
227
+ if (!isFromHandle) {
228
+ event.preventDefault();
229
+ return;
230
+ }
231
+ } else if (event.target && event.target.closest && event.target.closest(ROW_REORDER_IGNORE_SELECTOR)) {
232
+ event.preventDefault();
233
+ return;
234
+ }
235
+
236
+ const state = this._getRowDragState(table);
237
+ state.fromRow = row;
238
+ state.fromIndex = this._getBodyRowIndex(table, row);
239
+ state.overRow = null;
240
+ state.overAfter = false;
241
+ row.setAttribute('data-row-dragging', '1');
242
+
243
+ if (event.dataTransfer) {
244
+ event.dataTransfer.effectAllowed = 'move';
245
+ event.dataTransfer.setData('text/plain', 'row-reorder');
246
+ }
247
+ }
248
+
249
+ static _handleRowDragOver(table, event) {
250
+ const state = this._getRowDragState(table);
251
+ if (!state.fromRow) {
252
+ return;
253
+ }
254
+
255
+ const row = event.target && event.target.closest ? event.target.closest(`${ROW_SELECTOR}[data-row-reorder="1"]`) : null;
256
+ if (!row || !table.contains(row)) {
257
+ return;
258
+ }
259
+
260
+ event.preventDefault();
261
+ const rect = row.getBoundingClientRect();
262
+ const insertAfter = event.clientY > rect.top + (rect.height / 2);
263
+ state.overRow = row;
264
+ state.overAfter = insertAfter;
265
+
266
+ Array.from(table.querySelectorAll(`${ROW_SELECTOR}[data-row-drag-over]`)).forEach((node) => {
267
+ node.removeAttribute('data-row-drag-over');
268
+ });
269
+ if (row !== state.fromRow) {
270
+ row.setAttribute('data-row-drag-over', insertAfter ? 'after' : 'before');
271
+ }
272
+ }
273
+
274
+ static _handleRowDrop(table, event) {
275
+ const state = this._getRowDragState(table);
276
+ if (!state.fromRow) {
277
+ return;
278
+ }
279
+
280
+ event.preventDefault();
281
+ const targetRow = state.overRow;
282
+ if (!targetRow || !table.contains(targetRow)) {
283
+ this._clearRowDragState(table);
284
+ return;
285
+ }
286
+
287
+ const moved = this._moveRow(table, state.fromRow, targetRow, state.overAfter);
288
+ if (!moved) {
289
+ this._clearRowDragState(table);
290
+ return;
291
+ }
292
+
293
+ const toIndex = this._getBodyRowIndex(table, state.fromRow);
294
+ this._syncSelectAll(table);
295
+ this._emit(table, 'rowreorder', {
296
+ row: this._getRowPayload(table, state.fromRow),
297
+ fromIndex: state.fromIndex,
298
+ toIndex,
299
+ beforeRow: this._getSiblingRowPayload(table, state.fromRow, 'prev'),
300
+ afterRow: this._getSiblingRowPayload(table, state.fromRow, 'next'),
301
+ });
302
+ this._clearRowDragState(table);
303
+ }
304
+
305
+ static _moveRow(table, fromRow, targetRow, insertAfter) {
306
+ const body = table.tBodies && table.tBodies[0] ? table.tBodies[0] : null;
307
+ if (!body || !fromRow || !targetRow || fromRow === targetRow) {
308
+ return false;
309
+ }
310
+
311
+ if (insertAfter) {
312
+ if (targetRow.nextSibling === fromRow) {
313
+ return false;
314
+ }
315
+ body.insertBefore(fromRow, targetRow.nextSibling);
316
+ return true;
317
+ }
318
+
319
+ if (fromRow.nextSibling === targetRow) {
320
+ return false;
321
+ }
322
+ body.insertBefore(fromRow, targetRow);
323
+ return true;
324
+ }
325
+
326
+ static _getBodyRowIndex(table, row) {
327
+ const body = table.tBodies && table.tBodies[0] ? table.tBodies[0] : null;
328
+ if (!body || !row) {
329
+ return -1;
330
+ }
331
+ return Array.from(body.rows).indexOf(row);
332
+ }
333
+
334
+ static _getSiblingRowPayload(table, row, direction) {
335
+ if (!row) {
336
+ return null;
337
+ }
338
+ const sibling = direction === 'prev' ? row.previousElementSibling : row.nextElementSibling;
339
+ if (!sibling || sibling.tagName !== 'TR') {
340
+ return null;
341
+ }
342
+ return this._getRowPayload(table, sibling);
343
+ }
344
+
345
+ static _getRowPayload(table, row) {
346
+ if (!row) {
347
+ return null;
348
+ }
349
+ const headers = Array.from(table.querySelectorAll('thead th')).map((cell) => String(cell.textContent || '').trim());
350
+ const cells = Array.from(row.cells || []).map((cell) => String(cell.textContent || '').trim());
351
+ return {
352
+ index: this._getBodyRowIndex(table, row),
353
+ headers,
354
+ cells,
355
+ values: headers.reduce((acc, key, idx) => {
356
+ const normalized = key || `col_${idx}`;
357
+ acc[normalized] = cells[idx] || '';
358
+ return acc;
359
+ }, {}),
360
+ };
361
+ }
362
+
363
+ static _emit(table, action, payload = {}) {
364
+ const normalizedAction = String(action || '').toLowerCase().trim();
365
+ if (!normalizedAction) {
366
+ return;
367
+ }
368
+
369
+ const instance = this._getTableInstance(table);
370
+ const detail = Object.assign({
371
+ action: normalizedAction,
372
+ table,
373
+ isRemote: Boolean(instance && instance._isRemote),
374
+ }, payload);
375
+
376
+ const callbacks = instance && instance._params && instance._params.callbacks && typeof instance._params.callbacks === 'object'
377
+ ? instance._params.callbacks
378
+ : {};
379
+ const callbackName = ACTION_CALLBACK_MAP[normalizedAction] || '';
380
+ const callback = callbackName ? callbacks[callbackName] : null;
381
+ if (typeof callback === 'function') {
382
+ callback(detail, instance);
383
+ }
384
+
385
+ const events = instance && instance._params && instance._params.events ? instance._params.events : {};
386
+ const enabled = events.enable === undefined ? true : Boolean(events.enable);
387
+ if (!enabled || typeof window.CustomEvent !== 'function') {
388
+ return;
389
+ }
390
+
391
+ const prefix = String(events.prefix || 'vgdt').trim() || 'vgdt';
392
+ const bubbles = events.bubbles === undefined ? true : Boolean(events.bubbles);
393
+ table.dispatchEvent(new CustomEvent(`${prefix}:${normalizedAction}`, {
394
+ detail,
395
+ bubbles,
396
+ }));
397
+ }
398
+
399
+ static _getEditableOptions(instance = null) {
400
+ const current = instance || null;
401
+ return current && current._params && current._params.editable && typeof current._params.editable === 'object'
402
+ ? current._params.editable
403
+ : {};
404
+ }
405
+
406
+ static _getTableInstance(table) {
407
+ if (!table) {
408
+ return null;
409
+ }
410
+ return Data.get(table, TABLE_DATA_KEY);
411
+ }
412
+
413
+ static _getEventPrefix(table) {
414
+ const instance = this._getTableInstance(table);
415
+ const events = instance && instance._params && instance._params.events ? instance._params.events : {};
416
+ return String(events.prefix || 'vgdt').trim() || 'vgdt';
417
+ }
418
+
419
+ static _parseBool(value, fallback = false) {
420
+ if (value === undefined || value === null) {
421
+ return fallback;
422
+ }
423
+ const normalized = String(value).toLowerCase().trim();
424
+ if (normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on') {
425
+ return true;
426
+ }
427
+ if (normalized === '0' || normalized === 'false' || normalized === 'no' || normalized === 'off') {
428
+ return false;
429
+ }
430
+ return fallback;
431
+ }
432
+ }
433
+
434
+ EventHandler.on(document, 'DOMContentLoaded', () => {
435
+ Editable.initAll();
436
+ });
437
+
438
+ export default Editable;
@@ -0,0 +1,248 @@
1
+ class Expandable {
2
+ constructor(table, params = {}) {
3
+ this._table = table;
4
+ this._params = params && typeof params === 'object' ? params : {};
5
+ this._collapsed = new Set();
6
+ this._boundClick = this._handleClick.bind(this);
7
+ this._initialized = false;
8
+ }
9
+
10
+ init() {
11
+ if (!this._isEnabled() || this._initialized) {
12
+ return;
13
+ }
14
+ this._table.addEventListener('click', this._boundClick);
15
+ this._initialized = true;
16
+ this.refresh();
17
+ }
18
+
19
+ destroy() {
20
+ if (!this._initialized) {
21
+ return;
22
+ }
23
+ this._table.removeEventListener('click', this._boundClick);
24
+ this._initialized = false;
25
+ }
26
+
27
+ refresh() {
28
+ if (!this._isEnabled()) {
29
+ return;
30
+ }
31
+
32
+ const rows = this._getRows();
33
+ if (!rows.length) {
34
+ return;
35
+ }
36
+
37
+ this._resetExpandHidden(rows);
38
+ this._rememberBaseVisibility(rows);
39
+
40
+ const idAttr = this._getIdAttr();
41
+ const parentAttr = this._getParentAttr();
42
+ const rowById = new Map();
43
+ const parentById = new Map();
44
+ const childrenByParent = new Map();
45
+
46
+ rows.forEach((row, index) => {
47
+ const fallbackId = `row-${index + 1}`;
48
+ const rowId = String(row.getAttribute(idAttr) || fallbackId).trim() || fallbackId;
49
+ row.setAttribute(idAttr, rowId);
50
+ rowById.set(rowId, row);
51
+
52
+ const parentId = String(row.getAttribute(parentAttr) || '').trim();
53
+ parentById.set(rowId, parentId);
54
+ if (parentId) {
55
+ if (!childrenByParent.has(parentId)) {
56
+ childrenByParent.set(parentId, []);
57
+ }
58
+ childrenByParent.get(parentId).push(rowId);
59
+ }
60
+ });
61
+
62
+ const depthCache = new Map();
63
+ const getDepth = (rowId) => {
64
+ if (depthCache.has(rowId)) {
65
+ return depthCache.get(rowId);
66
+ }
67
+ const parentId = String(parentById.get(rowId) || '').trim();
68
+ if (!parentId) {
69
+ depthCache.set(rowId, 0);
70
+ return 0;
71
+ }
72
+ if (!rowById.has(parentId) || parentId === rowId) {
73
+ depthCache.set(rowId, 0);
74
+ return 0;
75
+ }
76
+ const depth = Math.max(0, getDepth(parentId) + 1);
77
+ depthCache.set(rowId, depth);
78
+ return depth;
79
+ };
80
+
81
+ const isAncestorCollapsed = (rowId) => {
82
+ let parentId = String(parentById.get(rowId) || '').trim();
83
+ while (parentId) {
84
+ if (this._collapsed.has(parentId)) {
85
+ return true;
86
+ }
87
+ parentId = String(parentById.get(parentId) || '').trim();
88
+ }
89
+ return false;
90
+ };
91
+
92
+ rowById.forEach((row, rowId) => {
93
+ const depth = getDepth(rowId);
94
+ row.setAttribute('data-expand-depth', String(depth));
95
+ row.style.setProperty('--vgdt-expand-depth', String(depth));
96
+
97
+ const hasChildren = childrenByParent.has(rowId) && childrenByParent.get(rowId).length > 0;
98
+ const seeded = row.getAttribute('data-expand-seeded') === '1';
99
+ if (hasChildren && !seeded) {
100
+ if (this._isInitiallyCollapsed(row)) {
101
+ this._collapsed.add(rowId);
102
+ }
103
+ row.setAttribute('data-expand-seeded', '1');
104
+ }
105
+ this._syncToggleNode(row, rowId, hasChildren);
106
+
107
+ const baseHidden = row.getAttribute('data-expand-base-hidden') === '1';
108
+ const collapseHidden = isAncestorCollapsed(rowId);
109
+ const nextHidden = baseHidden || collapseHidden;
110
+ row.hidden = nextHidden;
111
+ if (collapseHidden) {
112
+ row.setAttribute('data-expand-hidden', '1');
113
+ } else {
114
+ row.removeAttribute('data-expand-hidden');
115
+ }
116
+ });
117
+ }
118
+
119
+ _handleClick(event) {
120
+ const toggleSelector = this._getToggleSelector();
121
+ const rawTarget = event.target || null;
122
+ const target = rawTarget && rawTarget.nodeType === 3 ? rawTarget.parentElement : rawTarget;
123
+ const toggle = target && target.closest ? target.closest(toggleSelector) : null;
124
+ if (!toggle || !this._table.contains(toggle)) {
125
+ return;
126
+ }
127
+ event.preventDefault();
128
+
129
+ const row = toggle.closest('tr');
130
+ if (!row) {
131
+ return;
132
+ }
133
+ const rowId = String(row.getAttribute(this._getIdAttr()) || '').trim();
134
+ if (!rowId) {
135
+ return;
136
+ }
137
+
138
+ const collapsed = !this._collapsed.has(rowId);
139
+ if (collapsed) {
140
+ this._collapsed.add(rowId);
141
+ } else {
142
+ this._collapsed.delete(rowId);
143
+ }
144
+ this.refresh();
145
+
146
+ const payload = {
147
+ id: rowId,
148
+ collapsed,
149
+ row,
150
+ };
151
+ if (typeof this._params.onToggle === 'function') {
152
+ this._params.onToggle(payload);
153
+ }
154
+ }
155
+
156
+ _syncToggleNode(row, rowId, hasChildren) {
157
+ const selector = this._getToggleSelector();
158
+ let toggle = row.querySelector(selector);
159
+ if (!toggle && hasChildren) {
160
+ const firstCell = row.cells && row.cells[0] ? row.cells[0] : null;
161
+ if (firstCell) {
162
+ toggle = document.createElement('button');
163
+ toggle.type = 'button';
164
+ toggle.className = 'vgdt-expand-toggle';
165
+ toggle.setAttribute('data-expand-toggle', '1');
166
+ firstCell.prepend(toggle);
167
+ }
168
+ }
169
+ if (!toggle) {
170
+ return;
171
+ }
172
+
173
+ // Keep visual spacing stable even if CSS bundle is outdated.
174
+ toggle.style.marginInlineEnd = '10px';
175
+ toggle.style.cursor = 'pointer';
176
+
177
+ const nextNode = toggle.nextSibling;
178
+ const hasLeadingSpace = nextNode
179
+ && nextNode.nodeType === 3
180
+ && /^\s/.test(String(nextNode.textContent || ''));
181
+ if (!hasLeadingSpace) {
182
+ toggle.after(document.createTextNode(' '));
183
+ }
184
+
185
+ toggle.setAttribute('data-expand-toggle', rowId);
186
+ toggle.setAttribute('aria-expanded', this._collapsed.has(rowId) ? 'false' : 'true');
187
+ toggle.hidden = !hasChildren;
188
+ if (!hasChildren) {
189
+ return;
190
+ }
191
+ const collapsed = this._collapsed.has(rowId);
192
+ toggle.textContent = collapsed ? '+' : '−';
193
+ }
194
+
195
+ _getRows() {
196
+ const body = this._table.tBodies && this._table.tBodies[0];
197
+ if (!body) {
198
+ return [];
199
+ }
200
+ return Array.from(body.rows).filter((row) => !row.querySelector('[data-table-state]'));
201
+ }
202
+
203
+ _resetExpandHidden(rows) {
204
+ rows.forEach((row) => {
205
+ if (row.getAttribute('data-expand-hidden') === '1') {
206
+ row.hidden = false;
207
+ row.removeAttribute('data-expand-hidden');
208
+ }
209
+ });
210
+ }
211
+
212
+ _rememberBaseVisibility(rows) {
213
+ rows.forEach((row) => {
214
+ row.setAttribute('data-expand-base-hidden', row.hidden ? '1' : '0');
215
+ });
216
+ }
217
+
218
+ _isInitiallyCollapsed(row) {
219
+ const attr = row.getAttribute('data-expand-collapsed');
220
+ if (attr !== null) {
221
+ return this._isTruthy(attr);
222
+ }
223
+ return Boolean(this._params.collapsed);
224
+ }
225
+
226
+ _isEnabled() {
227
+ return Boolean(this._params.enable);
228
+ }
229
+
230
+ _getIdAttr() {
231
+ return String(this._params.idAttr || 'data-expand-id').trim() || 'data-expand-id';
232
+ }
233
+
234
+ _getParentAttr() {
235
+ return String(this._params.parentAttr || 'data-expand-parent-id').trim() || 'data-expand-parent-id';
236
+ }
237
+
238
+ _getToggleSelector() {
239
+ return String(this._params.toggleSelector || '[data-expand-toggle]').trim() || '[data-expand-toggle]';
240
+ }
241
+
242
+ _isTruthy(value) {
243
+ const normalized = String(value || '').toLowerCase().trim();
244
+ return normalized !== '' && normalized !== '0' && normalized !== 'false' && normalized !== 'off' && normalized !== 'no';
245
+ }
246
+ }
247
+
248
+ export default Expandable;