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.
- package/README.md +24 -2
- package/app/modules/base-module.js +14 -2
- package/app/modules/module-fn.js +5 -4
- package/app/modules/vgalert/js/vgalert.js +21 -1
- package/app/modules/vgalert/scss/vgalert.scss +48 -0
- package/app/modules/vgdropdown/js/vgdropdown.js +58 -27
- package/app/modules/vgfilepreview/js/renderers/video-modal.js +5 -1
- package/app/modules/vgfilepreview/js/renderers/video.js +6 -1
- package/app/modules/vgfilepreview/js/vgfilepreview.js +5 -1
- package/app/modules/vgfiles/js/base.js +22 -10
- package/app/modules/vgfiles/js/droppable.js +37 -31
- package/app/modules/vgfiles/js/vgfiles.js +5 -2
- package/app/modules/vgfiles/scss/_variables.scss +3 -1
- package/app/modules/vgfiles/scss/vgfiles.scss +55 -10
- package/app/modules/vgformsender/js/vgformsender.js +5 -2
- package/app/modules/vgloadmore/js/vgloadmore.js +35 -7
- package/app/modules/vgnestable/js/vgnestable.js +140 -0
- package/app/modules/vgnestable/scss/_variables.scss +16 -9
- package/app/modules/vgnestable/scss/vgnestable.scss +35 -2
- package/app/modules/vgtable/index.js +3 -0
- package/app/modules/vgtable/js/_columns.js +567 -0
- package/app/modules/vgtable/js/_expandable.js +242 -0
- package/app/modules/vgtable/js/_filters.js +264 -0
- package/app/modules/vgtable/js/_fixed-columns.js +249 -0
- package/app/modules/vgtable/js/_i18n.js +75 -0
- package/app/modules/vgtable/js/_options.js +428 -0
- package/app/modules/vgtable/js/_pagination.js +491 -0
- package/app/modules/vgtable/js/_panning.js +124 -0
- package/app/modules/vgtable/js/_params-groups.js +21 -0
- package/app/modules/vgtable/js/_remote.js +395 -0
- package/app/modules/vgtable/js/_row-reorder.js +268 -0
- package/app/modules/vgtable/js/_search.js +134 -0
- package/app/modules/vgtable/js/_selection.js +113 -0
- package/app/modules/vgtable/js/_skeleton.js +123 -0
- package/app/modules/vgtable/js/_sorting.js +340 -0
- package/app/modules/vgtable/js/_states.js +143 -0
- package/app/modules/vgtable/js/_sticky-header.js +193 -0
- package/app/modules/vgtable/js/_url-state.js +178 -0
- package/app/modules/vgtable/js/vgtable.js +1444 -0
- package/app/modules/vgtable/readme.md +922 -0
- package/app/modules/vgtable/scss/_columns.scss +52 -0
- package/app/modules/vgtable/scss/_expandable.scss +54 -0
- package/app/modules/vgtable/scss/_fixed-columns.scss +117 -0
- package/app/modules/vgtable/scss/_pagination.scss +191 -0
- package/app/modules/vgtable/scss/_row-reorder.scss +45 -0
- package/app/modules/vgtable/scss/_skeleton.scss +65 -0
- package/app/modules/vgtable/scss/_sorting.scss +89 -0
- package/app/modules/vgtable/scss/_states.scss +70 -0
- package/app/modules/vgtable/scss/_sticky-header.scss +121 -0
- package/app/modules/vgtable/scss/_variables.scss +296 -0
- package/app/modules/vgtable/scss/vgtable.scss +206 -0
- package/app/utils/js/components/placement.js +64 -10
- package/app/vgapp.js +3 -3
- package/build/vgapp.css +1 -1
- package/build/vgapp.css.map +1 -1
- package/build/vgapp.js +1 -1
- package/build/vgapp.js.map +1 -1
- package/index.js +1 -1
- package/index.scss +3 -3
- package/package.json +4 -4
- package/app/modules/vgdynamictable/index.js +0 -4
- package/app/modules/vgdynamictable/js/editable.js +0 -438
- package/app/modules/vgdynamictable/js/expandable.js +0 -248
- package/app/modules/vgdynamictable/js/filters.js +0 -450
- package/app/modules/vgdynamictable/js/fixed.js +0 -566
- package/app/modules/vgdynamictable/js/options.js +0 -650
- package/app/modules/vgdynamictable/js/pagination.js +0 -623
- package/app/modules/vgdynamictable/js/search.js +0 -82
- package/app/modules/vgdynamictable/js/skeleton.js +0 -144
- package/app/modules/vgdynamictable/js/sortable.js +0 -442
- package/app/modules/vgdynamictable/js/summary-footer.js +0 -284
- package/app/modules/vgdynamictable/js/table-remote.js +0 -821
- package/app/modules/vgdynamictable/js/table-state.js +0 -243
- package/app/modules/vgdynamictable/js/table-url-state.js +0 -444
- package/app/modules/vgdynamictable/js/utils/common.js +0 -48
- package/app/modules/vgdynamictable/js/vgdynamictable.js +0 -3870
- package/app/modules/vgdynamictable/js/viewport.js +0 -322
- package/app/modules/vgdynamictable/readme.md +0 -251
- package/app/modules/vgdynamictable/scss/_actions.scss +0 -196
- package/app/modules/vgdynamictable/scss/_pagination.scss +0 -186
- package/app/modules/vgdynamictable/scss/_skeleton.scss +0 -63
- package/app/modules/vgdynamictable/scss/_sortable.scss +0 -66
- package/app/modules/vgdynamictable/scss/_table.scss +0 -137
- package/app/modules/vgdynamictable/scss/_variables.scss +0 -138
- package/app/modules/vgdynamictable/scss/vgdynamictable.scss +0 -264
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Описание: основной класс выбора, отображения и AJAX-загрузки файлов VGFiles.
|
|
3
|
+
* Возможности: управляет списками и dropzone, прогрессом, удалением, повторной загрузкой и сортировкой.
|
|
4
|
+
*/
|
|
1
5
|
import VGFilesBase from "./base";
|
|
2
6
|
import FileUploader from "./loader";
|
|
3
7
|
import VGFilesDroppable from "./droppable";
|
|
@@ -810,8 +814,7 @@ class VGFiles extends VGFilesBase {
|
|
|
810
814
|
}
|
|
811
815
|
|
|
812
816
|
_setButtonElement(file, isAjax = false, status = '') {
|
|
813
|
-
let icon = getSVG('
|
|
814
|
-
if (!this._params.info) icon = getSVG('cross');
|
|
817
|
+
let icon = getSVG('remove'), action = 'data-vg-dismiss';
|
|
815
818
|
if (isAjax) {
|
|
816
819
|
icon = getSVG('spinner');
|
|
817
820
|
if (status === 'completed') icon = getSVG('check');
|
|
@@ -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:
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 &&
|
|
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 =
|
|
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 &&
|
|
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.
|
|
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:
|
|
29
|
-
inner-padding: .
|
|
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:
|
|
35
|
-
handle-border:
|
|
36
|
-
handle-bg:
|
|
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:
|
|
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
|
|
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
|
-
|
|
225
|
+
transform-origin: top left;
|
|
226
|
+
filter: drop-shadow(var(--vg-nestable-drag-element-shadow));
|
|
195
227
|
|
|
196
|
-
|
|
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
|
}
|