vgapp 0.4.0 → 0.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.
Files changed (34) hide show
  1. package/app/modules/vgalert/index.js +5 -0
  2. package/app/modules/vgcollapse/index.js +3 -0
  3. package/app/modules/vgdatatable/index.js +4 -0
  4. package/app/modules/vgdropdown/index.js +4 -0
  5. package/app/modules/vgformsender/index.js +4 -0
  6. package/app/modules/vglawcookie/index.js +4 -0
  7. package/app/modules/vgmodal/index.js +4 -0
  8. package/app/modules/vgnav/index.js +4 -0
  9. package/app/modules/vgnav/scss/_variables.scss +2 -0
  10. package/app/modules/vgnav/scss/vgnav.scss +2 -0
  11. package/app/modules/vgrollup/index.js +4 -0
  12. package/app/modules/vgselect/index.js +4 -0
  13. package/app/modules/vgsidebar/index.js +4 -0
  14. package/app/modules/vgspy/index.js +3 -0
  15. package/app/modules/vgspy/js/vgspy.js +159 -172
  16. package/app/modules/vgtoast/index.js +4 -0
  17. package/app/utils/js/dom/selectors.js +38 -2
  18. package/app/utils/js/functions.js +30 -1
  19. package/build/vgapp.css +4 -6
  20. package/build/vgapp.css.map +1 -1
  21. package/build/vgapp.js +1 -1
  22. package/build/vgapp.js.map +1 -1
  23. package/index.js +13 -53
  24. package/package.json +1 -1
  25. package/webpack.config.js +1 -1
  26. package/app/modules/vgspy/scss/_variables.scss +0 -26
  27. package/app/modules/vgspy/scss/vgtoast.css +0 -133
  28. package/app/modules/vgspy/scss/vgtoast.css.map +0 -1
  29. package/app/modules/vgspy/scss/vgtoast.scss +0 -125
  30. package/public/assets/build/vgapp.css +0 -20
  31. package/public/assets/build/vgapp.css.map +0 -1
  32. package/public/assets/build/vgapp.js +0 -3
  33. package/public/assets/build/vgapp.js.LICENSE.txt +0 -1
  34. package/public/assets/build/vgapp.js.map +0 -1
@@ -0,0 +1,5 @@
1
+ // alert
2
+ import "./scss/vgalert.scss";
3
+ import VGAlert from "./js/vgalert";
4
+
5
+ export default VGAlert;
@@ -0,0 +1,3 @@
1
+ import VGCollapse from "./js/vgcollapse";
2
+
3
+ export default VGCollapse;
@@ -0,0 +1,4 @@
1
+ import './scss/vgdatatable.scss'
2
+ import VGDatatable from "./js/vgdatatable";
3
+
4
+ export default VGDatatable;
@@ -0,0 +1,4 @@
1
+ import './scss/vgdropdown.scss';
2
+ import VGDropdown from "./js/vgdropdown";
3
+
4
+ export default VGDropdown;
@@ -0,0 +1,4 @@
1
+ import './scss/vgformsender.scss';
2
+ import VGFormSender from "./js/vgformsender";
3
+
4
+ export default VGFormSender;
@@ -0,0 +1,4 @@
1
+ import './scss/vglawcookie.scss';
2
+ import VGLawCookie from "./js/vglawcookie";
3
+
4
+ export default VGLawCookie;
@@ -0,0 +1,4 @@
1
+ import './scss/vgmodal.scss';
2
+ import VGModal from "./js/vgmodal";
3
+
4
+ export default VGModal;
@@ -0,0 +1,4 @@
1
+ import './scss/vgnav.scss';
2
+ import VGNav from "./js/vgnav";
3
+
4
+ export default VGNav;
@@ -39,6 +39,7 @@ $nav-drop-map: (
39
39
  z-index: 10,
40
40
  transition: map-get($nav-map, transition),
41
41
  width: 200px,
42
+ max-height: initial,
42
43
  border-color: $border-color,
43
44
  border-radius: 0,
44
45
  padding: 0,
@@ -54,6 +55,7 @@ $nav-drop-mega-map: (
54
55
  padding: 1rem,
55
56
  z-index: 10,
56
57
  transition: map-get($nav-map, transition),
58
+ max-height: initial,
57
59
  );
58
60
 
59
61
  $nav-drop-item-map: (
@@ -64,6 +64,8 @@
64
64
  border: 1px solid var(--vg-nav-drop-border-color);
65
65
  border-radius: var(--vg-nav-drop-border-radius);
66
66
  background-color: var(--vg-nav-drop-bg);
67
+ max-height: var(--vg-nav-drop-max-height);
68
+ overflow: auto;
67
69
 
68
70
  &:not(.show) {
69
71
  display: none;
@@ -0,0 +1,4 @@
1
+ import './scss/vgrollup.scss';
2
+ import VGRollup from "./js/vgrollup";
3
+
4
+ export default VGRollup;
@@ -0,0 +1,4 @@
1
+ import './scss/vgselect.scss';
2
+ import VGSelect from "./js/vgselect";
3
+
4
+ export default VGSelect;
@@ -0,0 +1,4 @@
1
+ import './scss/vgsidebar.scss';
2
+ import VGSidebar from "./js/vgsidebar";
3
+
4
+ export default VGSidebar;
@@ -0,0 +1,3 @@
1
+ import VGSpy from "./js/vgspy";
2
+
3
+ export default VGSpy;
@@ -1,7 +1,6 @@
1
1
  import BaseModule from "../../base-module";
2
+ import {mergeDeepObject, getElement, isDisabled, isVisible} from "../../../utils/js/functions";
2
3
  import EventHandler from "../../../utils/js/dom/event";
3
- import {dismissTrigger} from "../../module-fn";
4
- import {execute, isDisabled, makeRandomString, mergeDeepObject, noop} from "../../../utils/js/functions";
5
4
  import Selectors from "../../../utils/js/dom/selectors";
6
5
 
7
6
  /**
@@ -9,32 +8,52 @@ import Selectors from "../../../utils/js/dom/selectors";
9
8
  */
10
9
  const NAME = 'spy';
11
10
  const NAME_KEY = 'vg.spy';
11
+ const EVENT_KEY = `.${NAME_KEY}`
12
+ const DATA_API_KEY = '.data-api'
13
+
14
+ const EVENT_ACTIVATE = `activate${EVENT_KEY}`
15
+ const EVENT_CLICK = `click${EVENT_KEY}`
16
+ const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`
17
+
18
+ const CLASS_NAME_DROPDOWN_ITEM = 'vg-dropdown-item'
19
+ const CLASS_NAME_ACTIVE = 'active'
20
+
21
+ const SELECTOR_DATA_SPY = '[data-vg-toggle="spy"]'
22
+ const SELECTOR_TARGET_LINKS = '[href]'
23
+ const SELECTOR_NAV_LIST_GROUP = '.nav, .list-group'
24
+ const SELECTOR_NAV_LINKS = '.nav-link'
25
+ const SELECTOR_NAV_ITEMS = '.nav-item'
26
+ const SELECTOR_LIST_ITEMS = '.list-group-item'
27
+ const SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`
28
+ const SELECTOR_DROPDOWN = '.vg-dropdown'
29
+ const SELECTOR_DROPDOWN_TOGGLE = '[data-vg-toggle="dropdown"]'
12
30
 
13
31
 
14
32
  class VGSpy extends BaseModule {
15
- constructor(element, params = {}) {
33
+ constructor(element, params) {
16
34
  super(element, params);
17
35
 
18
36
  this._params = this._getParams(element, mergeDeepObject({
19
- speed: 1500,
20
- offset: 0,
21
- easing: 'easeInOutSine', // easeInOutSine:easeOutSine:easeInOutQuint
22
- isState: false,
23
- onActive: noop,
24
- onClick: noop,
25
- activeClass: ['active']
37
+ offset: null, // TODO: v6 @deprecated, keep it for backwards compatibility reasons
38
+ rootMargin: '0px 0px -25%',
39
+ smoothScroll: true,
40
+ target: this._element,
41
+ threshold: [0.1, 0.5, 1]
26
42
  }, params));
27
43
 
28
- this.isClick = false;
29
-
30
- this.links = this._element.querySelectorAll('[data-vg-target]').length ?
31
- this._element.querySelectorAll('[data-vg-target]') :
32
- this._element.querySelectorAll('a')
33
- ;
44
+ this._targetLinks = new Map()
45
+ this._observableSections = new Map()
46
+ this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element
47
+ this._activeTarget = null
48
+ this._observer = null
49
+ this._previousScrollData = {
50
+ visibleEntryTop: 0,
51
+ parentScrollTop: 0
52
+ }
53
+ this._params = this._configAfterMerge(this._params);
54
+ console.log(this._params)
34
55
 
35
- this.onLoad();
36
- this.onClick();
37
- this.onScroll();
56
+ this.refresh();
38
57
  }
39
58
 
40
59
  static get NAME() {
@@ -45,198 +64,166 @@ class VGSpy extends BaseModule {
45
64
  return NAME_KEY
46
65
  }
47
66
 
48
- onLoad() {
49
- let _this = this;
67
+ refresh() {
68
+ this._initializeTargetsAndObservables()
69
+ this._maybeEnableSmoothScroll()
50
70
 
51
- document.addEventListener('DOMContentLoaded', function () {
52
- _this.setCurrentSection(null);
53
- });
71
+ if (this._observer) {
72
+ this._observer.disconnect()
73
+ } else {
74
+ this._observer = this._getNewObserver()
75
+ }
76
+
77
+ for (const section of this._observableSections.values()) {
78
+ this._observer.observe(section)
79
+ }
54
80
  }
55
81
 
56
- onClick() {
57
- let _this = this;
82
+ dispose() {
83
+ this._observer.disconnect()
84
+ super.dispose()
85
+ }
58
86
 
59
- _this.links.forEach(el => {
60
- if (el) {
61
- el.onclick = function (e) {
62
- execute(_this._params.onClick, [e, this])
63
- _this.setCurrentSection(this);
87
+ _configAfterMerge(param) {
88
+ param.target = getElement(param.target) || document.body
89
+ param.rootMargin = param.offset ? `${param.offset}px 0px -30%` : param.rootMargin
64
90
 
65
- return false;
66
- }
67
- }
68
- });
91
+ if (typeof param.threshold === 'string') {
92
+ param.threshold = param.threshold.split(',').map(value => Number.parseFloat(value))
93
+ }
94
+
95
+ return param
69
96
  }
70
97
 
71
- onScroll() {
72
- let _this = this;
98
+ _maybeEnableSmoothScroll() {
99
+ if (!this._params.smoothScroll) {
100
+ return
101
+ }
73
102
 
74
- if (!_this.isClick) {
75
- window.onscroll = function () {
76
- _this.setCurrentSection(null);
103
+ EventHandler.off(this._params.target, EVENT_CLICK)
104
+
105
+ EventHandler.on(this._params.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => {
106
+ const observableSection = this._observableSections.get(event.target.hash)
107
+ if (observableSection) {
108
+ event.preventDefault()
109
+ const root = this._rootElement || window
110
+ const height = observableSection.offsetTop - this._element.offsetTop
111
+ if (root.scrollTo) {
112
+ root.scrollTo({ top: height, behavior: 'smooth' })
113
+ return
114
+ }
115
+ root.scrollTop = height
77
116
  }
78
- }
117
+ })
79
118
  }
80
119
 
81
- setCurrentSection($link = null) {
82
- this.removeCurrentActive();
120
+ _getNewObserver() {
121
+ const options = {
122
+ root: this._rootElement,
123
+ threshold: this._params.threshold,
124
+ rootMargin: this._params.rootMargin
125
+ }
83
126
 
84
- if (this._params.isState) {
85
- // TODO не тестили
86
- let target = window.location.hash;
87
- if (target) {
88
- let $element = document.querySelector('[href="'+ target +'"]') ||
89
- document.querySelector('[href="\/' + target +'"]') ||
90
- document.querySelector('[data-vg-target="'+ target.replace('#', '') +'"]') || null;
127
+ return new IntersectionObserver(entries => this._observerCallback(entries), options)
128
+ }
91
129
 
92
- if ($element !== null) {
93
- $link = $element;
94
- }
95
- }
130
+ _observerCallback(entries) {
131
+ const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`);
132
+
133
+ const activate = entry => {
134
+ this._previousScrollData.visibleEntryTop = entry.target.offsetTop;
135
+ this._process(targetElement(entry));
96
136
  }
97
137
 
98
- if ($link) {
99
- let target = this.attributes($link, 'target'),
100
- offset = this.attributes($link, 'offset'),
101
- section = document.getElementById(target);
102
-
103
- if (section) {
104
- let scrollTargetY = section.offsetTop + (offset) + (this._params.offset),
105
- scrollY = window.scrollY || document.documentElement.scrollTop,
106
- speed = this._params.speed,
107
- easing = this._params.easing,
108
- currentTime = 0;
109
-
110
- this.removeCurrentActive();
111
- this.setActive($link, section);
112
-
113
- let time = Math.max(.1, Math.min(Math.abs(scrollY - scrollTargetY) / speed, .8)),
114
- easingEquations = {
115
- easeOutSine: function (pos) {
116
- return Math.sin(pos * (Math.PI / 2));
117
- },
118
- easeInOutSine: function (pos) {
119
- return (-0.5 * (Math.cos(Math.PI * pos) - 1));
120
- },
121
- easeInOutQuint: function (pos) {
122
- if ((pos /= 0.5) < 1) {
123
- return 0.5 * Math.pow(pos, 5);
124
- }
125
- return 0.5 * (Math.pow((pos - 2), 5) + 2);
126
- }
127
- };
128
-
129
- window.requestAnimFrame = (function(){
130
- return window.requestAnimationFrame ||
131
- window.webkitRequestAnimationFrame ||
132
- window.mozRequestAnimationFrame ||
133
- function( callback ){
134
- window.setTimeout(callback, 1000 / 60);
135
- };
136
- })();
137
-
138
- function move() {
139
- currentTime += 1 / 60;
140
-
141
- let p = currentTime / time,
142
- t = easingEquations[easing](p);
143
-
144
- if (p < 1) {
145
- requestAnimFrame(move);
146
- window.scrollTo(0, scrollY + ((scrollTargetY - scrollY) * t));
147
- } else {
148
- window.scrollTo(0, scrollTargetY);
149
- }
150
- }
138
+ const parentScrollTop = (this._rootElement || document.documentElement).scrollTop
139
+ const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop
140
+ this._previousScrollData.parentScrollTop = parentScrollTop
151
141
 
152
- move();
142
+ for (const entry of entries) {
143
+ if (!entry.isIntersecting) {
144
+ this._activeTarget = null
145
+ this._clearActiveClass(targetElement(entry))
153
146
 
154
- this.isClick = false;
147
+ continue
155
148
  }
156
- } else {
157
- for (let i = 0; i < this.links.length; i++) {
158
- let target = this.attributes(this.links[i], 'target'),
159
- offset = this.attributes(this.links[i], 'offset'),
160
- section = document.getElementById(target);
161
-
162
- if (section) {
163
- let start = section.offsetTop + (offset) + (this._params.offset),
164
- end = start + section.offsetHeight,
165
- currentPosition = (document.documentElement.scrollTop || document.body.scrollTop),
166
- isInView = currentPosition >= start && currentPosition < end;
167
-
168
- if (isInView) {
169
- this.removeCurrentActive({ignore: this.links[i]});
170
- this.setActive(this.links[i], section);
171
- }
149
+
150
+ const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop
151
+ if (userScrollsDown && entryIsLowerThanPrevious) {
152
+ activate(entry)
153
+ if (!parentScrollTop) {
154
+ return
172
155
  }
156
+
157
+ continue
158
+ }
159
+
160
+ if (!userScrollsDown && !entryIsLowerThanPrevious) {
161
+ activate(entry)
173
162
  }
174
163
  }
175
164
  }
176
165
 
177
- setActive($link, $section) {
178
- const isActive = this._params.activeClass.every(function (value){
179
- return $link.classList.contains(value);
180
- });
166
+ _initializeTargetsAndObservables() {
167
+ this._targetLinks = new Map();
168
+ this._observableSections = new Map();
181
169
 
182
- if (this._params.isState) {
183
- let text = this.attributes($link, 'text'),
184
- target = this.attributes($link, 'target');
170
+ const targetLinks = Selectors.findAll(SELECTOR_TARGET_LINKS, this._params.target);
185
171
 
186
- history.pushState("", document.title + text, '#' + target);
187
- }
188
-
189
- if (!isActive) {
190
- if ($section) {
191
- $section.classList.add(...this._params.activeClass);
172
+ for (const anchor of targetLinks) {
173
+ if (!anchor.hash || isDisabled(anchor)) {
174
+ continue
192
175
  }
193
176
 
194
- if ($link) {
195
- $link.classList.add(...this._params.activeClass);
196
- }
177
+ const observableSection = Selectors.find(decodeURI(anchor.hash));
197
178
 
198
- execute(this._params.onActive, [$link, $section]);
179
+ if (isVisible(observableSection)) {
180
+ this._targetLinks.set(decodeURI(anchor.hash), anchor)
181
+ this._observableSections.set(anchor.hash, observableSection)
182
+ }
199
183
  }
200
184
  }
201
185
 
202
- removeCurrentActive(options = { ignore: null }) {
203
- for (let i = 0; i < this.links.length; i++) {
204
- let target = this.attributes(this.links[i], 'target'),
205
- section = document.getElementById(target);
206
-
207
- if ((options.ignore !== this.links[i]) && section) {
208
- this.links[i].classList.remove(...this._params.activeClass);
209
- section.classList.remove(...this._params.activeClass);
210
- }
186
+ _process(target) {
187
+ if (this._activeTarget === target) {
188
+ return
211
189
  }
212
- }
213
190
 
214
- attributes(self, prop = '') {
215
- let target = self.getAttribute('href') || self.dataset.vgTarget;
191
+ this._clearActiveClass(this._params.target)
192
+ this._activeTarget = target
193
+ target.classList.add(CLASS_NAME_ACTIVE)
194
+ this._activateParents(target)
216
195
 
217
- if (target !== 'undefined' && target.indexOf('#') !== -1) {
218
- target = target.replace(/(^.+)#/gm, '');
196
+ EventHandler.trigger(this._element, EVENT_ACTIVATE, { relatedTarget: target })
197
+ }
219
198
 
220
- if (target.indexOf('#') !== -1) {
221
- target = target.replace('#', '');
222
- }
223
- } else if (target !== 'undefined' && target.indexOf('#') === -1) {
224
- target = ''
199
+ _activateParents(target) {
200
+ if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {
201
+ Selectors.find(SELECTOR_DROPDOWN_TOGGLE, target.closest(SELECTOR_DROPDOWN))
202
+ .classList.add(CLASS_NAME_ACTIVE)
203
+ return
225
204
  }
226
205
 
227
- let offset = self.dataset.vgOffset ? parseInt(self.dataset.vgOffset) : 0;
228
- let text = self.innerHTML;
206
+ for (const listGroup of Selectors.parents(target, SELECTOR_NAV_LIST_GROUP)) {
207
+ for (const item of Selectors.prev(listGroup, SELECTOR_LINK_ITEMS)) {
208
+ item.classList.add(CLASS_NAME_ACTIVE)
209
+ }
210
+ }
211
+ }
229
212
 
230
- if (prop === 'target') return target;
231
- if (prop === 'offset') return offset;
232
- if (prop === 'text') return text;
213
+ _clearActiveClass(parent) {
214
+ parent.classList.remove(CLASS_NAME_ACTIVE)
233
215
 
234
- return {
235
- target: target,
236
- offset: offset,
237
- text: text
238
- };
216
+ const activeNodes = Selectors.findAll(`${SELECTOR_TARGET_LINKS}.${CLASS_NAME_ACTIVE}`, parent);
217
+ for (const node of activeNodes) {
218
+ node.classList.remove(CLASS_NAME_ACTIVE)
219
+ }
239
220
  }
240
221
  }
241
222
 
242
- export default VGSpy;
223
+ EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
224
+ for (const spy of Selectors.findAll(SELECTOR_DATA_SPY)) {
225
+ VGSpy.getOrCreateInstance(spy)
226
+ }
227
+ })
228
+
229
+ export default VGSpy;
@@ -0,0 +1,4 @@
1
+ import './scss/vgtoast.scss'
2
+ import VGToast from "./js/vgtoast";
3
+
4
+ export default VGToast;
@@ -61,8 +61,44 @@ const Selectors = {
61
61
  return selector ? Selectors.findAll(selector) : []
62
62
  },
63
63
 
64
- prev(element) {
65
- return element.previousElementSibling || null
64
+ parents(element, selector) {
65
+ const parents = []
66
+ let ancestor = element.parentNode.closest(selector)
67
+
68
+ while (ancestor) {
69
+ parents.push(ancestor)
70
+ ancestor = ancestor.parentNode.closest(selector)
71
+ }
72
+
73
+ return parents
74
+ },
75
+
76
+ next(element, selector) {
77
+ let next = element.nextElementSibling
78
+
79
+ while (next) {
80
+ if (next.matches(selector)) {
81
+ return [next]
82
+ }
83
+
84
+ next = next.nextElementSibling
85
+ }
86
+
87
+ return []
88
+ },
89
+
90
+ prev(element, selector) {
91
+ let previous = element.previousElementSibling
92
+
93
+ while (previous) {
94
+ if (previous.matches(selector)) {
95
+ return [previous]
96
+ }
97
+
98
+ previous = previous.previousElementSibling
99
+ }
100
+
101
+ return []
66
102
  }
67
103
  }
68
104
 
@@ -2,6 +2,20 @@
2
2
  * Набор скриптов для широкого применения
3
3
  */
4
4
 
5
+ /**
6
+ * Экранирование селекторов для корректной работы идентификаторов
7
+ * @param {string} selector
8
+ * @returns {string}
9
+ */
10
+ const parseSelector = selector => {
11
+ if (selector && window.CSS && window.CSS.escape) {
12
+ // document.querySelector needs escaping to handle IDs (html5+) containing for instance /
13
+ selector = selector.replace(/#([^\s"#']+)/g, (match, id) => `#${CSS.escape(id)}`)
14
+ }
15
+
16
+ return selector
17
+ }
18
+
5
19
  /**
6
20
  * Если что-нибудь в объекте
7
21
  * @param obj
@@ -17,6 +31,19 @@ function isEmptyObj(obj) {
17
31
  return true
18
32
  }
19
33
 
34
+ const getElement = object => {
35
+ // it's a jQuery object or a node element
36
+ if (isElement(object)) {
37
+ return object.jquery ? object[0] : object
38
+ }
39
+
40
+ if (typeof object === 'string' && object.length > 0) {
41
+ return document.querySelector(parseSelector(object))
42
+ }
43
+
44
+ return null
45
+ }
46
+
20
47
  /**
21
48
  * isElement
22
49
  * @param object
@@ -141,6 +168,8 @@ function removeElementArray(arr, el) {
141
168
  function mergeDeepObject(...objects) {
142
169
  const isObject = obj => obj && typeof obj === 'object';
143
170
 
171
+ if (!isObject) return;
172
+
144
173
  return objects.reduce((prev, obj) => {
145
174
  Object.keys(obj).forEach(key => {
146
175
  const pVal = prev[key];
@@ -293,4 +322,4 @@ function transliterate(text, enToRu) {
293
322
  */
294
323
  const isRTL = () => document.documentElement.dir === 'rtl'
295
324
 
296
- export {isElement, isVisible, isDisabled, isObject, isEmptyObj, mergeDeepObject, removeElementArray, normalizeData, execute, executeAfterTransition, reflow, noop, makeRandomString, isRTL, transliterate}
325
+ export {isElement, isVisible, isDisabled, isObject, isEmptyObj, mergeDeepObject, removeElementArray, normalizeData, execute, executeAfterTransition, reflow, noop, makeRandomString, isRTL, transliterate, getElement}