softui-css 1.6.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.js CHANGED
@@ -3771,6 +3771,135 @@ const SoftUI = (() => {
3771
3771
  openLightbox(images, index);
3772
3772
  });
3773
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
+
3774
3903
  // =========================================
3775
3904
  // Diff — Image Compare Slider
3776
3905
  // =========================================
@@ -3922,5 +4051,152 @@ const SoftUI = (() => {
3922
4051
  updateTreeParent(parentItem);
3923
4052
  }
3924
4053
 
3925
- return { modal, sheet, toast, carousel, sidebar };
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 };
3926
4202
  })();