softui-css 1.5.0 → 1.7.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/dist/softui.css +1998 -19
- package/dist/softui.js +554 -1
- package/dist/softui.min.css +1 -1
- package/dist/softui.min.js +1 -1
- package/package.json +1 -1
package/dist/softui.js
CHANGED
|
@@ -3645,5 +3645,558 @@ const SoftUI = (() => {
|
|
|
3645
3645
|
});
|
|
3646
3646
|
}, true);
|
|
3647
3647
|
|
|
3648
|
-
|
|
3648
|
+
// =========================================
|
|
3649
|
+
// Image Lightbox
|
|
3650
|
+
// =========================================
|
|
3651
|
+
var lightboxOverlay = null;
|
|
3652
|
+
var lightboxImages = [];
|
|
3653
|
+
var lightboxIndex = 0;
|
|
3654
|
+
|
|
3655
|
+
function createLightbox() {
|
|
3656
|
+
if (lightboxOverlay) return;
|
|
3657
|
+
lightboxOverlay = document.createElement('div');
|
|
3658
|
+
lightboxOverlay.className = 'sui-lightbox-overlay';
|
|
3659
|
+
lightboxOverlay.innerHTML =
|
|
3660
|
+
'<button class="sui-lightbox-close" aria-label="Close">×</button>' +
|
|
3661
|
+
'<span class="sui-lightbox-counter"></span>' +
|
|
3662
|
+
'<button class="sui-lightbox-prev" aria-label="Previous"><svg viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg></button>' +
|
|
3663
|
+
'<button class="sui-lightbox-next" aria-label="Next"><svg viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></button>' +
|
|
3664
|
+
'<img src="" alt="">' +
|
|
3665
|
+
'<div class="sui-lightbox-caption"></div>';
|
|
3666
|
+
document.body.appendChild(lightboxOverlay);
|
|
3667
|
+
|
|
3668
|
+
lightboxOverlay.querySelector('.sui-lightbox-close').addEventListener('click', closeLightbox);
|
|
3669
|
+
lightboxOverlay.querySelector('.sui-lightbox-prev').addEventListener('click', function() { showLightboxImage(lightboxIndex - 1); });
|
|
3670
|
+
lightboxOverlay.querySelector('.sui-lightbox-next').addEventListener('click', function() { showLightboxImage(lightboxIndex + 1); });
|
|
3671
|
+
lightboxOverlay.addEventListener('click', function(e) {
|
|
3672
|
+
if (e.target === lightboxOverlay) closeLightbox();
|
|
3673
|
+
if (e.target.tagName === 'IMG') {
|
|
3674
|
+
lightboxOverlay.classList.toggle('zoomed');
|
|
3675
|
+
}
|
|
3676
|
+
});
|
|
3677
|
+
document.addEventListener('keydown', function(e) {
|
|
3678
|
+
if (!lightboxOverlay || !lightboxOverlay.classList.contains('open')) return;
|
|
3679
|
+
if (e.key === 'Escape') closeLightbox();
|
|
3680
|
+
if (e.key === 'ArrowLeft') showLightboxImage(lightboxIndex - 1);
|
|
3681
|
+
if (e.key === 'ArrowRight') showLightboxImage(lightboxIndex + 1);
|
|
3682
|
+
});
|
|
3683
|
+
}
|
|
3684
|
+
|
|
3685
|
+
function openLightbox(images, index) {
|
|
3686
|
+
createLightbox();
|
|
3687
|
+
lightboxImages = images;
|
|
3688
|
+
lightboxIndex = index || 0;
|
|
3689
|
+
showLightboxImage(lightboxIndex);
|
|
3690
|
+
lightboxOverlay.classList.add('open');
|
|
3691
|
+
lightboxOverlay.classList.remove('zoomed');
|
|
3692
|
+
document.body.style.overflow = 'hidden';
|
|
3693
|
+
var hasMultiple = images.length > 1;
|
|
3694
|
+
lightboxOverlay.querySelector('.sui-lightbox-prev').style.display = hasMultiple ? '' : 'none';
|
|
3695
|
+
lightboxOverlay.querySelector('.sui-lightbox-next').style.display = hasMultiple ? '' : 'none';
|
|
3696
|
+
lightboxOverlay.querySelector('.sui-lightbox-counter').style.display = hasMultiple ? '' : 'none';
|
|
3697
|
+
}
|
|
3698
|
+
|
|
3699
|
+
function closeLightbox() {
|
|
3700
|
+
if (lightboxOverlay) {
|
|
3701
|
+
lightboxOverlay.classList.remove('open', 'zoomed');
|
|
3702
|
+
document.body.style.overflow = '';
|
|
3703
|
+
}
|
|
3704
|
+
}
|
|
3705
|
+
|
|
3706
|
+
function showLightboxImage(idx) {
|
|
3707
|
+
if (lightboxImages.length === 0) return;
|
|
3708
|
+
lightboxIndex = (idx + lightboxImages.length) % lightboxImages.length;
|
|
3709
|
+
var item = lightboxImages[lightboxIndex];
|
|
3710
|
+
var img = lightboxOverlay.querySelector('img');
|
|
3711
|
+
var caption = lightboxOverlay.querySelector('.sui-lightbox-caption');
|
|
3712
|
+
var counter = lightboxOverlay.querySelector('.sui-lightbox-counter');
|
|
3713
|
+
img.src = item.src;
|
|
3714
|
+
img.alt = item.alt || '';
|
|
3715
|
+
caption.textContent = item.caption || '';
|
|
3716
|
+
caption.style.display = item.caption ? '' : 'none';
|
|
3717
|
+
counter.textContent = (lightboxIndex + 1) + ' / ' + lightboxImages.length;
|
|
3718
|
+
lightboxOverlay.classList.remove('zoomed');
|
|
3719
|
+
}
|
|
3720
|
+
|
|
3721
|
+
// Vertical gallery — click side thumb to update main
|
|
3722
|
+
document.addEventListener('click', function(e) {
|
|
3723
|
+
var thumb = e.target.closest('.sui-lightbox-vertical-strip .sui-lightbox-thumb');
|
|
3724
|
+
if (!thumb) return;
|
|
3725
|
+
var gallery = thumb.closest('.sui-lightbox-vertical');
|
|
3726
|
+
var main = gallery.querySelector('.sui-lightbox-vertical-main img');
|
|
3727
|
+
var img = thumb.querySelector('img');
|
|
3728
|
+
if (main && img) {
|
|
3729
|
+
main.src = thumb.getAttribute('data-src') || img.src;
|
|
3730
|
+
main.alt = thumb.getAttribute('data-alt') || img.alt;
|
|
3731
|
+
}
|
|
3732
|
+
gallery.querySelectorAll('.sui-lightbox-vertical-strip .sui-lightbox-thumb').forEach(function(t) { t.classList.remove('active'); });
|
|
3733
|
+
thumb.classList.add('active');
|
|
3734
|
+
});
|
|
3735
|
+
|
|
3736
|
+
// Click main image in vertical gallery to open lightbox
|
|
3737
|
+
document.addEventListener('click', function(e) {
|
|
3738
|
+
var main = e.target.closest('.sui-lightbox-vertical-main');
|
|
3739
|
+
if (!main) return;
|
|
3740
|
+
var gallery = main.closest('.sui-lightbox-vertical');
|
|
3741
|
+
var thumbs = Array.from(gallery.querySelectorAll('.sui-lightbox-vertical-strip .sui-lightbox-thumb'));
|
|
3742
|
+
var images = thumbs.map(function(t) {
|
|
3743
|
+
var img = t.querySelector('img');
|
|
3744
|
+
return {
|
|
3745
|
+
src: t.getAttribute('data-src') || (img ? img.src : ''),
|
|
3746
|
+
alt: t.getAttribute('data-alt') || (img ? img.alt : ''),
|
|
3747
|
+
caption: t.getAttribute('data-caption') || ''
|
|
3748
|
+
};
|
|
3749
|
+
});
|
|
3750
|
+
var activeIdx = thumbs.findIndex(function(t) { return t.classList.contains('active'); });
|
|
3751
|
+
openLightbox(images, activeIdx >= 0 ? activeIdx : 0);
|
|
3752
|
+
});
|
|
3753
|
+
|
|
3754
|
+
// Click on thumbnail
|
|
3755
|
+
document.addEventListener('click', function(e) {
|
|
3756
|
+
var thumb = e.target.closest('.sui-lightbox-thumb');
|
|
3757
|
+
if (!thumb) return;
|
|
3758
|
+
// Skip if inside vertical strip (handled above)
|
|
3759
|
+
if (thumb.closest('.sui-lightbox-vertical-strip')) return;
|
|
3760
|
+
var grid = thumb.closest('.sui-lightbox-grid');
|
|
3761
|
+
var thumbs = grid ? Array.from(grid.querySelectorAll('.sui-lightbox-thumb')) : [thumb];
|
|
3762
|
+
var images = thumbs.map(function(t) {
|
|
3763
|
+
var img = t.querySelector('img');
|
|
3764
|
+
return {
|
|
3765
|
+
src: t.getAttribute('data-src') || (img ? img.src : ''),
|
|
3766
|
+
alt: t.getAttribute('data-alt') || (img ? img.alt : ''),
|
|
3767
|
+
caption: t.getAttribute('data-caption') || ''
|
|
3768
|
+
};
|
|
3769
|
+
});
|
|
3770
|
+
var index = thumbs.indexOf(thumb);
|
|
3771
|
+
openLightbox(images, index);
|
|
3772
|
+
});
|
|
3773
|
+
|
|
3774
|
+
// =========================================
|
|
3775
|
+
// Typewriter
|
|
3776
|
+
// =========================================
|
|
3777
|
+
function initTypewriters() {
|
|
3778
|
+
document.querySelectorAll('[data-sui-typewriter]').forEach(function(el) {
|
|
3779
|
+
if (el.dataset.suiTypewriterInit) return;
|
|
3780
|
+
el.dataset.suiTypewriterInit = '1';
|
|
3781
|
+
var words = el.getAttribute('data-words');
|
|
3782
|
+
var speed = parseInt(el.getAttribute('data-speed')) || 80;
|
|
3783
|
+
var deleteSpeed = parseInt(el.getAttribute('data-delete-speed')) || 40;
|
|
3784
|
+
var pause = parseInt(el.getAttribute('data-pause')) || 1500;
|
|
3785
|
+
var loop = el.hasAttribute('data-loop');
|
|
3786
|
+
|
|
3787
|
+
if (words) {
|
|
3788
|
+
// Multiple phrases mode
|
|
3789
|
+
var phrases = words.split('|').map(function(s) { return s.trim(); });
|
|
3790
|
+
var phraseIdx = 0;
|
|
3791
|
+
var charIdx = 0;
|
|
3792
|
+
var deleting = false;
|
|
3793
|
+
|
|
3794
|
+
function tick() {
|
|
3795
|
+
var current = phrases[phraseIdx];
|
|
3796
|
+
if (!deleting) {
|
|
3797
|
+
charIdx++;
|
|
3798
|
+
el.textContent = current.substring(0, charIdx);
|
|
3799
|
+
if (charIdx === current.length) {
|
|
3800
|
+
if (!loop && phraseIdx === phrases.length - 1) return;
|
|
3801
|
+
setTimeout(function() { deleting = true; tick(); }, pause);
|
|
3802
|
+
return;
|
|
3803
|
+
}
|
|
3804
|
+
setTimeout(tick, speed);
|
|
3805
|
+
} else {
|
|
3806
|
+
charIdx--;
|
|
3807
|
+
el.textContent = current.substring(0, charIdx);
|
|
3808
|
+
if (charIdx === 0) {
|
|
3809
|
+
deleting = false;
|
|
3810
|
+
phraseIdx = (phraseIdx + 1) % phrases.length;
|
|
3811
|
+
setTimeout(tick, speed);
|
|
3812
|
+
return;
|
|
3813
|
+
}
|
|
3814
|
+
setTimeout(tick, deleteSpeed);
|
|
3815
|
+
}
|
|
3816
|
+
}
|
|
3817
|
+
|
|
3818
|
+
el.textContent = '';
|
|
3819
|
+
setTimeout(tick, 500);
|
|
3820
|
+
} else {
|
|
3821
|
+
// Single text mode — type out existing content
|
|
3822
|
+
var text = el.textContent;
|
|
3823
|
+
el.textContent = '';
|
|
3824
|
+
var i = 0;
|
|
3825
|
+
function typeChar() {
|
|
3826
|
+
if (i < text.length) {
|
|
3827
|
+
el.textContent += text[i];
|
|
3828
|
+
i++;
|
|
3829
|
+
setTimeout(typeChar, speed);
|
|
3830
|
+
}
|
|
3831
|
+
}
|
|
3832
|
+
setTimeout(typeChar, 500);
|
|
3833
|
+
}
|
|
3834
|
+
});
|
|
3835
|
+
}
|
|
3836
|
+
|
|
3837
|
+
if (document.readyState === 'loading') {
|
|
3838
|
+
document.addEventListener('DOMContentLoaded', initTypewriters);
|
|
3839
|
+
} else {
|
|
3840
|
+
initTypewriters();
|
|
3841
|
+
}
|
|
3842
|
+
|
|
3843
|
+
// =========================================
|
|
3844
|
+
// Text Rotate
|
|
3845
|
+
// =========================================
|
|
3846
|
+
function initTextRotate() {
|
|
3847
|
+
document.querySelectorAll('[data-sui-text-rotate]').forEach(function(el) {
|
|
3848
|
+
if (el.dataset.suiRotateInit) return;
|
|
3849
|
+
el.dataset.suiRotateInit = '1';
|
|
3850
|
+
var words = el.querySelectorAll('.sui-text-rotate-word');
|
|
3851
|
+
if (words.length < 2) return;
|
|
3852
|
+
var interval = parseInt(el.getAttribute('data-interval')) || 2000;
|
|
3853
|
+
var index = 0;
|
|
3854
|
+
|
|
3855
|
+
words[0].classList.add('active');
|
|
3856
|
+
|
|
3857
|
+
setInterval(function() {
|
|
3858
|
+
var current = words[index];
|
|
3859
|
+
current.classList.remove('active');
|
|
3860
|
+
current.classList.add('exit');
|
|
3861
|
+
setTimeout(function() { current.classList.remove('exit'); }, 400);
|
|
3862
|
+
|
|
3863
|
+
index = (index + 1) % words.length;
|
|
3864
|
+
words[index].classList.add('active');
|
|
3865
|
+
}, interval);
|
|
3866
|
+
});
|
|
3867
|
+
}
|
|
3868
|
+
|
|
3869
|
+
if (document.readyState === 'loading') {
|
|
3870
|
+
document.addEventListener('DOMContentLoaded', initTextRotate);
|
|
3871
|
+
} else {
|
|
3872
|
+
initTextRotate();
|
|
3873
|
+
}
|
|
3874
|
+
|
|
3875
|
+
// =========================================
|
|
3876
|
+
// Copy Button
|
|
3877
|
+
// =========================================
|
|
3878
|
+
var clipboardSvg = '<svg viewBox="0 0 24 24"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>';
|
|
3879
|
+
var checkSvg = '<svg viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>';
|
|
3880
|
+
|
|
3881
|
+
document.addEventListener('click', function(e) {
|
|
3882
|
+
var btn = e.target.closest('[data-sui-copy]');
|
|
3883
|
+
if (!btn) return;
|
|
3884
|
+
var text = btn.getAttribute('data-sui-copy');
|
|
3885
|
+
if (!text) {
|
|
3886
|
+
var wrap = btn.closest('.sui-copy, .sui-copy-input');
|
|
3887
|
+
if (wrap) {
|
|
3888
|
+
var textEl = wrap.querySelector('.sui-copy-text');
|
|
3889
|
+
var inputEl = wrap.querySelector('.sui-input');
|
|
3890
|
+
text = textEl ? textEl.textContent : inputEl ? inputEl.value : '';
|
|
3891
|
+
}
|
|
3892
|
+
}
|
|
3893
|
+
if (!text) return;
|
|
3894
|
+
try { navigator.clipboard.writeText(text.trim()); } catch (err) {}
|
|
3895
|
+
btn.classList.add('copied');
|
|
3896
|
+
btn.innerHTML = checkSvg;
|
|
3897
|
+
setTimeout(function() {
|
|
3898
|
+
btn.classList.remove('copied');
|
|
3899
|
+
btn.innerHTML = clipboardSvg;
|
|
3900
|
+
}, 1500);
|
|
3901
|
+
});
|
|
3902
|
+
|
|
3903
|
+
// =========================================
|
|
3904
|
+
// Diff — Image Compare Slider
|
|
3905
|
+
// =========================================
|
|
3906
|
+
function initDiffSliders() {
|
|
3907
|
+
document.querySelectorAll('.sui-diff[data-sui-diff]').forEach(function(diff) {
|
|
3908
|
+
if (diff.dataset.suiDiffInit) return;
|
|
3909
|
+
diff.dataset.suiDiffInit = '1';
|
|
3910
|
+
var handle = diff.querySelector('.sui-diff-handle');
|
|
3911
|
+
var before = diff.querySelector('.sui-diff-before');
|
|
3912
|
+
if (!handle || !before) return;
|
|
3913
|
+
var isVertical = diff.classList.contains('sui-diff-vertical');
|
|
3914
|
+
|
|
3915
|
+
function onMove(e) {
|
|
3916
|
+
e.preventDefault();
|
|
3917
|
+
var rect = diff.getBoundingClientRect();
|
|
3918
|
+
var pos;
|
|
3919
|
+
if (isVertical) {
|
|
3920
|
+
var clientY = e.touches ? e.touches[0].clientY : e.clientY;
|
|
3921
|
+
pos = Math.max(0, Math.min(1, (clientY - rect.top) / rect.height));
|
|
3922
|
+
var pct = (pos * 100);
|
|
3923
|
+
before.style.clipPath = 'inset(0 0 ' + (100 - pct) + '% 0)';
|
|
3924
|
+
handle.style.top = pct + '%';
|
|
3925
|
+
} else {
|
|
3926
|
+
var clientX = e.touches ? e.touches[0].clientX : e.clientX;
|
|
3927
|
+
pos = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
|
|
3928
|
+
var pct = (pos * 100);
|
|
3929
|
+
before.style.clipPath = 'inset(0 ' + (100 - pct) + '% 0 0)';
|
|
3930
|
+
handle.style.left = pct + '%';
|
|
3931
|
+
}
|
|
3932
|
+
}
|
|
3933
|
+
|
|
3934
|
+
function onDown(e) {
|
|
3935
|
+
e.preventDefault();
|
|
3936
|
+
onMove(e);
|
|
3937
|
+
document.addEventListener('mousemove', onMove);
|
|
3938
|
+
document.addEventListener('mouseup', onUp);
|
|
3939
|
+
document.addEventListener('touchmove', onMove, { passive: false });
|
|
3940
|
+
document.addEventListener('touchend', onUp);
|
|
3941
|
+
}
|
|
3942
|
+
|
|
3943
|
+
function onUp() {
|
|
3944
|
+
document.removeEventListener('mousemove', onMove);
|
|
3945
|
+
document.removeEventListener('mouseup', onUp);
|
|
3946
|
+
document.removeEventListener('touchmove', onMove);
|
|
3947
|
+
document.removeEventListener('touchend', onUp);
|
|
3948
|
+
}
|
|
3949
|
+
|
|
3950
|
+
diff.addEventListener('mousedown', onDown);
|
|
3951
|
+
diff.addEventListener('touchstart', onDown, { passive: false });
|
|
3952
|
+
});
|
|
3953
|
+
}
|
|
3954
|
+
|
|
3955
|
+
if (document.readyState === 'loading') {
|
|
3956
|
+
document.addEventListener('DOMContentLoaded', initDiffSliders);
|
|
3957
|
+
} else {
|
|
3958
|
+
initDiffSliders();
|
|
3959
|
+
}
|
|
3960
|
+
|
|
3961
|
+
// =========================================
|
|
3962
|
+
// Speed Dial
|
|
3963
|
+
// =========================================
|
|
3964
|
+
document.addEventListener('click', function(e) {
|
|
3965
|
+
var trigger = e.target.closest('.sui-speed-dial-trigger');
|
|
3966
|
+
if (trigger) {
|
|
3967
|
+
var dial = trigger.closest('.sui-speed-dial');
|
|
3968
|
+
dial.classList.toggle('open');
|
|
3969
|
+
return;
|
|
3970
|
+
}
|
|
3971
|
+
var action = e.target.closest('.sui-speed-dial-action');
|
|
3972
|
+
if (action) {
|
|
3973
|
+
var dial = action.closest('.sui-speed-dial');
|
|
3974
|
+
dial.classList.remove('open');
|
|
3975
|
+
return;
|
|
3976
|
+
}
|
|
3977
|
+
// Close all open dials when clicking outside
|
|
3978
|
+
document.querySelectorAll('.sui-speed-dial.open').forEach(function(d) {
|
|
3979
|
+
d.classList.remove('open');
|
|
3980
|
+
});
|
|
3981
|
+
});
|
|
3982
|
+
|
|
3983
|
+
// Hover mode
|
|
3984
|
+
document.addEventListener('mouseenter', function(e) {
|
|
3985
|
+
var dial = e.target.closest('.sui-speed-dial-hover');
|
|
3986
|
+
if (dial) dial.classList.add('open');
|
|
3987
|
+
}, true);
|
|
3988
|
+
|
|
3989
|
+
document.addEventListener('mouseleave', function(e) {
|
|
3990
|
+
var dial = e.target.closest('.sui-speed-dial-hover');
|
|
3991
|
+
if (dial) dial.classList.remove('open');
|
|
3992
|
+
}, true);
|
|
3993
|
+
|
|
3994
|
+
// =========================================
|
|
3995
|
+
// Tree View
|
|
3996
|
+
// =========================================
|
|
3997
|
+
document.addEventListener('click', function(e) {
|
|
3998
|
+
var label = e.target.closest('.sui-tree-label');
|
|
3999
|
+
if (!label) return;
|
|
4000
|
+
if (e.target.closest('.sui-checkbox')) return;
|
|
4001
|
+
var item = label.closest('.sui-tree-item');
|
|
4002
|
+
var children = item.querySelector('.sui-tree-children');
|
|
4003
|
+
if (children) {
|
|
4004
|
+
item.classList.toggle('expanded');
|
|
4005
|
+
}
|
|
4006
|
+
});
|
|
4007
|
+
|
|
4008
|
+
// Tree checkbox propagation
|
|
4009
|
+
document.addEventListener('change', function(e) {
|
|
4010
|
+
if (!e.target.closest('.sui-tree .sui-checkbox input')) return;
|
|
4011
|
+
var checkbox = e.target;
|
|
4012
|
+
var item = checkbox.closest('.sui-tree-item');
|
|
4013
|
+
var checked = checkbox.checked;
|
|
4014
|
+
|
|
4015
|
+
// Propagate down — check/uncheck all children
|
|
4016
|
+
var childBoxes = item.querySelectorAll('.sui-tree-children .sui-checkbox input');
|
|
4017
|
+
childBoxes.forEach(function(cb) {
|
|
4018
|
+
cb.checked = checked;
|
|
4019
|
+
cb.indeterminate = false;
|
|
4020
|
+
});
|
|
4021
|
+
|
|
4022
|
+
// Propagate up — update parent state
|
|
4023
|
+
updateTreeParent(item);
|
|
4024
|
+
});
|
|
4025
|
+
|
|
4026
|
+
function updateTreeParent(item) {
|
|
4027
|
+
var parentChildren = item.closest('.sui-tree-children');
|
|
4028
|
+
if (!parentChildren) return;
|
|
4029
|
+
var parentItem = parentChildren.closest('.sui-tree-item');
|
|
4030
|
+
if (!parentItem) return;
|
|
4031
|
+
var parentCb = parentItem.querySelector(':scope > .sui-tree-label .sui-checkbox input');
|
|
4032
|
+
if (!parentCb) return;
|
|
4033
|
+
|
|
4034
|
+
var siblings = parentChildren.querySelectorAll(':scope > .sui-tree-item > .sui-tree-label .sui-checkbox input');
|
|
4035
|
+
var total = siblings.length;
|
|
4036
|
+
var checkedCount = 0;
|
|
4037
|
+
siblings.forEach(function(cb) { if (cb.checked) checkedCount++; });
|
|
4038
|
+
|
|
4039
|
+
if (checkedCount === 0) {
|
|
4040
|
+
parentCb.checked = false;
|
|
4041
|
+
parentCb.indeterminate = false;
|
|
4042
|
+
} else if (checkedCount === total) {
|
|
4043
|
+
parentCb.checked = true;
|
|
4044
|
+
parentCb.indeterminate = false;
|
|
4045
|
+
} else {
|
|
4046
|
+
parentCb.checked = false;
|
|
4047
|
+
parentCb.indeterminate = true;
|
|
4048
|
+
}
|
|
4049
|
+
|
|
4050
|
+
// Continue up the tree
|
|
4051
|
+
updateTreeParent(parentItem);
|
|
4052
|
+
}
|
|
4053
|
+
|
|
4054
|
+
// =========================================
|
|
4055
|
+
// Tour / Walkthrough
|
|
4056
|
+
// =========================================
|
|
4057
|
+
function tour(steps, options) {
|
|
4058
|
+
options = options || {};
|
|
4059
|
+
var currentStep = 0;
|
|
4060
|
+
var overlay, backdrop, spotlight, tooltip;
|
|
4061
|
+
var padding = options.padding || 8;
|
|
4062
|
+
var noOverlay = options.noOverlay || false;
|
|
4063
|
+
|
|
4064
|
+
function create() {
|
|
4065
|
+
overlay = document.createElement('div');
|
|
4066
|
+
overlay.className = 'sui-tour-overlay' + (noOverlay ? ' sui-tour-no-overlay' : '');
|
|
4067
|
+
backdrop = document.createElement('div');
|
|
4068
|
+
backdrop.className = 'sui-tour-backdrop';
|
|
4069
|
+
spotlight = document.createElement('div');
|
|
4070
|
+
spotlight.className = 'sui-tour-spotlight';
|
|
4071
|
+
tooltip = document.createElement('div');
|
|
4072
|
+
tooltip.className = 'sui-tour-tooltip';
|
|
4073
|
+
overlay.appendChild(backdrop);
|
|
4074
|
+
overlay.appendChild(spotlight);
|
|
4075
|
+
overlay.appendChild(tooltip);
|
|
4076
|
+
document.body.appendChild(overlay);
|
|
4077
|
+
|
|
4078
|
+
backdrop.addEventListener('click', close);
|
|
4079
|
+
}
|
|
4080
|
+
|
|
4081
|
+
var firstShow = true;
|
|
4082
|
+
|
|
4083
|
+
function show(idx) {
|
|
4084
|
+
currentStep = idx;
|
|
4085
|
+
var step = steps[idx];
|
|
4086
|
+
var target = document.querySelector(step.target);
|
|
4087
|
+
|
|
4088
|
+
// Only hide on first show to avoid flash between steps
|
|
4089
|
+
if (firstShow) {
|
|
4090
|
+
spotlight.style.opacity = '0';
|
|
4091
|
+
tooltip.style.opacity = '0';
|
|
4092
|
+
firstShow = false;
|
|
4093
|
+
}
|
|
4094
|
+
|
|
4095
|
+
// Scroll if element isn't comfortably in view (with margin for tooltip)
|
|
4096
|
+
var needsScroll = false;
|
|
4097
|
+
if (target) {
|
|
4098
|
+
var r = target.getBoundingClientRect();
|
|
4099
|
+
var margin = 120;
|
|
4100
|
+
needsScroll = r.top < margin || r.bottom > window.innerHeight - margin;
|
|
4101
|
+
if (needsScroll) target.scrollIntoView({ block: 'center', behavior: 'smooth' });
|
|
4102
|
+
}
|
|
4103
|
+
setTimeout(function() {
|
|
4104
|
+
if (target) {
|
|
4105
|
+
var rect = target.getBoundingClientRect();
|
|
4106
|
+
spotlight.style.top = (rect.top - padding) + 'px';
|
|
4107
|
+
spotlight.style.left = (rect.left - padding) + 'px';
|
|
4108
|
+
spotlight.style.width = (rect.width + padding * 2) + 'px';
|
|
4109
|
+
spotlight.style.height = (rect.height + padding * 2) + 'px';
|
|
4110
|
+
}
|
|
4111
|
+
|
|
4112
|
+
// Build tooltip content
|
|
4113
|
+
var dotsHtml = '';
|
|
4114
|
+
if (steps.length > 1) {
|
|
4115
|
+
dotsHtml = '<div class="sui-tour-dots">';
|
|
4116
|
+
for (var i = 0; i < steps.length; i++) {
|
|
4117
|
+
dotsHtml += '<span class="sui-tour-dot' + (i === idx ? ' active' : '') + '"></span>';
|
|
4118
|
+
}
|
|
4119
|
+
dotsHtml += '</div>';
|
|
4120
|
+
}
|
|
4121
|
+
|
|
4122
|
+
tooltip.innerHTML =
|
|
4123
|
+
'<div class="sui-tour-tooltip-title">' + (step.title || '') + '</div>' +
|
|
4124
|
+
'<div class="sui-tour-tooltip-desc">' + (step.description || '') + '</div>' +
|
|
4125
|
+
'<div class="sui-tour-tooltip-footer">' +
|
|
4126
|
+
dotsHtml +
|
|
4127
|
+
'<div class="sui-tour-tooltip-actions">' +
|
|
4128
|
+
(idx > 0 ? '<button class="sui-btn sui-btn-sm sui-tour-prev">Back</button>' : '<button class="sui-btn sui-btn-sm sui-tour-skip">Skip</button>') +
|
|
4129
|
+
(idx < steps.length - 1 ? '<button class="sui-btn sui-btn-primary sui-btn-sm sui-tour-next">Next</button>' : '<button class="sui-btn sui-btn-primary sui-btn-sm sui-tour-done">Done</button>') +
|
|
4130
|
+
'</div>' +
|
|
4131
|
+
'</div>';
|
|
4132
|
+
|
|
4133
|
+
// Button handlers
|
|
4134
|
+
var nextBtn = tooltip.querySelector('.sui-tour-next');
|
|
4135
|
+
var prevBtn = tooltip.querySelector('.sui-tour-prev');
|
|
4136
|
+
var skipBtn = tooltip.querySelector('.sui-tour-skip');
|
|
4137
|
+
var doneBtn = tooltip.querySelector('.sui-tour-done');
|
|
4138
|
+
if (nextBtn) nextBtn.addEventListener('click', function() { show(currentStep + 1); });
|
|
4139
|
+
if (prevBtn) prevBtn.addEventListener('click', function() { show(currentStep - 1); });
|
|
4140
|
+
if (skipBtn) skipBtn.addEventListener('click', close);
|
|
4141
|
+
if (doneBtn) doneBtn.addEventListener('click', close);
|
|
4142
|
+
|
|
4143
|
+
// Position tooltip after scroll settles
|
|
4144
|
+
if (target) {
|
|
4145
|
+
var rect = target.getBoundingClientRect();
|
|
4146
|
+
var pos = step.position || 'bottom';
|
|
4147
|
+
var tooltipW = 300;
|
|
4148
|
+
var centerX = rect.left + rect.width / 2 - tooltipW / 2;
|
|
4149
|
+
var top, left;
|
|
4150
|
+
|
|
4151
|
+
tooltip.style.transform = '';
|
|
4152
|
+
|
|
4153
|
+
if (pos === 'bottom') {
|
|
4154
|
+
top = rect.bottom + padding + 12;
|
|
4155
|
+
left = centerX;
|
|
4156
|
+
} else if (pos === 'top') {
|
|
4157
|
+
top = rect.top - padding - 12;
|
|
4158
|
+
left = centerX;
|
|
4159
|
+
tooltip.style.transform = 'translateY(-100%)';
|
|
4160
|
+
} else if (pos === 'left') {
|
|
4161
|
+
top = rect.top + rect.height / 2;
|
|
4162
|
+
left = rect.left - padding - tooltipW - 12;
|
|
4163
|
+
tooltip.style.transform = 'translateY(-50%)';
|
|
4164
|
+
} else if (pos === 'right') {
|
|
4165
|
+
top = rect.top + rect.height / 2;
|
|
4166
|
+
left = rect.right + padding + 12;
|
|
4167
|
+
tooltip.style.transform = 'translateY(-50%)';
|
|
4168
|
+
}
|
|
4169
|
+
|
|
4170
|
+
// Keep tooltip within viewport
|
|
4171
|
+
left = Math.max(8, Math.min(left, window.innerWidth - tooltipW - 8));
|
|
4172
|
+
top = Math.max(8, Math.min(top, window.innerHeight - 200));
|
|
4173
|
+
tooltip.style.top = top + 'px';
|
|
4174
|
+
tooltip.style.left = left + 'px';
|
|
4175
|
+
}
|
|
4176
|
+
// Reveal after positioning
|
|
4177
|
+
spotlight.style.opacity = '1';
|
|
4178
|
+
tooltip.style.opacity = '1';
|
|
4179
|
+
}, needsScroll ? 350 : 50);
|
|
4180
|
+
|
|
4181
|
+
overlay.classList.add('active');
|
|
4182
|
+
}
|
|
4183
|
+
|
|
4184
|
+
function close() {
|
|
4185
|
+
if (overlay) {
|
|
4186
|
+
overlay.classList.remove('active');
|
|
4187
|
+
setTimeout(function() {
|
|
4188
|
+
if (overlay && overlay.parentNode) overlay.parentNode.removeChild(overlay);
|
|
4189
|
+
overlay = null;
|
|
4190
|
+
}, 300);
|
|
4191
|
+
}
|
|
4192
|
+
if (options.onComplete) options.onComplete();
|
|
4193
|
+
}
|
|
4194
|
+
|
|
4195
|
+
create();
|
|
4196
|
+
show(0);
|
|
4197
|
+
|
|
4198
|
+
return { next: function() { show(currentStep + 1); }, prev: function() { show(currentStep - 1); }, close: close, goTo: show };
|
|
4199
|
+
}
|
|
4200
|
+
|
|
4201
|
+
return { modal, sheet, toast, carousel, sidebar, tour };
|
|
3649
4202
|
})();
|