vgapp 0.7.6 → 0.7.8
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/CHANGELOG.md +15 -4
- package/app/modules/base-module.js +52 -17
- package/app/modules/module-fn.js +10 -82
- package/app/modules/vgdropdown/js/vgdropdown.js +104 -118
- package/app/modules/vgdropdown/scss/vgdropdown.scss +1 -2
- package/app/modules/vgformsender/js/hideshowpass.js +7 -4
- package/app/modules/vgformsender/js/vgformsender.js +343 -160
- package/app/modules/vgformsender/readme.md +221 -0
- package/app/modules/vgformsender/scss/vgformsender.scss +11 -3
- package/app/modules/vgnav/js/vgnav.js +98 -26
- package/app/modules/vgnav/scss/_placement.scss +8 -93
- package/app/modules/vgnav/scss/vgnav.scss +0 -1
- package/app/utils/js/components/ajax.js +215 -0
- package/app/utils/js/components/lang.js +82 -0
- package/app/utils/js/components/params.js +5 -0
- package/app/utils/js/components/placement.js +111 -108
- package/app/utils/js/components/templater.js +365 -33
- package/app/utils/js/functions.js +275 -143
- package/app/utils/scss/default.scss +1 -0
- package/app/utils/scss/placement.scss +72 -0
- package/app/utils/scss/variables.scss +10 -5
- package/build/vgapp.css +1 -1
- package/build/vgapp.css.map +1 -1
- package/package.json +1 -2
- package/app/utils/js/components/alert.js +0 -8
|
@@ -1,55 +1,387 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {getSVG} from "../../../modules/module-fn";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
class LaravelHtmlBuilder {
|
|
4
|
+
constructor(mode = 'string') {
|
|
5
|
+
this.mode = mode; // 'string' или 'dom'
|
|
6
|
+
this.document = typeof document !== 'undefined' ? document : null;
|
|
7
|
+
this.tags = {
|
|
8
|
+
selfClosing: ['img', 'input', 'br', 'hr', 'meta', 'link', 'area', 'base', 'col', 'command', 'embed', 'keygen', 'param', 'source', 'track', 'wbr']
|
|
9
|
+
};
|
|
10
|
+
}
|
|
8
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Установка режима работы
|
|
14
|
+
*/
|
|
15
|
+
setMode(mode) {
|
|
16
|
+
this.mode = mode;
|
|
17
|
+
return this;
|
|
18
|
+
}
|
|
9
19
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Создание HTML элемента
|
|
22
|
+
*/
|
|
23
|
+
element(tag, attributes = {}, content = null, option = {}) {
|
|
24
|
+
if (this.mode === 'dom' && this.document) {
|
|
25
|
+
return this._createDomElement(tag, attributes, content, option);
|
|
14
26
|
}
|
|
15
|
-
|
|
16
|
-
this._element = el;
|
|
17
|
-
this._params = mergeDeepObject({
|
|
18
|
-
insert: 'afterend',
|
|
19
|
-
classes: []
|
|
20
|
-
}, params);
|
|
27
|
+
return this._createHtmlString(tag, attributes, content, option);
|
|
21
28
|
}
|
|
22
29
|
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Создание DOM элемента
|
|
32
|
+
*/
|
|
33
|
+
_createDomElement(tag, attributes, content, option = {}) {
|
|
34
|
+
const element = this.document.createElement(tag);
|
|
25
35
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
36
|
+
// Установка атрибутов
|
|
37
|
+
this._setDomAttributes(element, attributes);
|
|
38
|
+
|
|
39
|
+
// Добавление содержимого
|
|
40
|
+
if (content !== null && !this.tags.selfClosing.includes(tag)) {
|
|
41
|
+
if (Array.isArray(content)) {
|
|
42
|
+
content.forEach(item => {
|
|
43
|
+
if (typeof item === 'string') {
|
|
44
|
+
element.appendChild(this.document.createTextNode(item));
|
|
45
|
+
} else if (item instanceof Node) {
|
|
46
|
+
element.appendChild(item);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
} else if (content instanceof Node) {
|
|
50
|
+
element.appendChild(content);
|
|
51
|
+
} else {
|
|
52
|
+
if ('isHTML' in option && option.isHTML) {
|
|
53
|
+
element.innerHTML = content;
|
|
54
|
+
} else {
|
|
55
|
+
element.textContent = content;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
30
58
|
}
|
|
59
|
+
|
|
60
|
+
return element;
|
|
31
61
|
}
|
|
32
62
|
|
|
33
|
-
|
|
34
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Установка атрибутов DOM элемента
|
|
65
|
+
*/
|
|
66
|
+
_setDomAttributes(element, attributes) {
|
|
67
|
+
if (!attributes) return;
|
|
35
68
|
|
|
36
|
-
|
|
37
|
-
if (
|
|
38
|
-
|
|
69
|
+
Object.entries(attributes).forEach(([key, value]) => {
|
|
70
|
+
if (value === null || value === undefined || value === false) {
|
|
71
|
+
return;
|
|
39
72
|
}
|
|
73
|
+
|
|
74
|
+
if (value === true) {
|
|
75
|
+
element.setAttribute(key, '');
|
|
76
|
+
} else {
|
|
77
|
+
element.setAttribute(key, String(value));
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Создание HTML строки
|
|
84
|
+
*/
|
|
85
|
+
_createHtmlString(tag, attributes, content, isHTML = false) {
|
|
86
|
+
const attrString = this._attributesToString(attributes);
|
|
87
|
+
const isSelfClosing = this.tags.selfClosing.includes(tag);
|
|
88
|
+
|
|
89
|
+
if (isSelfClosing) {
|
|
90
|
+
return `<${tag}${attrString}>`;
|
|
40
91
|
}
|
|
41
92
|
|
|
42
|
-
|
|
93
|
+
const contentStr = content !== null ?
|
|
94
|
+
this._contentToString(content) : '';
|
|
43
95
|
|
|
44
|
-
|
|
45
|
-
|
|
96
|
+
if (tag === '') {
|
|
97
|
+
return `${contentStr}`;
|
|
98
|
+
} else {
|
|
99
|
+
return `<${tag}${attrString}>${contentStr}</${tag}>`;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Преобразование содержимого в строку
|
|
105
|
+
*/
|
|
106
|
+
_contentToString(content) {
|
|
107
|
+
if (Array.isArray(content)) {
|
|
108
|
+
return content.map(item => {
|
|
109
|
+
if (item instanceof Node) {
|
|
110
|
+
return this._nodeToString(item);
|
|
111
|
+
}
|
|
112
|
+
return String(item);
|
|
113
|
+
}).join('');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (content instanceof Node) {
|
|
117
|
+
return this._nodeToString(content);
|
|
118
|
+
}
|
|
46
119
|
|
|
47
|
-
return
|
|
120
|
+
return String(content);
|
|
48
121
|
}
|
|
49
122
|
|
|
50
|
-
|
|
123
|
+
/**
|
|
124
|
+
* Преобразование DOM узла в строку
|
|
125
|
+
*/
|
|
126
|
+
_nodeToString(node) {
|
|
127
|
+
if (node.outerHTML) {
|
|
128
|
+
return node.outerHTML;
|
|
129
|
+
}
|
|
130
|
+
return String(node);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Преобразование атрибутов в строку
|
|
135
|
+
*/
|
|
136
|
+
_attributesToString(attrs) {
|
|
137
|
+
if (!attrs || Object.keys(attrs).length === 0) {
|
|
138
|
+
return '';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const attributes = Object.entries(attrs)
|
|
142
|
+
.map(([key, value]) => {
|
|
143
|
+
if (value === null || value === undefined || value === false) {
|
|
144
|
+
return '';
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (value === true) {
|
|
148
|
+
return key;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const escapedValue = String(value)
|
|
152
|
+
.replace(/&/g, '&')
|
|
153
|
+
.replace(/"/g, '"')
|
|
154
|
+
.replace(/</g, '<')
|
|
155
|
+
.replace(/>/g, '>');
|
|
156
|
+
|
|
157
|
+
return `${key}="${escapedValue}"`;
|
|
158
|
+
})
|
|
159
|
+
.filter(attr => attr !== '');
|
|
160
|
+
|
|
161
|
+
return attributes.length ? ' ' + attributes.join(' ') : '';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Методы для конкретных элементов
|
|
166
|
+
*/
|
|
167
|
+
div(attributes = {}, content = '', options = {}) {
|
|
168
|
+
return this.element('div', attributes, content, options);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
span(attributes = {}, content = '', options = {}) {
|
|
172
|
+
return this.element('span', attributes, content, options);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
i(attributes = {}, content = '', options = {}) {
|
|
176
|
+
return this.element('i', attributes, content, options);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
p(attributes = {}, content = '', options = {}) {
|
|
180
|
+
return this.element('p', attributes, content, options);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
a(href, content = '', attributes = {}, options = {}) {
|
|
184
|
+
return this.element('a', { href, ...attributes }, content, options);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
img(src, alt = '', attributes = {}) {
|
|
188
|
+
return this.element('img', { src, alt, ...attributes });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
input(name, type = 'text', value = '', attributes = {}) {
|
|
192
|
+
return this.element('input', {
|
|
193
|
+
type,
|
|
194
|
+
name,
|
|
195
|
+
value,
|
|
196
|
+
...attributes
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
button(content = '', type = 'button', attributes = {}, options = {}) {
|
|
201
|
+
return this.element('button', { type, ...attributes }, content, options);
|
|
202
|
+
}
|
|
51
203
|
|
|
204
|
+
form(action = '', method = 'post', attributes = {}, content = '', options = {}) {
|
|
205
|
+
return this.element('form', { action, method, ...attributes }, content, options);
|
|
52
206
|
}
|
|
207
|
+
|
|
208
|
+
label(forId, content, attributes = {}, options = {}) {
|
|
209
|
+
return this.element('label', { for: forId, ...attributes }, content, options);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
textarea(name, content = '', attributes = {}) {
|
|
213
|
+
return this.element('textarea', { name, ...attributes }, content);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
ul(items = [], attributes = {}) {
|
|
217
|
+
const listItems = items.map(item =>
|
|
218
|
+
this.element('li', {}, item)
|
|
219
|
+
);
|
|
220
|
+
return this.element('ul', attributes, listItems);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
ol(items = [], attributes = {}) {
|
|
224
|
+
const listItems = items.map(item =>
|
|
225
|
+
this.element('li', {}, item)
|
|
226
|
+
);
|
|
227
|
+
return this.element('ol', attributes, listItems);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
h4(attributes = {}, content = '', options = {}) {
|
|
231
|
+
return this.element('h4', attributes, content, options);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Создание таблицы
|
|
236
|
+
*/
|
|
237
|
+
table(headers = [], rows = [], attributes = {}) {
|
|
238
|
+
const headerCells = headers.map(header =>
|
|
239
|
+
this.element('th', {}, header)
|
|
240
|
+
);
|
|
241
|
+
const headerRow = this.element('tr', {}, headerCells);
|
|
242
|
+
const thead = this.element('thead', {}, headerRow);
|
|
243
|
+
|
|
244
|
+
const bodyRows = rows.map(row => {
|
|
245
|
+
const cells = row.map(cell =>
|
|
246
|
+
this.element('td', {}, cell)
|
|
247
|
+
);
|
|
248
|
+
return this.element('tr', {}, cells);
|
|
249
|
+
});
|
|
250
|
+
const tbody = this.element('tbody', {}, bodyRows);
|
|
251
|
+
|
|
252
|
+
return this.element('table', attributes, [thead, tbody]);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Методы для форм
|
|
257
|
+
*/
|
|
258
|
+
csrfToken(token) {
|
|
259
|
+
return this.element('input', {
|
|
260
|
+
type: 'hidden',
|
|
261
|
+
name: '_token',
|
|
262
|
+
value: token
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
method(method) {
|
|
267
|
+
return this.element('input', {
|
|
268
|
+
type: 'hidden',
|
|
269
|
+
name: '_method',
|
|
270
|
+
value: method
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Создание элемента с обработчиками событий
|
|
276
|
+
*/
|
|
277
|
+
withEvents(element, events = {}) {
|
|
278
|
+
if (element instanceof Node && this.mode === 'dom') {
|
|
279
|
+
Object.entries(events).forEach(([event, handler]) => {
|
|
280
|
+
element.addEventListener(event, handler);
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
return element;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Добавление классов
|
|
288
|
+
*/
|
|
289
|
+
addClass(element, className) {
|
|
290
|
+
if (element instanceof Node && this.mode === 'dom') {
|
|
291
|
+
element.classList.add(className);
|
|
292
|
+
} else if (typeof element === 'string') {
|
|
293
|
+
// Для строкового режима - модифицируем атрибут class
|
|
294
|
+
const match = element.match(/<(\w+)([^>]*)>/);
|
|
295
|
+
if (match) {
|
|
296
|
+
const [fullMatch, tag, attrs] = match;
|
|
297
|
+
const newAttrs = this._addClassToAttributes(attrs, className);
|
|
298
|
+
return element.replace(fullMatch, `<${tag}${newAttrs}>`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return element;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
_addClassToAttributes(attrs, className) {
|
|
305
|
+
const classMatch = attrs.match(/class="([^"]*)"/);
|
|
306
|
+
if (classMatch) {
|
|
307
|
+
const existingClass = classMatch[1];
|
|
308
|
+
const newClass = existingClass ? `${existingClass} ${className}` : className;
|
|
309
|
+
return attrs.replace(/class="[^"]*"/, `class="${newClass}"`);
|
|
310
|
+
} else {
|
|
311
|
+
return `${attrs} class="${className}"`;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Создание компонента
|
|
317
|
+
*/
|
|
318
|
+
component(name, props = {}, slots = {}) {
|
|
319
|
+
// Можно добавить заготовленные компоненты
|
|
320
|
+
const components = {
|
|
321
|
+
'eye': (props) => {
|
|
322
|
+
const type = props.type || 'open';
|
|
323
|
+
const svg = this.element('', {}, getSVG(`eye-${type}`))
|
|
324
|
+
return this.span(
|
|
325
|
+
{
|
|
326
|
+
'data-vg-toggle': 'pass',
|
|
327
|
+
'data-bs-toggle': 'tooltip',
|
|
328
|
+
'title': 'Скрыть',
|
|
329
|
+
'class': props.class || ''
|
|
330
|
+
},
|
|
331
|
+
[
|
|
332
|
+
svg
|
|
333
|
+
]
|
|
334
|
+
);
|
|
335
|
+
},
|
|
336
|
+
'alert': (props) => {
|
|
337
|
+
const type = props.type || 'info';
|
|
338
|
+
return this.div(
|
|
339
|
+
{
|
|
340
|
+
class: `alert alert-${type}`,
|
|
341
|
+
role: 'alert'
|
|
342
|
+
},
|
|
343
|
+
props.content || ''
|
|
344
|
+
);
|
|
345
|
+
},
|
|
346
|
+
'card': (props) => {
|
|
347
|
+
const header = props.header ?
|
|
348
|
+
this.div({ class: 'card-header' }, props.header) : '';
|
|
349
|
+
const body = this.div({ class: 'card-body' }, props.body || '');
|
|
350
|
+
const footer = props.footer ?
|
|
351
|
+
this.div({ class: 'card-footer' }, props.footer) : '';
|
|
352
|
+
|
|
353
|
+
return this.div(
|
|
354
|
+
{ class: 'card' },
|
|
355
|
+
[header, body, footer]
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
if (components[name]) {
|
|
361
|
+
return components[name](props);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return this.div({ class: `component-${name}` }, '');
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function Html(mode = 'string') {
|
|
369
|
+
const builder = new LaravelHtmlBuilder(mode);
|
|
370
|
+
|
|
371
|
+
const handler = {
|
|
372
|
+
get(target, prop) {
|
|
373
|
+
if (prop in target) {
|
|
374
|
+
return target[prop].bind(target);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Динамическое создание метода для любого тега
|
|
378
|
+
return function(attributes = {}, content = '', options = {}) {
|
|
379
|
+
return target.element(prop, attributes, content, options);
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
return new Proxy(builder, handler);
|
|
53
385
|
}
|
|
54
386
|
|
|
55
|
-
export default
|
|
387
|
+
export default Html;
|