@madj2k/fe-frontend-kit 2.0.14 → 2.0.17

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@madj2k/fe-frontend-kit",
3
- "version": "2.0.14",
3
+ "version": "2.0.17",
4
4
  "description": "Shared frontend utilities, menus and mixins for projects",
5
5
  "main": "index.js",
6
6
  "style": "index.scss",
package/readme.md CHANGED
@@ -86,26 +86,33 @@ where content may change dynamically.
86
86
 
87
87
  Init with available options:
88
88
  ```
89
- const owlThumbnail = new Madj2kOwlThumbnail('.js-main-carousel', '.js-thumbs-carousel', {
90
- main: {
91
- items: 1,
92
- margin: 20,
93
- dots: true,
94
- nav: true,
95
- autoHeight: true
96
- },
97
- thumbs: {
98
- items: 3,
99
- margin: 10,
100
- dots: false,
101
- nav: true,
102
- center: true
103
- },
104
- resizeEvent: 'custom.resize',
105
- equalizeThumbHeights: true,
106
- noStageOffset: true
107
- }
108
- });
89
+ $('.js-slider-container').each(function () {
90
+ const owlThumbnail = new Madj2kOwlThumbnail(
91
+ '.js-main-carousel',
92
+ '.js-thumbs-carousel',
93
+ {
94
+ main: {
95
+ items: 1,
96
+ margin: 20,
97
+ dots: true,
98
+ nav: true,
99
+ autoHeight: true
100
+ },
101
+ thumbs: {
102
+ items: 3,
103
+ margin: 10,
104
+ dots: false,
105
+ nav: true,
106
+ center: true
107
+ },
108
+ resizeEvent: 'custom.resize',
109
+ equalizeThumbHeights: true,
110
+ noStageOffset: true
111
+ },
112
+ false,
113
+ this
114
+ });
115
+ });
109
116
  ```
110
117
 
111
118
  HTML:
@@ -160,14 +167,22 @@ Init with available options:
160
167
  ```
161
168
  import { Madj2kScrolling } from '@madj2k/frontend-kit/tools/scrolling';
162
169
  const scrolling = new Madj2kScrolling({
163
- anchorScrollingCollapsibleSelector: ['.collapse', '.custom-collapse'],
164
- anchorScrollingSelector: ['a[href^="#"]', '.btn-scroll'],
165
- anchorScrollingOffsetSelector: '#siteheader',
166
- anchorScrollingDisableSelector: '.js-no-scroll',
167
- appearOnScrollSelector: ['.js-appear-on-scroll'],
168
- appearOnScrollTimeout: 500,
169
- appearOnScrollThreshold: 25,
170
- debug: true
170
+ anchorScrolling: {
171
+ selector: ['a[href^="#"]'],
172
+ offsetSelector: null,
173
+ disableSelector: '.js-no-scroll',
174
+ collapsibleSelector: ['.collapse'],
175
+ behavior: 'smooth',
176
+ scriptScrollTimeout: 800,
177
+ timeout: 500,
178
+ threshold: 40
179
+ },
180
+ appearOnScroll: {
181
+ selector: ['.js-appear-on-scroll'],
182
+ timeout: 500,
183
+ threshold: 25
184
+ },
185
+ debug: false
171
186
  });
172
187
  ```
173
188
  Usage with Appear-On-Scroll (HTML):
@@ -17,31 +17,39 @@
17
17
  *
18
18
  * @author Steffen Kroggel <developer@steffenkroggel.de>
19
19
  * @copyright 2025 Steffen Kroggel
20
- * @version 1.0.0
20
+ * @version 2.0.1
21
21
  * @license GNU General Public License v3.0
22
22
  * @see https://www.gnu.org/licenses/gpl-3.0.en.html
23
23
  *
24
24
  * @example:
25
- * const owlThumbnail = new Madj2kOwlThumbnail('.js-main-carousel', '.js-thumbs-carousel', {
26
- * main: {
27
- * items: 1,
28
- * margin: 20,
29
- * dots: true,
30
- * nav: true,
31
- * autoHeight: true
32
- * },
33
- * thumbs: {
34
- * items: 3,
35
- * margin: 10,
36
- * dots: false,
37
- * nav: true,
38
- * center: true
39
- * },
40
- * resizeEvent: 'custom.resize',
41
- * equalizeThumbHeights: true,
42
- * noStageOffset: true
43
- * }
44
- * });
25
+ * $('.js-slider-container').each(function () {
26
+ * const owlThumbnail = new Madj2kOwlThumbnail(
27
+ * '.js-main-carousel',
28
+ * '.js-thumbs-carousel',
29
+ * {
30
+ * main: {
31
+ * items: 1,
32
+ * margin: 20,
33
+ * dots: true,
34
+ * nav: true,
35
+ * autoHeight: true
36
+ * },
37
+ * thumbs: {
38
+ * items: 3,
39
+ * margin: 10,
40
+ * dots: false,
41
+ * nav: true,
42
+ * center: true
43
+ * },
44
+ * resizeEvent: 'custom.resize',
45
+ * equalizeThumbHeights: true,
46
+ * noStageOffset: true
47
+ * },
48
+ * false,
49
+ * this
50
+ * });
51
+ * });
52
+ *
45
53
  * HTML example without data attributes:
46
54
  * <div class="js-main-carousel owl-carousel">
47
55
  * <div class="item"><img src="image1.jpg" alt=""></div>
@@ -68,10 +76,20 @@
68
76
  *
69
77
  */
70
78
  class Madj2kOwlThumbnail {
71
- constructor(mainSelector, thumbSelector, options = {}, debug = false) {
79
+ /**
80
+ * Creates a new OwlThumbnail instance
81
+ * @param {string} mainSelector - CSS selector for main carousel element
82
+ * @param {string} thumbSelector - CSS selector for thumbnail carousel element
83
+ * @param {Object} options - Configuration options
84
+ * @param {boolean} debug - Enables debug logging
85
+ * @param {Document} container - DOM container to use for event listeners
86
+ */
87
+ constructor(mainSelector, thumbSelector, options = {}, debug = false, container = document) {
72
88
  this.debug = debug;
73
- this.$main = $(mainSelector);
74
- this.$thumbs = $(thumbSelector);
89
+
90
+ this.$context = $(container);
91
+ this.$main = this.$context.find(mainSelector);
92
+ this.$thumbs = this.$context.find(thumbSelector);
75
93
  this.options = options;
76
94
  this.syncing = false;
77
95
 
@@ -12,7 +12,7 @@
12
12
  *
13
13
  * @author Steffen Kroggel <developer@steffenkroggel.de>
14
14
  * @copyright 2025 Steffen Kroggel
15
- * @version 2.0.1 – Added debug option + log helper, changed anchorScrollingDisableSelector
15
+ * @version 2.0.1
16
16
  * @license GNU General Public License v3.0
17
17
  * @see https://www.gnu.org/licenses/gpl-3.0.en.html
18
18
  *
@@ -23,14 +23,22 @@
23
23
  * @example
24
24
  * // Initialize with custom config
25
25
  * const scrolling = new Madj2kScrolling({
26
- * anchorScrollingCollapsibleSelector: ['.collapse', '.custom-collapse'],
27
- * anchorScrollingSelector: ['a[href^="#"]', '.btn-scroll'],
28
- * anchorScrollingOffsetSelector: '#siteheader',
29
- * anchorScrollingDisableSelector: '.js-no-scroll',
30
- * appearOnScrollSelector: ['.js-appear-on-scroll'],
31
- * appearOnScrollTimeout: 500,
32
- * appearOnScrollThreshold: 25,
33
- * debug: true
26
+ * anchorScrolling: {
27
+ * selector: ['a[href^="#"]'],
28
+ * offsetSelector: null,
29
+ * disableSelector: '.js-no-scroll',
30
+ * collapsibleSelector: ['.collapse'],
31
+ * behavior: 'smooth',
32
+ * scriptScrollTimeout: 800,
33
+ * timeout: 500,
34
+ * threshold: 40
35
+ * },
36
+ * appearOnScroll: {
37
+ * selector: ['.js-appear-on-scroll'],
38
+ * timeout: 500,
39
+ * threshold: 25
40
+ * },
41
+ * debug: false
34
42
  * });
35
43
  *
36
44
  * @example
@@ -64,20 +72,47 @@
64
72
 
65
73
  class Madj2kScrolling {
66
74
  config = {
67
- anchorScrollingSelector: ['a[href^="#"]'],
68
- anchorScrollingOffsetSelector: '',
69
- anchorScrollingScriptScrollTimeout: 800,
70
- anchorScrollingDisableSelector: '.js-no-scroll',
71
- anchorScrollingCollapsibleSelector: ['.collapse'],
72
- anchorScrollingBehavior: 'smooth',
73
- appearOnScrollSelector: ['.js-appear-on-scroll'],
74
- appearOnScrollTimeout: 500,
75
- appearOnScrollThreshold: 25,
75
+ anchorScrolling: {
76
+ selector: ['a[href^="#"]'],
77
+ offsetSelector: null,
78
+ disableSelector: '.js-no-scroll',
79
+ collapsibleSelector: ['.collapse'],
80
+ behavior: 'smooth',
81
+ scriptScrollTimeout: 800,
82
+ timeout: 500,
83
+ threshold: 40
84
+ },
85
+ appearOnScroll: {
86
+ selector: ['.js-appear-on-scroll'],
87
+ timeout: 500,
88
+ threshold: 25
89
+ },
76
90
  debug: false
77
91
  };
78
92
 
93
+
94
+ /**
95
+ *
96
+ * @param config
97
+ */
79
98
  constructor(config) {
80
- this.config = { ...this.config, ...config };
99
+
100
+ // backwards compatibility
101
+ this._normalizeNestedConfig(config, 'anchorScrolling', 'anchorScrolling');
102
+ this._normalizeNestedConfig(config, 'appearOnScroll', 'appearOnScroll');
103
+
104
+ this.config = {
105
+ ...this.config,
106
+ ...config,
107
+ anchorScrolling: {
108
+ ...this.config.anchorScrolling,
109
+ ...config.anchorScrolling
110
+ },
111
+ appearOnScroll: {
112
+ ...this.config.appearOnScroll,
113
+ ...config.appearOnScroll
114
+ }
115
+ };
81
116
 
82
117
  this.lastScrollTop = window.scrollY;
83
118
  this.lastContentHeight = document.documentElement.scrollHeight;
@@ -89,6 +124,29 @@ class Madj2kScrolling {
89
124
  this.initAppearOnScroll();
90
125
  }
91
126
 
127
+ /**
128
+ * Converts flat config keys starting with a prefix into nested objects.
129
+ * E.g., "anchorScrollingOffsetSelector" → config.anchorScrolling.offsetSelector
130
+ * @param {Object} config - The config object to normalize
131
+ * @param {String} prefix - The prefix to search for (e.g., "anchorScrolling")
132
+ * @param {String} targetKey - The target key to write into (e.g., "anchorScrolling")
133
+ * @private
134
+ */
135
+ _normalizeNestedConfig(config, prefix, targetKey) {
136
+ const prefixLength = prefix.length;
137
+
138
+ Object.keys(config).forEach((key) => {
139
+ if (key.startsWith(prefix) && key.length > prefixLength) {
140
+ const subKey = key.substring(prefixLength);
141
+ const camelCaseKey = subKey.charAt(0).toLowerCase() + subKey.slice(1);
142
+
143
+ config[targetKey] ??= {};
144
+ config[targetKey][camelCaseKey] = config[key];
145
+ delete config[key];
146
+ }
147
+ });
148
+ }
149
+
92
150
  /**
93
151
  * Adds scroll classes to body based on scroll direction
94
152
  * @private
@@ -132,13 +190,14 @@ class Madj2kScrolling {
132
190
  * @private
133
191
  */
134
192
  initAnchorScrolling() {
135
- const offsetElement = document.querySelector(this.config.anchorScrollingOffsetSelector);
193
+
136
194
  let scriptScrollTimer = null;
195
+ const offsetElement = document.querySelector(this.config.anchorScrolling.offsetSelector);
137
196
 
138
197
  const scrollToElement = (element) => {
139
198
  if (element) {
140
199
  const rect = element.getBoundingClientRect();
141
- let scrollTo = window.scrollY + rect.top - 40;
200
+ let scrollTo = window.scrollY + rect.top - this.config.anchorScrolling.threshold;
142
201
 
143
202
  if (offsetElement && offsetElement.offsetTop >= 0 && !offsetElement.hidden) {
144
203
  scrollTo -= offsetElement.offsetHeight;
@@ -151,11 +210,11 @@ class Madj2kScrolling {
151
210
 
152
211
  scriptScrollTimer = setTimeout(() => {
153
212
  document.body.classList.remove('block-scroll-classes');
154
- }, this.config.anchorScrollingScriptScrollTimeout);
213
+ }, this.config.anchorScrolling.scriptScrollTimeout);
155
214
 
156
215
  window.scrollTo({
157
216
  top: scrollTo,
158
- behavior: this.config.anchorScrollingBehavior
217
+ behavior: this.config.anchorScrolling.behavior
159
218
  });
160
219
 
161
220
  this._log('Anchor scroll to:', element);
@@ -163,11 +222,13 @@ class Madj2kScrolling {
163
222
  };
164
223
 
165
224
  const jumpToAnchorByUrl = () => {
166
- const anchorName = window.location.hash.replace('#', '');
167
- if (anchorName) {
168
- const anchor = document.querySelector(`a[id="${anchorName}"], #${anchorName}`);
169
- if (anchor) scrollToElement(anchor);
170
- }
225
+ setTimeout(() => {
226
+ const anchorName = window.location.hash.replace('#', '');
227
+ if (anchorName) {
228
+ const anchor = document.querySelector(`a[id="${anchorName}"], #${anchorName}`);
229
+ if (anchor) scrollToElement(anchor);
230
+ }
231
+ }, this.config.anchorScrolling.timeout);
171
232
  };
172
233
 
173
234
  const jumpToAnchorByLink = (event) => {
@@ -181,14 +242,14 @@ class Madj2kScrolling {
181
242
  };
182
243
 
183
244
  const getAnchorSelector = () => {
184
- return this.config.anchorScrollingSelector
185
- .map(sel => `${sel}:not(.visually-hidden-focusable):not(${this.config.anchorScrollingDisableSelector})`)
245
+ return this.config.anchorScrolling.selector
246
+ .map(sel => `${sel}:not(.visually-hidden-focusable):not(${this.config.anchorScrolling.disableSelector})`)
186
247
  .join(', ');
187
248
  };
188
249
 
189
250
  const getCollapsibleSelector = () => {
190
- return this.config.anchorScrollingCollapsibleSelector
191
- .map(sel => `${sel}:not(${this.config.anchorScrollingDisableSelector})`)
251
+ return this.config.anchorScrolling.collapsibleSelector
252
+ .map(sel => `${sel}:not(${this.config.anchorScrolling.disableSelector})`)
192
253
  .join(', ');
193
254
  };
194
255
 
@@ -238,7 +299,7 @@ class Madj2kScrolling {
238
299
  };
239
300
 
240
301
  const getAppearOnScrollSelector = () => {
241
- return this.config.appearOnScrollSelector.join(', ');
302
+ return this.config.appearOnScroll.selector.join(', ');
242
303
  };
243
304
 
244
305
  const updateOnScroll = () => {