maverick-wave 3.0.0 → 3.1.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.
@@ -0,0 +1,587 @@
1
+ (function () {
2
+ 'use strict';
3
+
4
+ // Initialize all components when DOM is ready
5
+ document.addEventListener('DOMContentLoaded', function () {
6
+ // Initialize all components
7
+ initGalleries();
8
+ initThemeToggle();
9
+ initColorSwatches();
10
+ initAccordions();
11
+ initMobileNav();
12
+ initProgressBars();
13
+ initSmoothScrolling();
14
+ initScrollSpy();
15
+ initTabs();
16
+ initAlerts();
17
+ initLocalhostIndicator();
18
+ initFormSliders();
19
+ initModals();
20
+ initHeaderLoginButton();
21
+ initImageSliders();
22
+ initCheckboxLists();
23
+ });
24
+
25
+ // ===== Checkbox Lists =====
26
+ function initCheckboxLists() {
27
+ const checkboxLists = document.querySelectorAll(
28
+ '.mw-item-list-checkbox, .mw-item-list-checkbox-scroll'
29
+ );
30
+
31
+ checkboxLists.forEach((list) => {
32
+ const listItems = list.querySelectorAll('li');
33
+
34
+ listItems.forEach((item) => {
35
+ const checkbox = item.querySelector('input[type="checkbox"]');
36
+ const label = item.querySelector('.mw-checkbox');
37
+
38
+ if (!checkbox || !label) return;
39
+
40
+ // Set initial state based on checkbox checked property
41
+ if (checkbox.checked) {
42
+ item.classList.add('mw-selected');
43
+ }
44
+
45
+ // Add click handler to the entire list item
46
+ item.addEventListener('click', function (e) {
47
+ if (
48
+ e.target === checkbox ||
49
+ e.target.closest('.mw-checkbox') === label
50
+ ) {
51
+ return;
52
+ }
53
+ toggleCheckbox(this);
54
+ });
55
+
56
+ // Add change handler to the checkbox itself
57
+ checkbox.addEventListener('change', function () {
58
+ const listItem = this.closest('li');
59
+ if (this.checked) {
60
+ listItem.classList.add('mw-selected');
61
+ } else {
62
+ listItem.classList.remove('mw-selected');
63
+ }
64
+
65
+ // Dispatch custom event
66
+ listItem.dispatchEvent(
67
+ new CustomEvent('checkboxToggle', {
68
+ detail: { checked: this.checked, item: listItem },
69
+ })
70
+ );
71
+ });
72
+
73
+ // Add label click handler
74
+ label.addEventListener('click', function (e) {
75
+ setTimeout(() => {
76
+ const listItem = this.closest('li');
77
+ const checkbox = this.querySelector('input[type="checkbox"]');
78
+
79
+ if (checkbox.checked) {
80
+ listItem.classList.add('mw-selected');
81
+ } else {
82
+ listItem.classList.remove('mw-selected');
83
+ }
84
+ }, 0);
85
+ });
86
+ });
87
+ });
88
+
89
+ // Listen for custom events (optional - for debugging or external handling)
90
+ document.addEventListener('checkboxToggle', function (event) {
91
+ console.log('Checkbox toggled:', event.detail.checked, event.detail.item);
92
+ });
93
+ }
94
+
95
+ // Global function for manual checkbox toggling (for onclick attributes)
96
+ window.toggleCheckbox = function (listItem) {
97
+ const checkbox = listItem.querySelector('input[type="checkbox"]');
98
+ if (!checkbox) return;
99
+
100
+ const isChecked = checkbox.checked;
101
+
102
+ // Toggle checkbox
103
+ checkbox.checked = !isChecked;
104
+
105
+ // Add/remove selected class for visual feedback
106
+ if (checkbox.checked) {
107
+ listItem.classList.add('mw-selected');
108
+ } else {
109
+ listItem.classList.remove('mw-selected');
110
+ }
111
+
112
+ // Dispatch custom event for external handling
113
+ listItem.dispatchEvent(
114
+ new CustomEvent('checkboxToggle', {
115
+ detail: { checked: checkbox.checked, item: listItem },
116
+ })
117
+ );
118
+ };
119
+
120
+ // ===== Gallery Component =====
121
+ function initGalleries() {
122
+ const track = document.querySelector('.mw-gallery-track');
123
+
124
+ if (track) {
125
+ const dots = document.querySelector('.mw-gallery-dots');
126
+ const slides = track.children;
127
+ const descBox = document.querySelector('.mw-gallery-desc');
128
+ const prevBtn = document.querySelector('.mw-gallery-navi-prev');
129
+ const nextBtn = document.querySelector('.mw-gallery-navi-next');
130
+ const sliderContainer = document.querySelector('.mw-gallery');
131
+
132
+ let current = 0;
133
+
134
+ function updateSliderPosition() {
135
+ const slideWidth = sliderContainer.offsetWidth;
136
+ track.style.transform = `translateX(-${current * slideWidth}px)`;
137
+ }
138
+
139
+ function goToSlide(index) {
140
+ const total = slides.length;
141
+ current = (index + total) % total;
142
+ updateSliderPosition();
143
+
144
+ document.querySelectorAll('.mw-gallery-dot').forEach((dot, i) => {
145
+ dot.classList.toggle('mw-active', i === current);
146
+ });
147
+
148
+ descBox.textContent = slides[current].dataset.desc;
149
+ }
150
+
151
+ function initDots() {
152
+ for (let i = 0; i < slides.length; i++) {
153
+ const dot = document.createElement('span');
154
+ dot.className = 'mw-gallery-dot' + (i === 0 ? ' mw-active' : '');
155
+ dot.addEventListener('click', () => goToSlide(i));
156
+ dots.appendChild(dot);
157
+ }
158
+ }
159
+
160
+ prevBtn.addEventListener('click', () => goToSlide(current - 1));
161
+ nextBtn.addEventListener('click', () => goToSlide(current + 1));
162
+
163
+ window.addEventListener('resize', updateSliderPosition);
164
+
165
+ // Optional: Swipe support
166
+ let startX = 0;
167
+ track.addEventListener(
168
+ 'touchstart',
169
+ (e) => (startX = e.touches[0].clientX)
170
+ );
171
+ track.addEventListener('touchend', (e) => {
172
+ const delta = e.changedTouches[0].clientX - startX;
173
+ if (delta > 50) goToSlide(current - 1);
174
+ if (delta < -50) goToSlide(current + 1);
175
+ });
176
+
177
+ initDots();
178
+ updateSliderPosition();
179
+ }
180
+ }
181
+
182
+ // ===== Theme Toggle =====
183
+ function initThemeToggle() {
184
+ const themeToggle = document.querySelector('.mw-theme-toggle');
185
+ if (!themeToggle) return;
186
+
187
+ const body = document.body;
188
+ const icon = themeToggle.querySelector('.mw-theme-toggle-slider i');
189
+
190
+ const computedStyle = getComputedStyle(document.documentElement);
191
+ const themeMode =
192
+ computedStyle.getPropertyValue('--mw-internal-theme-mode').trim() ||
193
+ 'switchable';
194
+
195
+ // mode dark or light
196
+ if (themeMode !== 'switchable') {
197
+ console.log(`Theme mode fixed to: ${themeMode}. Disabling toggle.`);
198
+ if (icon) {
199
+ icon.className = themeMode === 'light' ? 'fas fa-sun' : 'fas fa-moon';
200
+ }
201
+ // Adjust toggle 'active' state if needed (assuming 'active' shows sun)
202
+ if (themeMode === 'light') {
203
+ themeToggle.classList.add('active');
204
+ } else {
205
+ themeToggle.classList.remove('active');
206
+ }
207
+
208
+ // Disable existing toggle visually and functionally
209
+ themeToggle.style.opacity = '0.4';
210
+ themeToggle.style.pointerEvents = 'none';
211
+ themeToggle.style.cursor = 'default';
212
+ themeToggle.setAttribute('aria-disabled', 'true');
213
+
214
+ // Clean up potentially conflicting localStorage
215
+ localStorage.removeItem('mw-theme');
216
+
217
+ // Ensure body class is correct for fixed mode (remove light if fixed dark)
218
+ if (themeMode === 'dark') {
219
+ body.classList.remove('mw-theme-light');
220
+ }
221
+
222
+ return;
223
+ }
224
+
225
+ // mode switchable - only runs if themeMode === 'switchable'
226
+ let isLight = localStorage.getItem('mw-theme') === 'light';
227
+
228
+ // Function to apply theme styles and icon
229
+ const applyTheme = (lightMode) => {
230
+ // Use toggle's second argument for cleaner class switching
231
+ body.classList.toggle('mw-theme-light', lightMode);
232
+ themeToggle.classList.toggle('active', lightMode);
233
+ if (icon) {
234
+ icon.className = lightMode ? 'fas fa-sun' : 'fas fa-moon';
235
+ }
236
+ };
237
+
238
+ // Set initial theme based on isLight
239
+ applyTheme(isLight);
240
+
241
+ // Add click listener for switching
242
+ themeToggle.addEventListener('click', () => {
243
+ isLight = !isLight;
244
+ applyTheme(isLight);
245
+ localStorage.setItem('mw-theme', isLight ? 'light' : 'dark'); // Save
246
+
247
+ // update color swatches
248
+ setTimeout(updateColorSwatchHexValues, 400);
249
+ });
250
+ }
251
+
252
+ // ===== Color Swatches =====
253
+ function initColorSwatches() {
254
+ updateColorSwatchHexValues();
255
+ }
256
+
257
+ function updateColorSwatchHexValues() {
258
+ const colorSwatches = document.querySelectorAll('.color-swatch');
259
+ colorSwatches.forEach((swatch) => {
260
+ const bgColor = window.getComputedStyle(swatch).backgroundColor;
261
+ const hex = rgbToHex(bgColor);
262
+ const hexTextElement =
263
+ swatch.parentElement.querySelector('.mw-text-muted');
264
+ if (hexTextElement) {
265
+ hexTextElement.textContent = hex;
266
+ }
267
+ });
268
+ }
269
+
270
+ function rgbToHex(rgb) {
271
+ // Check if the color is in RGB/RGBA format
272
+ if (!rgb || rgb === 'transparent') return '#000000';
273
+
274
+ let rgbArray;
275
+ if (rgb.startsWith('rgba')) {
276
+ rgbArray = rgb.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*[\d.]+)?\)/);
277
+ } else {
278
+ rgbArray = rgb.match(/rgb?\((\d+),\s*(\d+),\s*(\d+)\)/);
279
+ }
280
+
281
+ if (!rgbArray) return rgb;
282
+
283
+ const r = parseInt(rgbArray[1], 10).toString(16).padStart(2, '0');
284
+ const g = parseInt(rgbArray[2], 10).toString(16).padStart(2, '0');
285
+ const b = parseInt(rgbArray[3], 10).toString(16).padStart(2, '0');
286
+
287
+ return `#${r}${g}${b}`.toUpperCase();
288
+ }
289
+
290
+ // ===== Accordions =====
291
+ function initAccordions() {
292
+ const accordionHeaders = document.querySelectorAll('.mw-accordion-header');
293
+ accordionHeaders.forEach((header) => {
294
+ header.addEventListener('click', function () {
295
+ this.classList.toggle('active');
296
+ const content = this.nextElementSibling;
297
+ if (content) content.classList.toggle('active');
298
+ });
299
+ });
300
+ }
301
+
302
+ // ===== Mobile Navigation =====
303
+ function initMobileNav() {
304
+ const menuBtn = document.querySelector('.mw-menu-btn');
305
+ const navbar = document.querySelector('.mw-navbar');
306
+
307
+ if (!menuBtn || !navbar) return;
308
+
309
+ function toggleMenu(e) {
310
+ e.preventDefault();
311
+ e.stopPropagation();
312
+ menuBtn.classList.toggle('open');
313
+ navbar.classList.toggle('open');
314
+ }
315
+
316
+ // Add multiple event listeners for better iOS compatibility
317
+ menuBtn.addEventListener('click', toggleMenu);
318
+ menuBtn.addEventListener('touchstart', toggleMenu, { passive: false });
319
+ }
320
+
321
+ // ===== Progress Bars =====
322
+ function initProgressBars() {
323
+ const fills = document.querySelectorAll('.mw-progress-fill');
324
+ if (!fills.length) return;
325
+
326
+ const obs = new IntersectionObserver(
327
+ (entries, observer) => {
328
+ entries.forEach((entry) => {
329
+ if (!entry.isIntersecting) return;
330
+ const bar = entry.target;
331
+ const pct = bar.dataset.value || 0;
332
+ // trigger the CSS transition
333
+ bar.style.width = pct + '%';
334
+ // stop observing this one
335
+ observer.unobserve(bar);
336
+ });
337
+ },
338
+ {
339
+ root: null,
340
+ threshold: 0.2,
341
+ }
342
+ );
343
+
344
+ fills.forEach((bar) => obs.observe(bar));
345
+ }
346
+
347
+ // ===== Smooth Scrolling =====
348
+ function initSmoothScrolling() {
349
+ document.querySelectorAll('a[href^="#"]').forEach((anchor) => {
350
+ anchor.addEventListener('click', function (e) {
351
+ const targetId = this.getAttribute('href');
352
+ if (targetId === '#') return;
353
+
354
+ const targetElement = document.querySelector(targetId);
355
+ if (!targetElement) return;
356
+
357
+ const menuBtn = document.querySelector('.mw-menu-btn');
358
+ const nav = document.querySelector('.mw-navbar');
359
+ if (menuBtn && nav) {
360
+ menuBtn.classList.remove('open');
361
+ nav.classList.remove('open');
362
+ }
363
+ });
364
+ });
365
+ }
366
+
367
+ // ===== Scroll Spy =====
368
+ function initScrollSpy() {
369
+ const sections = document.querySelectorAll('section[id]');
370
+ const navLinks = document.querySelectorAll('.mw-navbar-link');
371
+
372
+ if (sections.length === 0 || navLinks.length === 0) return;
373
+
374
+ window.addEventListener(
375
+ 'scroll',
376
+ debounce(function () {
377
+ let current = '';
378
+
379
+ sections.forEach((section) => {
380
+ const sectionTop = section.offsetTop - 100;
381
+ const sectionHeight = section.clientHeight;
382
+
383
+ if (scrollY >= sectionTop && scrollY < sectionTop + sectionHeight) {
384
+ current = section.getAttribute('id');
385
+ }
386
+ });
387
+
388
+ navLinks.forEach((link) => {
389
+ link.classList.remove('active');
390
+
391
+ const href = link.getAttribute('href');
392
+ if (href) {
393
+ const hashIndex = href.indexOf('#');
394
+ if (hashIndex !== -1) {
395
+ const linkTarget = href.substring(hashIndex + 1);
396
+ if (linkTarget === current) {
397
+ link.classList.add('active');
398
+ }
399
+ }
400
+ }
401
+ });
402
+ }, 100)
403
+ );
404
+ }
405
+
406
+ // ===== Tabs =====
407
+ function initTabs() {
408
+ const tabNavItems = document.querySelectorAll('.mw-tabs-nav-item');
409
+
410
+ tabNavItems.forEach((item) => {
411
+ item.addEventListener('click', function () {
412
+ const tabsContainer = this.closest('.mw-tabs');
413
+ if (!tabsContainer) return;
414
+
415
+ tabsContainer
416
+ .querySelectorAll('.mw-tabs-nav-item')
417
+ .forEach((navItem) => {
418
+ navItem.classList.remove('active');
419
+ });
420
+
421
+ this.classList.add('active');
422
+
423
+ const tabId = this.getAttribute('data-tab');
424
+ if (!tabId) return;
425
+
426
+ tabsContainer.querySelectorAll('.mw-tabs-panel').forEach((panel) => {
427
+ panel.classList.remove('active');
428
+ });
429
+
430
+ const targetPanel = document.getElementById(tabId);
431
+ if (targetPanel) targetPanel.classList.add('active');
432
+ });
433
+ });
434
+ }
435
+
436
+ // ===== Alerts =====
437
+ function initAlerts() {
438
+ const alertCloseButtons = document.querySelectorAll('.mw-alert-close');
439
+
440
+ alertCloseButtons.forEach((button) => {
441
+ button.addEventListener('click', function () {
442
+ const alert = this.closest('.mw-alert');
443
+ if (!alert) return;
444
+
445
+ alert.classList.add('mw-alert-closing');
446
+ setTimeout(() => {
447
+ alert.classList.add('mw-alert-closed');
448
+ }, 300);
449
+ });
450
+ });
451
+ }
452
+
453
+ // ===== Utility Functions =====
454
+ function debounce(func, wait) {
455
+ let timeout;
456
+ return function () {
457
+ const context = this;
458
+ const args = arguments;
459
+ clearTimeout(timeout);
460
+ timeout = setTimeout(() => func.apply(context, args), wait);
461
+ };
462
+ }
463
+
464
+ // ===== Utility Functions =====
465
+ function initLocalhostIndicator() {
466
+ const activated = document.querySelector(
467
+ '.mw-localhost-indicator-activated'
468
+ );
469
+ const header = document.querySelector('.mw-header');
470
+
471
+ if (activated) {
472
+ const isLocalhost =
473
+ window.location.hostname === 'localhost' ||
474
+ window.location.hostname === '127.0.0.1' ||
475
+ window.location.hostname.includes('192.168.');
476
+
477
+ if (isLocalhost) {
478
+ const indicator = document.createElement('div');
479
+ indicator.className = 'mw-localhost-indicator-pulse';
480
+ header.prepend(indicator);
481
+ }
482
+ }
483
+ }
484
+
485
+ // ===== Form Sliders =====
486
+ function initFormSliders() {
487
+ document.querySelectorAll('.mw-slider-container').forEach((wrapper) => {
488
+ const slider = wrapper.querySelector('.mw-slider');
489
+ const badge = wrapper.querySelector('.mw-slider-value');
490
+ if (!slider || !badge) return;
491
+
492
+ const update = () => {
493
+ const val = Number(slider.value);
494
+ const max = Number(slider.max) || 100;
495
+ const pct = Math.round((val / max) * 100);
496
+
497
+ // update track‐fill
498
+ slider.style.setProperty('--value', pct + '%');
499
+
500
+ // decide what to show in the badge
501
+ if (badge.classList.contains('mw-slider-numeric')) {
502
+ badge.setAttribute('data-value', val);
503
+ } else {
504
+ badge.setAttribute('data-value', pct);
505
+ }
506
+ };
507
+
508
+ slider.addEventListener('input', update);
509
+
510
+ update();
511
+ });
512
+ }
513
+
514
+ // ===== Modals =====
515
+ function initModals() {
516
+ document.querySelectorAll('.mw-modal-close').forEach((button) => {
517
+ button.addEventListener('click', function () {
518
+ const modal = this.closest('.mw-modal-overlay');
519
+ if (modal) {
520
+ modal.classList.remove('mw-modal-open');
521
+ }
522
+ });
523
+ });
524
+ }
525
+
526
+ // ===== Login Button =====
527
+ function initHeaderLoginButton() {
528
+ const loginButton = document.getElementById('login-button');
529
+
530
+ if (loginButton) {
531
+ loginButton.addEventListener('click', function () {
532
+ const icon = this.querySelector('i');
533
+
534
+ // Toggle between fa-user-lock and fa-user-tag
535
+ if (icon.classList.contains('fa-lock')) {
536
+ icon.classList.remove('fa-lock');
537
+ icon.classList.add('fa-lock-open');
538
+ } else {
539
+ icon.classList.remove('fa-lock-open');
540
+ icon.classList.add('fa-lock');
541
+ }
542
+ });
543
+ }
544
+ }
545
+
546
+ // ===== Image Sliders =====
547
+ function initImageSliders() {
548
+ const sliders = document.querySelectorAll('.mw-image-slider');
549
+
550
+ sliders.forEach((slider) => {
551
+ const overlayImages = slider.querySelectorAll(
552
+ '.mw-image-slider-overlay-image'
553
+ );
554
+ const buttons = slider.querySelectorAll('button[data-index]');
555
+
556
+ if (!overlayImages.length || !buttons.length) return;
557
+
558
+ let current = 0;
559
+
560
+ function updateSliderView() {
561
+ overlayImages.forEach((img) => {
562
+ const imgIndex = parseInt(img.dataset.index);
563
+ img.classList.toggle('active', imgIndex === current);
564
+ });
565
+
566
+ buttons.forEach((btn) => {
567
+ const btnIndex = parseInt(btn.dataset.index);
568
+ btn.classList.toggle('active', btnIndex === current);
569
+ });
570
+ }
571
+
572
+ function goToSlide(index) {
573
+ current = index;
574
+ updateSliderView();
575
+ }
576
+
577
+ buttons.forEach((button) => {
578
+ button.addEventListener('click', () => {
579
+ const index = parseInt(button.dataset.index);
580
+ goToSlide(index);
581
+ });
582
+ });
583
+
584
+ updateSliderView();
585
+ });
586
+ }
587
+ })();
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "maverick-wave",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "config": {
5
- "version_short": "3.0"
5
+ "version_short": "3.1"
6
6
  },
7
7
  "description": "A lightweight, modern CSS framework for building responsive websites with elegance and speed.",
8
8
  "main": "src/js/main.js",
package/release.sh CHANGED
@@ -12,6 +12,9 @@ git commit -m "release: ${VERSION}"
12
12
  git push origin release
13
13
  echo "## create new tag and push it"
14
14
  git tag v${VERSION} && git push origin release --tags
15
+ echo "## publish to npm"
16
+ npm whoami 2>/dev/null || npm login
17
+ npm publish --access public
15
18
  echo "## go back to main branch"
16
19
  git checkout main
17
20
  echo "#### script done ####"
@@ -6,8 +6,8 @@
6
6
  <h4 class="mw-mt-4">Option 1: CDN</h4>
7
7
  <pre
8
8
  class="mw-code-block"
9
- ><code>&lt;link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/m1well/maverick-wave@v{{VERSION}}/maverick-wave.min.css"&gt;
10
- &lt;script src="https://cdn.jsdelivr.net/gh/m1well/maverick-wave@v{{VERSION}}/maverick-wave.min.js"&gt;&lt;/script&gt;</code></pre>
9
+ ><code>&lt;link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/maverick-wave@{{VERSION}}/maverick-wave.min.css"&gt;
10
+ &lt;script src="https://cdn.jsdelivr.net/npm/maverick-wave@{{VERSION}}/maverick-wave.min.js"&gt;&lt;/script&gt;</code></pre>
11
11
 
12
12
  <h4 class="mw-mt-4">Option 2: Direct Download</h4>
13
13
  <div class="mw-d-flex mw-gap-4">
@@ -165,7 +165,7 @@ body {
165
165
  <code>src/index.html</code>:
166
166
  </p>
167
167
  <pre class="mw-code-block"><code>&lt;link rel="stylesheet"
168
- href="https://cdn.jsdelivr.net/gh/m1well/maverick-wave@v{{VERSION}}/maverick-wave.min.css"&gt;</code></pre>
168
+ href="https://cdn.jsdelivr.net/npm/maverick-wave@{{VERSION}}/maverick-wave.min.css"&gt;</code></pre>
169
169
  <p>
170
170
  For SCSS integration, copy the <code>src/scss/</code> source into your
171
171
  project and import it from <code>src/styles.scss</code>: