@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.
- package/index.js +7 -0
- package/index.scss +6 -0
- package/menus/flyout-menu/flyout-menu-2.0.0.js +331 -0
- package/menus/flyout-menu/flyout-menu-2.0.0.scss +47 -0
- package/menus/flyout-menu/index.js +2 -0
- package/menus/flyout-menu/index.scss +1 -0
- package/menus/pulldown-menu/index.js +2 -0
- package/menus/pulldown-menu/index.scss +1 -0
- package/menus/pulldown-menu/pulldown-menu-2.0.0.js +196 -0
- package/menus/pulldown-menu/pulldown-menu-2.0.0.scss +33 -0
- package/package.json +31 -0
- package/readme.md +218 -0
- package/sass/bootstrap-5.3.0/00_mixins/_accessibility.scss +42 -0
- package/sass/bootstrap-5.3.0/00_mixins/_colors.scss +99 -0
- package/sass/bootstrap-5.3.0/00_mixins/_effects.scss +45 -0
- package/sass/bootstrap-5.3.0/00_mixins/_flex-box.scss +104 -0
- package/sass/bootstrap-5.3.0/00_mixins/_form.scss +164 -0
- package/sass/bootstrap-5.3.0/00_mixins/_format.scss +208 -0
- package/sass/bootstrap-5.3.0/00_mixins/_icons.scss +129 -0
- package/sass/bootstrap-5.3.0/00_mixins/_nav.scss +327 -0
- package/sass/bootstrap-5.3.0/00_mixins/_page.scss +261 -0
- package/sass/bootstrap-5.3.0/00_mixins/_section.scss +111 -0
- package/sass/bootstrap-5.3.0/00_mixins/_toggle-list.scss +133 -0
- package/sass/bootstrap-5.3.0/00_mixins/_unit.scss +51 -0
- package/sass/bootstrap-5.3.0/10_config/_colors.scss +17 -0
- package/sass/bootstrap-5.3.0/10_config/_font.scss +228 -0
- package/sass/bootstrap-5.3.0/10_config/_maps.scss +51 -0
- package/sass/bootstrap-5.3.0/index.scss +20 -0
- package/tools/owl/index.js +2 -0
- package/tools/owl/index.scss +1 -0
- package/tools/owl/owl-thumbnail-2.0.0.js +355 -0
- package/tools/owl/owl-thumbnail-2.0.0.scss +0 -0
- package/tools/resize-end/index.js +2 -0
- package/tools/resize-end/index.scss +1 -0
- package/tools/resize-end/resize-end-2.0.0.js +108 -0
- package/tools/resize-end/resize-end-2.0.0.scss +10 -0
- package/tools/scrolling/index.js +2 -0
- package/tools/scrolling/index.scss +1 -0
- package/tools/scrolling/scrolling-2.0.0.js +244 -0
- 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 @@
|
|
|
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 @@
|
|
|
1
|
+
@forward './resize-end-2.0.0';
|