@madj2k/fe-frontend-kit 2.0.0

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 (40) hide show
  1. package/index.js +7 -0
  2. package/index.scss +6 -0
  3. package/menus/flyout-menu/flyout-menu-2.0.0.js +331 -0
  4. package/menus/flyout-menu/flyout-menu-2.0.0.scss +47 -0
  5. package/menus/flyout-menu/index.js +2 -0
  6. package/menus/flyout-menu/index.scss +1 -0
  7. package/menus/pulldown-menu/index.js +2 -0
  8. package/menus/pulldown-menu/index.scss +1 -0
  9. package/menus/pulldown-menu/pulldown-menu-2.0.0.js +196 -0
  10. package/menus/pulldown-menu/pulldown-menu-2.0.0.scss +33 -0
  11. package/package.json +31 -0
  12. package/readme.md +218 -0
  13. package/sass/bootstrap-5.3.0/00_mixins/_accessibility.scss +42 -0
  14. package/sass/bootstrap-5.3.0/00_mixins/_colors.scss +99 -0
  15. package/sass/bootstrap-5.3.0/00_mixins/_effects.scss +45 -0
  16. package/sass/bootstrap-5.3.0/00_mixins/_flex-box.scss +104 -0
  17. package/sass/bootstrap-5.3.0/00_mixins/_form.scss +164 -0
  18. package/sass/bootstrap-5.3.0/00_mixins/_format.scss +208 -0
  19. package/sass/bootstrap-5.3.0/00_mixins/_icons.scss +129 -0
  20. package/sass/bootstrap-5.3.0/00_mixins/_nav.scss +327 -0
  21. package/sass/bootstrap-5.3.0/00_mixins/_page.scss +261 -0
  22. package/sass/bootstrap-5.3.0/00_mixins/_section.scss +111 -0
  23. package/sass/bootstrap-5.3.0/00_mixins/_toggle-list.scss +133 -0
  24. package/sass/bootstrap-5.3.0/00_mixins/_unit.scss +51 -0
  25. package/sass/bootstrap-5.3.0/10_config/_colors.scss +17 -0
  26. package/sass/bootstrap-5.3.0/10_config/_font.scss +228 -0
  27. package/sass/bootstrap-5.3.0/10_config/_maps.scss +51 -0
  28. package/sass/bootstrap-5.3.0/index.scss +20 -0
  29. package/tools/owl/index.js +2 -0
  30. package/tools/owl/index.scss +1 -0
  31. package/tools/owl/owl-thumbnail-2.0.0.js +355 -0
  32. package/tools/owl/owl-thumbnail-2.0.0.scss +0 -0
  33. package/tools/resize-end/index.js +2 -0
  34. package/tools/resize-end/index.scss +1 -0
  35. package/tools/resize-end/resize-end-2.0.0.js +108 -0
  36. package/tools/resize-end/resize-end-2.0.0.scss +10 -0
  37. package/tools/scrolling/index.js +2 -0
  38. package/tools/scrolling/index.scss +1 -0
  39. package/tools/scrolling/scrolling-2.0.0.js +244 -0
  40. package/tools/scrolling/scrolling-2.0.0.scss +10 -0
@@ -0,0 +1,228 @@
1
+ /* ==================================================
2
+ * Font variables
3
+ * ================================================== */
4
+ // fonts
5
+ $font-family-sans: "Verdana", "Arial", sans-serif !default;
6
+ $font-family-sans-bold: "Verdana", "Arial", sans-serif !default;
7
+
8
+ $font-weight-lighter: lighter !default;
9
+ $font-weight-light: 300 !default;
10
+ $font-weight-normal: 400 !default;
11
+ $font-weight-medium: 500 !default;
12
+ $font-weight-semibold: 600 !default;
13
+ $font-weight-bold: 700 !default;
14
+ $font-weight-bolder: bolder !default;
15
+
16
+ $font-formats: (
17
+ 'h1': (
18
+ 'desktop': (
19
+ 'font-family': $font-family-sans,
20
+ 'font-weight': $font-weight-normal,
21
+ 'font-size': rem-calc(104),
22
+ 'line-height': rem-calc(100)
23
+ ),
24
+ 'tablet': (
25
+ 'font-family': $font-family-sans,
26
+ 'font-weight': $font-weight-normal,
27
+ 'font-size': rem-calc(40),
28
+ 'line-height': rem-calc(40)
29
+ ),
30
+ 'mobile': (
31
+ 'font-family': $font-family-sans,
32
+ 'font-weight': $font-weight-normal,
33
+ 'font-size': rem-calc(40),
34
+ 'line-height': rem-calc(40)
35
+ )
36
+ ),
37
+ 'h2': (
38
+ 'desktop': (
39
+ 'font-family': $font-family-sans,
40
+ 'font-weight': $font-weight-normal,
41
+ 'font-size': rem-calc(62),
42
+ 'line-height': rem-calc(60)
43
+ ),
44
+ 'tablet': (
45
+ 'font-family': $font-family-sans,
46
+ 'font-weight': $font-weight-normal,
47
+ 'font-size': rem-calc(32),
48
+ 'line-height': rem-calc(32)
49
+ ),
50
+ 'mobile': (
51
+ 'font-family': $font-family-sans,
52
+ 'font-weight': $font-weight-normal,
53
+ 'font-size': rem-calc(32),
54
+ 'line-height': rem-calc(32)
55
+ )
56
+ ),
57
+ 'h3': (
58
+ 'desktop': (
59
+ 'font-family': $font-family-sans,
60
+ 'font-weight': $font-weight-normal,
61
+ 'font-size': rem-calc(48),
62
+ 'line-height': rem-calc(48)
63
+ ),
64
+ 'tablet': (
65
+ 'font-family': $font-family-sans,
66
+ 'font-weight': $font-weight-normal,
67
+ 'font-size': rem-calc(26),
68
+ 'line-height': rem-calc(26)
69
+ ),
70
+ 'mobile': (
71
+ 'font-family': $font-family-sans,
72
+ 'font-weight': $font-weight-normal,
73
+ 'font-size': rem-calc(26),
74
+ 'line-height': rem-calc(26)
75
+ )
76
+ ),
77
+ 'h4': (
78
+ 'desktop': (
79
+ 'font-family': $font-family-sans,
80
+ 'font-weight': $font-weight-normal,
81
+ 'font-size': rem-calc(36),
82
+ 'line-height': rem-calc(36)
83
+ ),
84
+ 'tablet': (
85
+ 'font-family': $font-family-sans,
86
+ 'font-weight': $font-weight-normal,
87
+ 'font-size': rem-calc(22),
88
+ 'line-height': rem-calc(22)
89
+ ),
90
+ 'mobile': (
91
+ 'font-family': $font-family-sans,
92
+ 'font-weight': $font-weight-normal,
93
+ 'font-size': rem-calc(22),
94
+ 'line-height': rem-calc(22)
95
+ )
96
+ ),
97
+ 'h5': (
98
+ 'desktop': (
99
+ 'font-family': $font-family-sans,
100
+ 'font-weight': $font-weight-normal,
101
+ 'font-size': rem-calc(28),
102
+ 'line-height': rem-calc(28)
103
+ ),
104
+ 'tablet': (
105
+ 'font-family': $font-family-sans,
106
+ 'font-weight': $font-weight-normal,
107
+ 'font-size': rem-calc(18),
108
+ 'line-height': rem-calc(18)
109
+ ),
110
+ 'mobile': (
111
+ 'font-family': $font-family-sans,
112
+ 'font-weight': $font-weight-normal,
113
+ 'font-size': rem-calc(18),
114
+ 'line-height': rem-calc(18)
115
+ )
116
+ ),
117
+ 'h6': (
118
+ 'desktop': (
119
+ 'font-family': $font-family-sans,
120
+ 'font-weight': $font-weight-normal,
121
+ 'font-size': rem-calc(22),
122
+ 'line-height': rem-calc(22)
123
+ ),
124
+ 'tablet': (
125
+ 'font-family': $font-family-sans,
126
+ 'font-weight': $font-weight-normal,
127
+ 'font-size': rem-calc(16),
128
+ 'line-height': rem-calc(16)
129
+ ),
130
+ 'mobile': (
131
+ 'font-family': $font-family-sans,
132
+ 'font-weight': $font-weight-normal,
133
+ 'font-size': rem-calc(16),
134
+ 'line-height': rem-calc(16)
135
+ )
136
+ ),
137
+ 'copy': (
138
+ 'desktop': (
139
+ 'font-family': $font-family-sans,
140
+ 'font-weight': $font-weight-normal,
141
+ 'font-size': rem-calc(20),
142
+ 'line-height': rem-calc(27)
143
+ ),
144
+ 'tablet': (
145
+ 'font-family': $font-family-sans,
146
+ 'font-weight': $font-weight-normal,
147
+ 'font-size': rem-calc(16),
148
+ 'line-height': rem-calc(24)
149
+ ),
150
+ 'mobile': (
151
+ 'font-family': $font-family-sans,
152
+ 'font-weight': $font-weight-normal,
153
+ 'font-size': rem-calc(16),
154
+ 'line-height': rem-calc(24)
155
+ )
156
+ ),
157
+ 'copy-small': (
158
+ 'desktop': (
159
+ 'font-family': $font-family-sans,
160
+ 'font-weight': $font-weight-normal,
161
+ 'font-size': rem-calc(16),
162
+ 'line-height': rem-calc(16)
163
+ ),
164
+ 'tablet': (
165
+ 'font-family': $font-family-sans,
166
+ 'font-weight': $font-weight-normal,
167
+ 'font-size': rem-calc(14),
168
+ 'line-height': rem-calc(14)
169
+ ),
170
+ 'mobile': (
171
+ 'font-family': $font-family-sans,
172
+ 'font-weight': $font-weight-normal,
173
+ 'font-size': rem-calc(14),
174
+ 'line-height': rem-calc(14)
175
+ )
176
+ ),
177
+ 'overline': (
178
+ 'desktop': (
179
+ 'font-family': $font-family-sans,
180
+ 'font-weight': $font-weight-normal,
181
+ 'font-size': rem-calc(28),
182
+ 'line-height': rem-calc(28)
183
+ ),
184
+ 'tablet': (
185
+ 'font-family': $font-family-sans,
186
+ 'font-weight': $font-weight-normal,
187
+ 'font-size': rem-calc(18),
188
+ 'line-height': rem-calc(18)
189
+ ),
190
+ 'mobile': (
191
+ 'font-family': $font-family-sans,
192
+ 'font-weight': $font-weight-normal,
193
+ 'font-size': rem-calc(18),
194
+ 'line-height': rem-calc(18)
195
+ )
196
+ ),
197
+ 'subheader': (
198
+ 'desktop': (
199
+ 'font-family': $font-family-sans,
200
+ 'font-weight': $font-weight-normal,
201
+ 'font-size': rem-calc(28),
202
+ 'line-height': rem-calc(28)
203
+ ),
204
+ 'tablet': (
205
+ 'font-family': $font-family-sans,
206
+ 'font-weight': $font-weight-normal,
207
+ 'font-size': rem-calc(18),
208
+ 'line-height': rem-calc(18)
209
+ ),
210
+ 'mobile': (
211
+ 'font-family': $font-family-sans,
212
+ 'font-weight': $font-weight-normal,
213
+ 'font-size': rem-calc(18),
214
+ 'line-height': rem-calc(18)
215
+ )
216
+ ),
217
+ 'link': (
218
+ 'default': (
219
+ 'font-family': $font-family-sans,
220
+ ),
221
+ ),
222
+ 'bold': (
223
+ 'default': (
224
+ 'font-family': $font-family-sans-bold,
225
+ 'font-weight': 500
226
+ ),
227
+ ),
228
+ ) !default;
@@ -0,0 +1,51 @@
1
+ /* ==================================================
2
+ * Basic maps
3
+ * ================================================== */
4
+ @use 'sass:math';
5
+
6
+ $page-padding: (
7
+ xs: $spacer,
8
+ sm: $spacer, // mobile
9
+ md: $spacer,
10
+ lg: $spacer, // tablet
11
+ xl: $spacer,
12
+ xxl: $spacer,
13
+ xxxl: $spacer
14
+ ) !default;
15
+
16
+ $content-spacers-blocks: (
17
+ 'text',
18
+ 'text-image',
19
+ 'text-video'
20
+ ) !default;
21
+
22
+ $content-spacers: (
23
+ 'xs': (
24
+ 'section': map-get($spacers, 6), // space between different sections
25
+ 'block': map-get($spacers, 3), // space between content blocks
26
+ ),
27
+ 'sm': (
28
+ 'section': map-get($spacers, 6), // space between different sections
29
+ 'block': map-get($spacers, 3), // space between content blocks
30
+ ),
31
+ 'md': (
32
+ 'section': map-get($spacers, 6), // space between different sections
33
+ 'block': map-get($spacers, 3), // space between content blocks
34
+ ),
35
+ 'lg': (
36
+ 'section': map-get($spacers, 7), // space between different sections
37
+ 'block': map-get($spacers, 6), // space between content blocks
38
+ ),
39
+ 'xl': (
40
+ 'section': map-get($spacers, 7), // space between different sections
41
+ 'block': map-get($spacers, 6), // space between content blocks
42
+ ),
43
+ 'xxl': (
44
+ 'section': map-get($spacers, 7), // space between different sections
45
+ 'block': map-get($spacers, 6), // space between content blocks
46
+ ),
47
+ 'xxxl': (
48
+ 'section': map-get($spacers, 7), // space between different sections
49
+ 'block': map-get($spacers, 6), // space between content blocks
50
+ )
51
+ ) !default;
@@ -0,0 +1,20 @@
1
+ @use "bootstrap/scss/functions";
2
+ @use "bootstrap/scss/variables";
3
+ @use "bootstrap/scss/mixins";
4
+
5
+ @forward '00_mixins/accessibility';
6
+ @forward '00_mixins/colors';
7
+ @forward '00_mixins/effects';
8
+ @forward '00_mixins/flex-box';
9
+ @forward '00_mixins/form';
10
+ @forward '00_mixins/format';
11
+ @forward '00_mixins/icons';
12
+ @forward '00_mixins/nav';
13
+ @forward '00_mixins/page';
14
+ @forward '00_mixins/section';
15
+ @forward '00_mixins/toggle-list';
16
+ @forward '00_mixins/unit';
17
+
18
+ @forward '10_config/colors';
19
+ @forward '10_config/font';
20
+ @forward '10_config/maps';
@@ -0,0 +1,2 @@
1
+ import Madj2kOwlThumbnail from './owl-thumbnail-2.0.0.js';
2
+ export { Madj2kOwlThumbnail };
@@ -0,0 +1 @@
1
+ @forward './owl-thumbnail-2.0.0';
@@ -0,0 +1,355 @@
1
+ /**!
2
+ * Owl with thumbnails (requires jQuery and OWL Carousel)
3
+ *
4
+ * A JavaScript helper class to create a main OWL carousel with a synchronized thumbnail navigation carousel.
5
+ *
6
+ * Supports flexible configurations for both the main and the thumbnail carousels,
7
+ * including:
8
+ * - Custom navigation
9
+ * - Syncing main carousel to clicked thumbnails
10
+ * - Syncing thumbnails to main carousel changes
11
+ * - Optionally equalizing thumbnail heights
12
+ * - Optionally preventing the centered thumbnail stage shift (for designs where thumbs should stay left aligned)
13
+ * - Responsive and CMS-friendly (works with dynamic content)
14
+ *
15
+ * This is especially useful for image galleries or product carousels in CMS setups (e.g. TYPO3, WordPress, etc.)
16
+ * where content may change dynamically.
17
+ *
18
+ * @author Steffen Kroggel <developer@steffenkroggel.de>
19
+ * @copyright 2025 Steffen Kroggel
20
+ * @version 1.0.0
21
+ * @license GNU General Public License v3.0
22
+ * @see https://www.gnu.org/licenses/gpl-3.0.en.html
23
+ *
24
+ * HTML example without data attributes:
25
+ * <div class="js-main-carousel owl-carousel">
26
+ * <div class="item"><img src="image1.jpg" alt=""></div>
27
+ * <div class="item"><img src="image2.jpg" alt=""></div>
28
+ * <div class="item"><img src="image3.jpg" alt=""></div>
29
+ * </div>
30
+ * <div class="js-thumbs-carousel owl-carousel">
31
+ * <div class="item"><img src="thumb1.jpg" alt=""></div>
32
+ * <div class="item"><img src="thumb2.jpg" alt=""></div>
33
+ * <div class="item"><img src="thumb3.jpg" alt=""></div>
34
+ * </div>
35
+ *
36
+ * HTML example with data attributes:
37
+ * <div class="js-main-carousel owl-carousel">
38
+ * <div class="item"><img src="image1.jpg" alt=""></div>
39
+ * <div class="item"><img src="image2.jpg" alt=""></div>
40
+ * <div class="item"><img src="image3.jpg" alt=""></div>
41
+ * </div>
42
+ * <div class="js-thumbs-carousel owl-carousel">
43
+ * <div class="item" data-index="0"><img src="thumb1.jpg" alt=""></div>
44
+ * <div class="item" data-index="1"><img src="thumb2.jpg" alt=""></div>
45
+ * <div class="item" data-index="2"><img src="thumb3.jpg" alt=""></div>
46
+ * </div>
47
+ *
48
+ * @example:
49
+ *
50
+ * const owlThumbnail = new Madj2kOwlThumbnail('.js-main-carousel', '.js-thumbs-carousel', {
51
+ * main: {
52
+ * items: 1,
53
+ * margin: 20,
54
+ * dots: true,
55
+ * nav: true,
56
+ * autoHeight: true
57
+ * },
58
+ * thumbs: {
59
+ * items: 3,
60
+ * margin: 10,
61
+ * dots: false,
62
+ * nav: true,
63
+ * center: true
64
+ * },
65
+ * resizeEvent: 'custom.resize',
66
+ * equalizeThumbHeights: true,
67
+ * noStageOffset: true
68
+ * }
69
+ * });
70
+ */
71
+ class Madj2kOwlThumbnail {
72
+ constructor(mainSelector, thumbSelector, options = {}, debug = false) {
73
+ this.debug = debug;
74
+ this.$main = $(mainSelector);
75
+ this.$thumbs = $(thumbSelector);
76
+ this.options = options;
77
+ this.syncing = false;
78
+
79
+ if (!this.$main.length) {
80
+ this._log('Error: Main carousel element not found');
81
+ return;
82
+ }
83
+ if (!this.$thumbs.length) {
84
+ this._log('Error: Thumbnail carousel element not found');
85
+ return;
86
+ }
87
+
88
+ this._log('Initializing OwlThumbnail with elements:', {main: mainSelector, thumbs: thumbSelector});
89
+ this._init();
90
+
91
+ if (this.options.resizeEvent) {
92
+ this._log('Setting up resize event listener:', this.options.resizeEvent);
93
+ $(document).on(this.options.resizeEvent, () => this._reinit());
94
+ }
95
+ }
96
+ /**
97
+ * Initializes both main and thumbnail carousels with their configurations
98
+ * Sets up event handlers and syncs initial state
99
+ * @private
100
+ */
101
+ _init() {
102
+ const defaultMain = {
103
+ items: 1,
104
+ margin: 20,
105
+ dots: true,
106
+ nav: true,
107
+ autoHeight: true,
108
+ loop: false,
109
+ onChanged: this._syncThumbs.bind(this)
110
+ };
111
+
112
+ // Erweitere die Standard-Thumbnail-Konfiguration
113
+ const defaultThumbs = {
114
+ items: 3,
115
+ margin: 10,
116
+ dots: false,
117
+ nav: true,
118
+ center: true,
119
+ loop: false,
120
+ onInitialized: (event) => {
121
+ this._highlightInitialThumb(event);
122
+ this._equalizeThumbnailHeights();
123
+ this._repositionThumbNav();
124
+ },
125
+ onRefreshed: () => {
126
+ this._equalizeThumbnailHeights();
127
+ },
128
+ onResized: () => {
129
+ this._equalizeThumbnailHeights();
130
+ }
131
+ };
132
+
133
+ this.configMain = {...defaultMain, ...this.options.main, loop: false};
134
+ this.configThumbs = {...defaultThumbs, ...this.options.thumbs, loop: false};
135
+
136
+ this._log('Initializing main carousel with config:', this.configMain);
137
+ this.$main.owlCarousel(this.configMain);
138
+
139
+ this._log('Initializing thumbnail carousel with config:', this.configThumbs);
140
+ this.$thumbs.owlCarousel(this.configThumbs);
141
+
142
+ this._log('Binding event handlers');
143
+ this._bindThumbClick();
144
+ this._bindThumbNav();
145
+ }
146
+
147
+ /**
148
+ * Equalizes the height of all thumbnail items to match the height of the tallest item
149
+ * @private
150
+ * @returns {Promise<void>}
151
+ */
152
+ async _equalizeThumbnailHeights() {
153
+ if (!this.options.equalizeThumbHeights) return;
154
+
155
+ const $thumbItems = this.$thumbs.find('.owl-item');
156
+ const $images = $thumbItems.find('img');
157
+
158
+ // Wait until all images are loaded
159
+ try {
160
+ await Promise.all(
161
+ Array.from($images).map(img => {
162
+ if (img.complete) return Promise.resolve();
163
+ return new Promise((resolve, reject) => {
164
+ img.onload = resolve;
165
+ img.onerror = reject;
166
+ // Timeout nach 5 Sekunden
167
+ setTimeout(reject, 5000);
168
+ });
169
+ })
170
+ );
171
+
172
+ // Reset height
173
+ $thumbItems.css('height', 'auto');
174
+
175
+ // find maximum height
176
+ const maxHeight = Math.max(...$thumbItems.map((_, el) => $(el).outerHeight()).get());
177
+
178
+ // Set height
179
+ $thumbItems.css('height', maxHeight + 'px');
180
+
181
+ this._log('Thumbnail heights equalized to:', maxHeight);
182
+ } catch (error) {
183
+ this._log('Error equalizing thumbnail heights:', error);
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Reinitializes both main and thumbnail carousels after destroying them
189
+ * Used when owl carousels need to be rebuilt, e.g. after window resize
190
+ * @private
191
+ */
192
+ _reinit() {
193
+ this.$main.trigger('destroy.owl.carousel');
194
+ this.$thumbs.trigger('destroy.owl.carousel');
195
+
196
+ this.$main.owlCarousel(this.configMain);
197
+ this.$thumbs.owlCarousel(this.configThumbs);
198
+
199
+ this._bindThumbClick();
200
+ this._bindThumbNav();
201
+ }
202
+
203
+ /**
204
+ * Syncs thumbnail carousel position with main carousel changes
205
+ * Updates active thumbnail highlighting and centers thumbnails based on current item
206
+ * Uses page size or configured items count to determine visible thumbnails
207
+ * @param {Object} event - Owl carousel change event
208
+ * @private
209
+ */
210
+ _syncThumbs(event) {
211
+ if (this.syncing) return;
212
+ this.syncing = true;
213
+
214
+ const index = event.item.index;
215
+ this._log('Syncing thumbnails to index:', index);
216
+
217
+ let $target = this.$thumbs.find(`[data-index="${index}"]`);
218
+ if (!$target.length) {
219
+ this._log('No data-index found, falling back to index-based selection');
220
+ $target = this.$thumbs.find('.owl-item').eq(index).find('.item');
221
+ }
222
+
223
+ this.$thumbs.find('.owl-item').removeClass('current');
224
+ const $owlItem = $target.closest('.owl-item');
225
+ $owlItem.addClass('current');
226
+
227
+ const thumbItems = this.$thumbs.find('.owl-item');
228
+
229
+ // Use event.page.size for visible items, fallback to config or 3
230
+ const visible = event.page?.size || this.configThumbs.items || 3;
231
+ this._log('Visible thumbnails:', visible);
232
+
233
+ const itemIndex = $owlItem.index();
234
+ const halfVisible = Math.floor(visible / 2);
235
+ const maxIndex = thumbItems.length - visible;
236
+
237
+ let centerIndex = itemIndex - halfVisible;
238
+
239
+ // Set boundaries
240
+ if (centerIndex < 0) centerIndex = 0;
241
+ if (centerIndex > maxIndex) centerIndex = maxIndex;
242
+
243
+ this._log('Centering thumbnails to index:', centerIndex);
244
+ this.$thumbs.trigger('to.owl.carousel', [centerIndex, 300, true]);
245
+
246
+ this._repositionThumbNav(index );
247
+ this.syncing = false;
248
+ }
249
+
250
+ /**
251
+ * Binds click handler to thumbnail items
252
+ * Gets index from data attribute or item position and triggers main carousel slide
253
+ * @private
254
+ */
255
+ _bindThumbClick() {
256
+ this.$thumbs.on('click', '.owl-item', (e) => {
257
+ const $item = $(e.currentTarget);
258
+ let index = $item.find('[data-index]').data('index');
259
+ if (index === undefined) index = $item.index();
260
+
261
+ this.$main.trigger('to.owl.carousel', [index, 300, true]);
262
+ });
263
+ }
264
+
265
+ /**
266
+ * Binds navigation handlers to thumbnail carousel navigation buttons
267
+ * Prevents default scrolling and syncs with main carousel navigation
268
+ * @param {string} navClass - CSS class for navigation elements
269
+ * @private
270
+ */
271
+ _bindThumbNav(navClass = 'owl-nav') {
272
+ // Prevent default scrolling behavior for thumbnail nav buttons
273
+ this.$thumbs.find('.owl-prev, .owl-next').off('click').on('click', (e) => {
274
+ e.preventDefault();
275
+
276
+ // Get current index of main carousel
277
+ const mainData = this.$main.data('owl.carousel');
278
+ if (!mainData) return;
279
+
280
+ let newIndex;
281
+
282
+ if ($(e.currentTarget).hasClass('owl-prev')) {
283
+ // Previous slide in main carousel
284
+ newIndex = mainData.relative(mainData.current()) - 1;
285
+ if (newIndex < 0) newIndex = 0; // Kein Loop
286
+ } else if ($(e.currentTarget).hasClass('owl-next')) {
287
+ // Next slide in main carousel
288
+ newIndex = mainData.relative(mainData.current()) + 1;
289
+ if (newIndex >= mainData.items().length) newIndex = mainData.items().length - 1;
290
+ }
291
+
292
+ this.$main.trigger('to.owl.carousel', [newIndex, 300, true]);
293
+ });
294
+ }
295
+
296
+ /**
297
+ * Start thumbnails on the left side of the stage, even if they are centered!
298
+ * @private
299
+ */
300
+ _repositionThumbNav(currentIndex = 0) {
301
+ if (!this.options.noStageOffset) return;
302
+
303
+ const $thumbStage = this.$thumbs.find('.owl-stage');
304
+ const $thumbItems = this.$thumbs.find('.owl-item');
305
+ const thumbCount = $thumbItems.length;
306
+ const lastIndex = thumbCount - 1;
307
+
308
+ // first thumb aligned left
309
+ if (currentIndex === 0) {
310
+
311
+ $thumbStage.css('transform', 'translate3d(0px, 0px, 0px)');
312
+ this._log('Thumb stage repositioned: left-aligned (index 0)');
313
+
314
+ // last thumb aligned right
315
+ } else if (currentIndex === lastIndex) {
316
+ let totalWidth = 0;
317
+ $thumbItems.each((i, item) => {
318
+ totalWidth += $(item).outerWidth(true); // inklusive margin
319
+ });
320
+
321
+ const containerWidth = this.$thumbs.width();
322
+ const offset = Math.min(0, containerWidth - totalWidth);
323
+
324
+ $thumbStage.css('transform', `translate3d(${offset}px, 0px, 0px)`);
325
+ this._log(`Thumb stage repositioned: right-aligned (index ${lastIndex})`);
326
+ } else {
327
+ this._log(`Thumb stage normal centered (index ${currentIndex})`);
328
+ }
329
+ }
330
+
331
+
332
+ /**
333
+ * Highlights the initial active thumbnail when thumbnails carousel is initialized
334
+ * Tries to find thumbnail by data-index first, falls back to index-based selection
335
+ * Adds 'current' class to mark the active thumbnail
336
+ * @param {Object} event - Owl carousel initialization event
337
+ * @private
338
+ */
339
+ _highlightInitialThumb(event) {
340
+ const currentIndex = event.item.index;
341
+ let $target = this.$thumbs.find(`[data-index="${currentIndex}"]`).closest('.owl-item');
342
+ if (!$target.length) $target = this.$thumbs.find('.owl-item').eq(currentIndex);
343
+ $target.addClass('current');
344
+ }
345
+
346
+ /**
347
+ * Debug logging helper
348
+ * @private
349
+ */
350
+ _log(...args) {
351
+ if (this.debug) {
352
+ console.log('[OwlThumbnail]', ...args);
353
+ }
354
+ }
355
+ }
File without changes
@@ -0,0 +1,2 @@
1
+ import Madj2kResizeEnd from './resize-end-2.0.0.js';
2
+ export { Madj2kResizeEnd };
@@ -0,0 +1 @@
1
+ @forward './resize-end-2.0.0';