vgapp 1.4.0 → 1.4.2
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 +32 -4
- package/app/modules/vgalert/js/vgalert.js +266 -78
- package/app/modules/vgalert/scss/_variables.scss +14 -1
- package/app/modules/vgalert/scss/vgalert.scss +103 -29
- package/app/modules/vgdynamictable/js/editable.js +430 -430
- package/app/modules/vgdynamictable/js/options.js +619 -619
- package/app/modules/vgdynamictable/js/utils/common.js +34 -34
- package/app/modules/vgmodal/js/vgmodal.interaction.js +216 -0
- package/app/modules/vgmodal/js/vgmodal.js +355 -361
- package/app/modules/vgmodal/js/vgmodal.minimized.js +227 -0
- package/app/modules/vgmodal/scss/vgmodal.scss +68 -4
- package/app/modules/vgnav/js/vgnav.js +45 -22
- package/app/modules/vgnav/scss/vgnav.scss +22 -14
- package/app/modules/vgnestable/README.md +39 -10
- package/app/modules/vgnestable/js/vgnestable.js +358 -141
- package/app/modules/vgnestable/scss/_variables.scss +15 -8
- package/app/modules/vgnestable/scss/vgnestable.scss +51 -13
- package/app/modules/vgselect/js/vgselect.js +129 -82
- package/app/modules/vgselect/readme.md +11 -9
- package/app/modules/vgsidebar/js/vgsidebar.js +79 -44
- package/app/utils/js/components/backdrop.js +116 -89
- package/build/vgapp.css +4492 -2
- package/build/vgapp.css.map +1 -1
- package/build/vgapp.js +32145 -3
- package/build/vgapp.js.map +1 -1
- package/package.json +55 -1
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import Backdrop from "../../../utils/js/components/backdrop";
|
|
2
|
+
import Selectors from "../../../utils/js/dom/selectors";
|
|
3
|
+
import EventHandler from "../../../utils/js/dom/event";
|
|
4
|
+
import {reflow} from "../../../utils/js/functions";
|
|
5
|
+
|
|
6
|
+
const NAME_KEY = 'vg.modal';
|
|
7
|
+
|
|
8
|
+
const OPEN_SELECTOR = '.vg-modal.show:not([data-vg-persistent="true"])';
|
|
9
|
+
|
|
10
|
+
const CLASS_NAME_OPEN = 'vg-modal-open';
|
|
11
|
+
const CLASS_NAME_SHOW = 'show';
|
|
12
|
+
const CLASS_NAME_MINIMIZED = 'vg-modal-minimized-state';
|
|
13
|
+
const CLASS_NAME_MINIMIZE_BUTTON = 'vg-btn-minimize';
|
|
14
|
+
const CLASS_NAME_MINIMIZED_CONTAINER = 'vg-modal-minimized-container';
|
|
15
|
+
const CLASS_NAME_MINIMIZED_ITEM = 'vg-modal-minimized';
|
|
16
|
+
|
|
17
|
+
const EVENT_KEY_MINIMIZE = `${NAME_KEY}.minimize`;
|
|
18
|
+
const EVENT_KEY_MINIMIZED = `${NAME_KEY}.minimized`;
|
|
19
|
+
const EVENT_KEY_RESTORE = `${NAME_KEY}.restore`;
|
|
20
|
+
const EVENT_KEY_RESTORED = `${NAME_KEY}.restored`;
|
|
21
|
+
|
|
22
|
+
class VGModalMinimized {
|
|
23
|
+
constructor(modal) {
|
|
24
|
+
this._modal = modal;
|
|
25
|
+
this._element = modal._element;
|
|
26
|
+
this._dialog = modal._dialog;
|
|
27
|
+
this._content = modal._content;
|
|
28
|
+
this._minimizedElement = null;
|
|
29
|
+
this._isMinimized = false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
setup() {
|
|
33
|
+
const minimizeConfig = this._getConfig();
|
|
34
|
+
if (!minimizeConfig.enable || !this._content) return;
|
|
35
|
+
if (Selectors.find(`.${CLASS_NAME_MINIMIZE_BUTTON}`, this._content)) return;
|
|
36
|
+
|
|
37
|
+
const button = document.createElement('button');
|
|
38
|
+
button.type = 'button';
|
|
39
|
+
button.className = CLASS_NAME_MINIMIZE_BUTTON;
|
|
40
|
+
button.setAttribute('aria-label', minimizeConfig.label || 'Minimize');
|
|
41
|
+
button.innerHTML = '<span aria-hidden="true"></span>';
|
|
42
|
+
|
|
43
|
+
const closeButton = Selectors.find('.vg-btn-close', this._content);
|
|
44
|
+
if (closeButton && closeButton.parentNode) {
|
|
45
|
+
closeButton.parentNode.insertBefore(button, closeButton);
|
|
46
|
+
} else {
|
|
47
|
+
this._content.prepend(button);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
EventHandler.on(button, `click.${NAME_KEY}.minimize`, (event) => {
|
|
51
|
+
event.preventDefault();
|
|
52
|
+
this.minimize(button);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
isMinimized() {
|
|
57
|
+
return this._isMinimized;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
minimize(relatedTarget) {
|
|
61
|
+
if (!this._getConfig().enable || !this._modal._isShown || this._isMinimized || this._modal._isTransitioning) return;
|
|
62
|
+
|
|
63
|
+
const minimizeEvent = EventHandler.trigger(this._element, EVENT_KEY_MINIMIZE, { relatedTarget });
|
|
64
|
+
if (minimizeEvent.defaultPrevented) return;
|
|
65
|
+
|
|
66
|
+
this._isMinimized = true;
|
|
67
|
+
this._modal._disableInteractionHandlers();
|
|
68
|
+
this._element.classList.remove(CLASS_NAME_SHOW);
|
|
69
|
+
this._element.classList.add(CLASS_NAME_MINIMIZED);
|
|
70
|
+
this._element.style.display = 'none';
|
|
71
|
+
this._element.removeAttribute('aria-modal');
|
|
72
|
+
this._element.removeAttribute('role');
|
|
73
|
+
|
|
74
|
+
const remainingOpenModals = Selectors.findAll(OPEN_SELECTOR).filter(modal => modal !== this._element);
|
|
75
|
+
if (!remainingOpenModals.length) {
|
|
76
|
+
document.body.classList.remove(CLASS_NAME_OPEN);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const finish = () => {
|
|
80
|
+
if (!Backdrop.isActive()) {
|
|
81
|
+
this._modal._scrollBar.reset();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
this._ensureElement(relatedTarget);
|
|
85
|
+
EventHandler.trigger(this._element, EVENT_KEY_MINIMIZED, { relatedTarget });
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
if (this._modal._backdropElement) {
|
|
89
|
+
this._modal._hideBackdrop(finish);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
finish();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
restore(relatedTarget) {
|
|
97
|
+
if (!this._isMinimized || this._modal._isTransitioning) return;
|
|
98
|
+
|
|
99
|
+
const restoreEvent = EventHandler.trigger(this._element, EVENT_KEY_RESTORE, { relatedTarget });
|
|
100
|
+
if (restoreEvent.defaultPrevented) return;
|
|
101
|
+
|
|
102
|
+
const restoreElement = () => {
|
|
103
|
+
this.removeElement();
|
|
104
|
+
this._isMinimized = false;
|
|
105
|
+
this._element.classList.remove(CLASS_NAME_MINIMIZED);
|
|
106
|
+
this._element.style.display = 'block';
|
|
107
|
+
this._element.setAttribute('aria-modal', true);
|
|
108
|
+
this._element.setAttribute('role', 'dialog');
|
|
109
|
+
document.body.classList.add(CLASS_NAME_OPEN);
|
|
110
|
+
|
|
111
|
+
reflow(this._element);
|
|
112
|
+
this._element.classList.add(CLASS_NAME_SHOW);
|
|
113
|
+
this._modal._toggleInteractionHandlers();
|
|
114
|
+
this._modal._syncInteractiveBounds();
|
|
115
|
+
EventHandler.trigger(this._element, EVENT_KEY_RESTORED, { relatedTarget });
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
if (this._modal._params.backdrop) {
|
|
119
|
+
this._modal._scrollBar.hide();
|
|
120
|
+
this._modal._showBackdrop(restoreElement);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
restoreElement();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
reset() {
|
|
128
|
+
this.removeElement();
|
|
129
|
+
this._isMinimized = false;
|
|
130
|
+
this._element.classList.remove(CLASS_NAME_MINIMIZED);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
dispose() {
|
|
134
|
+
this.removeElement();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
_getConfig() {
|
|
138
|
+
const value = this._modal._params.minimize;
|
|
139
|
+
const defaults = {
|
|
140
|
+
enable: false,
|
|
141
|
+
title: '',
|
|
142
|
+
text: '',
|
|
143
|
+
label: 'Minimize',
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
if (typeof value === 'boolean') {
|
|
147
|
+
return {...defaults, enable: value};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (value && typeof value === 'object') {
|
|
151
|
+
const hasEnable = Object.prototype.hasOwnProperty.call(value, 'enable');
|
|
152
|
+
return {
|
|
153
|
+
...defaults,
|
|
154
|
+
...value,
|
|
155
|
+
enable: hasEnable ? Boolean(value.enable) : true,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return defaults;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
_ensureElement(relatedTarget) {
|
|
163
|
+
if (this._minimizedElement && this._minimizedElement.isConnected) return this._minimizedElement;
|
|
164
|
+
|
|
165
|
+
const minimizeConfig = this._getConfig();
|
|
166
|
+
const container = VGModalMinimized.getContainer();
|
|
167
|
+
const button = document.createElement('button');
|
|
168
|
+
button.type = 'button';
|
|
169
|
+
button.className = CLASS_NAME_MINIMIZED_ITEM;
|
|
170
|
+
button.setAttribute('data-vg-modal-target', `#${this._element.id}`);
|
|
171
|
+
|
|
172
|
+
const title = document.createElement('span');
|
|
173
|
+
title.className = `${CLASS_NAME_MINIMIZED_ITEM}__title`;
|
|
174
|
+
title.textContent = this._getTitle(minimizeConfig);
|
|
175
|
+
button.append(title);
|
|
176
|
+
|
|
177
|
+
const text = String(minimizeConfig.text || '').trim();
|
|
178
|
+
if (text) {
|
|
179
|
+
const description = document.createElement('span');
|
|
180
|
+
description.className = `${CLASS_NAME_MINIMIZED_ITEM}__text`;
|
|
181
|
+
description.textContent = text;
|
|
182
|
+
button.append(description);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
EventHandler.on(button, `click.${NAME_KEY}.restore`, (event) => {
|
|
186
|
+
event.preventDefault();
|
|
187
|
+
this.restore(button || relatedTarget);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
container.append(button);
|
|
191
|
+
this._minimizedElement = button;
|
|
192
|
+
return button;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
_getTitle(minimizeConfig) {
|
|
196
|
+
const configuredTitle = String(minimizeConfig.title || '').trim();
|
|
197
|
+
if (configuredTitle) return configuredTitle;
|
|
198
|
+
|
|
199
|
+
const titleElement = Selectors.find('.vg-modal-title, .vg-modal-chat__name, [data-vg-modal-title]', this._element);
|
|
200
|
+
const title = String(titleElement ? titleElement.textContent : '').trim();
|
|
201
|
+
return title || this._element.id || 'Modal';
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
removeElement() {
|
|
205
|
+
if (this._minimizedElement) {
|
|
206
|
+
this._minimizedElement.remove();
|
|
207
|
+
this._minimizedElement = null;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const container = Selectors.find(`.${CLASS_NAME_MINIMIZED_CONTAINER}`);
|
|
211
|
+
if (container && !container.children.length) {
|
|
212
|
+
container.remove();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
static getContainer() {
|
|
217
|
+
let container = Selectors.find(`.${CLASS_NAME_MINIMIZED_CONTAINER}`);
|
|
218
|
+
if (container) return container;
|
|
219
|
+
|
|
220
|
+
container = document.createElement('div');
|
|
221
|
+
container.className = CLASS_NAME_MINIMIZED_CONTAINER;
|
|
222
|
+
document.body.append(container);
|
|
223
|
+
return container;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export default VGModalMinimized;
|
|
@@ -91,10 +91,74 @@
|
|
|
91
91
|
border: 0;
|
|
92
92
|
border-radius: 0;
|
|
93
93
|
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.vg-btn-minimize {
|
|
98
|
+
position: absolute;
|
|
99
|
+
width: 30px;
|
|
100
|
+
height: 30px;
|
|
101
|
+
right: 55px;
|
|
102
|
+
top: 15px;
|
|
103
|
+
border: none;
|
|
104
|
+
background: transparent;
|
|
105
|
+
border-radius: 50%;
|
|
106
|
+
padding: 0;
|
|
107
|
+
display: flex;
|
|
108
|
+
align-items: center;
|
|
109
|
+
justify-content: center;
|
|
110
|
+
cursor: pointer;
|
|
111
|
+
z-index: 2;
|
|
112
|
+
|
|
113
|
+
span {
|
|
114
|
+
width: 14px;
|
|
115
|
+
height: 2px;
|
|
116
|
+
background: currentColor;
|
|
117
|
+
display: block;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.vg-modal-minimized-container {
|
|
122
|
+
position: fixed;
|
|
123
|
+
right: 20px;
|
|
124
|
+
bottom: 20px;
|
|
125
|
+
z-index: calc(var(--vg-modal-z-index, #{$modal-index}) + 1);
|
|
126
|
+
display: flex;
|
|
127
|
+
flex-direction: column;
|
|
128
|
+
align-items: flex-end;
|
|
129
|
+
gap: 10px;
|
|
130
|
+
pointer-events: none;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.vg-modal-minimized {
|
|
134
|
+
min-width: 220px;
|
|
135
|
+
max-width: 320px;
|
|
136
|
+
border: var(--vg-modal-border-width) var(--vg-modal-border-style) var(--vg-modal-border-color);
|
|
137
|
+
border-radius: var(--vg-modal-border-radius);
|
|
138
|
+
background: var(--vg-modal-background-color);
|
|
139
|
+
color: var(--vg-modal-color);
|
|
140
|
+
box-shadow: var(--vg-modal-box-shadow);
|
|
141
|
+
padding: .75rem 1rem;
|
|
142
|
+
cursor: pointer;
|
|
143
|
+
pointer-events: auto;
|
|
144
|
+
text-align: left;
|
|
145
|
+
display: flex;
|
|
146
|
+
flex-direction: column;
|
|
147
|
+
gap: .25rem;
|
|
148
|
+
|
|
149
|
+
&__title {
|
|
150
|
+
font-weight: 600;
|
|
151
|
+
line-height: 1.2;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
&__text {
|
|
155
|
+
font-size: .875rem;
|
|
156
|
+
opacity: .75;
|
|
157
|
+
line-height: 1.2;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
@media screen and (min-width: 576px){
|
|
98
162
|
.vg-modal {
|
|
99
163
|
--vg-modal-margin: #{$modal-dialog-margin-y-sm-up};
|
|
100
164
|
--vg-modal-box-shadow: #{$modal-content-box-shadow-sm-up};
|
|
@@ -49,38 +49,60 @@ class VGNav extends BaseModule {
|
|
|
49
49
|
super(element);
|
|
50
50
|
|
|
51
51
|
this._params = this._getParams(element, mergeDeepObject({
|
|
52
|
+
// Брейкпоинт, с которого навигация отображается в расширенном виде.
|
|
52
53
|
breakpoint: 'lg',
|
|
54
|
+
// Направление меню: влияет на классы и поведение позиционирования.
|
|
53
55
|
placement: 'horizontal',
|
|
56
|
+
// Включает открытие выпадающих пунктов при наведении на десктопе.
|
|
54
57
|
hover: true,
|
|
58
|
+
// Включает ограничение высоты и overflow для списка внутри выпадающего меню.
|
|
59
|
+
dropListScroll: true,
|
|
60
|
+
// Настройки плавного переключения между соседними пунктами первого уровня.
|
|
55
61
|
hoversmoothfirstlevel: {
|
|
62
|
+
// Включает режим плавного перехода без мгновенного закрытия соседнего дропа.
|
|
56
63
|
enable: false,
|
|
64
|
+
// Ограничивает плавное переключение только горизонтальным движением курсора.
|
|
57
65
|
horizontalOnly: true
|
|
58
66
|
},
|
|
67
|
+
// Настройки анимации открытия и закрытия выпадающих пунктов.
|
|
59
68
|
animation: {
|
|
69
|
+
// Включает анимацию переходов.
|
|
60
70
|
enable: true,
|
|
71
|
+
// Длительность ожидания завершения анимации перед финальным состоянием.
|
|
61
72
|
timeout: 700
|
|
62
73
|
},
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
// HTML-разметка иконки-указателя для пунктов с выпадающим меню.
|
|
75
|
+
toggle: '<span class="default"></span>',
|
|
76
|
+
// Настройки кнопки-гамбургера для мобильной/адаптивной навигации.
|
|
77
|
+
hamburger: {
|
|
78
|
+
// Разрешает создавать и использовать кнопку-гамбургер.
|
|
79
|
+
enable: true,
|
|
80
|
+
// Принудительно показывает гамбургер всегда, независимо от брейкпоинта.
|
|
81
|
+
always: false,
|
|
82
|
+
// Текстовый заголовок рядом с иконкой гамбургера.
|
|
83
|
+
title: '',
|
|
84
|
+
// Пользовательская HTML-разметка тела гамбургера вместо стандартных линий.
|
|
85
|
+
body: null,
|
|
86
|
+
// CSS-селектор сайдбара, который открывает кнопка-гамбургер.
|
|
87
|
+
target: '#sidebar-nav'
|
|
88
|
+
},
|
|
89
|
+
// Пользовательские обработчики жизненного цикла навигации.
|
|
90
|
+
callbacks: {
|
|
91
|
+
// Вызывается после построения навигации.
|
|
92
|
+
afterInit: noop,
|
|
93
|
+
// Вызывается после клика по пункту навигации.
|
|
94
|
+
afterClick: noop,
|
|
95
|
+
}
|
|
96
|
+
}, params));
|
|
76
97
|
|
|
77
98
|
this._classes = {
|
|
78
99
|
hamburgerActive: 'vg-nav-hamburger-active',
|
|
79
100
|
hamburgerAlways: 'vg-nav-hamburger-always',
|
|
80
|
-
hamburger: 'vg-nav-hamburger',
|
|
81
|
-
container: 'vg-nav-container',
|
|
82
|
-
wrapper: 'vg-nav-wrapper',
|
|
83
|
-
|
|
101
|
+
hamburger: 'vg-nav-hamburger',
|
|
102
|
+
container: 'vg-nav-container',
|
|
103
|
+
wrapper: 'vg-nav-wrapper',
|
|
104
|
+
dropListScroll: 'vg-nav-drop-list-scroll',
|
|
105
|
+
active: 'vg-nav-active',
|
|
84
106
|
expand: 'vg-nav-expand',
|
|
85
107
|
cloned: 'vg-nav-cloned',
|
|
86
108
|
hover: 'vg-nav-hover',
|
|
@@ -131,11 +153,12 @@ class VGNav extends BaseModule {
|
|
|
131
153
|
let params = this._params,
|
|
132
154
|
classes = this._classes;
|
|
133
155
|
|
|
134
|
-
// Вешаем основные классы
|
|
135
|
-
this._element.classList.add(classes.container);
|
|
136
|
-
this._element.classList.add('vg-nav-' + params.placement);
|
|
137
|
-
|
|
138
|
-
|
|
156
|
+
// Вешаем основные классы
|
|
157
|
+
this._element.classList.add(classes.container);
|
|
158
|
+
this._element.classList.add('vg-nav-' + params.placement);
|
|
159
|
+
this._element.classList.toggle(classes.dropListScroll, !!params.dropListScroll);
|
|
160
|
+
|
|
161
|
+
if (!params.hamburger.always) {
|
|
139
162
|
if (!params.breakpoint) {
|
|
140
163
|
this._element.classList.add(classes.expand);
|
|
141
164
|
} else if (params.breakpoint !== false) {
|
|
@@ -70,15 +70,9 @@
|
|
|
70
70
|
background-color: var(--vg-nav-drop-bg);
|
|
71
71
|
overflow: visible;
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
overflow-x: var(--vg-nav-drop-overflow-x);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
&:not(.show) {
|
|
80
|
-
display: none;
|
|
81
|
-
}
|
|
73
|
+
&:not(.show) {
|
|
74
|
+
display: none;
|
|
75
|
+
}
|
|
82
76
|
|
|
83
77
|
&.fade {
|
|
84
78
|
visibility: visible;
|
|
@@ -135,11 +129,25 @@
|
|
|
135
129
|
}
|
|
136
130
|
}
|
|
137
131
|
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
&.vg-nav-drop-list-scroll {
|
|
136
|
+
.vg-nav-wrapper {
|
|
137
|
+
.dropdown {
|
|
138
|
+
.dropdown-content {
|
|
139
|
+
> .vg-nav-drop-list {
|
|
140
|
+
max-height: var(--vg-nav-drop-max-height);
|
|
141
|
+
overflow-y: var(--vg-nav-drop-overflow-y);
|
|
142
|
+
overflow-x: var(--vg-nav-drop-overflow-x);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** set placement **/
|
|
150
|
+
@import "placement";
|
|
143
151
|
|
|
144
152
|
/** set hamburger **/
|
|
145
153
|
@import "hamburger";
|
|
@@ -69,16 +69,18 @@ const nestable = VGNestable.getOrCreateInstance("#myNestable", {
|
|
|
69
69
|
Значения по умолчанию:
|
|
70
70
|
|
|
71
71
|
```js
|
|
72
|
-
{
|
|
73
|
-
|
|
72
|
+
{
|
|
73
|
+
disabled: false,
|
|
74
|
+
disabledattribute: "data-disabled",
|
|
75
|
+
listselector: ".vg-nestable-list",
|
|
74
76
|
itemselector: ".vg-nestable-item",
|
|
75
77
|
handleselector: ".vg-nestable-handle",
|
|
76
78
|
idattribute: "data-id",
|
|
77
79
|
childlistclass: "vg-nestable-list",
|
|
78
80
|
handleicon: "",
|
|
79
|
-
indent:
|
|
81
|
+
indent: 18,
|
|
80
82
|
maxdepth: 6,
|
|
81
|
-
hoverthreshold: 0.
|
|
83
|
+
hoverthreshold: 0.25,
|
|
82
84
|
neighborchangethreshold: 0,
|
|
83
85
|
showplaceholder: true,
|
|
84
86
|
group: "",
|
|
@@ -117,22 +119,49 @@ const nestable = VGNestable.getOrCreateInstance("#myNestable", {
|
|
|
117
119
|
}
|
|
118
120
|
```
|
|
119
121
|
|
|
120
|
-
### Пояснение параметров
|
|
121
|
-
|
|
122
|
-
- `
|
|
122
|
+
### Пояснение параметров
|
|
123
|
+
|
|
124
|
+
- `disabled`: полностью отключает изменение дерева мышью, touch и клавиатурой. Также читается из `data-disabled="true"` корневого элемента.
|
|
125
|
+
- `disabledattribute`: атрибут блокировки отдельных элементов и вложенных списков (`data-disabled` по умолчанию).
|
|
126
|
+
- `listselector`: селектор корневого/вложенных списков.
|
|
123
127
|
- `itemselector`: селектор sortable-элемента.
|
|
124
128
|
- `handleselector`: селектор зоны, за которую можно начинать drag.
|
|
125
129
|
- `idattribute`: атрибут, из которого берется `id` в `serialize()`.
|
|
126
130
|
- `childlistclass`: классы для автосозданного дочернего списка.
|
|
127
131
|
- `handleicon`: HTML/SVG иконки хэндла (проходит SVG-санитизацию).
|
|
128
|
-
- `indent`:
|
|
132
|
+
- `indent`: ширина одного горизонтального шага вложенности. Каждый следующий шаг вправо переносит элемент ещё на один доступный уровень под предыдущий элемент.
|
|
129
133
|
- `maxdepth`: максимальная глубина дерева.
|
|
130
|
-
- `hoverthreshold`:
|
|
134
|
+
- `hoverthreshold`: половина высоты центральной зоны (0.05..0.45), используемой для вложения; выше и ниже неё находятся зоны `before/after`.
|
|
131
135
|
- `neighborchangethreshold`: порог в процентах (0..49), альтернативная логика смены позиции по краям элемента.
|
|
132
136
|
- `showplaceholder`: показывать/скрывать placeholder во время drag.
|
|
133
137
|
- `group`: имя группы списков для межспискового dnd.
|
|
134
138
|
- `connect`: включить связь списков внутри группы.
|
|
135
|
-
- `accept(item, sourceInstance, targetInstance)`: функция-фильтр разрешения дропа в target.
|
|
139
|
+
- `accept(item, sourceInstance, targetInstance)`: функция-фильтр разрешения дропа в target.
|
|
140
|
+
|
|
141
|
+
### Блокировка перетаскивания
|
|
142
|
+
|
|
143
|
+
Заблокировать всё дерево, в том числе запретить перенос в него из связанного списка:
|
|
144
|
+
|
|
145
|
+
```html
|
|
146
|
+
<div class="vg-nestable" data-vg-toggle="nestable" data-disabled="true">
|
|
147
|
+
<ol class="vg-nestable-list">...</ol>
|
|
148
|
+
</div>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Заблокировать только конкретный элемент. Другие элементы по-прежнему могут вставляться до/после него, поэтому его индекс в результате может измениться:
|
|
152
|
+
|
|
153
|
+
```html
|
|
154
|
+
<li class="vg-nestable-item" data-id="2" data-disabled="true">...</li>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Заблокировать внутреннюю группу. Её элементы нельзя переносить отдельно или добавлять в эту группу, но родительскую ветку можно перемещать целиком:
|
|
158
|
+
|
|
159
|
+
```html
|
|
160
|
+
<li class="vg-nestable-item" data-id="1">
|
|
161
|
+
<div class="vg-nestable-inner">...</div>
|
|
162
|
+
<ol class="vg-nestable-list" data-disabled="true">...</ol>
|
|
163
|
+
</li>
|
|
164
|
+
```
|
|
136
165
|
|
|
137
166
|
#### `collapse`
|
|
138
167
|
|