vgapp 1.4.7 → 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 +14 -5
  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 +5 -5
  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
@@ -52,9 +52,11 @@ $files-map: (
52
52
  drop-file-flex: calc(25% - 6px),
53
53
 
54
54
  drop-remove-offset: 10px,
55
- drop-remove-size: 36px,
55
+ drop-remove-size: 28px,
56
56
  drop-remove-radius: 3px,
57
57
  drop-remove-hover-bg: rgb(var(--vg-body-bg-rgb, 255 255 255) / .4),
58
+ drop-actions-gap: 4px,
59
+ drop-control-icon-size: 16px,
58
60
 
59
61
  // stat
60
62
  stat-padding: 12px 8px,
@@ -48,6 +48,7 @@
48
48
 
49
49
  svg {
50
50
  width: 24px;
51
+ height: 24px;
51
52
 
52
53
  path {
53
54
  fill: var(--vg-files-label-color);
@@ -195,7 +196,7 @@
195
196
  .stat-label {
196
197
  svg {
197
198
  path {
198
- fill: var(--vg-secondary-bg);
199
+ fill: var(--vg-text-color);
199
200
  }
200
201
  }
201
202
  }
@@ -261,7 +262,7 @@
261
262
  }
262
263
 
263
264
  .file {
264
- --vg-button-color: var(--vg-secondary);
265
+ --vg-button-color: var(--vg-secondary-color, var(--vg-secondary, var(--vg-text-muted-color, #6b7280)));
265
266
  display: flex;
266
267
  align-items: center;
267
268
  gap: var(--vg-files-info-gap);
@@ -434,7 +435,7 @@
434
435
  position: relative;
435
436
 
436
437
  &:hover {
437
- --vg-button-color: var(--vg-danger);
438
+ --vg-button-color: var(--vg-danger-color, var(--vg-danger, #ff2a2c));
438
439
  }
439
440
 
440
441
  i {
@@ -531,6 +532,7 @@
531
532
  }
532
533
 
533
534
  &-drop {
535
+ display: block;
534
536
  background-color: var(--vg-files-drop-bg);
535
537
  border: var(--vg-files-drop-border-width) var(--vg-files-drop-border-style) var(--vg-files-drop-border-color);
536
538
  cursor: pointer;
@@ -548,6 +550,14 @@
548
550
  height: auto;
549
551
  min-height: var(--vg-files-drop-height);
550
552
 
553
+ &:has(.vg-files-drop-message.has-files) {
554
+ cursor: default;
555
+ }
556
+
557
+ .vg-files-drop-message.has-files {
558
+ cursor: pointer;
559
+ }
560
+
551
561
  &:hover {
552
562
  animation: none;
553
563
  background-image: none;
@@ -637,7 +647,7 @@
637
647
  overflow: hidden;
638
648
 
639
649
  .file {
640
- --vg-button-color: var(--vg-body-color);
650
+ --vg-button-color: var(--vg-text-color, var(--vg-body-color, #000000));
641
651
  border-radius: var(--vg-files-drop-file-radius);
642
652
  position: relative;
643
653
  overflow: hidden;
@@ -708,7 +718,33 @@
708
718
  top: var(--vg-files-drop-remove-offset);
709
719
  display: flex;
710
720
  flex-direction: column;
711
- gap: 8px;
721
+ align-items: flex-end;
722
+ gap: var(--vg-files-drop-actions-gap);
723
+ opacity: .2;
724
+ transform: var(--vg-transition-base);
725
+
726
+ .file-actions {
727
+ display: flex;
728
+ flex-direction: column;
729
+ align-items: center;
730
+ gap: var(--vg-files-drop-actions-gap);
731
+
732
+ button {
733
+ width: var(--vg-files-drop-remove-size);
734
+ height: var(--vg-files-drop-remove-size);
735
+ min-width: var(--vg-files-drop-remove-size);
736
+ min-height: var(--vg-files-drop-remove-size);
737
+ max-width: var(--vg-files-drop-remove-size);
738
+ max-height: var(--vg-files-drop-remove-size);
739
+ padding: 0;
740
+ justify-content: center;
741
+
742
+ svg {
743
+ width: var(--vg-files-drop-control-icon-size);
744
+ height: var(--vg-files-drop-control-icon-size);
745
+ }
746
+ }
747
+ }
712
748
 
713
749
  .file-remove {
714
750
  .file-failing-actions {
@@ -717,7 +753,7 @@
717
753
 
718
754
  button {
719
755
  background: var(--vg-body-bg);
720
- color: var(--vg-body-color);
756
+ color: var(--vg-text-color, var(--vg-body-color, #000000));
721
757
  border: none;
722
758
  cursor: pointer;
723
759
  outline: none;
@@ -729,12 +765,12 @@
729
765
  position: relative;
730
766
  width: var(--vg-files-drop-remove-size);
731
767
  height: var(--vg-files-drop-remove-size);
768
+ min-width: var(--vg-files-drop-remove-size);
769
+ min-height: var(--vg-files-drop-remove-size);
770
+ max-width: var(--vg-files-drop-remove-size);
771
+ max-height: var(--vg-files-drop-remove-size);
732
772
  border-radius: var(--vg-files-drop-remove-radius);
733
773
 
734
- &:hover {
735
- background-color: var(--vg-files-drop-remove-hover-bg);
736
- }
737
-
738
774
  i {
739
775
  position: absolute;
740
776
  left: 0;
@@ -746,6 +782,9 @@
746
782
  justify-content: center;
747
783
 
748
784
  svg {
785
+ width: var(--vg-files-drop-control-icon-size);
786
+ height: var(--vg-files-drop-control-icon-size);
787
+
749
788
  path {
750
789
  fill: var(--vg-button-color);
751
790
  }
@@ -754,6 +793,12 @@
754
793
  }
755
794
  }
756
795
  }
796
+
797
+ &:hover {
798
+ .file-custom {
799
+ opacity: 1;
800
+ }
801
+ }
757
802
  }
758
803
  }
759
804
  }
@@ -1,4 +1,4 @@
1
- /**
1
+ /**
2
2
  * VGFormSender Module
3
3
  *
4
4
  * Этот модуль отвечает за отправку форм с поддержкой AJAX, валидации, отображения уведомлений
@@ -702,7 +702,10 @@ class VGFormSender extends BaseModule {
702
702
  $collapse.classList.add(_this._params.classes.alertCollapse);
703
703
  $collapse.classList.add('vg-collapse');
704
704
  $collapse.id = _this._params.classes.general + '-' + makeRandomString();
705
- $collapse.append(_this.setDataRelationStatus($collapse, status, data, 'collapse'));
705
+
706
+ const $alert = _this.setDataRelationStatus($collapse, status, data, 'collapse');
707
+ $alert.classList.add(CLASS_NAME_ALERT, CLASS_NAME_ALERT + '-' + status);
708
+ $collapse.append($alert);
706
709
 
707
710
  _this._element.prepend($collapse);
708
711
  }
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Описание: подгрузка статического и удалённого контента по кнопке или при прокрутке.
3
+ * Возможности: Data API, AJAX-пагинация, infinite scroll, события, анимация и автоcкрытие триггера.
4
+ */
1
5
  import BaseModule from "../../base-module";
2
6
  import EventHandler from "../../../utils/js/dom/event";
3
7
  import { execute, isObject, mergeDeepObject, normalizeData } from "../../../utils/js/functions";
@@ -213,7 +217,7 @@ class VGLoadMore extends BaseModule {
213
217
 
214
218
  const buttonData = {
215
219
  limit: this._params.limit,
216
- offset: this._params.offset,
220
+ offset: this._params.offset || this._params.limit,
217
221
  output: this._params.output,
218
222
  autohide: this._params.autohide,
219
223
  animate: this._params.animate,
@@ -278,7 +282,7 @@ class VGLoadMore extends BaseModule {
278
282
 
279
283
  this._observer.disconnect();
280
284
 
281
- const container = Selectors.find(this._params.target) || this._element.parentNode;
285
+ const container = this._getTargetContainer();
282
286
  const items = Selectors.findAll(`.${this._params.elements}`, container);
283
287
  const visibleItems = items.filter(item => item.classList.contains(CLASS_NAME_SHOW));
284
288
  const lastVisible = visibleItems[visibleItems.length - 1];
@@ -331,7 +335,10 @@ class VGLoadMore extends BaseModule {
331
335
 
332
336
  const originalText = this._params.button.text;
333
337
 
334
- this._params.ajax.data = { limit: this._params.limit, offset: this._params.offset };
338
+ this._params.ajax.data = mergeDeepObject(this._params.ajax.data, {
339
+ limit: this._params.limit,
340
+ offset: this._params.offset
341
+ });
335
342
 
336
343
  this._route((status, data, responseTarget) => {
337
344
  if (status === 'error' || typeof data?.response !== 'string') return;
@@ -348,7 +355,7 @@ class VGLoadMore extends BaseModule {
348
355
  this._observeLastVisibleItem();
349
356
 
350
357
  const noMoreData = !data.response.trim();
351
- if (this._params.autohide && this._params.detach && noMoreData) {
358
+ if (this._params.autohide && noMoreData) {
352
359
  this._autohideTrigger();
353
360
  }
354
361
 
@@ -366,7 +373,7 @@ class VGLoadMore extends BaseModule {
366
373
  * @private
367
374
  */
368
375
  staticLoad(callback) {
369
- const container = Selectors.find(this._params.target) || this._element.parentNode;
376
+ const container = this._getTargetContainer();
370
377
  const items = Selectors.findAll(`.${this._params.elements}`, container);
371
378
  const start = this._params.offset;
372
379
  const end = start + this._params.limit;
@@ -394,14 +401,32 @@ class VGLoadMore extends BaseModule {
394
401
  this._observeLastVisibleItem();
395
402
 
396
403
  const remaining = items.slice(end);
397
- if (this._params.autohide && this._params.detach && remaining.length === 0) {
404
+ if (this._params.autohide && remaining.length === 0) {
398
405
  this._autohideTrigger();
399
406
  }
400
407
 
401
408
  this._restoreElementState(this._params.button.text);
409
+ EventHandler.trigger(this._element, EVENT_KEY_LOADED, {
410
+ stats: 'success',
411
+ data: {
412
+ items: newItems,
413
+ offset: this._params.offset,
414
+ remaining: remaining.length
415
+ }
416
+ });
402
417
  execute(callback, [this, this._element]);
403
418
  }
404
419
 
420
+ /**
421
+ * Возвращает контейнер элементов для container- и trigger-режимов.
422
+ * @returns {Element|null}
423
+ * @private
424
+ */
425
+ _getTargetContainer() {
426
+ return Selectors.find(this._params.target)
427
+ || (this._isToggleElement ? this._element.parentNode : this._element);
428
+ }
429
+
405
430
  /**
406
431
  * Восстанавливает состояние кнопки (включает, устанавливает текст)
407
432
  * @param {string} text - Текст кнопки
@@ -425,6 +450,9 @@ class VGLoadMore extends BaseModule {
425
450
  this._observer = null;
426
451
  } else if (this._params.detach && this._element.parentNode) {
427
452
  this._element.remove();
453
+ } else {
454
+ this._element.classList.add(CLASS_NAME_HIDE);
455
+ this._element.setAttribute('aria-hidden', 'true');
428
456
  }
429
457
  }
430
458
 
@@ -456,4 +484,4 @@ EventHandler.on(document, EVENT_KEY_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, functi
456
484
  VGLoadMore.getOrCreateInstance(this).toggle();
457
485
  });
458
486
 
459
- export default VGLoadMore;
487
+ export default VGLoadMore;
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Описание: доступная вложенная сортировка списков в стиле Nestable без зависимости от jQuery.
3
+ * Возможности: drag-and-drop мышью и touch, клавиатурное перемещение, связанные списки, collapse API, сериализация и AJAX-сохранение.
4
+ */
1
5
  import BaseModule from "../../base-module";
2
6
  import VGCollapse from "../../vgcollapse/js/vgcollapse";
3
7
  import { mergeDeepObject, normalizeData } from "../../../utils/js/functions";
@@ -11,6 +15,7 @@ const NAME = "nestable";
11
15
  const NAME_KEY = "vg.nestable";
12
16
 
13
17
  const SELECTOR_DATA_TOGGLE = '[data-vg-toggle="nestable"]';
18
+ const SELECTOR_DATA_ACTION = '[data-vg-nestable-action]';
14
19
 
15
20
  const CLASS_ITEM_DRAGGING = "is-dragging";
16
21
  const CLASS_ITEM_GHOST = "is-drag-ghost";
@@ -24,6 +29,7 @@ const CLASS_INNER = "vg-nestable-inner";
24
29
  const CLASS_HANDLE = "vg-nestable-handle";
25
30
  const CLASS_HANDLE_ICON = "vg-nestable-handle-icon";
26
31
  const CLASS_COLLAPSE_TOGGLE = "vg-nestable-collapse-toggle";
32
+ const CLASS_EMPTY = "vg-nestable-empty";
27
33
  const CLASS_DROP_TARGET = "is-drop-target";
28
34
  const CLASS_DROP_DENIED = "is-drop-denied";
29
35
  const CLASS_DROP_BEFORE = "is-drop-before";
@@ -44,6 +50,8 @@ const EVENT_KEY_DROP = `${NAME_KEY}.drop`;
44
50
  const EVENT_KEY_TRANSFER = `${NAME_KEY}.transfer`;
45
51
  const EVENT_KEY_PLACEHOLDER_MOVE = `${NAME_KEY}.placeholdermove`;
46
52
  const EVENT_KEY_DESTROY = `${NAME_KEY}.destroy`;
53
+ const EVENT_KEY_COLLAPSE = `${NAME_KEY}.collapse`;
54
+ const EVENT_KEY_EXPAND = `${NAME_KEY}.expand`;
47
55
 
48
56
  class VGNestable extends BaseModule {
49
57
  static _groups = new Map();
@@ -58,8 +66,10 @@ class VGNestable extends BaseModule {
58
66
  itemselector: ".vg-nestable-item", // Sortable item selector.
59
67
  handleselector: ".vg-nestable-handle", // Drag handle selector.
60
68
  idattribute: "data-id", // Item id attribute used by serialize().
69
+ serializedata: true, // Include the remaining data-* attributes, like the original Nestable.
61
70
  childlistclass: "vg-nestable-list", // Class for auto-created child list.
62
71
  handleicon: '', // иконка для хендлера
72
+ emptytext: "Drop items here", // Accessible empty-list hint.
63
73
  indent: 18, // Small horizontal gesture (px) that moves the item under the previous sibling.
64
74
  maxdepth: 6, // Maximum nesting depth.
65
75
  moveaxis: "default", // Drag direction: default | vertical | horizontal.
@@ -85,6 +95,8 @@ class VGNestable extends BaseModule {
85
95
  drop: null,
86
96
  transfer: null,
87
97
  change: null,
98
+ collapse: null,
99
+ expand: null,
88
100
  end: null,
89
101
  save: null,
90
102
  destroy: null
@@ -143,6 +155,7 @@ class VGNestable extends BaseModule {
143
155
  this._activeTouchId = null;
144
156
  this._keyboardDraggedItem = null;
145
157
  this._keyboardStartSnapshot = "";
158
+ this._keyboardOrigin = null;
146
159
  this._liveRegion = null;
147
160
 
148
161
  if (!this._rootList) {
@@ -192,6 +205,10 @@ class VGNestable extends BaseModule {
192
205
  return EVENT_KEY_SAVE;
193
206
  case "destroy":
194
207
  return EVENT_KEY_DESTROY;
208
+ case "collapse":
209
+ return EVENT_KEY_COLLAPSE;
210
+ case "expand":
211
+ return EVENT_KEY_EXPAND;
195
212
  default:
196
213
  return `${NAME_KEY}.${action}`;
197
214
  }
@@ -307,11 +324,100 @@ class VGNestable extends BaseModule {
307
324
  this._syncItemCollapse(item);
308
325
  });
309
326
 
327
+ this._syncEmptyState();
328
+
310
329
  this._emit("refresh", {
311
330
  items: this._getItems().length
312
331
  });
313
332
  }
314
333
 
334
+ /**
335
+ * Expands one branch and keeps its toggle state synchronized.
336
+ * @param {HTMLElement|string} item Item element or selector inside this tree.
337
+ * @returns {boolean}
338
+ */
339
+ expandItem(item) {
340
+ return this._setItemExpanded(item, true);
341
+ }
342
+
343
+ /**
344
+ * Collapses one branch and keeps its toggle state synchronized.
345
+ * @param {HTMLElement|string} item Item element or selector inside this tree.
346
+ * @returns {boolean}
347
+ */
348
+ collapseItem(item) {
349
+ return this._setItemExpanded(item, false);
350
+ }
351
+
352
+ /** Expand every branch, matching the original Nestable public method. */
353
+ expandAll() {
354
+ let changed = 0;
355
+ this._getItems().forEach((item) => {
356
+ if (this.expandItem(item)) changed += 1;
357
+ });
358
+ return changed;
359
+ }
360
+
361
+ /** Collapse every branch, matching the original Nestable public method. */
362
+ collapseAll() {
363
+ let changed = 0;
364
+ this._getItems().forEach((item) => {
365
+ if (this.collapseItem(item)) changed += 1;
366
+ });
367
+ return changed;
368
+ }
369
+
370
+ _setItemExpanded(item, expanded) {
371
+ const target = typeof item === "string" ? this._element.querySelector(item) : item;
372
+ if (!target || !target.matches?.(this._params.itemselector) || !this._rootList.contains(target)) {
373
+ return false;
374
+ }
375
+
376
+ const childList = this._getDirectChildBySelector(target, this._params.listselector);
377
+ if (!childList) return false;
378
+
379
+ const isExpanded = childList.classList.contains("show");
380
+ if (isExpanded === expanded) return false;
381
+
382
+ const collapse = VGCollapse.getOrCreateInstance(childList, { toggle: false });
383
+ if (expanded) {
384
+ collapse.show();
385
+ } else {
386
+ collapse.hide();
387
+ }
388
+
389
+ this._emit(expanded ? "expand" : "collapse", {
390
+ item: target,
391
+ list: childList,
392
+ expanded
393
+ });
394
+ return true;
395
+ }
396
+
397
+ _syncEmptyState() {
398
+ if (!this._rootList) return;
399
+
400
+ const hasItems = Array.from(this._rootList.children)
401
+ .some((child) => child.matches(this._params.itemselector));
402
+ let empty = this._getDirectChildBySelector(this._rootList, `.${CLASS_EMPTY}`);
403
+
404
+ if (hasItems) {
405
+ empty?.remove();
406
+ this._rootList.classList.remove("is-empty");
407
+ return;
408
+ }
409
+
410
+ if (!empty) {
411
+ empty = document.createElement(this._rootList.tagName?.toLowerCase() === "ol" ? "li" : "div");
412
+ empty.className = CLASS_EMPTY;
413
+ empty.setAttribute("aria-live", "polite");
414
+ this._rootList.append(empty);
415
+ }
416
+
417
+ empty.textContent = String(this._params.emptytext || "Drop items here");
418
+ this._rootList.classList.add("is-empty");
419
+ }
420
+
315
421
  _syncItemCollapse(item) {
316
422
  if (!item || !this._params.collapse?.enabled) {
317
423
  return;
@@ -826,6 +932,10 @@ class VGNestable extends BaseModule {
826
932
 
827
933
  this._keyboardDraggedItem = item;
828
934
  this._keyboardStartSnapshot = JSON.stringify(this.serialize());
935
+ this._keyboardOrigin = {
936
+ parent: item.parentElement,
937
+ next: item.nextSibling
938
+ };
829
939
  item.classList.add(CLASS_ITEM_DRAGGING);
830
940
  item.setAttribute("aria-grabbed", "true");
831
941
 
@@ -844,6 +954,13 @@ class VGNestable extends BaseModule {
844
954
 
845
955
  const draggedItem = this._keyboardDraggedItem;
846
956
  const previousPayload = this._keyboardStartSnapshot ? JSON.parse(this._keyboardStartSnapshot) : [];
957
+ if (options.cancelled && this._keyboardOrigin?.parent) {
958
+ const { parent, next } = this._keyboardOrigin;
959
+ parent.insertBefore(draggedItem, next?.parentElement === parent ? next : null);
960
+ this._cleanupEmptyLists();
961
+ this.refresh();
962
+ draggedItem.focus?.();
963
+ }
847
964
  const payload = this.serialize();
848
965
  const changed = this._keyboardStartSnapshot !== JSON.stringify(payload);
849
966
 
@@ -890,6 +1007,7 @@ class VGNestable extends BaseModule {
890
1007
 
891
1008
  this._keyboardDraggedItem = null;
892
1009
  this._keyboardStartSnapshot = "";
1010
+ this._keyboardOrigin = null;
893
1011
  }
894
1012
 
895
1013
  _moveKeyboardUp(item) {
@@ -1878,6 +1996,15 @@ class VGNestable extends BaseModule {
1878
1996
  const childList = item.querySelector(`:scope > ${this._params.listselector}`);
1879
1997
 
1880
1998
  const data = { id: id ?? null };
1999
+ if (normalizeData(this._params.serializedata) !== false) {
2000
+ Array.from(item.attributes).forEach((attribute) => {
2001
+ if (!attribute.name.startsWith("data-") || attribute.name === this._params.idattribute) return;
2002
+ const key = attribute.name
2003
+ .slice(5)
2004
+ .replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
2005
+ data[key] = normalizeData(attribute.value === "" ? "true" : attribute.value);
2006
+ });
2007
+ }
1881
2008
 
1882
2009
  if (childList) {
1883
2010
  const children = this._serializeList(childList);
@@ -1895,6 +2022,19 @@ EventHandler.on(document, `DOMContentLoaded.${NAME_KEY}.data.api`, () => {
1895
2022
  Selectors.findAll(SELECTOR_DATA_TOGGLE).forEach((el) => VGNestable.getOrCreateInstance(el));
1896
2023
  });
1897
2024
 
2025
+ EventHandler.on(document, `click.${NAME_KEY}.data.api`, SELECTOR_DATA_ACTION, function (event) {
2026
+ event.preventDefault();
2027
+ const selector = this.getAttribute("data-vg-target");
2028
+ const element = selector ? Selectors.find(selector) : this.closest(SELECTOR_DATA_TOGGLE);
2029
+ if (!element) return;
2030
+
2031
+ const instance = VGNestable.getOrCreateInstance(element);
2032
+ const action = String(this.getAttribute("data-vg-nestable-action") || "").trim();
2033
+ if (["expandAll", "collapseAll"].includes(action)) {
2034
+ instance[action]();
2035
+ }
2036
+ });
2037
+
1898
2038
  export default VGNestable;
1899
2039
 
1900
2040
 
@@ -1,6 +1,13 @@
1
1
  @use "../../../utils/scss" as vg;
2
2
 
3
3
  $nestable-map: (
4
+ max-width: 40rem,
5
+ empty-min-height: 5rem,
6
+ empty-padding: 1rem,
7
+ empty-border: vg.$border-width dashed var(--vg-border-disabled-color, rgba(0, 0, 0, .18)),
8
+ empty-bg: var(--vg-surface-subtle-bg, rgba(0, 0, 0, .025)),
9
+ empty-color: var(--vg-text-muted-color, rgba(0, 0, 0, .6)),
10
+ empty-border-radius: .5rem,
4
11
  list-transition: "background-color .15s ease, outline-color .15s ease",
5
12
  list-drop-target-bg: var(--vg-primary-subtle-bg, rgba(13, 110, 253, .06)),
6
13
  list-drop-target-outline: vg.$border-width dashed var(--vg-primary-outline-color, rgba(13, 110, 253, .45)),
@@ -9,7 +16,7 @@ $nestable-map: (
9
16
  list-drop-denied-outline: vg.$border-width dashed var(--vg-danger-outline-color, rgba(220, 53, 69, .5)),
10
17
  list-drop-denied-outline-offset: 2px,
11
18
  list-nested-margin-top: .35rem,
12
- list-nested-padding-left: 1.25rem,
19
+ list-nested-padding-left: 1.875rem,
13
20
  list-nested-border-left: vg.$border-width dashed var(--vg-border-disabled-color, rgba(0, 0, 0, .12)),
14
21
 
15
22
  item-margin: .35rem 0,
@@ -25,15 +32,15 @@ $nestable-map: (
25
32
  item-disabled-handle-color: var(--vg-text-placeholder-color, rgba(0, 0, 0, .28)),
26
33
 
27
34
  inner-gap: .5rem,
28
- inner-min-height: 40px,
29
- inner-padding: .45rem .65rem,
35
+ inner-min-height: 42px,
36
+ inner-padding: .4rem .65rem .4rem .35rem,
30
37
  inner-border: vg.$border-width vg.$border-style var(--vg-border-disabled-color, rgba(0, 0, 0, .12)),
31
38
  inner-bg: var(--vg-surface-bg, #{vg.$surface-bg}),
32
39
  inner-border-radius: .45rem,
33
40
 
34
- handle-size: 1.85rem,
35
- handle-border: vg.$border-width vg.$border-style var(--vg-border-disabled-color, rgba(0, 0, 0, .12)),
36
- handle-bg: var(--vg-surface-subtle-bg, rgba(0, 0, 0, .03)),
41
+ handle-size: 2rem,
42
+ handle-border: 0,
43
+ handle-bg: transparent,
37
44
  handle-border-radius: .35rem,
38
45
  handle-cursor: grab,
39
46
  handle-active-cursor: grabbing,
@@ -61,9 +68,9 @@ $nestable-map: (
61
68
  placeholder-bg: var(--vg-primary-subtle-bg, rgba(13, 110, 253, .08)),
62
69
  placeholder-border-radius: .45rem,
63
70
  placeholder-margin: .35rem 0,
64
- placeholder-min-height: 40px,
71
+ placeholder-min-height: 42px,
65
72
 
66
73
  drag-element-opacity: 1,
67
- drag-element-transform: scale(1.01),
68
- drag-element-shadow: 0 12px 30px var(--vg-shadow-color, rgba(0, 0, 0, .2))
74
+ drag-element-transform: translate3d(0, 0, 0) scale(1.01),
75
+ drag-element-shadow: 0 10px 24px var(--vg-shadow-color, rgba(0, 0, 0, .18))
69
76
  ) !default;
@@ -12,6 +12,23 @@
12
12
 
13
13
  .vg-nestable {
14
14
  @include vg.mix-vars('nestable', vars.$nestable-map);
15
+ position: relative;
16
+ display: block;
17
+ width: 100%;
18
+ max-width: var(--vg-nestable-max-width);
19
+
20
+ &-empty {
21
+ display: flex;
22
+ align-items: center;
23
+ justify-content: center;
24
+ min-height: var(--vg-nestable-empty-min-height);
25
+ padding: var(--vg-nestable-empty-padding);
26
+ border: var(--vg-nestable-empty-border);
27
+ background: var(--vg-nestable-empty-bg);
28
+ color: var(--vg-nestable-empty-color);
29
+ border-radius: var(--vg-nestable-empty-border-radius);
30
+ text-align: center;
31
+ }
15
32
 
16
33
  &-list {
17
34
  list-style: none;
@@ -19,6 +36,10 @@
19
36
  padding: 0;
20
37
  transition: var(--vg-nestable-list-transition);
21
38
 
39
+ &.is-empty {
40
+ min-height: var(--vg-nestable-empty-min-height);
41
+ }
42
+
22
43
  &.is-drop-target {
23
44
  background: var(--vg-nestable-list-drop-target-bg);
24
45
  outline: var(--vg-nestable-list-drop-target-outline);
@@ -94,6 +115,7 @@
94
115
  border: var(--vg-nestable-inner-border);
95
116
  background: var(--vg-nestable-inner-bg);
96
117
  border-radius: var(--vg-nestable-inner-border-radius);
118
+ box-shadow: 0 1px 1px rgba(0, 0, 0, .025);
97
119
  }
98
120
 
99
121
  &-handle {
@@ -110,6 +132,14 @@
110
132
  cursor: var(--vg-nestable-handle-cursor);
111
133
  user-select: none;
112
134
  touch-action: none;
135
+ color: var(--vg-nestable-handle-icon-color);
136
+
137
+ &:hover,
138
+ &:focus-visible {
139
+ background: var(--vg-nestable-toggle-hover-bg);
140
+ color: var(--vg-text-color, currentColor);
141
+ outline: none;
142
+ }
113
143
 
114
144
  &:active {
115
145
  cursor: var(--vg-nestable-handle-active-cursor);
@@ -178,6 +208,7 @@
178
208
  border-radius: var(--vg-nestable-placeholder-border-radius);
179
209
  margin: var(--vg-nestable-placeholder-margin);
180
210
  min-height: var(--vg-nestable-placeholder-min-height);
211
+ box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--vg-nestable-item-drop-line) 12%, transparent);
181
212
  }
182
213
 
183
214
  &-placeholder-hidden {
@@ -191,10 +222,12 @@
191
222
  &-item.is-drag-ghost#{&}-drag-element {
192
223
  opacity: var(--vg-nestable-drag-element-opacity);
193
224
  transform: var(--vg-nestable-drag-element-transform);
194
- box-shadow: var(--vg-nestable-drag-element-shadow);
225
+ transform-origin: top left;
226
+ filter: drop-shadow(var(--vg-nestable-drag-element-shadow));
195
227
 
196
- > .vg-nestable-inner {
228
+ .vg-nestable-inner {
197
229
  background: var(--vg-nestable-inner-bg);
230
+ box-shadow: 0 2px 5px rgba(0, 0, 0, .08);
198
231
  }
199
232
  }
200
233
  }
@@ -0,0 +1,3 @@
1
+ import VGTable from "./js/vgtable";
2
+
3
+ export default VGTable;