softui-css 1.3.0 → 1.5.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
@@ -909,7 +909,8 @@ const SoftUI = (() => {
909
909
 
910
910
  items.forEach(function(item) {
911
911
  var text = item.textContent.toLowerCase();
912
- var match = !query || text.indexOf(query) !== -1;
912
+ var keywords = (item.getAttribute('data-keywords') || '').toLowerCase();
913
+ var match = !query || text.indexOf(query) !== -1 || keywords.indexOf(query) !== -1;
913
914
  item.hidden = !match;
914
915
  if (match) anyVisible = true;
915
916
  });
@@ -2898,5 +2899,751 @@ const SoftUI = (() => {
2898
2899
  return { collapse: collapse, expand: expand, toggle: toggle, isCollapsed: isCollapsed, el: el };
2899
2900
  }
2900
2901
 
2902
+ // =========================================
2903
+ // Rating
2904
+ // =========================================
2905
+ function ratingIsHalf(star, e) {
2906
+ var rect = star.getBoundingClientRect();
2907
+ return e.clientX < rect.left + rect.width / 2;
2908
+ }
2909
+
2910
+ function ratingEnsureDualSvg(star) {
2911
+ var svgs = star.querySelectorAll('svg');
2912
+ if (svgs.length < 2) {
2913
+ var clone = svgs[0].cloneNode(true);
2914
+ star.appendChild(clone);
2915
+ }
2916
+ }
2917
+
2918
+ function ratingResetSvg(star) {
2919
+ var svgs = star.querySelectorAll('svg');
2920
+ if (svgs.length > 1) {
2921
+ for (var i = svgs.length - 1; i > 0; i--) { svgs[i].remove(); }
2922
+ }
2923
+ }
2924
+
2925
+ document.addEventListener('click', function(e) {
2926
+ var star = e.target.closest('.sui-rating:not(.sui-rating-readonly) .sui-rating-star');
2927
+ if (!star) return;
2928
+ var rating = star.closest('.sui-rating');
2929
+ var stars = Array.from(rating.querySelectorAll('.sui-rating-star'));
2930
+ var index = stars.indexOf(star);
2931
+ var allowHalf = rating.classList.contains('sui-rating-half');
2932
+ var isHalf = allowHalf && ratingIsHalf(star, e);
2933
+ var value = isHalf ? index + 0.5 : index + 1;
2934
+ stars.forEach(function(s, i) {
2935
+ s.classList.remove('active', 'half', 'hover', 'hover-half');
2936
+ ratingResetSvg(s);
2937
+ if (i < index) {
2938
+ s.classList.add('active');
2939
+ } else if (i === index) {
2940
+ if (isHalf) {
2941
+ ratingEnsureDualSvg(s);
2942
+ s.classList.add('half');
2943
+ } else {
2944
+ s.classList.add('active');
2945
+ }
2946
+ }
2947
+ });
2948
+ rating.setAttribute('data-value', value);
2949
+ rating.dispatchEvent(new CustomEvent('sui-rating-change', { detail: { value: value } }));
2950
+ });
2951
+
2952
+ document.addEventListener('mousemove', function(e) {
2953
+ var star = e.target.closest('.sui-rating:not(.sui-rating-readonly) .sui-rating-star');
2954
+ if (!star) return;
2955
+ var rating = star.closest('.sui-rating');
2956
+ var stars = Array.from(rating.querySelectorAll('.sui-rating-star'));
2957
+ var index = stars.indexOf(star);
2958
+ var allowHalf = rating.classList.contains('sui-rating-half');
2959
+ var isHalf = allowHalf && ratingIsHalf(star, e);
2960
+ stars.forEach(function(s, i) {
2961
+ s.classList.remove('hover', 'hover-half');
2962
+ ratingResetSvg(s);
2963
+ if (i < index) {
2964
+ s.classList.add('hover');
2965
+ } else if (i === index) {
2966
+ if (isHalf) {
2967
+ ratingEnsureDualSvg(s);
2968
+ s.classList.add('hover-half');
2969
+ } else {
2970
+ s.classList.add('hover');
2971
+ }
2972
+ }
2973
+ });
2974
+ });
2975
+
2976
+ document.addEventListener('mouseout', function(e) {
2977
+ var star = e.target.closest('.sui-rating:not(.sui-rating-readonly) .sui-rating-star');
2978
+ if (!star) return;
2979
+ var rating = star.closest('.sui-rating');
2980
+ var stars = Array.from(rating.querySelectorAll('.sui-rating-star'));
2981
+ stars.forEach(function(s) {
2982
+ s.classList.remove('hover', 'hover-half');
2983
+ if (!s.classList.contains('half')) { ratingResetSvg(s); }
2984
+ });
2985
+ });
2986
+
2987
+ // =========================================
2988
+ // Color Picker
2989
+ // =========================================
2990
+ document.addEventListener('click', function(e) {
2991
+ var swatch = e.target.closest('.sui-color-picker .sui-color-swatch');
2992
+ if (!swatch) return;
2993
+ var picker = swatch.closest('.sui-color-picker');
2994
+ picker.querySelectorAll('.sui-color-swatch').forEach(function(s) {
2995
+ s.classList.remove('active');
2996
+ });
2997
+ swatch.classList.add('active');
2998
+ var color = swatch.getAttribute('data-color') || swatch.style.background || swatch.style.backgroundColor;
2999
+ picker.setAttribute('data-value', color);
3000
+ picker.dispatchEvent(new CustomEvent('sui-color-change', { detail: { color: color } }));
3001
+ });
3002
+
3003
+ // =========================================
3004
+ // Color Spectrum Picker
3005
+ // =========================================
3006
+ function initSpectrumPickers() {
3007
+ var pickers = document.querySelectorAll('.sui-color-spectrum');
3008
+ pickers.forEach(function(picker) { initSpectrum(picker); });
3009
+ }
3010
+
3011
+ function initSpectrum(picker) {
3012
+ var canvasWrap = picker.querySelector('.sui-color-spectrum-canvas');
3013
+ var canvas = canvasWrap.querySelector('canvas');
3014
+ var ctx = canvas.getContext('2d');
3015
+ var cursor = canvasWrap.querySelector('.sui-color-spectrum-cursor');
3016
+ var hueBar = picker.querySelector('.sui-color-spectrum-hue');
3017
+ var hueCursor = picker.querySelector('.sui-color-spectrum-hue-cursor');
3018
+ var preview = picker.querySelector('.sui-color-spectrum-preview');
3019
+ var hexInput = picker.querySelector('.sui-color-spectrum-hex input');
3020
+ var rInput = picker.querySelector('input[data-channel="r"]');
3021
+ var gInput = picker.querySelector('input[data-channel="g"]');
3022
+ var bInput = picker.querySelector('input[data-channel="b"]');
3023
+
3024
+ var hue = 0, sat = 1, val = 1;
3025
+
3026
+ function resizeCanvas() {
3027
+ canvas.width = canvasWrap.offsetWidth;
3028
+ canvas.height = canvasWrap.offsetHeight;
3029
+ drawSatVal();
3030
+ }
3031
+
3032
+ function hsvToRgb(h, s, v) {
3033
+ var i = Math.floor(h / 60) % 6;
3034
+ var f = h / 60 - Math.floor(h / 60);
3035
+ var p = v * (1 - s);
3036
+ var q = v * (1 - f * s);
3037
+ var t = v * (1 - (1 - f) * s);
3038
+ var r, g, b;
3039
+ switch (i) {
3040
+ case 0: r = v; g = t; b = p; break;
3041
+ case 1: r = q; g = v; b = p; break;
3042
+ case 2: r = p; g = v; b = t; break;
3043
+ case 3: r = p; g = q; b = v; break;
3044
+ case 4: r = t; g = p; b = v; break;
3045
+ case 5: r = v; g = p; b = q; break;
3046
+ }
3047
+ return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
3048
+ }
3049
+
3050
+ function rgbToHex(r, g, b) {
3051
+ return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
3052
+ }
3053
+
3054
+ function hexToRgb(hex) {
3055
+ hex = hex.replace('#', '');
3056
+ if (hex.length === 3) hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
3057
+ var n = parseInt(hex, 16);
3058
+ return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
3059
+ }
3060
+
3061
+ function rgbToHsv(r, g, b) {
3062
+ r /= 255; g /= 255; b /= 255;
3063
+ var max = Math.max(r, g, b), min = Math.min(r, g, b);
3064
+ var h, s, v = max;
3065
+ var d = max - min;
3066
+ s = max === 0 ? 0 : d / max;
3067
+ if (max === min) { h = 0; }
3068
+ else {
3069
+ switch (max) {
3070
+ case r: h = (g - b) / d + (g < b ? 6 : 0); break;
3071
+ case g: h = (b - r) / d + 2; break;
3072
+ case b: h = (r - g) / d + 4; break;
3073
+ }
3074
+ h *= 60;
3075
+ }
3076
+ return [h, s, v];
3077
+ }
3078
+
3079
+ function drawSatVal() {
3080
+ var w = canvas.width, h = canvas.height;
3081
+ var hueRgb = hsvToRgb(hue, 1, 1);
3082
+ var hueColor = 'rgb(' + hueRgb[0] + ',' + hueRgb[1] + ',' + hueRgb[2] + ')';
3083
+ ctx.fillStyle = hueColor;
3084
+ ctx.fillRect(0, 0, w, h);
3085
+ var whiteGrad = ctx.createLinearGradient(0, 0, w, 0);
3086
+ whiteGrad.addColorStop(0, 'rgba(255,255,255,1)');
3087
+ whiteGrad.addColorStop(1, 'rgba(255,255,255,0)');
3088
+ ctx.fillStyle = whiteGrad;
3089
+ ctx.fillRect(0, 0, w, h);
3090
+ var blackGrad = ctx.createLinearGradient(0, 0, 0, h);
3091
+ blackGrad.addColorStop(0, 'rgba(0,0,0,0)');
3092
+ blackGrad.addColorStop(1, 'rgba(0,0,0,1)');
3093
+ ctx.fillStyle = blackGrad;
3094
+ ctx.fillRect(0, 0, w, h);
3095
+ }
3096
+
3097
+ function updateUI() {
3098
+ var rgb = hsvToRgb(hue, sat, val);
3099
+ var hex = rgbToHex(rgb[0], rgb[1], rgb[2]);
3100
+ if (preview) preview.style.background = hex;
3101
+ if (hexInput) hexInput.value = hex.toUpperCase().slice(1);
3102
+ if (rInput) rInput.value = rgb[0];
3103
+ if (gInput) gInput.value = rgb[1];
3104
+ if (bInput) bInput.value = rgb[2];
3105
+
3106
+ cursor.style.left = (sat * 100) + '%';
3107
+ cursor.style.top = ((1 - val) * 100) + '%';
3108
+
3109
+ var hueRgb = hsvToRgb(hue, 1, 1);
3110
+ hueCursor.style.left = (hue / 360 * 100) + '%';
3111
+ hueCursor.style.background = 'rgb(' + hueRgb[0] + ',' + hueRgb[1] + ',' + hueRgb[2] + ')';
3112
+
3113
+ picker.setAttribute('data-value', hex);
3114
+ picker.dispatchEvent(new CustomEvent('sui-color-change', { detail: { hex: hex, rgb: rgb } }));
3115
+ }
3116
+
3117
+ // Canvas drag
3118
+ function onCanvasMove(e) {
3119
+ var rect = canvasWrap.getBoundingClientRect();
3120
+ var x = (e.touches ? e.touches[0].clientX : e.clientX) - rect.left;
3121
+ var y = (e.touches ? e.touches[0].clientY : e.clientY) - rect.top;
3122
+ sat = Math.max(0, Math.min(1, x / rect.width));
3123
+ val = Math.max(0, Math.min(1, 1 - y / rect.height));
3124
+ updateUI();
3125
+ }
3126
+
3127
+ canvasWrap.addEventListener('mousedown', function(e) {
3128
+ e.preventDefault();
3129
+ onCanvasMove(e);
3130
+ function move(ev) { onCanvasMove(ev); }
3131
+ function up() { document.removeEventListener('mousemove', move); document.removeEventListener('mouseup', up); }
3132
+ document.addEventListener('mousemove', move);
3133
+ document.addEventListener('mouseup', up);
3134
+ });
3135
+
3136
+ canvasWrap.addEventListener('touchstart', function(e) {
3137
+ e.preventDefault();
3138
+ onCanvasMove(e);
3139
+ function move(ev) { ev.preventDefault(); onCanvasMove(ev); }
3140
+ function up() { document.removeEventListener('touchmove', move); document.removeEventListener('touchend', up); }
3141
+ document.addEventListener('touchmove', move, { passive: false });
3142
+ document.addEventListener('touchend', up);
3143
+ }, { passive: false });
3144
+
3145
+ // Hue drag
3146
+ function onHueMove(e) {
3147
+ var rect = hueBar.getBoundingClientRect();
3148
+ var x = (e.touches ? e.touches[0].clientX : e.clientX) - rect.left;
3149
+ hue = Math.max(0, Math.min(360, x / rect.width * 360));
3150
+ drawSatVal();
3151
+ updateUI();
3152
+ }
3153
+
3154
+ hueBar.addEventListener('mousedown', function(e) {
3155
+ e.preventDefault();
3156
+ onHueMove(e);
3157
+ function move(ev) { onHueMove(ev); }
3158
+ function up() { document.removeEventListener('mousemove', move); document.removeEventListener('mouseup', up); }
3159
+ document.addEventListener('mousemove', move);
3160
+ document.addEventListener('mouseup', up);
3161
+ });
3162
+
3163
+ hueBar.addEventListener('touchstart', function(e) {
3164
+ e.preventDefault();
3165
+ onHueMove(e);
3166
+ function move(ev) { ev.preventDefault(); onHueMove(ev); }
3167
+ function up() { document.removeEventListener('touchmove', move); document.removeEventListener('touchend', up); }
3168
+ document.addEventListener('touchmove', move, { passive: false });
3169
+ document.addEventListener('touchend', up);
3170
+ }, { passive: false });
3171
+
3172
+ // Hex input
3173
+ if (hexInput) {
3174
+ hexInput.addEventListener('input', function() {
3175
+ var v = hexInput.value.replace('#', '');
3176
+ if (v.length === 6 && /^[0-9A-Fa-f]{6}$/.test(v)) {
3177
+ var rgb = hexToRgb(v);
3178
+ var hsv = rgbToHsv(rgb[0], rgb[1], rgb[2]);
3179
+ hue = hsv[0]; sat = hsv[1]; val = hsv[2];
3180
+ drawSatVal();
3181
+ updateUI();
3182
+ }
3183
+ });
3184
+ }
3185
+
3186
+ // RGB inputs
3187
+ function onRgbInput() {
3188
+ var r = parseInt(rInput.value) || 0;
3189
+ var g = parseInt(gInput.value) || 0;
3190
+ var b = parseInt(bInput.value) || 0;
3191
+ r = Math.max(0, Math.min(255, r));
3192
+ g = Math.max(0, Math.min(255, g));
3193
+ b = Math.max(0, Math.min(255, b));
3194
+ var hsv = rgbToHsv(r, g, b);
3195
+ hue = hsv[0]; sat = hsv[1]; val = hsv[2];
3196
+ drawSatVal();
3197
+ updateUI();
3198
+ }
3199
+
3200
+ if (rInput) rInput.addEventListener('input', onRgbInput);
3201
+ if (gInput) gInput.addEventListener('input', onRgbInput);
3202
+ if (bInput) bInput.addEventListener('input', onRgbInput);
3203
+
3204
+ // Init from data-color attribute or default
3205
+ var initColor = picker.getAttribute('data-color') || '#5B54E0';
3206
+ var initRgb = hexToRgb(initColor);
3207
+ var initHsv = rgbToHsv(initRgb[0], initRgb[1], initRgb[2]);
3208
+ hue = initHsv[0]; sat = initHsv[1]; val = initHsv[2];
3209
+
3210
+ resizeCanvas();
3211
+ updateUI();
3212
+ window.addEventListener('resize', resizeCanvas);
3213
+ }
3214
+
3215
+ if (document.readyState === 'loading') {
3216
+ document.addEventListener('DOMContentLoaded', initSpectrumPickers);
3217
+ } else {
3218
+ initSpectrumPickers();
3219
+ }
3220
+
3221
+ // =========================================
3222
+ // File Upload
3223
+ // =========================================
3224
+ function formatFileSize(bytes) {
3225
+ if (bytes < 1024) return bytes + ' B';
3226
+ if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB';
3227
+ return (bytes / 1048576).toFixed(1) + ' MB';
3228
+ }
3229
+
3230
+ var fileIcons = {
3231
+ file: '<svg viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>',
3232
+ image: '<svg viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>',
3233
+ pdf: '<svg viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="9" y1="15" x2="15" y2="15"/><line x1="9" y1="18" x2="13" y2="18"/><line x1="9" y1="12" x2="11" y2="12"/></svg>',
3234
+ video: '<svg viewBox="0 0 24 24"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>',
3235
+ audio: '<svg viewBox="0 0 24 24"><path d="M9 18V5l12-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="16" r="3"/></svg>',
3236
+ code: '<svg viewBox="0 0 24 24"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>',
3237
+ archive: '<svg viewBox="0 0 24 24"><path d="M21 8v13H3V8"/><path d="M1 3h22v5H1z"/><path d="M10 12h4"/></svg>',
3238
+ spreadsheet: '<svg viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="8" y1="13" x2="16" y2="13"/><line x1="8" y1="17" x2="16" y2="17"/><line x1="12" y1="9" x2="12" y2="21"/></svg>'
3239
+ };
3240
+
3241
+ function getFileType(file) {
3242
+ var type = file.type || '';
3243
+ var ext = file.name.split('.').pop().toLowerCase();
3244
+ if (type.startsWith('image/')) return 'image';
3245
+ if (type === 'application/pdf' || ext === 'pdf') return 'pdf';
3246
+ if (type.startsWith('video/')) return 'video';
3247
+ if (type.startsWith('audio/')) return 'audio';
3248
+ if (/^(js|ts|jsx|tsx|html|css|json|xml|py|rb|go|rs|java|c|cpp|php|sh|yml|yaml)$/.test(ext)) return 'code';
3249
+ if (/^(zip|rar|7z|tar|gz|bz2)$/.test(ext)) return 'archive';
3250
+ if (/^(csv|xlsx|xls|ods)$/.test(ext)) return 'spreadsheet';
3251
+ return 'file';
3252
+ }
3253
+
3254
+ function getFileIcon(file) {
3255
+ return fileIcons[getFileType(file)];
3256
+ }
3257
+
3258
+ function getFileIconClass(file) {
3259
+ return 'sui-file-item-icon-' + getFileType(file);
3260
+ }
3261
+
3262
+ function getOrCreateContainer(zone, cls) {
3263
+ var wrap = zone.closest('.sui-file-upload-wrap');
3264
+ if (!wrap) {
3265
+ wrap = document.createElement('div');
3266
+ wrap.className = 'sui-file-upload-wrap';
3267
+ zone.parentNode.insertBefore(wrap, zone);
3268
+ wrap.appendChild(zone);
3269
+ }
3270
+ var container = wrap.querySelector('.' + cls);
3271
+ if (!container) {
3272
+ container = document.createElement('div');
3273
+ container.className = cls;
3274
+ wrap.appendChild(container);
3275
+ }
3276
+ return container;
3277
+ }
3278
+
3279
+ function renderFileList(zone, files, append) {
3280
+ var container = getOrCreateContainer(zone, 'sui-file-list');
3281
+ if (!append) container.innerHTML = '';
3282
+ for (var i = 0; i < files.length; i++) {
3283
+ var f = files[i];
3284
+ var item = document.createElement('div');
3285
+ item.className = 'sui-file-item';
3286
+ item.innerHTML =
3287
+ '<div class="sui-file-item-icon ' + getFileIconClass(f) + '">' + getFileIcon(f) + '</div>' +
3288
+ '<div class="sui-file-item-info">' +
3289
+ '<div class="sui-file-item-name">' + f.name + '</div>' +
3290
+ '<div class="sui-file-item-size">' + formatFileSize(f.size) + '</div>' +
3291
+ '</div>' +
3292
+ '<button class="sui-file-item-remove" aria-label="Remove">&times;</button>';
3293
+ container.appendChild(item);
3294
+ }
3295
+ }
3296
+
3297
+ function renderFileProgress(zone, files, append) {
3298
+ var container = getOrCreateContainer(zone, 'sui-file-list');
3299
+ if (!append) container.innerHTML = '';
3300
+ for (var i = 0; i < files.length; i++) {
3301
+ var f = files[i];
3302
+ var item = document.createElement('div');
3303
+ item.className = 'sui-file-item';
3304
+ item.innerHTML =
3305
+ '<div class="sui-file-item-icon ' + getFileIconClass(f) + '">' + getFileIcon(f) + '</div>' +
3306
+ '<div class="sui-file-item-info sui-file-item-progress">' +
3307
+ '<div style="display:flex;justify-content:space-between;align-items:center;">' +
3308
+ '<div class="sui-file-item-name">' + f.name + '</div>' +
3309
+ '<span class="sui-file-item-status sui-file-item-status-uploading">0%</span>' +
3310
+ '</div>' +
3311
+ '<div class="sui-progress sui-progress-sm"><div class="sui-progress-bar sui-progress-primary" style="width:0%;"></div></div>' +
3312
+ '</div>' +
3313
+ '<button class="sui-file-item-remove" aria-label="Remove">&times;</button>';
3314
+ container.appendChild(item);
3315
+ simulateProgress(item);
3316
+ }
3317
+ }
3318
+
3319
+ function simulateProgress(item) {
3320
+ var bar = item.querySelector('.sui-progress-bar');
3321
+ var status = item.querySelector('.sui-file-item-status');
3322
+ var pct = 0;
3323
+ var interval = setInterval(function() {
3324
+ pct += Math.floor(Math.random() * 15) + 5;
3325
+ if (pct >= 100) {
3326
+ pct = 100;
3327
+ clearInterval(interval);
3328
+ bar.style.width = '100%';
3329
+ bar.className = 'sui-progress-bar sui-progress-success';
3330
+ status.className = 'sui-file-item-status sui-file-item-status-complete';
3331
+ status.textContent = '\u2713';
3332
+ } else {
3333
+ bar.style.width = pct + '%';
3334
+ status.textContent = pct + '%';
3335
+ }
3336
+ }, 300 + Math.random() * 200);
3337
+ }
3338
+
3339
+ function renderFilePreview(zone, files, append) {
3340
+ var container = getOrCreateContainer(zone, 'sui-file-preview-grid');
3341
+ if (!append) container.innerHTML = '';
3342
+ for (var i = 0; i < files.length; i++) {
3343
+ var f = files[i];
3344
+ if (!f.type || !f.type.startsWith('image/')) continue;
3345
+ var item = document.createElement('div');
3346
+ item.className = 'sui-file-preview-item';
3347
+ item.innerHTML =
3348
+ '<img alt="' + f.name + '">' +
3349
+ '<button class="sui-file-preview-item-remove" aria-label="Remove">&times;</button>';
3350
+ container.appendChild(item);
3351
+ (function(img, file) {
3352
+ var reader = new FileReader();
3353
+ reader.onload = function(e) { img.src = e.target.result; };
3354
+ reader.readAsDataURL(file);
3355
+ })(item.querySelector('img'), f);
3356
+ }
3357
+ }
3358
+
3359
+ // Delegated change on file inputs
3360
+ document.addEventListener('change', function(e) {
3361
+ if (!e.target.matches('.sui-file-upload input[type="file"]')) return;
3362
+ var input = e.target;
3363
+ var zone = input.closest('.sui-file-upload');
3364
+ if (!zone) return;
3365
+ var files = Array.from(input.files);
3366
+ if (!files.length) return;
3367
+ var mode = zone.getAttribute('data-sui-upload') || 'list';
3368
+ if (mode === 'preview') {
3369
+ renderFilePreview(zone, files);
3370
+ } else if (mode === 'progress') {
3371
+ renderFileProgress(zone, files);
3372
+ } else {
3373
+ renderFileList(zone, files);
3374
+ }
3375
+ input.value = '';
3376
+ });
3377
+
3378
+ // Delegated remove clicks
3379
+ document.addEventListener('click', function(e) {
3380
+ var removeBtn = e.target.closest('.sui-file-item-remove, .sui-file-preview-item-remove');
3381
+ if (!removeBtn) return;
3382
+ var item = removeBtn.closest('.sui-file-item, .sui-file-preview-item');
3383
+ if (item) item.remove();
3384
+ });
3385
+
3386
+ // Dragover styling
3387
+ document.addEventListener('dragover', function(e) {
3388
+ var zone = e.target.closest('.sui-file-upload');
3389
+ if (!zone) return;
3390
+ e.preventDefault();
3391
+ zone.classList.add('sui-file-upload-dragover');
3392
+ });
3393
+
3394
+ document.addEventListener('dragleave', function(e) {
3395
+ var zone = e.target.closest('.sui-file-upload');
3396
+ if (!zone) return;
3397
+ zone.classList.remove('sui-file-upload-dragover');
3398
+ });
3399
+
3400
+ document.addEventListener('drop', function(e) {
3401
+ var zone = e.target.closest('.sui-file-upload');
3402
+ if (!zone) return;
3403
+ e.preventDefault();
3404
+ zone.classList.remove('sui-file-upload-dragover');
3405
+ var files = Array.from(e.dataTransfer.files);
3406
+ if (!files.length) return;
3407
+ var mode = zone.getAttribute('data-sui-upload') || 'list';
3408
+ if (mode === 'preview') {
3409
+ renderFilePreview(zone, files, true);
3410
+ } else if (mode === 'progress') {
3411
+ renderFileProgress(zone, files, true);
3412
+ } else {
3413
+ renderFileList(zone, files, true);
3414
+ }
3415
+ });
3416
+
3417
+ // =========================================
3418
+ // Radial Progress
3419
+ // =========================================
3420
+ function initRadialProgress() {
3421
+ var radials = document.querySelectorAll('.sui-radial[data-value]');
3422
+ radials.forEach(function(el) {
3423
+ var fill = el.querySelector('.sui-radial-fill');
3424
+ if (!fill) return;
3425
+ var value = parseFloat(el.getAttribute('data-value')) || 0;
3426
+ value = Math.max(0, Math.min(100, value));
3427
+ var circumference = parseFloat(fill.getAttribute('stroke-dasharray') || fill.style.strokeDasharray);
3428
+ if (!circumference) {
3429
+ var r = fill.getAttribute('r');
3430
+ circumference = 2 * Math.PI * parseFloat(r);
3431
+ }
3432
+ fill.style.strokeDasharray = circumference;
3433
+ fill.style.strokeDashoffset = circumference;
3434
+ var valueEl = el.querySelector('.sui-radial-value');
3435
+ var duration = el.classList.contains('sui-radial-animated') ? 1200 : 600;
3436
+
3437
+ requestAnimationFrame(function() {
3438
+ requestAnimationFrame(function() {
3439
+ var offset = circumference - (value / 100) * circumference;
3440
+ fill.style.strokeDashoffset = offset;
3441
+
3442
+ if (valueEl) {
3443
+ var start = performance.now();
3444
+ function tick(now) {
3445
+ var elapsed = now - start;
3446
+ var progress = Math.min(elapsed / duration, 1);
3447
+ var current = Math.round(progress * value);
3448
+ valueEl.textContent = current + '%';
3449
+ if (progress < 1) requestAnimationFrame(tick);
3450
+ }
3451
+ requestAnimationFrame(tick);
3452
+ }
3453
+ });
3454
+ });
3455
+ });
3456
+ }
3457
+
3458
+ if (document.readyState === 'loading') {
3459
+ document.addEventListener('DOMContentLoaded', initRadialProgress);
3460
+ } else {
3461
+ initRadialProgress();
3462
+ }
3463
+
3464
+ // =========================================
3465
+ // Number Input
3466
+ // =========================================
3467
+ document.addEventListener('click', function(e) {
3468
+ var btn = e.target.closest('.sui-number-input-btn');
3469
+ if (!btn) return;
3470
+ var wrap = btn.closest('.sui-number-input');
3471
+ var input = wrap.querySelector('input[type="number"]');
3472
+ if (!input) return;
3473
+ var step = parseFloat(input.step) || 1;
3474
+ var min = input.min !== '' ? parseFloat(input.min) : -Infinity;
3475
+ var max = input.max !== '' ? parseFloat(input.max) : Infinity;
3476
+ var val = parseFloat(input.value) || 0;
3477
+ if (btn.getAttribute('data-action') === 'decrement') {
3478
+ val = Math.max(min, val - step);
3479
+ } else {
3480
+ val = Math.min(max, val + step);
3481
+ }
3482
+ input.value = val;
3483
+ input.dispatchEvent(new Event('change', { bubbles: true }));
3484
+ });
3485
+
3486
+ // =========================================
3487
+ // Password Toggle
3488
+ // =========================================
3489
+ document.addEventListener('click', function(e) {
3490
+ var btn = e.target.closest('.sui-password-toggle');
3491
+ if (!btn) return;
3492
+ e.stopPropagation();
3493
+ var wrap = btn.closest('.sui-password-input');
3494
+ var input = wrap.querySelector('input');
3495
+ if (!input) return;
3496
+ var isPassword = input.type === 'password';
3497
+ input.type = isPassword ? 'text' : 'password';
3498
+ btn.classList.toggle('active');
3499
+ }, true);
3500
+
3501
+ // =========================================
3502
+ // Tags Input
3503
+ // =========================================
3504
+ document.addEventListener('keydown', function(e) {
3505
+ var input = e.target.closest('.sui-tags-input-field');
3506
+ if (!input) return;
3507
+ var wrap = input.closest('.sui-tags-input');
3508
+ if (e.key === 'Enter' || e.key === ',') {
3509
+ e.preventDefault();
3510
+ var val = input.value.trim().replace(/,$/, '');
3511
+ if (!val) return;
3512
+ var tag = document.createElement('span');
3513
+ tag.className = 'sui-chip';
3514
+ tag.textContent = val;
3515
+ var closeBtn = document.createElement('button');
3516
+ closeBtn.className = 'sui-chip-close';
3517
+ closeBtn.setAttribute('aria-label', 'Remove');
3518
+ tag.appendChild(closeBtn);
3519
+ wrap.insertBefore(tag, input);
3520
+ input.value = '';
3521
+ } else if (e.key === 'Backspace' && !input.value) {
3522
+ var tags = wrap.querySelectorAll('.sui-chip');
3523
+ if (tags.length) tags[tags.length - 1].remove();
3524
+ }
3525
+ });
3526
+
3527
+ document.addEventListener('click', function(e) {
3528
+ var dismiss = e.target.closest('.sui-tags-input .sui-chip-close');
3529
+ if (dismiss) {
3530
+ dismiss.closest('.sui-chip').remove();
3531
+ return;
3532
+ }
3533
+ var wrap = e.target.closest('.sui-tags-input');
3534
+ if (wrap) {
3535
+ var input = wrap.querySelector('.sui-tags-input-field');
3536
+ if (input) input.focus();
3537
+ }
3538
+ });
3539
+
3540
+ // =========================================
3541
+ // Swap
3542
+ // =========================================
3543
+ // Lock slide swap dimensions so absolute children don't collapse container
3544
+ function initSlideSwaps() {
3545
+ document.querySelectorAll('.sui-swap-slide, .sui-swap-slide-x').forEach(function(swap) {
3546
+ if (swap.dataset.suiSlideInit) return;
3547
+ var children = swap.querySelectorAll('.sui-swap-on, .sui-swap-off, .sui-swap-state');
3548
+ var maxW = 0, maxH = 0;
3549
+ children.forEach(function(c) {
3550
+ var prev = c.style.cssText;
3551
+ c.style.position = 'relative';
3552
+ c.style.opacity = '1';
3553
+ c.style.transform = 'none';
3554
+ maxW = Math.max(maxW, c.offsetWidth);
3555
+ maxH = Math.max(maxH, c.offsetHeight);
3556
+ c.style.cssText = prev;
3557
+ });
3558
+ if (maxW) swap.style.width = maxW + 'px';
3559
+ if (maxH) swap.style.height = maxH + 'px';
3560
+ swap.dataset.suiSlideInit = '1';
3561
+ });
3562
+ }
3563
+
3564
+ if (document.readyState === 'loading') {
3565
+ document.addEventListener('DOMContentLoaded', initSlideSwaps);
3566
+ } else {
3567
+ initSlideSwaps();
3568
+ }
3569
+
3570
+ document.addEventListener('click', function(e) {
3571
+ var swap = e.target.closest('.sui-swap');
3572
+ if (!swap) return;
3573
+ if (swap.classList.contains('sui-swap-cycle')) {
3574
+ var states = Array.from(swap.querySelectorAll('.sui-swap-state'));
3575
+ var current = states.findIndex(function(s) { return s.classList.contains('active'); });
3576
+ var next = (current + 1) % states.length;
3577
+ states.forEach(function(s) { s.classList.remove('active'); });
3578
+ states[next].classList.add('active');
3579
+ swap.setAttribute('data-state', next);
3580
+ swap.dispatchEvent(new CustomEvent('sui-swap-change', { detail: { state: next, total: states.length } }));
3581
+ } else {
3582
+ swap.classList.toggle('active');
3583
+ swap.dispatchEvent(new CustomEvent('sui-swap-change', { detail: { active: swap.classList.contains('active') } }));
3584
+ }
3585
+ });
3586
+
3587
+ // =========================================
3588
+ // Dock — magnification effect
3589
+ // =========================================
3590
+ var dockMaxScale = 1.5;
3591
+ var dockRange = 3;
3592
+
3593
+ document.addEventListener('mousemove', function(e) {
3594
+ var dock = e.target.closest('.sui-dock');
3595
+ if (!dock) return;
3596
+ if (dock.classList.contains('sui-dock-no-scale')) return;
3597
+ var iconOnly = dock.classList.contains('sui-dock-icon-scale');
3598
+ var items = Array.from(dock.querySelectorAll('.sui-dock-item'));
3599
+ var isVertical = dock.classList.contains('sui-dock-vertical');
3600
+
3601
+ items.forEach(function(item) {
3602
+ var rect = item.getBoundingClientRect();
3603
+ var center = isVertical
3604
+ ? rect.top + rect.height / 2
3605
+ : rect.left + rect.width / 2;
3606
+ var mouse = isVertical ? e.clientY : e.clientX;
3607
+ var baseSize = dock.classList.contains('sui-dock-sm') ? 32
3608
+ : dock.classList.contains('sui-dock-lg') ? 52 : 40;
3609
+ var dist = Math.abs(mouse - center) / baseSize;
3610
+
3611
+ if (dist < dockRange) {
3612
+ var scale = dockMaxScale - (dist / dockRange) * (dockMaxScale - 1);
3613
+ if (iconOnly) {
3614
+ item.style.width = '';
3615
+ item.style.height = '';
3616
+ var svg = item.querySelector('svg');
3617
+ if (svg) svg.style.transform = 'scale(' + scale + ')';
3618
+ } else {
3619
+ var newSize = Math.round(baseSize * scale);
3620
+ item.style.width = newSize + 'px';
3621
+ item.style.height = newSize + 'px';
3622
+ }
3623
+ } else {
3624
+ item.style.width = '';
3625
+ item.style.height = '';
3626
+ if (iconOnly) {
3627
+ var svg = item.querySelector('svg');
3628
+ if (svg) svg.style.transform = '';
3629
+ }
3630
+ }
3631
+ });
3632
+ });
3633
+
3634
+ document.addEventListener('mouseleave', function(e) {
3635
+ if (!e.target.classList || !e.target.classList.contains('sui-dock')) return;
3636
+ var iconOnly = e.target.classList.contains('sui-dock-icon-scale');
3637
+ var items = e.target.querySelectorAll('.sui-dock-item');
3638
+ items.forEach(function(item) {
3639
+ item.style.width = '';
3640
+ item.style.height = '';
3641
+ if (iconOnly) {
3642
+ var svg = item.querySelector('svg');
3643
+ if (svg) svg.style.transform = '';
3644
+ }
3645
+ });
3646
+ }, true);
3647
+
2901
3648
  return { modal, sheet, toast, carousel, sidebar };
2902
3649
  })();