softui-css 1.6.0 → 1.8.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/README.md +23 -10
- package/dist/softui.css +1033 -9
- package/dist/softui.js +295 -3
- package/dist/softui.min.css +1 -1
- package/dist/softui.min.js +1 -1
- package/dist/tokens.json +58 -0
- package/package.json +1 -1
package/dist/softui.js
CHANGED
|
@@ -576,6 +576,7 @@ const SoftUI = (() => {
|
|
|
576
576
|
el.className = cls;
|
|
577
577
|
el.setAttribute('role', 'alert');
|
|
578
578
|
el.setAttribute('aria-live', 'polite');
|
|
579
|
+
el.setAttribute('aria-atomic', 'true');
|
|
579
580
|
|
|
580
581
|
let html = '<div class="sui-toast-body">';
|
|
581
582
|
if (opts.title) html += '<div class="sui-toast-title">' + opts.title + '</div>';
|
|
@@ -675,15 +676,30 @@ const SoftUI = (() => {
|
|
|
675
676
|
});
|
|
676
677
|
});
|
|
677
678
|
|
|
678
|
-
// Escape closes dropdowns
|
|
679
|
+
// Escape closes dropdowns, arrow keys navigate items
|
|
679
680
|
document.addEventListener('keydown', (e) => {
|
|
680
681
|
if (e.key === 'Escape') {
|
|
681
682
|
document.querySelectorAll('.sui-dropdown.open, .sui-dropdown-split.open').forEach(d => {
|
|
682
683
|
d.classList.remove('open');
|
|
683
684
|
const t = d.querySelector('[data-sui-dropdown], .sui-dropdown-toggle');
|
|
684
|
-
if (t) t.setAttribute('aria-expanded', 'false');
|
|
685
|
+
if (t) { t.setAttribute('aria-expanded', 'false'); t.focus(); }
|
|
685
686
|
});
|
|
686
687
|
}
|
|
688
|
+
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
|
|
689
|
+
const openDD = document.querySelector('.sui-dropdown.open, .sui-dropdown-split.open');
|
|
690
|
+
if (!openDD) return;
|
|
691
|
+
const items = Array.from(openDD.querySelectorAll('.sui-dropdown-item:not(.disabled)'));
|
|
692
|
+
if (!items.length) return;
|
|
693
|
+
e.preventDefault();
|
|
694
|
+
const cur = items.indexOf(document.activeElement);
|
|
695
|
+
let next;
|
|
696
|
+
if (e.key === 'ArrowDown') {
|
|
697
|
+
next = cur < items.length - 1 ? cur + 1 : 0;
|
|
698
|
+
} else {
|
|
699
|
+
next = cur > 0 ? cur - 1 : items.length - 1;
|
|
700
|
+
}
|
|
701
|
+
items[next].focus();
|
|
702
|
+
}
|
|
687
703
|
});
|
|
688
704
|
}
|
|
689
705
|
|
|
@@ -3771,6 +3787,135 @@ const SoftUI = (() => {
|
|
|
3771
3787
|
openLightbox(images, index);
|
|
3772
3788
|
});
|
|
3773
3789
|
|
|
3790
|
+
// =========================================
|
|
3791
|
+
// Typewriter
|
|
3792
|
+
// =========================================
|
|
3793
|
+
function initTypewriters() {
|
|
3794
|
+
document.querySelectorAll('[data-sui-typewriter]').forEach(function(el) {
|
|
3795
|
+
if (el.dataset.suiTypewriterInit) return;
|
|
3796
|
+
el.dataset.suiTypewriterInit = '1';
|
|
3797
|
+
var words = el.getAttribute('data-words');
|
|
3798
|
+
var speed = parseInt(el.getAttribute('data-speed')) || 80;
|
|
3799
|
+
var deleteSpeed = parseInt(el.getAttribute('data-delete-speed')) || 40;
|
|
3800
|
+
var pause = parseInt(el.getAttribute('data-pause')) || 1500;
|
|
3801
|
+
var loop = el.hasAttribute('data-loop');
|
|
3802
|
+
|
|
3803
|
+
if (words) {
|
|
3804
|
+
// Multiple phrases mode
|
|
3805
|
+
var phrases = words.split('|').map(function(s) { return s.trim(); });
|
|
3806
|
+
var phraseIdx = 0;
|
|
3807
|
+
var charIdx = 0;
|
|
3808
|
+
var deleting = false;
|
|
3809
|
+
|
|
3810
|
+
function tick() {
|
|
3811
|
+
var current = phrases[phraseIdx];
|
|
3812
|
+
if (!deleting) {
|
|
3813
|
+
charIdx++;
|
|
3814
|
+
el.textContent = current.substring(0, charIdx);
|
|
3815
|
+
if (charIdx === current.length) {
|
|
3816
|
+
if (!loop && phraseIdx === phrases.length - 1) return;
|
|
3817
|
+
setTimeout(function() { deleting = true; tick(); }, pause);
|
|
3818
|
+
return;
|
|
3819
|
+
}
|
|
3820
|
+
setTimeout(tick, speed);
|
|
3821
|
+
} else {
|
|
3822
|
+
charIdx--;
|
|
3823
|
+
el.textContent = current.substring(0, charIdx);
|
|
3824
|
+
if (charIdx === 0) {
|
|
3825
|
+
deleting = false;
|
|
3826
|
+
phraseIdx = (phraseIdx + 1) % phrases.length;
|
|
3827
|
+
setTimeout(tick, speed);
|
|
3828
|
+
return;
|
|
3829
|
+
}
|
|
3830
|
+
setTimeout(tick, deleteSpeed);
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3833
|
+
|
|
3834
|
+
el.textContent = '';
|
|
3835
|
+
setTimeout(tick, 500);
|
|
3836
|
+
} else {
|
|
3837
|
+
// Single text mode — type out existing content
|
|
3838
|
+
var text = el.textContent;
|
|
3839
|
+
el.textContent = '';
|
|
3840
|
+
var i = 0;
|
|
3841
|
+
function typeChar() {
|
|
3842
|
+
if (i < text.length) {
|
|
3843
|
+
el.textContent += text[i];
|
|
3844
|
+
i++;
|
|
3845
|
+
setTimeout(typeChar, speed);
|
|
3846
|
+
}
|
|
3847
|
+
}
|
|
3848
|
+
setTimeout(typeChar, 500);
|
|
3849
|
+
}
|
|
3850
|
+
});
|
|
3851
|
+
}
|
|
3852
|
+
|
|
3853
|
+
if (document.readyState === 'loading') {
|
|
3854
|
+
document.addEventListener('DOMContentLoaded', initTypewriters);
|
|
3855
|
+
} else {
|
|
3856
|
+
initTypewriters();
|
|
3857
|
+
}
|
|
3858
|
+
|
|
3859
|
+
// =========================================
|
|
3860
|
+
// Text Rotate
|
|
3861
|
+
// =========================================
|
|
3862
|
+
function initTextRotate() {
|
|
3863
|
+
document.querySelectorAll('[data-sui-text-rotate]').forEach(function(el) {
|
|
3864
|
+
if (el.dataset.suiRotateInit) return;
|
|
3865
|
+
el.dataset.suiRotateInit = '1';
|
|
3866
|
+
var words = el.querySelectorAll('.sui-text-rotate-word');
|
|
3867
|
+
if (words.length < 2) return;
|
|
3868
|
+
var interval = parseInt(el.getAttribute('data-interval')) || 2000;
|
|
3869
|
+
var index = 0;
|
|
3870
|
+
|
|
3871
|
+
words[0].classList.add('active');
|
|
3872
|
+
|
|
3873
|
+
setInterval(function() {
|
|
3874
|
+
var current = words[index];
|
|
3875
|
+
current.classList.remove('active');
|
|
3876
|
+
current.classList.add('exit');
|
|
3877
|
+
setTimeout(function() { current.classList.remove('exit'); }, 400);
|
|
3878
|
+
|
|
3879
|
+
index = (index + 1) % words.length;
|
|
3880
|
+
words[index].classList.add('active');
|
|
3881
|
+
}, interval);
|
|
3882
|
+
});
|
|
3883
|
+
}
|
|
3884
|
+
|
|
3885
|
+
if (document.readyState === 'loading') {
|
|
3886
|
+
document.addEventListener('DOMContentLoaded', initTextRotate);
|
|
3887
|
+
} else {
|
|
3888
|
+
initTextRotate();
|
|
3889
|
+
}
|
|
3890
|
+
|
|
3891
|
+
// =========================================
|
|
3892
|
+
// Copy Button
|
|
3893
|
+
// =========================================
|
|
3894
|
+
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>';
|
|
3895
|
+
var checkSvg = '<svg viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>';
|
|
3896
|
+
|
|
3897
|
+
document.addEventListener('click', function(e) {
|
|
3898
|
+
var btn = e.target.closest('[data-sui-copy]');
|
|
3899
|
+
if (!btn) return;
|
|
3900
|
+
var text = btn.getAttribute('data-sui-copy');
|
|
3901
|
+
if (!text) {
|
|
3902
|
+
var wrap = btn.closest('.sui-copy, .sui-copy-input');
|
|
3903
|
+
if (wrap) {
|
|
3904
|
+
var textEl = wrap.querySelector('.sui-copy-text');
|
|
3905
|
+
var inputEl = wrap.querySelector('.sui-input');
|
|
3906
|
+
text = textEl ? textEl.textContent : inputEl ? inputEl.value : '';
|
|
3907
|
+
}
|
|
3908
|
+
}
|
|
3909
|
+
if (!text) return;
|
|
3910
|
+
try { navigator.clipboard.writeText(text.trim()); } catch (err) {}
|
|
3911
|
+
btn.classList.add('copied');
|
|
3912
|
+
btn.innerHTML = checkSvg;
|
|
3913
|
+
setTimeout(function() {
|
|
3914
|
+
btn.classList.remove('copied');
|
|
3915
|
+
btn.innerHTML = clipboardSvg;
|
|
3916
|
+
}, 1500);
|
|
3917
|
+
});
|
|
3918
|
+
|
|
3774
3919
|
// =========================================
|
|
3775
3920
|
// Diff — Image Compare Slider
|
|
3776
3921
|
// =========================================
|
|
@@ -3922,5 +4067,152 @@ const SoftUI = (() => {
|
|
|
3922
4067
|
updateTreeParent(parentItem);
|
|
3923
4068
|
}
|
|
3924
4069
|
|
|
3925
|
-
|
|
4070
|
+
// =========================================
|
|
4071
|
+
// Tour / Walkthrough
|
|
4072
|
+
// =========================================
|
|
4073
|
+
function tour(steps, options) {
|
|
4074
|
+
options = options || {};
|
|
4075
|
+
var currentStep = 0;
|
|
4076
|
+
var overlay, backdrop, spotlight, tooltip;
|
|
4077
|
+
var padding = options.padding || 8;
|
|
4078
|
+
var noOverlay = options.noOverlay || false;
|
|
4079
|
+
|
|
4080
|
+
function create() {
|
|
4081
|
+
overlay = document.createElement('div');
|
|
4082
|
+
overlay.className = 'sui-tour-overlay' + (noOverlay ? ' sui-tour-no-overlay' : '');
|
|
4083
|
+
backdrop = document.createElement('div');
|
|
4084
|
+
backdrop.className = 'sui-tour-backdrop';
|
|
4085
|
+
spotlight = document.createElement('div');
|
|
4086
|
+
spotlight.className = 'sui-tour-spotlight';
|
|
4087
|
+
tooltip = document.createElement('div');
|
|
4088
|
+
tooltip.className = 'sui-tour-tooltip';
|
|
4089
|
+
overlay.appendChild(backdrop);
|
|
4090
|
+
overlay.appendChild(spotlight);
|
|
4091
|
+
overlay.appendChild(tooltip);
|
|
4092
|
+
document.body.appendChild(overlay);
|
|
4093
|
+
|
|
4094
|
+
backdrop.addEventListener('click', close);
|
|
4095
|
+
}
|
|
4096
|
+
|
|
4097
|
+
var firstShow = true;
|
|
4098
|
+
|
|
4099
|
+
function show(idx) {
|
|
4100
|
+
currentStep = idx;
|
|
4101
|
+
var step = steps[idx];
|
|
4102
|
+
var target = document.querySelector(step.target);
|
|
4103
|
+
|
|
4104
|
+
// Only hide on first show to avoid flash between steps
|
|
4105
|
+
if (firstShow) {
|
|
4106
|
+
spotlight.style.opacity = '0';
|
|
4107
|
+
tooltip.style.opacity = '0';
|
|
4108
|
+
firstShow = false;
|
|
4109
|
+
}
|
|
4110
|
+
|
|
4111
|
+
// Scroll if element isn't comfortably in view (with margin for tooltip)
|
|
4112
|
+
var needsScroll = false;
|
|
4113
|
+
if (target) {
|
|
4114
|
+
var r = target.getBoundingClientRect();
|
|
4115
|
+
var margin = 120;
|
|
4116
|
+
needsScroll = r.top < margin || r.bottom > window.innerHeight - margin;
|
|
4117
|
+
if (needsScroll) target.scrollIntoView({ block: 'center', behavior: 'smooth' });
|
|
4118
|
+
}
|
|
4119
|
+
setTimeout(function() {
|
|
4120
|
+
if (target) {
|
|
4121
|
+
var rect = target.getBoundingClientRect();
|
|
4122
|
+
spotlight.style.top = (rect.top - padding) + 'px';
|
|
4123
|
+
spotlight.style.left = (rect.left - padding) + 'px';
|
|
4124
|
+
spotlight.style.width = (rect.width + padding * 2) + 'px';
|
|
4125
|
+
spotlight.style.height = (rect.height + padding * 2) + 'px';
|
|
4126
|
+
}
|
|
4127
|
+
|
|
4128
|
+
// Build tooltip content
|
|
4129
|
+
var dotsHtml = '';
|
|
4130
|
+
if (steps.length > 1) {
|
|
4131
|
+
dotsHtml = '<div class="sui-tour-dots">';
|
|
4132
|
+
for (var i = 0; i < steps.length; i++) {
|
|
4133
|
+
dotsHtml += '<span class="sui-tour-dot' + (i === idx ? ' active' : '') + '"></span>';
|
|
4134
|
+
}
|
|
4135
|
+
dotsHtml += '</div>';
|
|
4136
|
+
}
|
|
4137
|
+
|
|
4138
|
+
tooltip.innerHTML =
|
|
4139
|
+
'<div class="sui-tour-tooltip-title">' + (step.title || '') + '</div>' +
|
|
4140
|
+
'<div class="sui-tour-tooltip-desc">' + (step.description || '') + '</div>' +
|
|
4141
|
+
'<div class="sui-tour-tooltip-footer">' +
|
|
4142
|
+
dotsHtml +
|
|
4143
|
+
'<div class="sui-tour-tooltip-actions">' +
|
|
4144
|
+
(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>') +
|
|
4145
|
+
(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>') +
|
|
4146
|
+
'</div>' +
|
|
4147
|
+
'</div>';
|
|
4148
|
+
|
|
4149
|
+
// Button handlers
|
|
4150
|
+
var nextBtn = tooltip.querySelector('.sui-tour-next');
|
|
4151
|
+
var prevBtn = tooltip.querySelector('.sui-tour-prev');
|
|
4152
|
+
var skipBtn = tooltip.querySelector('.sui-tour-skip');
|
|
4153
|
+
var doneBtn = tooltip.querySelector('.sui-tour-done');
|
|
4154
|
+
if (nextBtn) nextBtn.addEventListener('click', function() { show(currentStep + 1); });
|
|
4155
|
+
if (prevBtn) prevBtn.addEventListener('click', function() { show(currentStep - 1); });
|
|
4156
|
+
if (skipBtn) skipBtn.addEventListener('click', close);
|
|
4157
|
+
if (doneBtn) doneBtn.addEventListener('click', close);
|
|
4158
|
+
|
|
4159
|
+
// Position tooltip after scroll settles
|
|
4160
|
+
if (target) {
|
|
4161
|
+
var rect = target.getBoundingClientRect();
|
|
4162
|
+
var pos = step.position || 'bottom';
|
|
4163
|
+
var tooltipW = 300;
|
|
4164
|
+
var centerX = rect.left + rect.width / 2 - tooltipW / 2;
|
|
4165
|
+
var top, left;
|
|
4166
|
+
|
|
4167
|
+
tooltip.style.transform = '';
|
|
4168
|
+
|
|
4169
|
+
if (pos === 'bottom') {
|
|
4170
|
+
top = rect.bottom + padding + 12;
|
|
4171
|
+
left = centerX;
|
|
4172
|
+
} else if (pos === 'top') {
|
|
4173
|
+
top = rect.top - padding - 12;
|
|
4174
|
+
left = centerX;
|
|
4175
|
+
tooltip.style.transform = 'translateY(-100%)';
|
|
4176
|
+
} else if (pos === 'left') {
|
|
4177
|
+
top = rect.top + rect.height / 2;
|
|
4178
|
+
left = rect.left - padding - tooltipW - 12;
|
|
4179
|
+
tooltip.style.transform = 'translateY(-50%)';
|
|
4180
|
+
} else if (pos === 'right') {
|
|
4181
|
+
top = rect.top + rect.height / 2;
|
|
4182
|
+
left = rect.right + padding + 12;
|
|
4183
|
+
tooltip.style.transform = 'translateY(-50%)';
|
|
4184
|
+
}
|
|
4185
|
+
|
|
4186
|
+
// Keep tooltip within viewport
|
|
4187
|
+
left = Math.max(8, Math.min(left, window.innerWidth - tooltipW - 8));
|
|
4188
|
+
top = Math.max(8, Math.min(top, window.innerHeight - 200));
|
|
4189
|
+
tooltip.style.top = top + 'px';
|
|
4190
|
+
tooltip.style.left = left + 'px';
|
|
4191
|
+
}
|
|
4192
|
+
// Reveal after positioning
|
|
4193
|
+
spotlight.style.opacity = '1';
|
|
4194
|
+
tooltip.style.opacity = '1';
|
|
4195
|
+
}, needsScroll ? 350 : 50);
|
|
4196
|
+
|
|
4197
|
+
overlay.classList.add('active');
|
|
4198
|
+
}
|
|
4199
|
+
|
|
4200
|
+
function close() {
|
|
4201
|
+
if (overlay) {
|
|
4202
|
+
overlay.classList.remove('active');
|
|
4203
|
+
setTimeout(function() {
|
|
4204
|
+
if (overlay && overlay.parentNode) overlay.parentNode.removeChild(overlay);
|
|
4205
|
+
overlay = null;
|
|
4206
|
+
}, 300);
|
|
4207
|
+
}
|
|
4208
|
+
if (options.onComplete) options.onComplete();
|
|
4209
|
+
}
|
|
4210
|
+
|
|
4211
|
+
create();
|
|
4212
|
+
show(0);
|
|
4213
|
+
|
|
4214
|
+
return { next: function() { show(currentStep + 1); }, prev: function() { show(currentStep - 1); }, close: close, goTo: show };
|
|
4215
|
+
}
|
|
4216
|
+
|
|
4217
|
+
return { modal, sheet, toast, carousel, sidebar, tour };
|
|
3926
4218
|
})();
|