softui-css 1.11.0 → 1.12.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
@@ -300,6 +300,9 @@ const SoftUI = (() => {
300
300
  // Selectable pricing
301
301
  initSelectablePricing();
302
302
 
303
+ // Drawers
304
+ initDrawers();
305
+
303
306
  // Data Tables
304
307
  initDataTables();
305
308
 
@@ -527,16 +530,16 @@ const SoftUI = (() => {
527
530
  });
528
531
 
529
532
  document.addEventListener('click', function(e) {
530
- var trigger = e.target.closest('.sui-collapsible-trigger');
533
+ const trigger = e.target.closest('.sui-collapsible-trigger');
531
534
  if (!trigger) return;
532
535
 
533
- var collapsible = trigger.closest('.sui-collapsible');
536
+ const collapsible = trigger.closest('.sui-collapsible');
534
537
  if (!collapsible) return;
535
538
 
536
- var content = collapsible.querySelector('.sui-collapsible-content');
539
+ const content = collapsible.querySelector('.sui-collapsible-content');
537
540
  if (!content) return;
538
541
 
539
- var isOpen = collapsible.classList.contains('open');
542
+ const isOpen = collapsible.classList.contains('open');
540
543
 
541
544
  if (isOpen) {
542
545
  collapsible.classList.remove('open');
@@ -714,7 +717,7 @@ const SoftUI = (() => {
714
717
  // Context Menu
715
718
  // =========================================
716
719
  function initContextMenu() {
717
- var openMenu = null;
720
+ let openMenu = null;
718
721
 
719
722
  function closeAll() {
720
723
  if (openMenu) {
@@ -731,9 +734,9 @@ const SoftUI = (() => {
731
734
  menu.style.top = '0px';
732
735
  menu.classList.add('open');
733
736
 
734
- var rect = menu.getBoundingClientRect();
735
- var vw = window.innerWidth;
736
- var vh = window.innerHeight;
737
+ const rect = menu.getBoundingClientRect();
738
+ const vw = window.innerWidth;
739
+ const vh = window.innerHeight;
737
740
 
738
741
  if (x + rect.width > vw) x = vw - rect.width - 4;
739
742
  if (y + rect.height > vh) y = vh - rect.height - 4;
@@ -746,21 +749,21 @@ const SoftUI = (() => {
746
749
 
747
750
  // Right-click triggers
748
751
  document.addEventListener('contextmenu', function(e) {
749
- var trigger = e.target.closest('[data-sui-context]');
752
+ const trigger = e.target.closest('[data-sui-context]');
750
753
  if (!trigger) return;
751
754
 
752
755
  e.preventDefault();
753
756
  closeAll();
754
757
 
755
- var menuId = trigger.getAttribute('data-sui-context');
756
- var menu = document.getElementById(menuId);
758
+ const menuId = trigger.getAttribute('data-sui-context');
759
+ const menu = document.getElementById(menuId);
757
760
  if (!menu) return;
758
761
 
759
762
  positionMenu(menu, e.clientX, e.clientY);
760
763
  openMenu = menu;
761
764
 
762
765
  // Focus first item for keyboard nav
763
- var firstItem = menu.querySelector('.sui-context-item:not(.disabled), .sui-context-sub-trigger');
766
+ const firstItem = menu.querySelector('.sui-context-item:not(.disabled), .sui-context-sub-trigger');
764
767
  if (firstItem) firstItem.focus();
765
768
  });
766
769
 
@@ -773,15 +776,15 @@ const SoftUI = (() => {
773
776
 
774
777
  // Click on item closes (unless checkbox/radio)
775
778
  document.addEventListener('click', function(e) {
776
- var item = e.target.closest('.sui-context-item');
779
+ const item = e.target.closest('.sui-context-item');
777
780
  if (!item || !openMenu) return;
778
781
  if (!item.closest('.sui-context-menu')) return;
779
782
 
780
783
  // Checkbox toggle
781
784
  if (item.hasAttribute('data-sui-context-check')) {
782
- var check = item.querySelector('.sui-context-check');
785
+ const check = item.querySelector('.sui-context-check');
783
786
  if (check) {
784
- var isChecked = check.textContent.trim() !== '';
787
+ const isChecked = check.textContent.trim() !== '';
785
788
  check.textContent = isChecked ? '' : '\u2713';
786
789
  }
787
790
  return; // Don't close on checkbox click
@@ -789,11 +792,11 @@ const SoftUI = (() => {
789
792
 
790
793
  // Radio toggle
791
794
  if (item.hasAttribute('data-sui-context-radio')) {
792
- var group = item.getAttribute('data-sui-context-radio');
795
+ const group = item.getAttribute('data-sui-context-radio');
793
796
  openMenu.querySelectorAll('[data-sui-context-radio="' + group + '"] .sui-context-check').forEach(function(c) {
794
797
  c.textContent = '';
795
798
  });
796
- var radio = item.querySelector('.sui-context-check');
799
+ const radio = item.querySelector('.sui-context-check');
797
800
  if (radio) radio.textContent = '\u2022';
798
801
  return; // Don't close on radio click
799
802
  }
@@ -806,13 +809,13 @@ const SoftUI = (() => {
806
809
 
807
810
  // Submenu hover
808
811
  document.addEventListener('mouseenter', function(e) {
809
- var subTrigger = e.target.closest && e.target.closest('.sui-context-sub-trigger');
812
+ const subTrigger = e.target.closest && e.target.closest('.sui-context-sub-trigger');
810
813
  if (!subTrigger) return;
811
- var sub = subTrigger.closest('.sui-context-sub');
814
+ const sub = subTrigger.closest('.sui-context-sub');
812
815
  if (!sub) return;
813
816
 
814
817
  // Close sibling subs
815
- var parent = sub.parentElement;
818
+ const parent = sub.parentElement;
816
819
  if (parent) {
817
820
  parent.querySelectorAll(':scope > .sui-context-sub.open').forEach(function(s) {
818
821
  if (s !== sub) s.classList.remove('open');
@@ -822,7 +825,7 @@ const SoftUI = (() => {
822
825
  }, true);
823
826
 
824
827
  document.addEventListener('mouseleave', function(e) {
825
- var sub = e.target.closest && e.target.closest('.sui-context-sub');
828
+ const sub = e.target.closest && e.target.closest('.sui-context-sub');
826
829
  if (!sub) return;
827
830
  // Only close if not moving into the sub-content
828
831
  setTimeout(function() {
@@ -836,7 +839,7 @@ const SoftUI = (() => {
836
839
  document.addEventListener('keydown', function(e) {
837
840
  if (e.key === 'Escape' && openMenu) {
838
841
  // If a sub is open, close it first
839
- var openSub = openMenu.querySelector('.sui-context-sub.open');
842
+ const openSub = openMenu.querySelector('.sui-context-sub.open');
840
843
  if (openSub) {
841
844
  openSub.classList.remove('open');
842
845
  openSub.querySelector('.sui-context-sub-trigger').focus();
@@ -850,11 +853,11 @@ const SoftUI = (() => {
850
853
  // Arrow key navigation
851
854
  if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
852
855
  e.preventDefault();
853
- var activeContainer = openMenu.querySelector('.sui-context-sub.open > .sui-context-sub-content') || openMenu;
854
- var items = Array.from(activeContainer.querySelectorAll(':scope > .sui-context-item:not(.disabled), :scope > .sui-context-sub > .sui-context-sub-trigger'));
856
+ const activeContainer = openMenu.querySelector('.sui-context-sub.open > .sui-context-sub-content') || openMenu;
857
+ const items = Array.from(activeContainer.querySelectorAll(':scope > .sui-context-item:not(.disabled), :scope > .sui-context-sub > .sui-context-sub-trigger'));
855
858
  if (items.length === 0) return;
856
859
 
857
- var current = items.indexOf(document.activeElement);
860
+ let current = items.indexOf(document.activeElement);
858
861
  if (e.key === 'ArrowDown') {
859
862
  current = current < items.length - 1 ? current + 1 : 0;
860
863
  } else {
@@ -865,12 +868,12 @@ const SoftUI = (() => {
865
868
 
866
869
  // ArrowRight opens submenu
867
870
  if (e.key === 'ArrowRight') {
868
- var focused = document.activeElement;
871
+ const focused = document.activeElement;
869
872
  if (focused && focused.classList.contains('sui-context-sub-trigger')) {
870
- var sub = focused.closest('.sui-context-sub');
873
+ const sub = focused.closest('.sui-context-sub');
871
874
  if (sub) {
872
875
  sub.classList.add('open');
873
- var first = sub.querySelector('.sui-context-sub-content .sui-context-item:not(.disabled), .sui-context-sub-content .sui-context-sub-trigger');
876
+ const first = sub.querySelector('.sui-context-sub-content .sui-context-item:not(.disabled), .sui-context-sub-content .sui-context-sub-trigger');
874
877
  if (first) first.focus();
875
878
  }
876
879
  }
@@ -878,7 +881,7 @@ const SoftUI = (() => {
878
881
 
879
882
  // ArrowLeft closes submenu
880
883
  if (e.key === 'ArrowLeft') {
881
- var openSub = document.activeElement && document.activeElement.closest('.sui-context-sub.open');
884
+ const openSub = document.activeElement && document.activeElement.closest('.sui-context-sub.open');
882
885
  if (openSub && openSub.closest('.sui-context-menu') === openMenu) {
883
886
  openSub.classList.remove('open');
884
887
  openSub.querySelector('.sui-context-sub-trigger').focus();
@@ -887,7 +890,7 @@ const SoftUI = (() => {
887
890
 
888
891
  // Enter activates
889
892
  if (e.key === 'Enter') {
890
- var focused = document.activeElement;
893
+ const focused = document.activeElement;
891
894
  if (focused && (focused.classList.contains('sui-context-item') || focused.classList.contains('sui-context-sub-trigger'))) {
892
895
  focused.click();
893
896
  }
@@ -904,15 +907,15 @@ const SoftUI = (() => {
904
907
  // =========================================
905
908
  function initCommand() {
906
909
  document.querySelectorAll('.sui-command[data-sui-command]').forEach(function(cmd) {
907
- var input = cmd.querySelector('.sui-command-input');
908
- var list = cmd.querySelector('.sui-command-list');
909
- var empty = cmd.querySelector('.sui-command-empty');
910
+ const input = cmd.querySelector('.sui-command-input');
911
+ const list = cmd.querySelector('.sui-command-list');
912
+ const empty = cmd.querySelector('.sui-command-empty');
910
913
  if (!input || !list) return;
911
914
 
912
- var items = list.querySelectorAll('.sui-command-item');
913
- var groups = list.querySelectorAll('.sui-command-group');
914
- var separators = list.querySelectorAll('.sui-command-separator');
915
- var focusedIndex = -1;
915
+ const items = list.querySelectorAll('.sui-command-item');
916
+ const groups = list.querySelectorAll('.sui-command-group');
917
+ const separators = list.querySelectorAll('.sui-command-separator');
918
+ let focusedIndex = -1;
916
919
 
917
920
  function getVisibleItems() {
918
921
  return Array.from(list.querySelectorAll('.sui-command-item:not([hidden])'));
@@ -927,29 +930,29 @@ const SoftUI = (() => {
927
930
  }
928
931
 
929
932
  function filter() {
930
- var query = input.value.toLowerCase().trim();
931
- var anyVisible = false;
933
+ const query = input.value.toLowerCase().trim();
934
+ let anyVisible = false;
932
935
 
933
936
  items.forEach(function(item) {
934
- var text = item.textContent.toLowerCase();
935
- var keywords = (item.getAttribute('data-keywords') || '').toLowerCase();
936
- var match = !query || text.indexOf(query) !== -1 || keywords.indexOf(query) !== -1;
937
+ const text = item.textContent.toLowerCase();
938
+ const keywords = (item.getAttribute('data-keywords') || '').toLowerCase();
939
+ const match = !query || text.indexOf(query) !== -1 || keywords.indexOf(query) !== -1;
937
940
  item.hidden = !match;
938
941
  if (match) anyVisible = true;
939
942
  });
940
943
 
941
944
  // Hide groups with no visible items
942
945
  groups.forEach(function(group) {
943
- var hasVisible = group.querySelector('.sui-command-item:not([hidden])');
946
+ const hasVisible = group.querySelector('.sui-command-item:not([hidden])');
944
947
  group.hidden = !hasVisible;
945
948
  });
946
949
 
947
950
  // Hide separators between hidden groups
948
951
  separators.forEach(function(sep) {
949
- var next = sep.nextElementSibling;
950
- var prev = sep.previousElementSibling;
951
- var nextHidden = next && next.hidden;
952
- var prevHidden = prev && prev.hidden;
952
+ const next = sep.nextElementSibling;
953
+ const prev = sep.previousElementSibling;
954
+ const nextHidden = next && next.hidden;
955
+ const prevHidden = prev && prev.hidden;
953
956
  sep.hidden = nextHidden || prevHidden;
954
957
  });
955
958
 
@@ -965,7 +968,7 @@ const SoftUI = (() => {
965
968
 
966
969
  // Keyboard nav
967
970
  cmd.addEventListener('keydown', function(e) {
968
- var visibleItems = getVisibleItems();
971
+ const visibleItems = getVisibleItems();
969
972
 
970
973
  if (e.key === 'ArrowDown') {
971
974
  e.preventDefault();
@@ -990,14 +993,14 @@ const SoftUI = (() => {
990
993
  // Mouse hover updates focus
991
994
  items.forEach(function(item) {
992
995
  item.addEventListener('mouseenter', function() {
993
- var visibleItems = getVisibleItems();
996
+ const visibleItems = getVisibleItems();
994
997
  focusedIndex = visibleItems.indexOf(item);
995
998
  updateFocus(visibleItems);
996
999
  });
997
1000
  });
998
1001
 
999
1002
  // Initial focus on first item
1000
- var initial = getVisibleItems();
1003
+ const initial = getVisibleItems();
1001
1004
  if (initial.length > 0) {
1002
1005
  focusedIndex = 0;
1003
1006
  updateFocus(initial);
@@ -1006,8 +1009,8 @@ const SoftUI = (() => {
1006
1009
 
1007
1010
  // Dialog mode — Cmd+K / Ctrl+K
1008
1011
  document.querySelectorAll('.sui-command-dialog').forEach(function(dialog) {
1009
- var cmd = dialog.querySelector('.sui-command');
1010
- var input = cmd ? cmd.querySelector('.sui-command-input') : null;
1012
+ const cmd = dialog.querySelector('.sui-command');
1013
+ const input = cmd ? cmd.querySelector('.sui-command-input') : null;
1011
1014
 
1012
1015
  function openDialog() {
1013
1016
  dialog.classList.add('open');
@@ -1025,7 +1028,7 @@ const SoftUI = (() => {
1025
1028
  }
1026
1029
 
1027
1030
  // Cmd+K / Ctrl+K to open
1028
- var shortcut = dialog.dataset.suiCommandKey || 'k';
1031
+ const shortcut = dialog.dataset.suiCommandKey || 'k';
1029
1032
  document.addEventListener('keydown', function(e) {
1030
1033
  if ((e.metaKey || e.ctrlKey) && e.key === shortcut) {
1031
1034
  e.preventDefault();
@@ -1065,9 +1068,9 @@ const SoftUI = (() => {
1065
1068
  // Calendar
1066
1069
  // =========================================
1067
1070
  function initCalendar() {
1068
- var MONTHS = ['January','February','March','April','May','June','July','August','September','October','November','December'];
1069
- var MONTHS_SHORT = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
1070
- var DAYS = ['Su','Mo','Tu','We','Th','Fr','Sa'];
1071
+ const MONTHS = ['January','February','March','April','May','June','July','August','September','October','November','December'];
1072
+ const MONTHS_SHORT = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
1073
+ const DAYS = ['Su','Mo','Tu','We','Th','Fr','Sa'];
1071
1074
 
1072
1075
  function daysInMonth(year, month) {
1073
1076
  return new Date(year, month + 1, 0).getDate();
@@ -1079,23 +1082,23 @@ const SoftUI = (() => {
1079
1082
 
1080
1083
  function between(d, start, end) {
1081
1084
  if (!start || !end) return false;
1082
- var t = d.getTime(), s = Math.min(start.getTime(), end.getTime()), e = Math.max(start.getTime(), end.getTime());
1085
+ const t = d.getTime(), s = Math.min(start.getTime(), end.getTime()), e = Math.max(start.getTime(), end.getTime());
1083
1086
  return t > s && t < e;
1084
1087
  }
1085
1088
 
1086
1089
  function parseDate(str) {
1087
1090
  if (!str) return null;
1088
1091
  if (str === 'today') return new Date(new Date().setHours(0,0,0,0));
1089
- var parts = str.split('-');
1092
+ const parts = str.split('-');
1090
1093
  if (parts.length === 3) return new Date(+parts[0], +parts[1] - 1, +parts[2]);
1091
1094
  return null;
1092
1095
  }
1093
1096
 
1094
1097
  function formatDate(d, includeTime, hour, minute, period) {
1095
- var str = MONTHS[d.getMonth()].substring(0, 3) + ' ' + d.getDate() + ', ' + d.getFullYear();
1098
+ let str = MONTHS[d.getMonth()].substring(0, 3) + ' ' + d.getDate() + ', ' + d.getFullYear();
1096
1099
  if (includeTime) {
1097
- var hh = (hour !== undefined && hour !== null) ? hour : 12;
1098
- var mm = (minute !== undefined && minute !== null) ? minute : 0;
1100
+ const hh = (hour !== undefined && hour !== null) ? hour : 12;
1101
+ const mm = (minute !== undefined && minute !== null) ? minute : 0;
1099
1102
  str += ' ' + pad(hh) + ':' + pad(mm);
1100
1103
  if (period) str += ' ' + period;
1101
1104
  }
@@ -1103,23 +1106,23 @@ const SoftUI = (() => {
1103
1106
  }
1104
1107
 
1105
1108
  document.querySelectorAll('.sui-calendar[data-sui-calendar]').forEach(function(cal) {
1106
- var mode = cal.dataset.suiCalendar || 'single';
1107
- var today = new Date();
1109
+ const mode = cal.dataset.suiCalendar || 'single';
1110
+ const today = new Date();
1108
1111
  today.setHours(0,0,0,0);
1109
1112
 
1110
- var minDate = parseDate(cal.dataset.suiMin);
1111
- var maxDate = parseDate(cal.dataset.suiMax);
1113
+ const minDate = parseDate(cal.dataset.suiMin);
1114
+ const maxDate = parseDate(cal.dataset.suiMax);
1112
1115
 
1113
- var disabledDays = [];
1116
+ const disabledDays = [];
1114
1117
  if (cal.dataset.suiDisabled) {
1115
1118
  cal.dataset.suiDisabled.split(',').forEach(function(s) {
1116
- var d = parseDate(s.trim());
1119
+ const d = parseDate(s.trim());
1117
1120
  if (d) disabledDays.push(d);
1118
1121
  });
1119
1122
  }
1120
1123
 
1121
1124
  function isDisabled(d) {
1122
- for (var i = 0; i < disabledDays.length; i++) {
1125
+ for (let i = 0; i < disabledDays.length; i++) {
1123
1126
  if (sameDay(d, disabledDays[i])) return true;
1124
1127
  }
1125
1128
  if (minDate && d < minDate) return true;
@@ -1127,18 +1130,18 @@ const SoftUI = (() => {
1127
1130
  return false;
1128
1131
  }
1129
1132
 
1130
- var selected = null;
1131
- var rangeStart = null;
1132
- var rangeEnd = null;
1133
- var defaultPlaceholder = '';
1134
- var viewMode = 'days'; // 'days', 'months', 'years'
1135
- var yearPageStart = 0;
1133
+ let selected = null;
1134
+ let rangeStart = null;
1135
+ let rangeEnd = null;
1136
+ let defaultPlaceholder = '';
1137
+ let viewMode = 'days'; // 'days', 'months', 'years'
1138
+ let yearPageStart = 0;
1136
1139
 
1137
1140
  // Time picker
1138
- var hasTime = cal.hasAttribute('data-sui-calendar-time');
1139
- var is24h = cal.getAttribute('data-sui-calendar-time') === '24h';
1140
- var timeHour = is24h ? 0 : 12, timeMinute = 0, timePeriod = 'AM';
1141
- var timeRow = null, hourInput = null, minuteInput = null, periodBtn = null;
1141
+ const hasTime = cal.hasAttribute('data-sui-calendar-time');
1142
+ const is24h = cal.getAttribute('data-sui-calendar-time') === '24h';
1143
+ let timeHour = is24h ? 0 : 12, timeMinute = 0, timePeriod = 'AM';
1144
+ let timeRow = null, hourInput = null, minuteInput = null, periodBtn = null;
1142
1145
 
1143
1146
  if (hasTime) {
1144
1147
  timeRow = cal.querySelector('.sui-calendar-time');
@@ -1146,7 +1149,7 @@ const SoftUI = (() => {
1146
1149
  timeRow = document.createElement('div');
1147
1150
  timeRow.className = 'sui-calendar-time';
1148
1151
 
1149
- var label = document.createElement('span');
1152
+ const label = document.createElement('span');
1150
1153
  label.className = 'sui-calendar-time-label';
1151
1154
  label.textContent = 'Time';
1152
1155
  timeRow.appendChild(label);
@@ -1159,7 +1162,7 @@ const SoftUI = (() => {
1159
1162
  hourInput.setAttribute('aria-label', 'Hour');
1160
1163
  timeRow.appendChild(hourInput);
1161
1164
 
1162
- var sep = document.createElement('span');
1165
+ const sep = document.createElement('span');
1163
1166
  sep.className = 'sui-calendar-time-sep';
1164
1167
  sep.textContent = ':';
1165
1168
  timeRow.appendChild(sep);
@@ -1181,7 +1184,7 @@ const SoftUI = (() => {
1181
1184
  }
1182
1185
 
1183
1186
  // Insert before clear button or append
1184
- var clearBtn = cal.querySelector('[data-sui-calendar-clear]');
1187
+ const clearBtn = cal.querySelector('[data-sui-calendar-clear]');
1185
1188
  if (clearBtn) {
1186
1189
  cal.insertBefore(timeRow, clearBtn);
1187
1190
  } else {
@@ -1193,11 +1196,11 @@ const SoftUI = (() => {
1193
1196
  periodBtn = timeRow.querySelector('.sui-calendar-time-period');
1194
1197
  }
1195
1198
 
1196
- var hourMax = is24h ? 23 : 12;
1197
- var hourMin = is24h ? 0 : 1;
1199
+ const hourMax = is24h ? 23 : 12;
1200
+ const hourMin = is24h ? 0 : 1;
1198
1201
 
1199
1202
  function parseHour(v) {
1200
- var n = parseInt(v, 10);
1203
+ let n = parseInt(v, 10);
1201
1204
  if (isNaN(n) || n < 0) n = 0;
1202
1205
  if (n > 23) n = 23;
1203
1206
  if (is24h) return { hour: n };
@@ -1207,10 +1210,10 @@ const SoftUI = (() => {
1207
1210
  if (n === 12) return { hour: 12, period: 'PM' };
1208
1211
  return { hour: n - 12, period: 'PM' };
1209
1212
  }
1210
- function clampMinute(v) { var n = parseInt(v, 10); if (isNaN(n) || n < 0) return 0; if (n > 59) return 59; return n; }
1213
+ function clampMinute(v) { const n = parseInt(v, 10); if (isNaN(n) || n < 0) return 0; if (n > 59) return 59; return n; }
1211
1214
 
1212
1215
  hourInput.addEventListener('blur', function() {
1213
- var result = parseHour(this.value);
1216
+ const result = parseHour(this.value);
1214
1217
  timeHour = result.hour;
1215
1218
  if (!is24h && result.period) {
1216
1219
  timePeriod = result.period;
@@ -1253,31 +1256,31 @@ const SoftUI = (() => {
1253
1256
  }
1254
1257
  }
1255
1258
 
1256
- var monthContainers = cal.querySelectorAll('.sui-calendar-month');
1257
- var isMultiMonth = monthContainers.length > 0;
1259
+ let monthContainers = cal.querySelectorAll('.sui-calendar-month');
1260
+ const isMultiMonth = monthContainers.length > 0;
1258
1261
  if (!isMultiMonth) monthContainers = [cal];
1259
1262
 
1260
- var viewOffsets = [];
1263
+ const viewOffsets = [];
1261
1264
  monthContainers.forEach(function(mc, i) { viewOffsets.push(i); });
1262
1265
 
1263
- var viewYear = today.getFullYear();
1264
- var viewMonth = today.getMonth();
1266
+ let viewYear = today.getFullYear();
1267
+ let viewMonth = today.getMonth();
1265
1268
 
1266
- var prevBtn = cal.querySelector('[data-sui-calendar-prev]');
1267
- var nextBtn = cal.querySelector('[data-sui-calendar-next]');
1268
- var titleEl = cal.querySelector('.sui-calendar-header .sui-calendar-title');
1269
+ const prevBtn = cal.querySelector('[data-sui-calendar-prev]');
1270
+ const nextBtn = cal.querySelector('[data-sui-calendar-next]');
1271
+ const titleEl = cal.querySelector('.sui-calendar-header .sui-calendar-title');
1269
1272
 
1270
1273
  function renderDays() {
1271
1274
  viewMode = 'days';
1272
1275
  if (timeRow) timeRow.style.display = '';
1273
1276
  monthContainers.forEach(function(mc, idx) {
1274
- var m = viewMonth + viewOffsets[idx];
1275
- var y = viewYear;
1277
+ let m = viewMonth + viewOffsets[idx];
1278
+ let y = viewYear;
1276
1279
  while (m > 11) { m -= 12; y++; }
1277
1280
  while (m < 0) { m += 12; y--; }
1278
1281
 
1279
1282
  // Title
1280
- var t = mc.querySelector('.sui-calendar-title');
1283
+ const t = mc.querySelector('.sui-calendar-title');
1281
1284
  if (t) {
1282
1285
  if (idx === 0 && !isMultiMonth && t === titleEl) {
1283
1286
  t.textContent = MONTHS[m] + ' ' + y;
@@ -1287,24 +1290,24 @@ const SoftUI = (() => {
1287
1290
  }
1288
1291
  }
1289
1292
 
1290
- var grid = mc.querySelector('.sui-calendar-grid');
1293
+ const grid = mc.querySelector('.sui-calendar-grid');
1291
1294
  if (!grid) return;
1292
1295
  grid.innerHTML = '';
1293
1296
  grid.style.gridTemplateColumns = 'repeat(7, 1fr)';
1294
1297
 
1295
1298
  DAYS.forEach(function(d) {
1296
- var lbl = document.createElement('div');
1299
+ const lbl = document.createElement('div');
1297
1300
  lbl.className = 'sui-calendar-day-label';
1298
1301
  lbl.textContent = d;
1299
1302
  grid.appendChild(lbl);
1300
1303
  });
1301
1304
 
1302
- var firstDay = new Date(y, m, 1).getDay();
1303
- var total = daysInMonth(y, m);
1305
+ const firstDay = new Date(y, m, 1).getDay();
1306
+ const total = daysInMonth(y, m);
1304
1307
 
1305
- var prevTotal = daysInMonth(y, m - 1);
1306
- for (var p = firstDay - 1; p >= 0; p--) {
1307
- var btn = document.createElement('button');
1308
+ const prevTotal = daysInMonth(y, m - 1);
1309
+ for (let p = firstDay - 1; p >= 0; p--) {
1310
+ const btn = document.createElement('button');
1308
1311
  btn.className = 'sui-calendar-day outside';
1309
1312
  btn.textContent = prevTotal - p;
1310
1313
  btn.type = 'button';
@@ -1312,9 +1315,9 @@ const SoftUI = (() => {
1312
1315
  grid.appendChild(btn);
1313
1316
  }
1314
1317
 
1315
- for (var d = 1; d <= total; d++) {
1316
- var date = new Date(y, m, d);
1317
- var btn = document.createElement('button');
1318
+ for (let d = 1; d <= total; d++) {
1319
+ const date = new Date(y, m, d);
1320
+ const btn = document.createElement('button');
1318
1321
  btn.className = 'sui-calendar-day';
1319
1322
  btn.textContent = d;
1320
1323
  btn.type = 'button';
@@ -1335,7 +1338,7 @@ const SoftUI = (() => {
1335
1338
  e.stopPropagation();
1336
1339
  if (mode === 'single') {
1337
1340
  selected = dt;
1338
- var detail = { date: dt };
1341
+ const detail = { date: dt };
1339
1342
  if (hasTime) { detail.hour = timeHour; detail.minute = timeMinute; detail.period = timePeriod; }
1340
1343
  cal.dispatchEvent(new CustomEvent('sui-date-select', { detail: detail }));
1341
1344
  } else if (mode === 'range') {
@@ -1355,10 +1358,10 @@ const SoftUI = (() => {
1355
1358
  grid.appendChild(btn);
1356
1359
  }
1357
1360
 
1358
- var totalCells = firstDay + total;
1359
- var remaining = totalCells % 7 === 0 ? 0 : 7 - (totalCells % 7);
1360
- for (var n = 1; n <= remaining; n++) {
1361
- var btn = document.createElement('button');
1361
+ const totalCells = firstDay + total;
1362
+ const remaining = totalCells % 7 === 0 ? 0 : 7 - (totalCells % 7);
1363
+ for (let n = 1; n <= remaining; n++) {
1364
+ const btn = document.createElement('button');
1362
1365
  btn.className = 'sui-calendar-day outside';
1363
1366
  btn.textContent = n;
1364
1367
  btn.type = 'button';
@@ -1379,13 +1382,13 @@ const SoftUI = (() => {
1379
1382
  }
1380
1383
 
1381
1384
  // Only render in first (or only) grid
1382
- var grid = monthContainers[0].querySelector('.sui-calendar-grid');
1385
+ const grid = monthContainers[0].querySelector('.sui-calendar-grid');
1383
1386
  if (!grid) return;
1384
1387
  grid.innerHTML = '';
1385
1388
  grid.style.gridTemplateColumns = 'repeat(4, 1fr)';
1386
1389
 
1387
- for (var m = 0; m < 12; m++) {
1388
- var btn = document.createElement('button');
1390
+ for (let m = 0; m < 12; m++) {
1391
+ const btn = document.createElement('button');
1389
1392
  btn.className = 'sui-calendar-day';
1390
1393
  btn.textContent = MONTHS_SHORT[m];
1391
1394
  btn.type = 'button';
@@ -1408,20 +1411,20 @@ const SoftUI = (() => {
1408
1411
  function renderYears() {
1409
1412
  viewMode = 'years';
1410
1413
  if (timeRow) timeRow.style.display = 'none';
1411
- var start = yearPageStart;
1412
- var end = start + 11;
1414
+ const start = yearPageStart;
1415
+ const end = start + 11;
1413
1416
  if (titleEl) {
1414
1417
  titleEl.textContent = start + ' – ' + end;
1415
1418
  titleEl.style.cursor = 'default';
1416
1419
  }
1417
1420
 
1418
- var grid = monthContainers[0].querySelector('.sui-calendar-grid');
1421
+ const grid = monthContainers[0].querySelector('.sui-calendar-grid');
1419
1422
  if (!grid) return;
1420
1423
  grid.innerHTML = '';
1421
1424
  grid.style.gridTemplateColumns = 'repeat(4, 1fr)';
1422
1425
 
1423
- for (var yr = start; yr <= end; yr++) {
1424
- var btn = document.createElement('button');
1426
+ for (let yr = start; yr <= end; yr++) {
1427
+ const btn = document.createElement('button');
1425
1428
  btn.className = 'sui-calendar-day';
1426
1429
  btn.textContent = yr;
1427
1430
  btn.type = 'button';
@@ -1442,9 +1445,9 @@ const SoftUI = (() => {
1442
1445
  }
1443
1446
 
1444
1447
  function updateClear() {
1445
- var clearBtn = cal.querySelector('[data-sui-calendar-clear]');
1448
+ const clearBtn = cal.querySelector('[data-sui-calendar-clear]');
1446
1449
  if (clearBtn) {
1447
- var hasSelection = mode === 'single' ? !!selected : !!(rangeStart || rangeEnd);
1450
+ const hasSelection = mode === 'single' ? !!selected : !!(rangeStart || rangeEnd);
1448
1451
  clearBtn.style.display = hasSelection ? '' : 'none';
1449
1452
  }
1450
1453
  }
@@ -1515,11 +1518,11 @@ const SoftUI = (() => {
1515
1518
  renderDays();
1516
1519
 
1517
1520
  // Date Picker integration
1518
- var picker = cal.closest('.sui-datepicker');
1521
+ const picker = cal.closest('.sui-datepicker');
1519
1522
  if (picker) {
1520
- var trigger = picker.querySelector('.sui-datepicker-trigger');
1521
- var popover = picker.querySelector('.sui-datepicker-popover');
1522
- var placeholderEl = trigger ? trigger.querySelector('.sui-datepicker-placeholder') : null;
1523
+ const trigger = picker.querySelector('.sui-datepicker-trigger');
1524
+ const popover = picker.querySelector('.sui-datepicker-popover');
1525
+ const placeholderEl = trigger ? trigger.querySelector('.sui-datepicker-placeholder') : null;
1523
1526
  if (placeholderEl) defaultPlaceholder = placeholderEl.textContent;
1524
1527
 
1525
1528
  if (trigger && popover) {
@@ -1537,13 +1540,13 @@ const SoftUI = (() => {
1537
1540
 
1538
1541
  cal.addEventListener('sui-date-select', function(e) {
1539
1542
  if (!trigger) return;
1540
- var span = trigger.querySelector('.sui-datepicker-value') || trigger.querySelector('.sui-datepicker-placeholder');
1543
+ const span = trigger.querySelector('.sui-datepicker-value') || trigger.querySelector('.sui-datepicker-placeholder');
1541
1544
  if (mode === 'single' && e.detail.date) {
1542
- var text = formatDate(e.detail.date, hasTime, e.detail.hour, e.detail.minute, e.detail.is24h ? null : e.detail.period);
1545
+ const text = formatDate(e.detail.date, hasTime, e.detail.hour, e.detail.minute, e.detail.is24h ? null : e.detail.period);
1543
1546
  if (span) { span.textContent = text; span.className = 'sui-datepicker-value'; }
1544
1547
  if (!hasTime && popover) popover.classList.remove('open');
1545
1548
  } else if (mode === 'range' && e.detail.start && e.detail.end) {
1546
- var text = formatDate(e.detail.start) + ' – ' + formatDate(e.detail.end);
1549
+ const text = formatDate(e.detail.start) + ' – ' + formatDate(e.detail.end);
1547
1550
  if (span) { span.textContent = text; span.className = 'sui-datepicker-value'; }
1548
1551
  if (popover) popover.classList.remove('open');
1549
1552
  }
@@ -1551,7 +1554,7 @@ const SoftUI = (() => {
1551
1554
  });
1552
1555
 
1553
1556
  cal.addEventListener('sui-date-clear', function() {
1554
- var span = trigger.querySelector('.sui-datepicker-value') || trigger.querySelector('.sui-datepicker-placeholder');
1557
+ const span = trigger.querySelector('.sui-datepicker-value') || trigger.querySelector('.sui-datepicker-placeholder');
1555
1558
  if (span) { span.textContent = defaultPlaceholder; span.className = 'sui-datepicker-placeholder'; }
1556
1559
  updateClear();
1557
1560
  });
@@ -1564,18 +1567,18 @@ const SoftUI = (() => {
1564
1567
  // =========================================
1565
1568
  function initTimePicker() {
1566
1569
  document.querySelectorAll('.sui-timepicker[data-sui-timepicker]').forEach(function(tp) {
1567
- var is24h = tp.getAttribute('data-sui-timepicker') === '24h';
1568
- var hourMax = is24h ? 23 : 12;
1569
- var hourMin = is24h ? 0 : 1;
1570
- var tHour = is24h ? 0 : 12, tMinute = 0, tPeriod = 'AM';
1570
+ const is24h = tp.getAttribute('data-sui-timepicker') === '24h';
1571
+ const hourMax = is24h ? 23 : 12;
1572
+ const hourMin = is24h ? 0 : 1;
1573
+ let tHour = is24h ? 0 : 12, tMinute = 0, tPeriod = 'AM';
1571
1574
 
1572
- var hInput = tp.querySelectorAll('.sui-calendar-time-input')[0];
1573
- var mInput = tp.querySelectorAll('.sui-calendar-time-input')[1];
1574
- var pBtn = tp.querySelector('.sui-calendar-time-period');
1575
+ let hInput = tp.querySelectorAll('.sui-calendar-time-input')[0];
1576
+ let mInput = tp.querySelectorAll('.sui-calendar-time-input')[1];
1577
+ let pBtn = tp.querySelector('.sui-calendar-time-period');
1575
1578
 
1576
1579
  if (!hInput || !mInput) {
1577
1580
  // Auto-build the UI
1578
- var label = document.createElement('span');
1581
+ const label = document.createElement('span');
1579
1582
  label.className = 'sui-calendar-time-label';
1580
1583
  label.textContent = 'Time';
1581
1584
  tp.appendChild(label);
@@ -1588,7 +1591,7 @@ const SoftUI = (() => {
1588
1591
  hInput.setAttribute('aria-label', 'Hour');
1589
1592
  tp.appendChild(hInput);
1590
1593
 
1591
- var sep = document.createElement('span');
1594
+ const sep = document.createElement('span');
1592
1595
  sep.className = 'sui-calendar-time-sep';
1593
1596
  sep.textContent = ':';
1594
1597
  tp.appendChild(sep);
@@ -1611,7 +1614,7 @@ const SoftUI = (() => {
1611
1614
  }
1612
1615
 
1613
1616
  function parseH(v) {
1614
- var n = parseInt(v, 10);
1617
+ let n = parseInt(v, 10);
1615
1618
  if (isNaN(n) || n < 0) n = 0;
1616
1619
  if (n > 23) n = 23;
1617
1620
  if (is24h) return { hour: n };
@@ -1620,14 +1623,14 @@ const SoftUI = (() => {
1620
1623
  if (n === 12) return { hour: 12, period: 'PM' };
1621
1624
  return { hour: n - 12, period: 'PM' };
1622
1625
  }
1623
- function clampM(v) { var n = parseInt(v, 10); if (isNaN(n) || n < 0) return 0; if (n > 59) return 59; return n; }
1626
+ function clampM(v) { const n = parseInt(v, 10); if (isNaN(n) || n < 0) return 0; if (n > 59) return 59; return n; }
1624
1627
 
1625
1628
  function fireChange() {
1626
1629
  tp.dispatchEvent(new CustomEvent('sui-time-change', { detail: { hour: tHour, minute: tMinute, period: is24h ? null : tPeriod, is24h: is24h } }));
1627
1630
  }
1628
1631
 
1629
1632
  hInput.addEventListener('blur', function() {
1630
- var result = parseH(this.value);
1633
+ const result = parseH(this.value);
1631
1634
  tHour = result.hour;
1632
1635
  if (!is24h && result.period) { tPeriod = result.period; if (pBtn) pBtn.textContent = tPeriod; }
1633
1636
  this.value = pad(tHour);
@@ -1661,7 +1664,7 @@ const SoftUI = (() => {
1661
1664
  // Menubar
1662
1665
  // =========================================
1663
1666
  function initMenubar() {
1664
- var menubarOpen = false;
1667
+ let menubarOpen = false;
1665
1668
 
1666
1669
  function closeAllMenus(bar) {
1667
1670
  bar.querySelectorAll('.sui-menubar-menu.open').forEach(function(m) {
@@ -1685,9 +1688,9 @@ const SoftUI = (() => {
1685
1688
  }
1686
1689
 
1687
1690
  document.addEventListener('click', function(e) {
1688
- var trigger = e.target.closest('.sui-menubar-trigger');
1691
+ const trigger = e.target.closest('.sui-menubar-trigger');
1689
1692
  if (trigger) {
1690
- var menu = trigger.closest('.sui-menubar-menu');
1693
+ const menu = trigger.closest('.sui-menubar-menu');
1691
1694
  if (menu.classList.contains('open')) {
1692
1695
  closeAllMenus(menu.closest('.sui-menubar'));
1693
1696
  } else {
@@ -1698,10 +1701,10 @@ const SoftUI = (() => {
1698
1701
  }
1699
1702
 
1700
1703
  // Submenu triggers
1701
- var subTrigger = e.target.closest('.sui-menubar-sub-trigger');
1704
+ const subTrigger = e.target.closest('.sui-menubar-sub-trigger');
1702
1705
  if (subTrigger) {
1703
- var sub = subTrigger.closest('.sui-menubar-sub');
1704
- var isOpen = sub.classList.contains('open');
1706
+ const sub = subTrigger.closest('.sui-menubar-sub');
1707
+ const isOpen = sub.classList.contains('open');
1705
1708
  // Close sibling subs
1706
1709
  sub.parentElement.querySelectorAll('.sui-menubar-sub.open').forEach(function(s) {
1707
1710
  s.classList.remove('open');
@@ -1712,9 +1715,9 @@ const SoftUI = (() => {
1712
1715
  }
1713
1716
 
1714
1717
  // Clicking a menubar item closes the menu
1715
- var item = e.target.closest('.sui-menubar-item');
1718
+ const item = e.target.closest('.sui-menubar-item');
1716
1719
  if (item && item.closest('.sui-menubar')) {
1717
- var bar = item.closest('.sui-menubar');
1720
+ const bar = item.closest('.sui-menubar');
1718
1721
  closeAllMenus(bar);
1719
1722
  return;
1720
1723
  }
@@ -1728,9 +1731,9 @@ const SoftUI = (() => {
1728
1731
  // Hover to switch menus when one is already open
1729
1732
  document.addEventListener('mouseenter', function(e) {
1730
1733
  if (!menubarOpen) return;
1731
- var trigger = e.target.closest ? e.target.closest('.sui-menubar-trigger') : null;
1734
+ const trigger = e.target.closest ? e.target.closest('.sui-menubar-trigger') : null;
1732
1735
  if (!trigger) return;
1733
- var menu = trigger.closest('.sui-menubar-menu');
1736
+ const menu = trigger.closest('.sui-menubar-menu');
1734
1737
  if (menu && !menu.classList.contains('open')) {
1735
1738
  openMenu(menu);
1736
1739
  }
@@ -1764,16 +1767,16 @@ const SoftUI = (() => {
1764
1767
  // =========================================
1765
1768
  function initCombobox() {
1766
1769
  document.querySelectorAll('.sui-combobox').forEach(function(combo) {
1767
- var trigger = combo.querySelector('.sui-combobox-trigger');
1768
- var content = combo.querySelector('.sui-combobox-content');
1769
- var input = combo.querySelector('.sui-combobox-input');
1770
- var items = combo.querySelectorAll('.sui-combobox-item');
1771
- var valueEl = combo.querySelector('.sui-combobox-value');
1772
- var chipsEl = combo.querySelector('.sui-combobox-chips');
1773
- var emptyEl = combo.querySelector('.sui-combobox-empty');
1774
- var clearBtn = combo.querySelector('.sui-combobox-clear');
1775
- var isMultiple = combo.classList.contains('sui-combobox-multiple');
1776
- var placeholder = valueEl ? valueEl.textContent : '';
1770
+ const trigger = combo.querySelector('.sui-combobox-trigger');
1771
+ const content = combo.querySelector('.sui-combobox-content');
1772
+ const input = combo.querySelector('.sui-combobox-input');
1773
+ const items = combo.querySelectorAll('.sui-combobox-item');
1774
+ const valueEl = combo.querySelector('.sui-combobox-value');
1775
+ const chipsEl = combo.querySelector('.sui-combobox-chips');
1776
+ const emptyEl = combo.querySelector('.sui-combobox-empty');
1777
+ const clearBtn = combo.querySelector('.sui-combobox-clear');
1778
+ const isMultiple = combo.classList.contains('sui-combobox-multiple');
1779
+ let placeholder = valueEl ? valueEl.textContent : '';
1777
1780
  if (chipsEl) placeholder = chipsEl.textContent.trim();
1778
1781
 
1779
1782
  if (!trigger || !content) return;
@@ -1797,18 +1800,18 @@ const SoftUI = (() => {
1797
1800
  }
1798
1801
 
1799
1802
  function filterItems(query) {
1800
- var q = query.toLowerCase();
1801
- var visibleCount = 0;
1803
+ const q = query.toLowerCase();
1804
+ let visibleCount = 0;
1802
1805
  items.forEach(function(item) {
1803
- var text = item.textContent.toLowerCase();
1804
- var match = !q || text.indexOf(q) !== -1;
1806
+ const text = item.textContent.toLowerCase();
1807
+ const match = !q || text.indexOf(q) !== -1;
1805
1808
  item.style.display = match ? '' : 'none';
1806
1809
  if (match) visibleCount++;
1807
1810
  });
1808
1811
  // Show/hide groups based on visible children
1809
1812
  combo.querySelectorAll('.sui-combobox-label').forEach(function(label) {
1810
- var next = label.nextElementSibling;
1811
- var hasVisible = false;
1813
+ let next = label.nextElementSibling;
1814
+ let hasVisible = false;
1812
1815
  while (next && !next.classList.contains('sui-combobox-label') && !next.classList.contains('sui-combobox-separator')) {
1813
1816
  if (next.classList.contains('sui-combobox-item') && next.style.display !== 'none') hasVisible = true;
1814
1817
  next = next.nextElementSibling;
@@ -1825,7 +1828,7 @@ const SoftUI = (() => {
1825
1828
 
1826
1829
  function updateClear() {
1827
1830
  if (!clearBtn) return;
1828
- var hasSelection = combo.querySelectorAll('.sui-combobox-item.selected').length > 0;
1831
+ const hasSelection = combo.querySelectorAll('.sui-combobox-item.selected').length > 0;
1829
1832
  clearBtn.classList.toggle('visible', hasSelection);
1830
1833
  }
1831
1834
 
@@ -1842,9 +1845,9 @@ const SoftUI = (() => {
1842
1845
  function updateChips() {
1843
1846
  if (!chipsEl) return;
1844
1847
  chipsEl.innerHTML = '';
1845
- var selectedItems = combo.querySelectorAll('.sui-combobox-item.selected');
1848
+ const selectedItems = combo.querySelectorAll('.sui-combobox-item.selected');
1846
1849
  if (selectedItems.length === 0) {
1847
- var ph = document.createElement('span');
1850
+ const ph = document.createElement('span');
1848
1851
  ph.className = 'placeholder';
1849
1852
  ph.textContent = placeholder;
1850
1853
  chipsEl.appendChild(ph);
@@ -1852,10 +1855,10 @@ const SoftUI = (() => {
1852
1855
  return;
1853
1856
  }
1854
1857
  selectedItems.forEach(function(item) {
1855
- var chip = document.createElement('span');
1858
+ const chip = document.createElement('span');
1856
1859
  chip.className = 'sui-combobox-chip';
1857
1860
  chip.textContent = item.getAttribute('data-value') || item.textContent.trim();
1858
- var remove = document.createElement('span');
1861
+ const remove = document.createElement('span');
1859
1862
  remove.className = 'sui-combobox-chip-remove';
1860
1863
  remove.innerHTML = '&#10005;';
1861
1864
  remove.addEventListener('click', function(e) {
@@ -1935,19 +1938,19 @@ const SoftUI = (() => {
1935
1938
  // =========================================
1936
1939
  function initResizable() {
1937
1940
  document.querySelectorAll('.sui-resizable').forEach(function(container) {
1938
- var isVertical = container.classList.contains('sui-resizable-vertical');
1939
- var handles = container.querySelectorAll(':scope > .sui-resizable-handle');
1941
+ const isVertical = container.classList.contains('sui-resizable-vertical');
1942
+ const handles = container.querySelectorAll(':scope > .sui-resizable-handle');
1940
1943
 
1941
1944
  // Initialize panels with flex-grow from data-size or equal split
1942
- var panels = Array.from(container.querySelectorAll(':scope > .sui-resizable-panel'));
1945
+ const panels = Array.from(container.querySelectorAll(':scope > .sui-resizable-panel'));
1943
1946
  panels.forEach(function(p) {
1944
- var size = parseFloat(p.getAttribute('data-size')) || (100 / panels.length);
1947
+ const size = parseFloat(p.getAttribute('data-size')) || (100 / panels.length);
1945
1948
  p.style.flexGrow = size;
1946
1949
  });
1947
1950
 
1948
1951
  handles.forEach(function(handle) {
1949
- var prevPanel = handle.previousElementSibling;
1950
- var nextPanel = handle.nextElementSibling;
1952
+ const prevPanel = handle.previousElementSibling;
1953
+ const nextPanel = handle.nextElementSibling;
1951
1954
  if (!prevPanel || !nextPanel) return;
1952
1955
 
1953
1956
  handle.setAttribute('tabindex', '0');
@@ -1963,13 +1966,13 @@ const SoftUI = (() => {
1963
1966
  }
1964
1967
 
1965
1968
  function resize(delta) {
1966
- var prevG = getGrow(prevPanel);
1967
- var nextG = getGrow(nextPanel);
1968
- var total = prevG + nextG;
1969
- var prevMin = getMin(prevPanel);
1970
- var nextMin = getMin(nextPanel);
1971
- var newPrev = Math.max(prevMin, Math.min(total - nextMin, prevG + delta));
1972
- var newNext = total - newPrev;
1969
+ const prevG = getGrow(prevPanel);
1970
+ const nextG = getGrow(nextPanel);
1971
+ const total = prevG + nextG;
1972
+ const prevMin = getMin(prevPanel);
1973
+ const nextMin = getMin(nextPanel);
1974
+ const newPrev = Math.max(prevMin, Math.min(total - nextMin, prevG + delta));
1975
+ const newNext = total - newPrev;
1973
1976
  prevPanel.style.flexGrow = newPrev;
1974
1977
  nextPanel.style.flexGrow = newNext;
1975
1978
  }
@@ -1979,27 +1982,27 @@ const SoftUI = (() => {
1979
1982
  handle.focus();
1980
1983
  handle.classList.add('dragging');
1981
1984
 
1982
- var prevG = getGrow(prevPanel);
1983
- var nextG = getGrow(nextPanel);
1984
- var totalG = prevG + nextG;
1985
+ const prevG = getGrow(prevPanel);
1986
+ const nextG = getGrow(nextPanel);
1987
+ const totalG = prevG + nextG;
1985
1988
 
1986
1989
  // Measure actual pixel sizes of the two panels
1987
- var prevPx = isVertical ? prevPanel.offsetHeight : prevPanel.offsetWidth;
1988
- var nextPx = isVertical ? nextPanel.offsetHeight : nextPanel.offsetWidth;
1989
- var pairPx = prevPx + nextPx;
1990
- var startPos = isVertical ? e.clientY : e.clientX;
1990
+ const prevPx = isVertical ? prevPanel.offsetHeight : prevPanel.offsetWidth;
1991
+ const nextPx = isVertical ? nextPanel.offsetHeight : nextPanel.offsetWidth;
1992
+ const pairPx = prevPx + nextPx;
1993
+ const startPos = isVertical ? e.clientY : e.clientX;
1991
1994
 
1992
- var prevMin = getMin(prevPanel);
1993
- var nextMin = getMin(nextPanel);
1995
+ const prevMin = getMin(prevPanel);
1996
+ const nextMin = getMin(nextPanel);
1994
1997
 
1995
1998
  function onPointerMove(ev) {
1996
- var pos = isVertical ? ev.clientY : ev.clientX;
1997
- var delta = pos - startPos;
1999
+ const pos = isVertical ? ev.clientY : ev.clientX;
2000
+ let delta = pos - startPos;
1998
2001
  // Clamp delta so panels stay within 0..pairPx range
1999
2002
  delta = Math.max(-prevPx, Math.min(nextPx, delta));
2000
- var ratio = pairPx > 0 ? delta / pairPx : 0;
2001
- var newPrev = Math.max(prevMin, Math.min(totalG - nextMin, prevG + ratio * totalG));
2002
- var newNext = totalG - newPrev;
2003
+ const ratio = pairPx > 0 ? delta / pairPx : 0;
2004
+ const newPrev = Math.max(prevMin, Math.min(totalG - nextMin, prevG + ratio * totalG));
2005
+ const newNext = totalG - newPrev;
2003
2006
  prevPanel.style.flexGrow = newPrev;
2004
2007
  nextPanel.style.flexGrow = newNext;
2005
2008
  }
@@ -2018,9 +2021,9 @@ const SoftUI = (() => {
2018
2021
 
2019
2022
  // Keyboard: Arrow keys resize, Home/End for extremes
2020
2023
  handle.addEventListener('keydown', function(e) {
2021
- var step = e.shiftKey ? 10 : 2;
2022
- var growKey = isVertical ? 'ArrowDown' : 'ArrowRight';
2023
- var shrinkKey = isVertical ? 'ArrowUp' : 'ArrowLeft';
2024
+ const step = e.shiftKey ? 10 : 2;
2025
+ const growKey = isVertical ? 'ArrowDown' : 'ArrowRight';
2026
+ const shrinkKey = isVertical ? 'ArrowUp' : 'ArrowLeft';
2024
2027
 
2025
2028
  if (e.key === growKey) {
2026
2029
  e.preventDefault();
@@ -2144,13 +2147,13 @@ const SoftUI = (() => {
2144
2147
  document.querySelectorAll('.sui-otp[data-sui-otp]').forEach(function(otp) {
2145
2148
  if (otp.dataset.suiOtpDisabled !== undefined) return;
2146
2149
 
2147
- var slots = otp.querySelectorAll('.sui-otp-slot');
2148
- var len = slots.length;
2149
- var pattern = otp.dataset.suiOtpPattern || 'digits'; // "digits" or "alphanumeric"
2150
- var regex = pattern === 'alphanumeric' ? /^[a-zA-Z0-9]$/ : /^[0-9]$/;
2150
+ const slots = otp.querySelectorAll('.sui-otp-slot');
2151
+ const len = slots.length;
2152
+ const pattern = otp.dataset.suiOtpPattern || 'digits'; // "digits" or "alphanumeric"
2153
+ const regex = pattern === 'alphanumeric' ? /^[a-zA-Z0-9]$/ : /^[0-9]$/;
2151
2154
 
2152
2155
  // Create hidden input
2153
- var input = document.createElement('input');
2156
+ const input = document.createElement('input');
2154
2157
  input.className = 'sui-otp-input';
2155
2158
  input.setAttribute('inputmode', pattern === 'alphanumeric' ? 'text' : 'numeric');
2156
2159
  input.setAttribute('autocomplete', 'one-time-code');
@@ -2159,14 +2162,14 @@ const SoftUI = (() => {
2159
2162
  otp.appendChild(input);
2160
2163
 
2161
2164
  function updateSlots() {
2162
- var val = input.value;
2165
+ const val = input.value;
2163
2166
  slots.forEach(function(slot, i) {
2164
2167
  slot.textContent = val[i] || '';
2165
2168
  slot.classList.toggle('sui-otp-filled', !!val[i]);
2166
2169
  slot.classList.remove('sui-otp-active');
2167
2170
  });
2168
2171
  // Show cursor on current slot
2169
- var pos = Math.min(val.length, len - 1);
2172
+ const pos = Math.min(val.length, len - 1);
2170
2173
  if (document.activeElement === input && val.length < len) {
2171
2174
  slots[pos].classList.add('sui-otp-active');
2172
2175
  } else if (document.activeElement === input && val.length === len) {
@@ -2176,8 +2179,8 @@ const SoftUI = (() => {
2176
2179
 
2177
2180
  input.addEventListener('input', function() {
2178
2181
  // Filter to allowed characters
2179
- var filtered = '';
2180
- for (var i = 0; i < input.value.length && filtered.length < len; i++) {
2182
+ let filtered = '';
2183
+ for (let i = 0; i < input.value.length && filtered.length < len; i++) {
2181
2184
  if (regex.test(input.value[i])) {
2182
2185
  filtered += pattern === 'alphanumeric' ? input.value[i].toUpperCase() : input.value[i];
2183
2186
  }
@@ -2207,7 +2210,7 @@ const SoftUI = (() => {
2207
2210
  e.stopPropagation();
2208
2211
  input.focus();
2209
2212
  // Set cursor position
2210
- var pos = Math.min(i, input.value.length);
2213
+ const pos = Math.min(i, input.value.length);
2211
2214
  input.setSelectionRange(pos, pos);
2212
2215
  updateSlots();
2213
2216
  });
@@ -2222,13 +2225,13 @@ const SoftUI = (() => {
2222
2225
  // =========================================
2223
2226
  function initToggleGroups() {
2224
2227
  document.querySelectorAll('.sui-toggle-group[data-sui-toggle]').forEach(function(group) {
2225
- var mode = group.dataset.suiToggle; // "single" or "multi"
2226
- var items = group.querySelectorAll('.sui-toggle-group-item:not([disabled])');
2228
+ const mode = group.dataset.suiToggle; // "single" or "multi"
2229
+ const items = group.querySelectorAll('.sui-toggle-group-item:not([disabled])');
2227
2230
 
2228
2231
  items.forEach(function(item) {
2229
2232
  item.addEventListener('click', function() {
2230
2233
  if (mode === 'single') {
2231
- var wasActive = item.classList.contains('active');
2234
+ const wasActive = item.classList.contains('active');
2232
2235
  items.forEach(function(it) {
2233
2236
  it.classList.remove('active');
2234
2237
  it.setAttribute('aria-pressed', 'false');
@@ -2289,33 +2292,33 @@ const SoftUI = (() => {
2289
2292
  // Seamless loop: clone slides at both ends
2290
2293
  let cloneCount = 0;
2291
2294
  if (isSeamless && totalReal > visible) {
2292
- for (var i = totalReal - 1; i >= totalReal - visible; i--) {
2293
- var clone = realItems[i].cloneNode(true);
2295
+ for (let i = totalReal - 1; i >= totalReal - visible; i--) {
2296
+ const clone = realItems[i].cloneNode(true);
2294
2297
  clone.setAttribute('aria-hidden', 'true');
2295
2298
  track.insertBefore(clone, track.firstChild);
2296
2299
  }
2297
- for (var i = 0; i < visible; i++) {
2298
- var clone = realItems[i].cloneNode(true);
2300
+ for (let i = 0; i < visible; i++) {
2301
+ const clone = realItems[i].cloneNode(true);
2299
2302
  clone.setAttribute('aria-hidden', 'true');
2300
2303
  track.appendChild(clone);
2301
2304
  }
2302
2305
  cloneCount = visible;
2303
2306
  }
2304
2307
 
2305
- var allItems = Array.from(track.children);
2308
+ const allItems = Array.from(track.children);
2306
2309
 
2307
2310
  function moveTo(displayIndex, animate) {
2308
2311
  if (animate === false) track.style.transition = 'none';
2309
2312
 
2310
2313
  if (isVertical) {
2311
- var vh = track.parentElement.offsetHeight;
2312
- var itemH = vh / visible;
2314
+ const vh = track.parentElement.offsetHeight;
2315
+ const itemH = vh / visible;
2313
2316
  allItems.forEach(function(it) { it.style.height = itemH + 'px'; });
2314
2317
  track.style.transform = 'translateY(-' + (displayIndex * itemH) + 'px)';
2315
2318
  } else {
2316
- var item = allItems[displayIndex];
2317
- var base = allItems[0];
2318
- var px = (item && base) ? item.offsetLeft - base.offsetLeft : 0;
2319
+ const item = allItems[displayIndex];
2320
+ const base = allItems[0];
2321
+ const px = (item && base) ? item.offsetLeft - base.offsetLeft : 0;
2319
2322
  track.style.transform = 'translateX(-' + px + 'px)';
2320
2323
  }
2321
2324
 
@@ -2326,10 +2329,10 @@ const SoftUI = (() => {
2326
2329
  }
2327
2330
 
2328
2331
  function update(animate) {
2329
- var displayIndex = current + cloneCount;
2332
+ const displayIndex = current + cloneCount;
2330
2333
  moveTo(displayIndex, animate);
2331
2334
 
2332
- var dotIndex = ((current % totalReal) + totalReal) % totalReal;
2335
+ const dotIndex = ((current % totalReal) + totalReal) % totalReal;
2333
2336
  dots.forEach(function(d, i) { d.classList.toggle('active', i === dotIndex); });
2334
2337
 
2335
2338
  if (!isLoop) {
@@ -2435,45 +2438,45 @@ const SoftUI = (() => {
2435
2438
  document.querySelectorAll('.sui-chart-bar-col').forEach(function(col) {
2436
2439
  // Skip grouped bars (handled separately)
2437
2440
  if (col.querySelector('.sui-chart-bar-group')) return;
2438
- var fill = col.querySelector('.sui-chart-bar-fill');
2441
+ const fill = col.querySelector('.sui-chart-bar-fill');
2439
2442
  if (!fill) return;
2440
- var val = parseFloat(fill.getAttribute('data-value'));
2443
+ const val = parseFloat(fill.getAttribute('data-value'));
2441
2444
  if (isNaN(val)) return;
2442
- var max = parseFloat(fill.getAttribute('data-max')) || 100;
2443
- var pct = Math.min(100, Math.max(0, (val / max) * 100));
2445
+ const max = parseFloat(fill.getAttribute('data-max')) || 100;
2446
+ const pct = Math.min(100, Math.max(0, (val / max) * 100));
2444
2447
  fill.style.height = pct + '%';
2445
2448
  });
2446
2449
 
2447
2450
  // Grouped bars — set heights for each fill in a group
2448
2451
  document.querySelectorAll('.sui-chart-bar-group').forEach(function(group) {
2449
2452
  group.querySelectorAll('.sui-chart-bar-fill').forEach(function(fill) {
2450
- var val = parseFloat(fill.getAttribute('data-value'));
2453
+ const val = parseFloat(fill.getAttribute('data-value'));
2451
2454
  if (isNaN(val)) return;
2452
- var max = parseFloat(fill.getAttribute('data-max')) || 100;
2453
- var pct = Math.min(100, Math.max(0, (val / max) * 100));
2455
+ const max = parseFloat(fill.getAttribute('data-max')) || 100;
2456
+ const pct = Math.min(100, Math.max(0, (val / max) * 100));
2454
2457
  fill.style.height = pct + '%';
2455
2458
  });
2456
2459
  });
2457
2460
 
2458
2461
  // Horizontal bars
2459
2462
  document.querySelectorAll('.sui-chart-bar-row').forEach(function(row) {
2460
- var fill = row.querySelector('.sui-chart-bar-fill');
2463
+ const fill = row.querySelector('.sui-chart-bar-fill');
2461
2464
  if (!fill) return;
2462
- var val = parseFloat(fill.getAttribute('data-value'));
2465
+ const val = parseFloat(fill.getAttribute('data-value'));
2463
2466
  if (isNaN(val)) return;
2464
- var max = parseFloat(fill.getAttribute('data-max')) || 100;
2465
- var pct = Math.min(100, Math.max(0, (val / max) * 100));
2467
+ const max = parseFloat(fill.getAttribute('data-max')) || 100;
2468
+ const pct = Math.min(100, Math.max(0, (val / max) * 100));
2466
2469
  fill.style.width = pct + '%';
2467
2470
  });
2468
2471
 
2469
2472
  // Stacked bars
2470
2473
  document.querySelectorAll('.sui-chart-bar-track-stacked').forEach(function(track) {
2471
- var fills = track.querySelectorAll('.sui-chart-bar-fill');
2472
- var total = 0;
2474
+ const fills = track.querySelectorAll('.sui-chart-bar-fill');
2475
+ let total = 0;
2473
2476
  fills.forEach(function(f) { total += parseFloat(f.getAttribute('data-value')) || 0; });
2474
- var max = parseFloat(track.getAttribute('data-max')) || total || 100;
2477
+ const max = parseFloat(track.getAttribute('data-max')) || total || 100;
2475
2478
  fills.forEach(function(f) {
2476
- var v = parseFloat(f.getAttribute('data-value')) || 0;
2479
+ const v = parseFloat(f.getAttribute('data-value')) || 0;
2477
2480
  f.style.height = ((v / max) * 100) + '%';
2478
2481
  });
2479
2482
  });
@@ -2481,25 +2484,25 @@ const SoftUI = (() => {
2481
2484
  // Donut / Pie charts — build conic-gradient from data-segments
2482
2485
  document.querySelectorAll('.sui-chart-donut[data-segments]').forEach(function(donut) {
2483
2486
  try {
2484
- var segments = JSON.parse(donut.getAttribute('data-segments'));
2485
- var total = 0;
2487
+ const segments = JSON.parse(donut.getAttribute('data-segments'));
2488
+ let total = 0;
2486
2489
  segments.forEach(function(s) { total += s.value; });
2487
- var stops = [];
2488
- var cumulative = 0;
2490
+ const stops = [];
2491
+ let cumulative = 0;
2489
2492
  segments.forEach(function(s) {
2490
- var start = (cumulative / total) * 100;
2493
+ const start = (cumulative / total) * 100;
2491
2494
  cumulative += s.value;
2492
- var end = (cumulative / total) * 100;
2495
+ const end = (cumulative / total) * 100;
2493
2496
  stops.push(s.color + ' ' + start + '% ' + end + '%');
2494
2497
  });
2495
2498
  donut.style.background = 'conic-gradient(' + stops.join(', ') + ')';
2496
- } catch(e) {}
2499
+ } catch(_) {}
2497
2500
  });
2498
2501
 
2499
2502
  // Line / Area charts — measure path length for animation
2500
2503
  document.querySelectorAll('.sui-chart-line-wrap .chart-line').forEach(function(path) {
2501
2504
  if (path.getTotalLength) {
2502
- var len = path.getTotalLength();
2505
+ const len = path.getTotalLength();
2503
2506
  path.style.setProperty('--line-length', len);
2504
2507
  path.style.strokeDasharray = len;
2505
2508
  path.style.strokeDashoffset = len;
@@ -2508,27 +2511,27 @@ const SoftUI = (() => {
2508
2511
 
2509
2512
  // SVG dot tooltips
2510
2513
  document.querySelectorAll('.sui-chart-line-wrap').forEach(function(wrap) {
2511
- var dots = wrap.querySelectorAll('.chart-dot[data-value]');
2514
+ const dots = wrap.querySelectorAll('.chart-dot[data-value]');
2512
2515
  if (!dots.length) return;
2513
2516
 
2514
- var tip = document.createElement('div');
2517
+ const tip = document.createElement('div');
2515
2518
  tip.className = 'sui-chart-tooltip';
2516
2519
  wrap.appendChild(tip);
2517
2520
 
2518
2521
  dots.forEach(function(dot) {
2519
2522
  dot.addEventListener('mouseenter', function() {
2520
- var val = dot.getAttribute('data-value');
2523
+ const val = dot.getAttribute('data-value');
2521
2524
  tip.textContent = val;
2522
- var svg = wrap.querySelector('svg');
2523
- var svgRect = svg.getBoundingClientRect();
2524
- var wrapRect = wrap.getBoundingClientRect();
2525
- var cx = parseFloat(dot.getAttribute('cx'));
2526
- var cy = parseFloat(dot.getAttribute('cy'));
2527
- var viewBox = svg.viewBox.baseVal;
2528
- var scaleX = svgRect.width / viewBox.width;
2529
- var scaleY = svgRect.height / viewBox.height;
2530
- var px = (cx * scaleX) + (svgRect.left - wrapRect.left);
2531
- var py = (cy * scaleY) + (svgRect.top - wrapRect.top);
2525
+ const svg = wrap.querySelector('svg');
2526
+ const svgRect = svg.getBoundingClientRect();
2527
+ const wrapRect = wrap.getBoundingClientRect();
2528
+ const cx = parseFloat(dot.getAttribute('cx'));
2529
+ const cy = parseFloat(dot.getAttribute('cy'));
2530
+ const viewBox = svg.viewBox.baseVal;
2531
+ const scaleX = svgRect.width / viewBox.width;
2532
+ const scaleY = svgRect.height / viewBox.height;
2533
+ const px = (cx * scaleX) + (svgRect.left - wrapRect.left);
2534
+ const py = (cy * scaleY) + (svgRect.top - wrapRect.top);
2532
2535
  tip.style.left = px + 'px';
2533
2536
  tip.style.top = (py - 8) + 'px';
2534
2537
  tip.classList.add('visible');
@@ -2542,7 +2545,7 @@ const SoftUI = (() => {
2542
2545
 
2543
2546
  function initSelectablePricing() {
2544
2547
  document.querySelectorAll('.sui-pricing-selectable').forEach(function(container) {
2545
- var cards = container.querySelectorAll('.sui-pricing-card');
2548
+ const cards = container.querySelectorAll('.sui-pricing-card');
2546
2549
  cards.forEach(function(card) {
2547
2550
  card.addEventListener('click', function() {
2548
2551
  cards.forEach(function(c) { c.classList.remove('selected'); });
@@ -2556,17 +2559,17 @@ const SoftUI = (() => {
2556
2559
 
2557
2560
  function initStyledSelects() {
2558
2561
  document.querySelectorAll('.sui-styled-select').forEach(function(sel) {
2559
- var trigger = sel.querySelector('.sui-styled-select-trigger');
2560
- var menu = sel.querySelector('.sui-styled-select-menu');
2561
- var valueEl = sel.querySelector('.sui-styled-select-value');
2562
- var options = sel.querySelectorAll('.sui-styled-select-option');
2563
- var placeholder = sel.getAttribute('data-placeholder') || '';
2564
- var focusIdx = -1;
2562
+ const trigger = sel.querySelector('.sui-styled-select-trigger');
2563
+ const menu = sel.querySelector('.sui-styled-select-menu');
2564
+ const valueEl = sel.querySelector('.sui-styled-select-value');
2565
+ const options = sel.querySelectorAll('.sui-styled-select-option');
2566
+ const placeholder = sel.getAttribute('data-placeholder') || '';
2567
+ let focusIdx = -1;
2565
2568
 
2566
2569
  if (!trigger || !menu) return;
2567
2570
 
2568
2571
  // Set initial value
2569
- var selected = sel.querySelector('.sui-styled-select-option.selected');
2572
+ const selected = sel.querySelector('.sui-styled-select-option.selected');
2570
2573
  if (selected && valueEl) {
2571
2574
  valueEl.textContent = selected.textContent;
2572
2575
  valueEl.classList.remove('sui-styled-select-placeholder');
@@ -2609,7 +2612,7 @@ const SoftUI = (() => {
2609
2612
 
2610
2613
  // Keyboard navigation
2611
2614
  trigger.addEventListener('keydown', function(e) {
2612
- var isOpen = sel.classList.contains('open');
2615
+ const isOpen = sel.classList.contains('open');
2613
2616
  if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
2614
2617
  e.preventDefault();
2615
2618
  if (!isOpen) { sel.classList.add('open'); focusIdx = -1; }
@@ -2643,58 +2646,129 @@ const SoftUI = (() => {
2643
2646
  });
2644
2647
  }
2645
2648
 
2649
+ function initDrawers() {
2650
+ document.querySelectorAll('.sui-drawer').forEach(function(backdrop) {
2651
+ const panel = backdrop.querySelector('.sui-sheet-bottom');
2652
+ const handle = backdrop.querySelector('.sui-drawer-handle');
2653
+ if (!panel || !handle) return;
2654
+
2655
+ let startY = 0;
2656
+ let startHeight = 0;
2657
+ let dragging = false;
2658
+
2659
+ function onStart(e) {
2660
+ dragging = true;
2661
+ startY = e.touches ? e.touches[0].clientY : e.clientY;
2662
+ startHeight = panel.getBoundingClientRect().height;
2663
+ panel.style.transition = 'none';
2664
+ document.body.style.userSelect = 'none';
2665
+ }
2666
+
2667
+ function onMove(e) {
2668
+ if (!dragging) return;
2669
+ const clientY = e.touches ? e.touches[0].clientY : e.clientY;
2670
+ const delta = startY - clientY;
2671
+ const newHeight = Math.max(0, startHeight + delta);
2672
+ const maxHeight = window.innerHeight * 0.85;
2673
+ panel.style.height = Math.min(newHeight, maxHeight) + 'px';
2674
+ }
2675
+
2676
+ function onEnd() {
2677
+ if (!dragging) return;
2678
+ dragging = false;
2679
+ panel.style.transition = '';
2680
+ document.body.style.userSelect = '';
2681
+
2682
+ const currentHeight = panel.getBoundingClientRect().height;
2683
+ const vh = window.innerHeight;
2684
+
2685
+ // Snap points or dismiss
2686
+ const snapPoints = backdrop.getAttribute('data-snap');
2687
+ if (snapPoints) {
2688
+ const points = snapPoints.split(',').map(function(p) { return parseFloat(p) / 100 * vh; });
2689
+ points.push(0); // dismiss point
2690
+ let closest = points[0];
2691
+ let minDist = Math.abs(currentHeight - closest);
2692
+ points.forEach(function(p) {
2693
+ const dist = Math.abs(currentHeight - p);
2694
+ if (dist < minDist) { minDist = dist; closest = p; }
2695
+ });
2696
+ if (closest === 0) {
2697
+ SoftUI.sheet(backdrop).close();
2698
+ panel.style.height = '';
2699
+ } else {
2700
+ panel.style.height = closest + 'px';
2701
+ }
2702
+ } else {
2703
+ // No snap points — dismiss if dragged below 30% of starting height
2704
+ if (currentHeight < startHeight * 0.3) {
2705
+ SoftUI.sheet(backdrop).close();
2706
+ panel.style.height = '';
2707
+ }
2708
+ }
2709
+ }
2710
+
2711
+ handle.addEventListener('mousedown', onStart);
2712
+ handle.addEventListener('touchstart', onStart, { passive: true });
2713
+ document.addEventListener('mousemove', onMove);
2714
+ document.addEventListener('touchmove', onMove, { passive: false });
2715
+ document.addEventListener('mouseup', onEnd);
2716
+ document.addEventListener('touchend', onEnd);
2717
+ });
2718
+ }
2719
+
2646
2720
  function initDataTables() {
2647
2721
  document.querySelectorAll('.sui-datatable').forEach(function(dt) {
2648
- var table = dt.querySelector('.sui-table');
2722
+ const table = dt.querySelector('.sui-table');
2649
2723
  if (!table) return;
2650
2724
 
2651
- var tbody = table.querySelector('tbody');
2725
+ const tbody = table.querySelector('tbody');
2652
2726
  if (!tbody) return;
2653
2727
 
2654
- var allRows = Array.prototype.slice.call(tbody.querySelectorAll('tr'));
2655
- var filteredRows = allRows.slice();
2656
- var currentPage = 1;
2728
+ const allRows = Array.prototype.slice.call(tbody.querySelectorAll('tr'));
2729
+ let filteredRows = allRows.slice();
2730
+ let currentPage = 1;
2657
2731
 
2658
2732
  // Per-page selector (supports native <select> and .sui-styled-select)
2659
- var perpageNative = dt.querySelector('.sui-datatable-perpage select');
2660
- var perpageStyled = dt.querySelector('.sui-datatable-perpage .sui-styled-select');
2661
- var perpageSelect = perpageNative || perpageStyled;
2733
+ const perpageNative = dt.querySelector('.sui-datatable-perpage select');
2734
+ const perpageStyled = dt.querySelector('.sui-datatable-perpage .sui-styled-select');
2735
+ const perpageSelect = perpageNative || perpageStyled;
2662
2736
  function getPerpageValue() {
2663
2737
  if (perpageNative) return parseInt(perpageNative.value, 10);
2664
2738
  if (perpageStyled) return parseInt(perpageStyled.getAttribute('data-value') || '', 10);
2665
2739
  return allRows.length;
2666
2740
  }
2667
- var perPage = perpageSelect ? getPerpageValue() : allRows.length;
2741
+ let perPage = perpageSelect ? getPerpageValue() : allRows.length;
2668
2742
 
2669
2743
  // Info & pagination elements
2670
- var infoEl = dt.querySelector('.sui-datatable-info');
2671
- var paginationEl = dt.querySelector('.sui-datatable-pagination');
2744
+ const infoEl = dt.querySelector('.sui-datatable-info');
2745
+ const paginationEl = dt.querySelector('.sui-datatable-pagination');
2672
2746
 
2673
2747
  // Search input
2674
- var searchInput = dt.querySelector('.sui-datatable-search input');
2748
+ const searchInput = dt.querySelector('.sui-datatable-search input');
2675
2749
 
2676
2750
  function render() {
2677
- var total = filteredRows.length;
2678
- var totalPages = perPage > 0 ? Math.ceil(total / perPage) : 1;
2751
+ const total = filteredRows.length;
2752
+ const totalPages = perPage > 0 ? Math.ceil(total / perPage) : 1;
2679
2753
  if (currentPage > totalPages) currentPage = totalPages;
2680
2754
  if (currentPage < 1) currentPage = 1;
2681
2755
 
2682
- var start = (currentPage - 1) * perPage;
2683
- var end = Math.min(start + perPage, total);
2756
+ const start = (currentPage - 1) * perPage;
2757
+ const end = Math.min(start + perPage, total);
2684
2758
 
2685
2759
  // Hide all rows, then show only current page
2686
2760
  allRows.forEach(function(row) { row.style.display = 'none'; });
2687
- for (var i = start; i < end; i++) {
2761
+ for (let i = start; i < end; i++) {
2688
2762
  filteredRows[i].style.display = '';
2689
2763
  }
2690
2764
 
2691
2765
  // Show empty message if no results
2692
- var emptyRow = tbody.querySelector('.sui-datatable-empty-row');
2766
+ let emptyRow = tbody.querySelector('.sui-datatable-empty-row');
2693
2767
  if (total === 0) {
2694
2768
  if (!emptyRow) {
2695
2769
  emptyRow = document.createElement('tr');
2696
2770
  emptyRow.className = 'sui-datatable-empty-row';
2697
- var td = document.createElement('td');
2771
+ const td = document.createElement('td');
2698
2772
  td.className = 'sui-datatable-empty';
2699
2773
  td.colSpan = table.querySelectorAll('thead th').length;
2700
2774
  td.textContent = 'No matching records found.';
@@ -2719,7 +2793,7 @@ const SoftUI = (() => {
2719
2793
  if (paginationEl) {
2720
2794
  paginationEl.innerHTML = '';
2721
2795
 
2722
- var prevBtn = document.createElement('button');
2796
+ const prevBtn = document.createElement('button');
2723
2797
  prevBtn.textContent = '\u2039';
2724
2798
  prevBtn.disabled = currentPage <= 1;
2725
2799
  prevBtn.addEventListener('click', function() {
@@ -2727,9 +2801,9 @@ const SoftUI = (() => {
2727
2801
  });
2728
2802
  paginationEl.appendChild(prevBtn);
2729
2803
 
2730
- for (var p = 1; p <= totalPages; p++) {
2804
+ for (let p = 1; p <= totalPages; p++) {
2731
2805
  (function(page) {
2732
- var btn = document.createElement('button');
2806
+ const btn = document.createElement('button');
2733
2807
  btn.textContent = page;
2734
2808
  if (page === currentPage) btn.className = 'active';
2735
2809
  btn.addEventListener('click', function() {
@@ -2740,7 +2814,7 @@ const SoftUI = (() => {
2740
2814
  })(p);
2741
2815
  }
2742
2816
 
2743
- var nextBtn = document.createElement('button');
2817
+ const nextBtn = document.createElement('button');
2744
2818
  nextBtn.textContent = '\u203A';
2745
2819
  nextBtn.disabled = currentPage >= totalPages;
2746
2820
  nextBtn.addEventListener('click', function() {
@@ -2751,23 +2825,23 @@ const SoftUI = (() => {
2751
2825
  }
2752
2826
 
2753
2827
  // Filter elements (supports <select> and .sui-dropdown)
2754
- var filterEls = dt.querySelectorAll('.sui-datatable-filter');
2828
+ const filterEls = dt.querySelectorAll('.sui-datatable-filter');
2755
2829
 
2756
2830
  function getFilterValue(el) {
2757
2831
  if (el.tagName === 'SELECT') return el.value;
2758
2832
  // Dropdown-based filter: read from active item
2759
- var active = el.querySelector('.sui-dropdown-item.active');
2833
+ const active = el.querySelector('.sui-dropdown-item.active');
2760
2834
  return active ? (active.getAttribute('data-value') || '') : '';
2761
2835
  }
2762
2836
 
2763
2837
  function applyFilters() {
2764
- var query = searchInput ? searchInput.value.toLowerCase().trim() : '';
2838
+ const query = searchInput ? searchInput.value.toLowerCase().trim() : '';
2765
2839
  filteredRows = allRows.filter(function(row) {
2766
2840
  if (query && row.textContent.toLowerCase().indexOf(query) === -1) return false;
2767
- var pass = true;
2841
+ let pass = true;
2768
2842
  filterEls.forEach(function(el) {
2769
- var attr = el.getAttribute('data-filter-attr') || 'data-status';
2770
- var val = getFilterValue(el);
2843
+ const attr = el.getAttribute('data-filter-attr') || 'data-status';
2844
+ const val = getFilterValue(el);
2771
2845
  if (val && row.getAttribute(attr) !== val) pass = false;
2772
2846
  });
2773
2847
  return pass;
@@ -2793,11 +2867,11 @@ const SoftUI = (() => {
2793
2867
  el.querySelectorAll('.sui-dropdown-item').forEach(function(i) { i.classList.remove('active'); });
2794
2868
  item.classList.add('active');
2795
2869
  // Update label
2796
- var label = el.querySelector('.sui-datatable-filter-label');
2870
+ const label = el.querySelector('.sui-datatable-filter-label');
2797
2871
  if (label) label.textContent = item.textContent;
2798
2872
  // Close dropdown
2799
2873
  el.classList.remove('open');
2800
- var toggle = el.querySelector('.sui-dropdown-toggle');
2874
+ const toggle = el.querySelector('.sui-dropdown-toggle');
2801
2875
  if (toggle) toggle.setAttribute('aria-expanded', 'false');
2802
2876
  applyFilters();
2803
2877
  });
@@ -2815,14 +2889,14 @@ const SoftUI = (() => {
2815
2889
  }
2816
2890
 
2817
2891
  // Sortable headers (unsorted → asc → desc → unsorted)
2818
- var ths = table.querySelectorAll('th[data-sort]');
2892
+ const ths = table.querySelectorAll('th[data-sort]');
2819
2893
  ths.forEach(function(th) {
2820
2894
  th.addEventListener('click', function() {
2821
- var colIndex = Array.prototype.indexOf.call(th.parentElement.children, th);
2822
- var type = th.getAttribute('data-sort');
2895
+ const colIndex = Array.prototype.indexOf.call(th.parentElement.children, th);
2896
+ const type = th.getAttribute('data-sort');
2823
2897
 
2824
2898
  // Cycle: unsorted → asc → desc → unsorted
2825
- var dir;
2899
+ let dir;
2826
2900
  if (th.classList.contains('sort-asc')) {
2827
2901
  dir = 'desc';
2828
2902
  } else if (th.classList.contains('sort-desc')) {
@@ -2840,12 +2914,12 @@ const SoftUI = (() => {
2840
2914
  } else {
2841
2915
  th.classList.add(dir === 'asc' ? 'sort-asc' : 'sort-desc');
2842
2916
  filteredRows.sort(function(a, b) {
2843
- var aText = a.children[colIndex] ? a.children[colIndex].textContent.trim() : '';
2844
- var bText = b.children[colIndex] ? b.children[colIndex].textContent.trim() : '';
2917
+ const aText = a.children[colIndex] ? a.children[colIndex].textContent.trim() : '';
2918
+ const bText = b.children[colIndex] ? b.children[colIndex].textContent.trim() : '';
2845
2919
 
2846
2920
  if (type === 'number') {
2847
- var aNum = parseFloat(aText.replace(/[^0-9.\-]/g, '')) || 0;
2848
- var bNum = parseFloat(bText.replace(/[^0-9.\-]/g, '')) || 0;
2921
+ const aNum = parseFloat(aText.replace(/[^0-9.\-]/g, '')) || 0;
2922
+ const bNum = parseFloat(bText.replace(/[^0-9.\-]/g, '')) || 0;
2849
2923
  return dir === 'asc' ? aNum - bNum : bNum - aNum;
2850
2924
  }
2851
2925
 
@@ -2868,11 +2942,11 @@ const SoftUI = (() => {
2868
2942
  function initDragDrop() {
2869
2943
  // ── Sortable Lists ──
2870
2944
  document.querySelectorAll('.sui-sortable').forEach(function(list) {
2871
- var dragItem = null;
2945
+ let dragItem = null;
2872
2946
 
2873
2947
  list.querySelectorAll('.sui-sortable-item').forEach(function(item) {
2874
- var handle = item.querySelector('.sui-sortable-handle');
2875
- var dragTarget = handle || item;
2948
+ const handle = item.querySelector('.sui-sortable-handle');
2949
+ const dragTarget = handle || item;
2876
2950
 
2877
2951
  dragTarget.setAttribute('draggable', 'true');
2878
2952
  if (handle) item.classList.add('has-handle');
@@ -2899,9 +2973,9 @@ const SoftUI = (() => {
2899
2973
  e.preventDefault();
2900
2974
  item.classList.remove('drag-over');
2901
2975
  if (!dragItem || dragItem === item) return;
2902
- var items = Array.prototype.slice.call(list.children);
2903
- var fromIndex = items.indexOf(dragItem);
2904
- var toIndex = items.indexOf(item);
2976
+ const items = Array.prototype.slice.call(list.children);
2977
+ const fromIndex = items.indexOf(dragItem);
2978
+ const toIndex = items.indexOf(item);
2905
2979
  if (fromIndex < toIndex) {
2906
2980
  list.insertBefore(dragItem, item.nextSibling);
2907
2981
  } else {
@@ -2921,7 +2995,7 @@ const SoftUI = (() => {
2921
2995
 
2922
2996
  // ── Kanban ──
2923
2997
  document.querySelectorAll('.sui-kanban').forEach(function(kanban) {
2924
- var dragCard = null;
2998
+ let dragCard = null;
2925
2999
 
2926
3000
  kanban.querySelectorAll('.sui-kanban-card').forEach(function(card) {
2927
3001
  card.setAttribute('draggable', 'true');
@@ -2949,7 +3023,7 @@ const SoftUI = (() => {
2949
3023
 
2950
3024
  // Position among siblings
2951
3025
  if (!dragCard) return;
2952
- var afterEl = getDragAfterElement(col, e.clientY);
3026
+ const afterEl = getDragAfterElement(col, e.clientY);
2953
3027
  if (afterEl) {
2954
3028
  col.insertBefore(dragCard, afterEl);
2955
3029
  } else {
@@ -2971,14 +3045,14 @@ const SoftUI = (() => {
2971
3045
  });
2972
3046
 
2973
3047
  function getDragAfterElement(container, y) {
2974
- var els = Array.prototype.slice.call(
3048
+ const els = Array.prototype.slice.call(
2975
3049
  container.querySelectorAll('.sui-kanban-card:not(.dragging)')
2976
3050
  );
2977
- var closest = null;
2978
- var closestOffset = Number.NEGATIVE_INFINITY;
3051
+ let closest = null;
3052
+ let closestOffset = Number.NEGATIVE_INFINITY;
2979
3053
  els.forEach(function(el) {
2980
- var box = el.getBoundingClientRect();
2981
- var offset = y - box.top - box.height / 2;
3054
+ const box = el.getBoundingClientRect();
3055
+ const offset = y - box.top - box.height / 2;
2982
3056
  if (offset < 0 && offset > closestOffset) {
2983
3057
  closestOffset = offset;
2984
3058
  closest = el;
@@ -2990,7 +3064,7 @@ const SoftUI = (() => {
2990
3064
  // ── Drop Zone ──
2991
3065
  document.querySelectorAll('.sui-dropzone').forEach(function(zone) {
2992
3066
  // Click-to-upload: create hidden file input
2993
- var fileInput = document.createElement('input');
3067
+ const fileInput = document.createElement('input');
2994
3068
  fileInput.type = 'file';
2995
3069
  fileInput.multiple = true;
2996
3070
  fileInput.style.display = 'none';
@@ -3002,16 +3076,16 @@ const SoftUI = (() => {
3002
3076
  });
3003
3077
 
3004
3078
  fileInput.addEventListener('change', function() {
3005
- var files = fileInput.files;
3079
+ const files = fileInput.files;
3006
3080
  if (!files.length) return;
3007
- var fileList = zone.querySelector('.sui-dropzone-files');
3081
+ let fileList = zone.querySelector('.sui-dropzone-files');
3008
3082
  if (!fileList) {
3009
3083
  fileList = document.createElement('div');
3010
3084
  fileList.className = 'sui-dropzone-files';
3011
3085
  zone.appendChild(fileList);
3012
3086
  }
3013
3087
  Array.prototype.slice.call(files).forEach(function(file) {
3014
- var item = document.createElement('div');
3088
+ const item = document.createElement('div');
3015
3089
  item.className = 'sui-dropzone-file';
3016
3090
  item.innerHTML = '<span>' + file.name + '</span><button class="sui-dropzone-file-remove" type="button">&times;</button>';
3017
3091
  item.querySelector('.sui-dropzone-file-remove').addEventListener('click', function(ev) {
@@ -3038,16 +3112,16 @@ const SoftUI = (() => {
3038
3112
  zone.addEventListener('drop', function(e) {
3039
3113
  e.preventDefault();
3040
3114
  zone.classList.remove('drag-over');
3041
- var files = e.dataTransfer.files;
3115
+ const files = e.dataTransfer.files;
3042
3116
  if (!files.length) return;
3043
- var fileList = zone.querySelector('.sui-dropzone-files');
3117
+ let fileList = zone.querySelector('.sui-dropzone-files');
3044
3118
  if (!fileList) {
3045
3119
  fileList = document.createElement('div');
3046
3120
  fileList.className = 'sui-dropzone-files';
3047
3121
  zone.appendChild(fileList);
3048
3122
  }
3049
3123
  Array.prototype.slice.call(files).forEach(function(file) {
3050
- var item = document.createElement('div');
3124
+ const item = document.createElement('div');
3051
3125
  item.className = 'sui-dropzone-file';
3052
3126
  item.innerHTML = '<span>' + file.name + '</span><button class="sui-dropzone-file-remove" type="button">&times;</button>';
3053
3127
  item.querySelector('.sui-dropzone-file-remove').addEventListener('click', function(ev) {
@@ -3064,16 +3138,16 @@ const SoftUI = (() => {
3064
3138
  // Sidebar — collapsible toggle
3065
3139
  // =========================================
3066
3140
  document.addEventListener('click', function(e) {
3067
- var btn = e.target.closest('[data-sidebar-toggle]');
3141
+ const btn = e.target.closest('[data-sidebar-toggle]');
3068
3142
  if (!btn) return;
3069
- var sidebar = btn.closest('.sui-sidebar');
3143
+ const sidebar = btn.closest('.sui-sidebar');
3070
3144
  if (sidebar) {
3071
3145
  sidebar.classList.toggle('sui-sidebar-collapsed');
3072
3146
  }
3073
3147
  });
3074
3148
 
3075
3149
  function sidebar(selector) {
3076
- var el = typeof selector === 'string' ? document.querySelector(selector) : selector;
3150
+ const el = typeof selector === 'string' ? document.querySelector(selector) : selector;
3077
3151
  if (!el) return null;
3078
3152
 
3079
3153
  function collapse() { el.classList.add('sui-sidebar-collapsed'); }
@@ -3088,34 +3162,34 @@ const SoftUI = (() => {
3088
3162
  // Rating
3089
3163
  // =========================================
3090
3164
  function ratingIsHalf(star, e) {
3091
- var rect = star.getBoundingClientRect();
3165
+ const rect = star.getBoundingClientRect();
3092
3166
  return e.clientX < rect.left + rect.width / 2;
3093
3167
  }
3094
3168
 
3095
3169
  function ratingEnsureDualSvg(star) {
3096
- var svgs = star.querySelectorAll('svg');
3170
+ const svgs = star.querySelectorAll('svg');
3097
3171
  if (svgs.length < 2) {
3098
- var clone = svgs[0].cloneNode(true);
3172
+ const clone = svgs[0].cloneNode(true);
3099
3173
  star.appendChild(clone);
3100
3174
  }
3101
3175
  }
3102
3176
 
3103
3177
  function ratingResetSvg(star) {
3104
- var svgs = star.querySelectorAll('svg');
3178
+ const svgs = star.querySelectorAll('svg');
3105
3179
  if (svgs.length > 1) {
3106
- for (var i = svgs.length - 1; i > 0; i--) { svgs[i].remove(); }
3180
+ for (let i = svgs.length - 1; i > 0; i--) { svgs[i].remove(); }
3107
3181
  }
3108
3182
  }
3109
3183
 
3110
3184
  document.addEventListener('click', function(e) {
3111
- var star = e.target.closest('.sui-rating:not(.sui-rating-readonly) .sui-rating-star');
3185
+ const star = e.target.closest('.sui-rating:not(.sui-rating-readonly) .sui-rating-star');
3112
3186
  if (!star) return;
3113
- var rating = star.closest('.sui-rating');
3114
- var stars = Array.from(rating.querySelectorAll('.sui-rating-star'));
3115
- var index = stars.indexOf(star);
3116
- var allowHalf = rating.classList.contains('sui-rating-half');
3117
- var isHalf = allowHalf && ratingIsHalf(star, e);
3118
- var value = isHalf ? index + 0.5 : index + 1;
3187
+ const rating = star.closest('.sui-rating');
3188
+ const stars = Array.from(rating.querySelectorAll('.sui-rating-star'));
3189
+ const index = stars.indexOf(star);
3190
+ const allowHalf = rating.classList.contains('sui-rating-half');
3191
+ const isHalf = allowHalf && ratingIsHalf(star, e);
3192
+ const value = isHalf ? index + 0.5 : index + 1;
3119
3193
  stars.forEach(function(s, i) {
3120
3194
  s.classList.remove('active', 'half', 'hover', 'hover-half');
3121
3195
  ratingResetSvg(s);
@@ -3135,13 +3209,13 @@ const SoftUI = (() => {
3135
3209
  });
3136
3210
 
3137
3211
  document.addEventListener('mousemove', function(e) {
3138
- var star = e.target.closest('.sui-rating:not(.sui-rating-readonly) .sui-rating-star');
3212
+ const star = e.target.closest('.sui-rating:not(.sui-rating-readonly) .sui-rating-star');
3139
3213
  if (!star) return;
3140
- var rating = star.closest('.sui-rating');
3141
- var stars = Array.from(rating.querySelectorAll('.sui-rating-star'));
3142
- var index = stars.indexOf(star);
3143
- var allowHalf = rating.classList.contains('sui-rating-half');
3144
- var isHalf = allowHalf && ratingIsHalf(star, e);
3214
+ const rating = star.closest('.sui-rating');
3215
+ const stars = Array.from(rating.querySelectorAll('.sui-rating-star'));
3216
+ const index = stars.indexOf(star);
3217
+ const allowHalf = rating.classList.contains('sui-rating-half');
3218
+ const isHalf = allowHalf && ratingIsHalf(star, e);
3145
3219
  stars.forEach(function(s, i) {
3146
3220
  s.classList.remove('hover', 'hover-half');
3147
3221
  ratingResetSvg(s);
@@ -3159,10 +3233,10 @@ const SoftUI = (() => {
3159
3233
  });
3160
3234
 
3161
3235
  document.addEventListener('mouseout', function(e) {
3162
- var star = e.target.closest('.sui-rating:not(.sui-rating-readonly) .sui-rating-star');
3236
+ const star = e.target.closest('.sui-rating:not(.sui-rating-readonly) .sui-rating-star');
3163
3237
  if (!star) return;
3164
- var rating = star.closest('.sui-rating');
3165
- var stars = Array.from(rating.querySelectorAll('.sui-rating-star'));
3238
+ const rating = star.closest('.sui-rating');
3239
+ const stars = Array.from(rating.querySelectorAll('.sui-rating-star'));
3166
3240
  stars.forEach(function(s) {
3167
3241
  s.classList.remove('hover', 'hover-half');
3168
3242
  if (!s.classList.contains('half')) { ratingResetSvg(s); }
@@ -3173,14 +3247,14 @@ const SoftUI = (() => {
3173
3247
  // Color Picker
3174
3248
  // =========================================
3175
3249
  document.addEventListener('click', function(e) {
3176
- var swatch = e.target.closest('.sui-color-picker .sui-color-swatch');
3250
+ const swatch = e.target.closest('.sui-color-picker .sui-color-swatch');
3177
3251
  if (!swatch) return;
3178
- var picker = swatch.closest('.sui-color-picker');
3252
+ const picker = swatch.closest('.sui-color-picker');
3179
3253
  picker.querySelectorAll('.sui-color-swatch').forEach(function(s) {
3180
3254
  s.classList.remove('active');
3181
3255
  });
3182
3256
  swatch.classList.add('active');
3183
- var color = swatch.getAttribute('data-color') || swatch.style.background || swatch.style.backgroundColor;
3257
+ const color = swatch.getAttribute('data-color') || swatch.style.background || swatch.style.backgroundColor;
3184
3258
  picker.setAttribute('data-value', color);
3185
3259
  picker.dispatchEvent(new CustomEvent('sui-color-change', { detail: { color: color } }));
3186
3260
  });
@@ -3189,24 +3263,24 @@ const SoftUI = (() => {
3189
3263
  // Color Spectrum Picker
3190
3264
  // =========================================
3191
3265
  function initSpectrumPickers() {
3192
- var pickers = document.querySelectorAll('.sui-color-spectrum');
3266
+ const pickers = document.querySelectorAll('.sui-color-spectrum');
3193
3267
  pickers.forEach(function(picker) { initSpectrum(picker); });
3194
3268
  }
3195
3269
 
3196
3270
  function initSpectrum(picker) {
3197
- var canvasWrap = picker.querySelector('.sui-color-spectrum-canvas');
3198
- var canvas = canvasWrap.querySelector('canvas');
3199
- var ctx = canvas.getContext('2d');
3200
- var cursor = canvasWrap.querySelector('.sui-color-spectrum-cursor');
3201
- var hueBar = picker.querySelector('.sui-color-spectrum-hue');
3202
- var hueCursor = picker.querySelector('.sui-color-spectrum-hue-cursor');
3203
- var preview = picker.querySelector('.sui-color-spectrum-preview');
3204
- var hexInput = picker.querySelector('.sui-color-spectrum-hex input');
3205
- var rInput = picker.querySelector('input[data-channel="r"]');
3206
- var gInput = picker.querySelector('input[data-channel="g"]');
3207
- var bInput = picker.querySelector('input[data-channel="b"]');
3208
-
3209
- var hue = 0, sat = 1, val = 1;
3271
+ const canvasWrap = picker.querySelector('.sui-color-spectrum-canvas');
3272
+ const canvas = canvasWrap.querySelector('canvas');
3273
+ const ctx = canvas.getContext('2d');
3274
+ const cursor = canvasWrap.querySelector('.sui-color-spectrum-cursor');
3275
+ const hueBar = picker.querySelector('.sui-color-spectrum-hue');
3276
+ const hueCursor = picker.querySelector('.sui-color-spectrum-hue-cursor');
3277
+ const preview = picker.querySelector('.sui-color-spectrum-preview');
3278
+ const hexInput = picker.querySelector('.sui-color-spectrum-hex input');
3279
+ const rInput = picker.querySelector('input[data-channel="r"]');
3280
+ const gInput = picker.querySelector('input[data-channel="g"]');
3281
+ const bInput = picker.querySelector('input[data-channel="b"]');
3282
+
3283
+ let hue = 0, sat = 1, val = 1;
3210
3284
 
3211
3285
  function resizeCanvas() {
3212
3286
  canvas.width = canvasWrap.offsetWidth;
@@ -3215,12 +3289,12 @@ const SoftUI = (() => {
3215
3289
  }
3216
3290
 
3217
3291
  function hsvToRgb(h, s, v) {
3218
- var i = Math.floor(h / 60) % 6;
3219
- var f = h / 60 - Math.floor(h / 60);
3220
- var p = v * (1 - s);
3221
- var q = v * (1 - f * s);
3222
- var t = v * (1 - (1 - f) * s);
3223
- var r, g, b;
3292
+ const i = Math.floor(h / 60) % 6;
3293
+ const f = h / 60 - Math.floor(h / 60);
3294
+ const p = v * (1 - s);
3295
+ const q = v * (1 - f * s);
3296
+ const t = v * (1 - (1 - f) * s);
3297
+ let r, g, b;
3224
3298
  switch (i) {
3225
3299
  case 0: r = v; g = t; b = p; break;
3226
3300
  case 1: r = q; g = v; b = p; break;
@@ -3239,16 +3313,17 @@ const SoftUI = (() => {
3239
3313
  function hexToRgb(hex) {
3240
3314
  hex = hex.replace('#', '');
3241
3315
  if (hex.length === 3) hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
3242
- var n = parseInt(hex, 16);
3316
+ const n = parseInt(hex, 16);
3243
3317
  return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
3244
3318
  }
3245
3319
 
3246
3320
  function rgbToHsv(r, g, b) {
3247
3321
  r /= 255; g /= 255; b /= 255;
3248
- var max = Math.max(r, g, b), min = Math.min(r, g, b);
3249
- var h, s, v = max;
3250
- var d = max - min;
3251
- s = max === 0 ? 0 : d / max;
3322
+ const max = Math.max(r, g, b), min = Math.min(r, g, b);
3323
+ let h;
3324
+ const v = max;
3325
+ const d = max - min;
3326
+ const s = max === 0 ? 0 : d / max;
3252
3327
  if (max === min) { h = 0; }
3253
3328
  else {
3254
3329
  switch (max) {
@@ -3262,17 +3337,17 @@ const SoftUI = (() => {
3262
3337
  }
3263
3338
 
3264
3339
  function drawSatVal() {
3265
- var w = canvas.width, h = canvas.height;
3266
- var hueRgb = hsvToRgb(hue, 1, 1);
3267
- var hueColor = 'rgb(' + hueRgb[0] + ',' + hueRgb[1] + ',' + hueRgb[2] + ')';
3340
+ const w = canvas.width, h = canvas.height;
3341
+ const hueRgb = hsvToRgb(hue, 1, 1);
3342
+ const hueColor = 'rgb(' + hueRgb[0] + ',' + hueRgb[1] + ',' + hueRgb[2] + ')';
3268
3343
  ctx.fillStyle = hueColor;
3269
3344
  ctx.fillRect(0, 0, w, h);
3270
- var whiteGrad = ctx.createLinearGradient(0, 0, w, 0);
3345
+ const whiteGrad = ctx.createLinearGradient(0, 0, w, 0);
3271
3346
  whiteGrad.addColorStop(0, 'rgba(255,255,255,1)');
3272
3347
  whiteGrad.addColorStop(1, 'rgba(255,255,255,0)');
3273
3348
  ctx.fillStyle = whiteGrad;
3274
3349
  ctx.fillRect(0, 0, w, h);
3275
- var blackGrad = ctx.createLinearGradient(0, 0, 0, h);
3350
+ const blackGrad = ctx.createLinearGradient(0, 0, 0, h);
3276
3351
  blackGrad.addColorStop(0, 'rgba(0,0,0,0)');
3277
3352
  blackGrad.addColorStop(1, 'rgba(0,0,0,1)');
3278
3353
  ctx.fillStyle = blackGrad;
@@ -3280,8 +3355,8 @@ const SoftUI = (() => {
3280
3355
  }
3281
3356
 
3282
3357
  function updateUI() {
3283
- var rgb = hsvToRgb(hue, sat, val);
3284
- var hex = rgbToHex(rgb[0], rgb[1], rgb[2]);
3358
+ const rgb = hsvToRgb(hue, sat, val);
3359
+ const hex = rgbToHex(rgb[0], rgb[1], rgb[2]);
3285
3360
  if (preview) preview.style.background = hex;
3286
3361
  if (hexInput) hexInput.value = hex.toUpperCase().slice(1);
3287
3362
  if (rInput) rInput.value = rgb[0];
@@ -3291,7 +3366,7 @@ const SoftUI = (() => {
3291
3366
  cursor.style.left = (sat * 100) + '%';
3292
3367
  cursor.style.top = ((1 - val) * 100) + '%';
3293
3368
 
3294
- var hueRgb = hsvToRgb(hue, 1, 1);
3369
+ const hueRgb = hsvToRgb(hue, 1, 1);
3295
3370
  hueCursor.style.left = (hue / 360 * 100) + '%';
3296
3371
  hueCursor.style.background = 'rgb(' + hueRgb[0] + ',' + hueRgb[1] + ',' + hueRgb[2] + ')';
3297
3372
 
@@ -3301,9 +3376,9 @@ const SoftUI = (() => {
3301
3376
 
3302
3377
  // Canvas drag
3303
3378
  function onCanvasMove(e) {
3304
- var rect = canvasWrap.getBoundingClientRect();
3305
- var x = (e.touches ? e.touches[0].clientX : e.clientX) - rect.left;
3306
- var y = (e.touches ? e.touches[0].clientY : e.clientY) - rect.top;
3379
+ const rect = canvasWrap.getBoundingClientRect();
3380
+ const x = (e.touches ? e.touches[0].clientX : e.clientX) - rect.left;
3381
+ const y = (e.touches ? e.touches[0].clientY : e.clientY) - rect.top;
3307
3382
  sat = Math.max(0, Math.min(1, x / rect.width));
3308
3383
  val = Math.max(0, Math.min(1, 1 - y / rect.height));
3309
3384
  updateUI();
@@ -3329,8 +3404,8 @@ const SoftUI = (() => {
3329
3404
 
3330
3405
  // Hue drag
3331
3406
  function onHueMove(e) {
3332
- var rect = hueBar.getBoundingClientRect();
3333
- var x = (e.touches ? e.touches[0].clientX : e.clientX) - rect.left;
3407
+ const rect = hueBar.getBoundingClientRect();
3408
+ const x = (e.touches ? e.touches[0].clientX : e.clientX) - rect.left;
3334
3409
  hue = Math.max(0, Math.min(360, x / rect.width * 360));
3335
3410
  drawSatVal();
3336
3411
  updateUI();
@@ -3357,10 +3432,10 @@ const SoftUI = (() => {
3357
3432
  // Hex input
3358
3433
  if (hexInput) {
3359
3434
  hexInput.addEventListener('input', function() {
3360
- var v = hexInput.value.replace('#', '');
3435
+ const v = hexInput.value.replace('#', '');
3361
3436
  if (v.length === 6 && /^[0-9A-Fa-f]{6}$/.test(v)) {
3362
- var rgb = hexToRgb(v);
3363
- var hsv = rgbToHsv(rgb[0], rgb[1], rgb[2]);
3437
+ const rgb = hexToRgb(v);
3438
+ const hsv = rgbToHsv(rgb[0], rgb[1], rgb[2]);
3364
3439
  hue = hsv[0]; sat = hsv[1]; val = hsv[2];
3365
3440
  drawSatVal();
3366
3441
  updateUI();
@@ -3370,13 +3445,13 @@ const SoftUI = (() => {
3370
3445
 
3371
3446
  // RGB inputs
3372
3447
  function onRgbInput() {
3373
- var r = parseInt(rInput.value) || 0;
3374
- var g = parseInt(gInput.value) || 0;
3375
- var b = parseInt(bInput.value) || 0;
3448
+ let r = parseInt(rInput.value) || 0;
3449
+ let g = parseInt(gInput.value) || 0;
3450
+ let b = parseInt(bInput.value) || 0;
3376
3451
  r = Math.max(0, Math.min(255, r));
3377
3452
  g = Math.max(0, Math.min(255, g));
3378
3453
  b = Math.max(0, Math.min(255, b));
3379
- var hsv = rgbToHsv(r, g, b);
3454
+ const hsv = rgbToHsv(r, g, b);
3380
3455
  hue = hsv[0]; sat = hsv[1]; val = hsv[2];
3381
3456
  drawSatVal();
3382
3457
  updateUI();
@@ -3387,9 +3462,9 @@ const SoftUI = (() => {
3387
3462
  if (bInput) bInput.addEventListener('input', onRgbInput);
3388
3463
 
3389
3464
  // Init from data-color attribute or default
3390
- var initColor = picker.getAttribute('data-color') || '#5B54E0';
3391
- var initRgb = hexToRgb(initColor);
3392
- var initHsv = rgbToHsv(initRgb[0], initRgb[1], initRgb[2]);
3465
+ const initColor = picker.getAttribute('data-color') || '#5B54E0';
3466
+ const initRgb = hexToRgb(initColor);
3467
+ const initHsv = rgbToHsv(initRgb[0], initRgb[1], initRgb[2]);
3393
3468
  hue = initHsv[0]; sat = initHsv[1]; val = initHsv[2];
3394
3469
 
3395
3470
  resizeCanvas();
@@ -3412,7 +3487,7 @@ const SoftUI = (() => {
3412
3487
  return (bytes / 1048576).toFixed(1) + ' MB';
3413
3488
  }
3414
3489
 
3415
- var fileIcons = {
3490
+ const fileIcons = {
3416
3491
  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>',
3417
3492
  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>',
3418
3493
  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>',
@@ -3424,8 +3499,8 @@ const SoftUI = (() => {
3424
3499
  };
3425
3500
 
3426
3501
  function getFileType(file) {
3427
- var type = file.type || '';
3428
- var ext = file.name.split('.').pop().toLowerCase();
3502
+ const type = file.type || '';
3503
+ const ext = file.name.split('.').pop().toLowerCase();
3429
3504
  if (type.startsWith('image/')) return 'image';
3430
3505
  if (type === 'application/pdf' || ext === 'pdf') return 'pdf';
3431
3506
  if (type.startsWith('video/')) return 'video';
@@ -3445,14 +3520,14 @@ const SoftUI = (() => {
3445
3520
  }
3446
3521
 
3447
3522
  function getOrCreateContainer(zone, cls) {
3448
- var wrap = zone.closest('.sui-file-upload-wrap');
3523
+ let wrap = zone.closest('.sui-file-upload-wrap');
3449
3524
  if (!wrap) {
3450
3525
  wrap = document.createElement('div');
3451
3526
  wrap.className = 'sui-file-upload-wrap';
3452
3527
  zone.parentNode.insertBefore(wrap, zone);
3453
3528
  wrap.appendChild(zone);
3454
3529
  }
3455
- var container = wrap.querySelector('.' + cls);
3530
+ let container = wrap.querySelector('.' + cls);
3456
3531
  if (!container) {
3457
3532
  container = document.createElement('div');
3458
3533
  container.className = cls;
@@ -3462,11 +3537,11 @@ const SoftUI = (() => {
3462
3537
  }
3463
3538
 
3464
3539
  function renderFileList(zone, files, append) {
3465
- var container = getOrCreateContainer(zone, 'sui-file-list');
3540
+ const container = getOrCreateContainer(zone, 'sui-file-list');
3466
3541
  if (!append) container.innerHTML = '';
3467
- for (var i = 0; i < files.length; i++) {
3468
- var f = files[i];
3469
- var item = document.createElement('div');
3542
+ for (let i = 0; i < files.length; i++) {
3543
+ const f = files[i];
3544
+ const item = document.createElement('div');
3470
3545
  item.className = 'sui-file-item';
3471
3546
  item.innerHTML =
3472
3547
  '<div class="sui-file-item-icon ' + getFileIconClass(f) + '">' + getFileIcon(f) + '</div>' +
@@ -3480,11 +3555,11 @@ const SoftUI = (() => {
3480
3555
  }
3481
3556
 
3482
3557
  function renderFileProgress(zone, files, append) {
3483
- var container = getOrCreateContainer(zone, 'sui-file-list');
3558
+ const container = getOrCreateContainer(zone, 'sui-file-list');
3484
3559
  if (!append) container.innerHTML = '';
3485
- for (var i = 0; i < files.length; i++) {
3486
- var f = files[i];
3487
- var item = document.createElement('div');
3560
+ for (let i = 0; i < files.length; i++) {
3561
+ const f = files[i];
3562
+ const item = document.createElement('div');
3488
3563
  item.className = 'sui-file-item';
3489
3564
  item.innerHTML =
3490
3565
  '<div class="sui-file-item-icon ' + getFileIconClass(f) + '">' + getFileIcon(f) + '</div>' +
@@ -3502,10 +3577,10 @@ const SoftUI = (() => {
3502
3577
  }
3503
3578
 
3504
3579
  function simulateProgress(item) {
3505
- var bar = item.querySelector('.sui-progress-bar');
3506
- var status = item.querySelector('.sui-file-item-status');
3507
- var pct = 0;
3508
- var interval = setInterval(function() {
3580
+ const bar = item.querySelector('.sui-progress-bar');
3581
+ const status = item.querySelector('.sui-file-item-status');
3582
+ let pct = 0;
3583
+ const interval = setInterval(function() {
3509
3584
  pct += Math.floor(Math.random() * 15) + 5;
3510
3585
  if (pct >= 100) {
3511
3586
  pct = 100;
@@ -3522,19 +3597,19 @@ const SoftUI = (() => {
3522
3597
  }
3523
3598
 
3524
3599
  function renderFilePreview(zone, files, append) {
3525
- var container = getOrCreateContainer(zone, 'sui-file-preview-grid');
3600
+ const container = getOrCreateContainer(zone, 'sui-file-preview-grid');
3526
3601
  if (!append) container.innerHTML = '';
3527
- for (var i = 0; i < files.length; i++) {
3528
- var f = files[i];
3602
+ for (let i = 0; i < files.length; i++) {
3603
+ const f = files[i];
3529
3604
  if (!f.type || !f.type.startsWith('image/')) continue;
3530
- var item = document.createElement('div');
3605
+ const item = document.createElement('div');
3531
3606
  item.className = 'sui-file-preview-item';
3532
3607
  item.innerHTML =
3533
3608
  '<img alt="' + f.name + '">' +
3534
3609
  '<button class="sui-file-preview-item-remove" aria-label="Remove">&times;</button>';
3535
3610
  container.appendChild(item);
3536
3611
  (function(img, file) {
3537
- var reader = new FileReader();
3612
+ const reader = new FileReader();
3538
3613
  reader.onload = function(e) { img.src = e.target.result; };
3539
3614
  reader.readAsDataURL(file);
3540
3615
  })(item.querySelector('img'), f);
@@ -3544,12 +3619,12 @@ const SoftUI = (() => {
3544
3619
  // Delegated change on file inputs
3545
3620
  document.addEventListener('change', function(e) {
3546
3621
  if (!e.target.matches('.sui-file-upload input[type="file"]')) return;
3547
- var input = e.target;
3548
- var zone = input.closest('.sui-file-upload');
3622
+ const input = e.target;
3623
+ const zone = input.closest('.sui-file-upload');
3549
3624
  if (!zone) return;
3550
- var files = Array.from(input.files);
3625
+ const files = Array.from(input.files);
3551
3626
  if (!files.length) return;
3552
- var mode = zone.getAttribute('data-sui-upload') || 'list';
3627
+ const mode = zone.getAttribute('data-sui-upload') || 'list';
3553
3628
  if (mode === 'preview') {
3554
3629
  renderFilePreview(zone, files);
3555
3630
  } else if (mode === 'progress') {
@@ -3562,34 +3637,34 @@ const SoftUI = (() => {
3562
3637
 
3563
3638
  // Delegated remove clicks
3564
3639
  document.addEventListener('click', function(e) {
3565
- var removeBtn = e.target.closest('.sui-file-item-remove, .sui-file-preview-item-remove');
3640
+ const removeBtn = e.target.closest('.sui-file-item-remove, .sui-file-preview-item-remove');
3566
3641
  if (!removeBtn) return;
3567
- var item = removeBtn.closest('.sui-file-item, .sui-file-preview-item');
3642
+ const item = removeBtn.closest('.sui-file-item, .sui-file-preview-item');
3568
3643
  if (item) item.remove();
3569
3644
  });
3570
3645
 
3571
3646
  // Dragover styling
3572
3647
  document.addEventListener('dragover', function(e) {
3573
- var zone = e.target.closest('.sui-file-upload');
3648
+ const zone = e.target.closest('.sui-file-upload');
3574
3649
  if (!zone) return;
3575
3650
  e.preventDefault();
3576
3651
  zone.classList.add('sui-file-upload-dragover');
3577
3652
  });
3578
3653
 
3579
3654
  document.addEventListener('dragleave', function(e) {
3580
- var zone = e.target.closest('.sui-file-upload');
3655
+ const zone = e.target.closest('.sui-file-upload');
3581
3656
  if (!zone) return;
3582
3657
  zone.classList.remove('sui-file-upload-dragover');
3583
3658
  });
3584
3659
 
3585
3660
  document.addEventListener('drop', function(e) {
3586
- var zone = e.target.closest('.sui-file-upload');
3661
+ const zone = e.target.closest('.sui-file-upload');
3587
3662
  if (!zone) return;
3588
3663
  e.preventDefault();
3589
3664
  zone.classList.remove('sui-file-upload-dragover');
3590
- var files = Array.from(e.dataTransfer.files);
3665
+ const files = Array.from(e.dataTransfer.files);
3591
3666
  if (!files.length) return;
3592
- var mode = zone.getAttribute('data-sui-upload') || 'list';
3667
+ const mode = zone.getAttribute('data-sui-upload') || 'list';
3593
3668
  if (mode === 'preview') {
3594
3669
  renderFilePreview(zone, files, true);
3595
3670
  } else if (mode === 'progress') {
@@ -3603,33 +3678,33 @@ const SoftUI = (() => {
3603
3678
  // Radial Progress
3604
3679
  // =========================================
3605
3680
  function initRadialProgress() {
3606
- var radials = document.querySelectorAll('.sui-radial[data-value]');
3681
+ const radials = document.querySelectorAll('.sui-radial[data-value]');
3607
3682
  radials.forEach(function(el) {
3608
- var fill = el.querySelector('.sui-radial-fill');
3683
+ const fill = el.querySelector('.sui-radial-fill');
3609
3684
  if (!fill) return;
3610
- var value = parseFloat(el.getAttribute('data-value')) || 0;
3685
+ let value = parseFloat(el.getAttribute('data-value')) || 0;
3611
3686
  value = Math.max(0, Math.min(100, value));
3612
- var circumference = parseFloat(fill.getAttribute('stroke-dasharray') || fill.style.strokeDasharray);
3687
+ let circumference = parseFloat(fill.getAttribute('stroke-dasharray') || fill.style.strokeDasharray);
3613
3688
  if (!circumference) {
3614
- var r = fill.getAttribute('r');
3689
+ const r = fill.getAttribute('r');
3615
3690
  circumference = 2 * Math.PI * parseFloat(r);
3616
3691
  }
3617
3692
  fill.style.strokeDasharray = circumference;
3618
3693
  fill.style.strokeDashoffset = circumference;
3619
- var valueEl = el.querySelector('.sui-radial-value');
3620
- var duration = el.classList.contains('sui-radial-animated') ? 1200 : 600;
3694
+ const valueEl = el.querySelector('.sui-radial-value');
3695
+ const duration = el.classList.contains('sui-radial-animated') ? 1200 : 600;
3621
3696
 
3622
3697
  requestAnimationFrame(function() {
3623
3698
  requestAnimationFrame(function() {
3624
- var offset = circumference - (value / 100) * circumference;
3699
+ const offset = circumference - (value / 100) * circumference;
3625
3700
  fill.style.strokeDashoffset = offset;
3626
3701
 
3627
3702
  if (valueEl) {
3628
- var start = performance.now();
3703
+ const start = performance.now();
3629
3704
  function tick(now) {
3630
- var elapsed = now - start;
3631
- var progress = Math.min(elapsed / duration, 1);
3632
- var current = Math.round(progress * value);
3705
+ const elapsed = now - start;
3706
+ const progress = Math.min(elapsed / duration, 1);
3707
+ const current = Math.round(progress * value);
3633
3708
  valueEl.textContent = current + '%';
3634
3709
  if (progress < 1) requestAnimationFrame(tick);
3635
3710
  }
@@ -3650,15 +3725,15 @@ const SoftUI = (() => {
3650
3725
  // Number Input
3651
3726
  // =========================================
3652
3727
  document.addEventListener('click', function(e) {
3653
- var btn = e.target.closest('.sui-number-input-btn');
3728
+ const btn = e.target.closest('.sui-number-input-btn');
3654
3729
  if (!btn) return;
3655
- var wrap = btn.closest('.sui-number-input');
3656
- var input = wrap.querySelector('input[type="number"]');
3730
+ const wrap = btn.closest('.sui-number-input');
3731
+ const input = wrap.querySelector('input[type="number"]');
3657
3732
  if (!input) return;
3658
- var step = parseFloat(input.step) || 1;
3659
- var min = input.min !== '' ? parseFloat(input.min) : -Infinity;
3660
- var max = input.max !== '' ? parseFloat(input.max) : Infinity;
3661
- var val = parseFloat(input.value) || 0;
3733
+ const step = parseFloat(input.step) || 1;
3734
+ const min = input.min !== '' ? parseFloat(input.min) : -Infinity;
3735
+ const max = input.max !== '' ? parseFloat(input.max) : Infinity;
3736
+ let val = parseFloat(input.value) || 0;
3662
3737
  if (btn.getAttribute('data-action') === 'decrement') {
3663
3738
  val = Math.max(min, val - step);
3664
3739
  } else {
@@ -3672,13 +3747,13 @@ const SoftUI = (() => {
3672
3747
  // Password Toggle
3673
3748
  // =========================================
3674
3749
  document.addEventListener('click', function(e) {
3675
- var btn = e.target.closest('.sui-password-toggle');
3750
+ const btn = e.target.closest('.sui-password-toggle');
3676
3751
  if (!btn) return;
3677
3752
  e.stopPropagation();
3678
- var wrap = btn.closest('.sui-password-input');
3679
- var input = wrap.querySelector('input');
3753
+ const wrap = btn.closest('.sui-password-input');
3754
+ const input = wrap.querySelector('input');
3680
3755
  if (!input) return;
3681
- var isPassword = input.type === 'password';
3756
+ const isPassword = input.type === 'password';
3682
3757
  input.type = isPassword ? 'text' : 'password';
3683
3758
  btn.classList.toggle('active');
3684
3759
  }, true);
@@ -3687,37 +3762,37 @@ const SoftUI = (() => {
3687
3762
  // Tags Input
3688
3763
  // =========================================
3689
3764
  document.addEventListener('keydown', function(e) {
3690
- var input = e.target.closest('.sui-tags-input-field');
3765
+ const input = e.target.closest('.sui-tags-input-field');
3691
3766
  if (!input) return;
3692
- var wrap = input.closest('.sui-tags-input');
3767
+ const wrap = input.closest('.sui-tags-input');
3693
3768
  if (e.key === 'Enter' || e.key === ',') {
3694
3769
  e.preventDefault();
3695
- var val = input.value.trim().replace(/,$/, '');
3770
+ const val = input.value.trim().replace(/,$/, '');
3696
3771
  if (!val) return;
3697
- var tag = document.createElement('span');
3772
+ const tag = document.createElement('span');
3698
3773
  tag.className = 'sui-chip';
3699
3774
  tag.textContent = val;
3700
- var closeBtn = document.createElement('button');
3775
+ const closeBtn = document.createElement('button');
3701
3776
  closeBtn.className = 'sui-chip-close';
3702
3777
  closeBtn.setAttribute('aria-label', 'Remove');
3703
3778
  tag.appendChild(closeBtn);
3704
3779
  wrap.insertBefore(tag, input);
3705
3780
  input.value = '';
3706
3781
  } else if (e.key === 'Backspace' && !input.value) {
3707
- var tags = wrap.querySelectorAll('.sui-chip');
3782
+ const tags = wrap.querySelectorAll('.sui-chip');
3708
3783
  if (tags.length) tags[tags.length - 1].remove();
3709
3784
  }
3710
3785
  });
3711
3786
 
3712
3787
  document.addEventListener('click', function(e) {
3713
- var dismiss = e.target.closest('.sui-tags-input .sui-chip-close');
3788
+ const dismiss = e.target.closest('.sui-tags-input .sui-chip-close');
3714
3789
  if (dismiss) {
3715
3790
  dismiss.closest('.sui-chip').remove();
3716
3791
  return;
3717
3792
  }
3718
- var wrap = e.target.closest('.sui-tags-input');
3793
+ const wrap = e.target.closest('.sui-tags-input');
3719
3794
  if (wrap) {
3720
- var input = wrap.querySelector('.sui-tags-input-field');
3795
+ const input = wrap.querySelector('.sui-tags-input-field');
3721
3796
  if (input) input.focus();
3722
3797
  }
3723
3798
  });
@@ -3729,10 +3804,10 @@ const SoftUI = (() => {
3729
3804
  function initSlideSwaps() {
3730
3805
  document.querySelectorAll('.sui-swap-slide, .sui-swap-slide-x').forEach(function(swap) {
3731
3806
  if (swap.dataset.suiSlideInit) return;
3732
- var children = swap.querySelectorAll('.sui-swap-on, .sui-swap-off, .sui-swap-state');
3733
- var maxW = 0, maxH = 0;
3807
+ const children = swap.querySelectorAll('.sui-swap-on, .sui-swap-off, .sui-swap-state');
3808
+ let maxW = 0, maxH = 0;
3734
3809
  children.forEach(function(c) {
3735
- var prev = c.style.cssText;
3810
+ const prev = c.style.cssText;
3736
3811
  c.style.position = 'relative';
3737
3812
  c.style.opacity = '1';
3738
3813
  c.style.transform = 'none';
@@ -3753,12 +3828,12 @@ const SoftUI = (() => {
3753
3828
  }
3754
3829
 
3755
3830
  document.addEventListener('click', function(e) {
3756
- var swap = e.target.closest('.sui-swap');
3831
+ const swap = e.target.closest('.sui-swap');
3757
3832
  if (!swap) return;
3758
3833
  if (swap.classList.contains('sui-swap-cycle')) {
3759
- var states = Array.from(swap.querySelectorAll('.sui-swap-state'));
3760
- var current = states.findIndex(function(s) { return s.classList.contains('active'); });
3761
- var next = (current + 1) % states.length;
3834
+ const states = Array.from(swap.querySelectorAll('.sui-swap-state'));
3835
+ const current = states.findIndex(function(s) { return s.classList.contains('active'); });
3836
+ const next = (current + 1) % states.length;
3762
3837
  states.forEach(function(s) { s.classList.remove('active'); });
3763
3838
  states[next].classList.add('active');
3764
3839
  swap.setAttribute('data-state', next);
@@ -3772,36 +3847,36 @@ const SoftUI = (() => {
3772
3847
  // =========================================
3773
3848
  // Dock — magnification effect
3774
3849
  // =========================================
3775
- var dockMaxScale = 1.5;
3776
- var dockRange = 3;
3850
+ const dockMaxScale = 1.5;
3851
+ const dockRange = 3;
3777
3852
 
3778
3853
  document.addEventListener('mousemove', function(e) {
3779
- var dock = e.target.closest('.sui-dock');
3854
+ const dock = e.target.closest('.sui-dock');
3780
3855
  if (!dock) return;
3781
3856
  if (dock.classList.contains('sui-dock-no-scale')) return;
3782
- var iconOnly = dock.classList.contains('sui-dock-icon-scale');
3783
- var items = Array.from(dock.querySelectorAll('.sui-dock-item'));
3784
- var isVertical = dock.classList.contains('sui-dock-vertical');
3857
+ const iconOnly = dock.classList.contains('sui-dock-icon-scale');
3858
+ const items = Array.from(dock.querySelectorAll('.sui-dock-item'));
3859
+ const isVertical = dock.classList.contains('sui-dock-vertical');
3785
3860
 
3786
3861
  items.forEach(function(item) {
3787
- var rect = item.getBoundingClientRect();
3788
- var center = isVertical
3862
+ const rect = item.getBoundingClientRect();
3863
+ const center = isVertical
3789
3864
  ? rect.top + rect.height / 2
3790
3865
  : rect.left + rect.width / 2;
3791
- var mouse = isVertical ? e.clientY : e.clientX;
3792
- var baseSize = dock.classList.contains('sui-dock-sm') ? 32
3866
+ const mouse = isVertical ? e.clientY : e.clientX;
3867
+ const baseSize = dock.classList.contains('sui-dock-sm') ? 32
3793
3868
  : dock.classList.contains('sui-dock-lg') ? 52 : 40;
3794
- var dist = Math.abs(mouse - center) / baseSize;
3869
+ const dist = Math.abs(mouse - center) / baseSize;
3795
3870
 
3796
3871
  if (dist < dockRange) {
3797
- var scale = dockMaxScale - (dist / dockRange) * (dockMaxScale - 1);
3872
+ const scale = dockMaxScale - (dist / dockRange) * (dockMaxScale - 1);
3798
3873
  if (iconOnly) {
3799
3874
  item.style.width = '';
3800
3875
  item.style.height = '';
3801
- var svg = item.querySelector('svg');
3876
+ const svg = item.querySelector('svg');
3802
3877
  if (svg) svg.style.transform = 'scale(' + scale + ')';
3803
3878
  } else {
3804
- var newSize = Math.round(baseSize * scale);
3879
+ const newSize = Math.round(baseSize * scale);
3805
3880
  item.style.width = newSize + 'px';
3806
3881
  item.style.height = newSize + 'px';
3807
3882
  }
@@ -3809,7 +3884,7 @@ const SoftUI = (() => {
3809
3884
  item.style.width = '';
3810
3885
  item.style.height = '';
3811
3886
  if (iconOnly) {
3812
- var svg = item.querySelector('svg');
3887
+ const svg = item.querySelector('svg');
3813
3888
  if (svg) svg.style.transform = '';
3814
3889
  }
3815
3890
  }
@@ -3818,13 +3893,13 @@ const SoftUI = (() => {
3818
3893
 
3819
3894
  document.addEventListener('mouseleave', function(e) {
3820
3895
  if (!e.target.classList || !e.target.classList.contains('sui-dock')) return;
3821
- var iconOnly = e.target.classList.contains('sui-dock-icon-scale');
3822
- var items = e.target.querySelectorAll('.sui-dock-item');
3896
+ const iconOnly = e.target.classList.contains('sui-dock-icon-scale');
3897
+ const items = e.target.querySelectorAll('.sui-dock-item');
3823
3898
  items.forEach(function(item) {
3824
3899
  item.style.width = '';
3825
3900
  item.style.height = '';
3826
3901
  if (iconOnly) {
3827
- var svg = item.querySelector('svg');
3902
+ const svg = item.querySelector('svg');
3828
3903
  if (svg) svg.style.transform = '';
3829
3904
  }
3830
3905
  });
@@ -3833,9 +3908,9 @@ const SoftUI = (() => {
3833
3908
  // =========================================
3834
3909
  // Image Lightbox
3835
3910
  // =========================================
3836
- var lightboxOverlay = null;
3837
- var lightboxImages = [];
3838
- var lightboxIndex = 0;
3911
+ let lightboxOverlay = null;
3912
+ let lightboxImages = [];
3913
+ let lightboxIndex = 0;
3839
3914
 
3840
3915
  function createLightbox() {
3841
3916
  if (lightboxOverlay) return;
@@ -3875,7 +3950,7 @@ const SoftUI = (() => {
3875
3950
  lightboxOverlay.classList.add('open');
3876
3951
  lightboxOverlay.classList.remove('zoomed');
3877
3952
  document.body.style.overflow = 'hidden';
3878
- var hasMultiple = images.length > 1;
3953
+ const hasMultiple = images.length > 1;
3879
3954
  lightboxOverlay.querySelector('.sui-lightbox-prev').style.display = hasMultiple ? '' : 'none';
3880
3955
  lightboxOverlay.querySelector('.sui-lightbox-next').style.display = hasMultiple ? '' : 'none';
3881
3956
  lightboxOverlay.querySelector('.sui-lightbox-counter').style.display = hasMultiple ? '' : 'none';
@@ -3891,10 +3966,10 @@ const SoftUI = (() => {
3891
3966
  function showLightboxImage(idx) {
3892
3967
  if (lightboxImages.length === 0) return;
3893
3968
  lightboxIndex = (idx + lightboxImages.length) % lightboxImages.length;
3894
- var item = lightboxImages[lightboxIndex];
3895
- var img = lightboxOverlay.querySelector('img');
3896
- var caption = lightboxOverlay.querySelector('.sui-lightbox-caption');
3897
- var counter = lightboxOverlay.querySelector('.sui-lightbox-counter');
3969
+ const item = lightboxImages[lightboxIndex];
3970
+ const img = lightboxOverlay.querySelector('img');
3971
+ const caption = lightboxOverlay.querySelector('.sui-lightbox-caption');
3972
+ const counter = lightboxOverlay.querySelector('.sui-lightbox-counter');
3898
3973
  img.src = item.src;
3899
3974
  img.alt = item.alt || '';
3900
3975
  caption.textContent = item.caption || '';
@@ -3905,11 +3980,11 @@ const SoftUI = (() => {
3905
3980
 
3906
3981
  // Vertical gallery — click side thumb to update main
3907
3982
  document.addEventListener('click', function(e) {
3908
- var thumb = e.target.closest('.sui-lightbox-vertical-strip .sui-lightbox-thumb');
3983
+ const thumb = e.target.closest('.sui-lightbox-vertical-strip .sui-lightbox-thumb');
3909
3984
  if (!thumb) return;
3910
- var gallery = thumb.closest('.sui-lightbox-vertical');
3911
- var main = gallery.querySelector('.sui-lightbox-vertical-main img');
3912
- var img = thumb.querySelector('img');
3985
+ const gallery = thumb.closest('.sui-lightbox-vertical');
3986
+ const main = gallery.querySelector('.sui-lightbox-vertical-main img');
3987
+ const img = thumb.querySelector('img');
3913
3988
  if (main && img) {
3914
3989
  main.src = thumb.getAttribute('data-src') || img.src;
3915
3990
  main.alt = thumb.getAttribute('data-alt') || img.alt;
@@ -3920,39 +3995,39 @@ const SoftUI = (() => {
3920
3995
 
3921
3996
  // Click main image in vertical gallery to open lightbox
3922
3997
  document.addEventListener('click', function(e) {
3923
- var main = e.target.closest('.sui-lightbox-vertical-main');
3998
+ const main = e.target.closest('.sui-lightbox-vertical-main');
3924
3999
  if (!main) return;
3925
- var gallery = main.closest('.sui-lightbox-vertical');
3926
- var thumbs = Array.from(gallery.querySelectorAll('.sui-lightbox-vertical-strip .sui-lightbox-thumb'));
3927
- var images = thumbs.map(function(t) {
3928
- var img = t.querySelector('img');
4000
+ const gallery = main.closest('.sui-lightbox-vertical');
4001
+ const thumbs = Array.from(gallery.querySelectorAll('.sui-lightbox-vertical-strip .sui-lightbox-thumb'));
4002
+ const images = thumbs.map(function(t) {
4003
+ const img = t.querySelector('img');
3929
4004
  return {
3930
4005
  src: t.getAttribute('data-src') || (img ? img.src : ''),
3931
4006
  alt: t.getAttribute('data-alt') || (img ? img.alt : ''),
3932
4007
  caption: t.getAttribute('data-caption') || ''
3933
4008
  };
3934
4009
  });
3935
- var activeIdx = thumbs.findIndex(function(t) { return t.classList.contains('active'); });
4010
+ const activeIdx = thumbs.findIndex(function(t) { return t.classList.contains('active'); });
3936
4011
  openLightbox(images, activeIdx >= 0 ? activeIdx : 0);
3937
4012
  });
3938
4013
 
3939
4014
  // Click on thumbnail
3940
4015
  document.addEventListener('click', function(e) {
3941
- var thumb = e.target.closest('.sui-lightbox-thumb');
4016
+ const thumb = e.target.closest('.sui-lightbox-thumb');
3942
4017
  if (!thumb) return;
3943
4018
  // Skip if inside vertical strip (handled above)
3944
4019
  if (thumb.closest('.sui-lightbox-vertical-strip')) return;
3945
- var grid = thumb.closest('.sui-lightbox-grid');
3946
- var thumbs = grid ? Array.from(grid.querySelectorAll('.sui-lightbox-thumb')) : [thumb];
3947
- var images = thumbs.map(function(t) {
3948
- var img = t.querySelector('img');
4020
+ const grid = thumb.closest('.sui-lightbox-grid');
4021
+ const thumbs = grid ? Array.from(grid.querySelectorAll('.sui-lightbox-thumb')) : [thumb];
4022
+ const images = thumbs.map(function(t) {
4023
+ const img = t.querySelector('img');
3949
4024
  return {
3950
4025
  src: t.getAttribute('data-src') || (img ? img.src : ''),
3951
4026
  alt: t.getAttribute('data-alt') || (img ? img.alt : ''),
3952
4027
  caption: t.getAttribute('data-caption') || ''
3953
4028
  };
3954
4029
  });
3955
- var index = thumbs.indexOf(thumb);
4030
+ const index = thumbs.indexOf(thumb);
3956
4031
  openLightbox(images, index);
3957
4032
  });
3958
4033
 
@@ -3963,21 +4038,21 @@ const SoftUI = (() => {
3963
4038
  document.querySelectorAll('[data-sui-typewriter]').forEach(function(el) {
3964
4039
  if (el.dataset.suiTypewriterInit) return;
3965
4040
  el.dataset.suiTypewriterInit = '1';
3966
- var words = el.getAttribute('data-words');
3967
- var speed = parseInt(el.getAttribute('data-speed')) || 80;
3968
- var deleteSpeed = parseInt(el.getAttribute('data-delete-speed')) || 40;
3969
- var pause = parseInt(el.getAttribute('data-pause')) || 1500;
3970
- var loop = el.hasAttribute('data-loop');
4041
+ const words = el.getAttribute('data-words');
4042
+ const speed = parseInt(el.getAttribute('data-speed')) || 80;
4043
+ const deleteSpeed = parseInt(el.getAttribute('data-delete-speed')) || 40;
4044
+ const pause = parseInt(el.getAttribute('data-pause')) || 1500;
4045
+ const loop = el.hasAttribute('data-loop');
3971
4046
 
3972
4047
  if (words) {
3973
4048
  // Multiple phrases mode
3974
- var phrases = words.split('|').map(function(s) { return s.trim(); });
3975
- var phraseIdx = 0;
3976
- var charIdx = 0;
3977
- var deleting = false;
4049
+ const phrases = words.split('|').map(function(s) { return s.trim(); });
4050
+ let phraseIdx = 0;
4051
+ let charIdx = 0;
4052
+ let deleting = false;
3978
4053
 
3979
4054
  function tick() {
3980
- var current = phrases[phraseIdx];
4055
+ const current = phrases[phraseIdx];
3981
4056
  if (!deleting) {
3982
4057
  charIdx++;
3983
4058
  el.textContent = current.substring(0, charIdx);
@@ -4004,9 +4079,9 @@ const SoftUI = (() => {
4004
4079
  setTimeout(tick, 500);
4005
4080
  } else {
4006
4081
  // Single text mode — type out existing content
4007
- var text = el.textContent;
4082
+ const text = el.textContent;
4008
4083
  el.textContent = '';
4009
- var i = 0;
4084
+ let i = 0;
4010
4085
  function typeChar() {
4011
4086
  if (i < text.length) {
4012
4087
  el.textContent += text[i];
@@ -4032,15 +4107,15 @@ const SoftUI = (() => {
4032
4107
  document.querySelectorAll('[data-sui-text-rotate]').forEach(function(el) {
4033
4108
  if (el.dataset.suiRotateInit) return;
4034
4109
  el.dataset.suiRotateInit = '1';
4035
- var words = el.querySelectorAll('.sui-text-rotate-word');
4110
+ const words = el.querySelectorAll('.sui-text-rotate-word');
4036
4111
  if (words.length < 2) return;
4037
- var interval = parseInt(el.getAttribute('data-interval')) || 2000;
4038
- var index = 0;
4112
+ const interval = parseInt(el.getAttribute('data-interval')) || 2000;
4113
+ let index = 0;
4039
4114
 
4040
4115
  words[0].classList.add('active');
4041
4116
 
4042
4117
  setInterval(function() {
4043
- var current = words[index];
4118
+ const current = words[index];
4044
4119
  current.classList.remove('active');
4045
4120
  current.classList.add('exit');
4046
4121
  setTimeout(function() { current.classList.remove('exit'); }, 400);
@@ -4060,23 +4135,23 @@ const SoftUI = (() => {
4060
4135
  // =========================================
4061
4136
  // Copy Button
4062
4137
  // =========================================
4063
- 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>';
4064
- var checkSvg = '<svg viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>';
4138
+ const 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>';
4139
+ const checkSvg = '<svg viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>';
4065
4140
 
4066
4141
  document.addEventListener('click', function(e) {
4067
- var btn = e.target.closest('[data-sui-copy]');
4142
+ const btn = e.target.closest('[data-sui-copy]');
4068
4143
  if (!btn) return;
4069
- var text = btn.getAttribute('data-sui-copy');
4144
+ let text = btn.getAttribute('data-sui-copy');
4070
4145
  if (!text) {
4071
- var wrap = btn.closest('.sui-copy, .sui-copy-input');
4146
+ const wrap = btn.closest('.sui-copy, .sui-copy-input');
4072
4147
  if (wrap) {
4073
- var textEl = wrap.querySelector('.sui-copy-text');
4074
- var inputEl = wrap.querySelector('.sui-input');
4148
+ const textEl = wrap.querySelector('.sui-copy-text');
4149
+ const inputEl = wrap.querySelector('.sui-input');
4075
4150
  text = textEl ? textEl.textContent : inputEl ? inputEl.value : '';
4076
4151
  }
4077
4152
  }
4078
4153
  if (!text) return;
4079
- try { navigator.clipboard.writeText(text.trim()); } catch (err) {}
4154
+ try { navigator.clipboard.writeText(text.trim()); } catch (_) {}
4080
4155
  btn.classList.add('copied');
4081
4156
  btn.innerHTML = checkSvg;
4082
4157
  setTimeout(function() {
@@ -4092,25 +4167,25 @@ const SoftUI = (() => {
4092
4167
  document.querySelectorAll('.sui-diff[data-sui-diff]').forEach(function(diff) {
4093
4168
  if (diff.dataset.suiDiffInit) return;
4094
4169
  diff.dataset.suiDiffInit = '1';
4095
- var handle = diff.querySelector('.sui-diff-handle');
4096
- var before = diff.querySelector('.sui-diff-before');
4170
+ const handle = diff.querySelector('.sui-diff-handle');
4171
+ const before = diff.querySelector('.sui-diff-before');
4097
4172
  if (!handle || !before) return;
4098
- var isVertical = diff.classList.contains('sui-diff-vertical');
4173
+ const isVertical = diff.classList.contains('sui-diff-vertical');
4099
4174
 
4100
4175
  function onMove(e) {
4101
4176
  e.preventDefault();
4102
- var rect = diff.getBoundingClientRect();
4103
- var pos;
4177
+ const rect = diff.getBoundingClientRect();
4178
+ let pos;
4104
4179
  if (isVertical) {
4105
- var clientY = e.touches ? e.touches[0].clientY : e.clientY;
4180
+ const clientY = e.touches ? e.touches[0].clientY : e.clientY;
4106
4181
  pos = Math.max(0, Math.min(1, (clientY - rect.top) / rect.height));
4107
- var pct = (pos * 100);
4182
+ const pct = (pos * 100);
4108
4183
  before.style.clipPath = 'inset(0 0 ' + (100 - pct) + '% 0)';
4109
4184
  handle.style.top = pct + '%';
4110
4185
  } else {
4111
- var clientX = e.touches ? e.touches[0].clientX : e.clientX;
4186
+ const clientX = e.touches ? e.touches[0].clientX : e.clientX;
4112
4187
  pos = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
4113
- var pct = (pos * 100);
4188
+ const pct = (pos * 100);
4114
4189
  before.style.clipPath = 'inset(0 ' + (100 - pct) + '% 0 0)';
4115
4190
  handle.style.left = pct + '%';
4116
4191
  }
@@ -4147,15 +4222,15 @@ const SoftUI = (() => {
4147
4222
  // Speed Dial
4148
4223
  // =========================================
4149
4224
  document.addEventListener('click', function(e) {
4150
- var trigger = e.target.closest('.sui-speed-dial-trigger');
4225
+ const trigger = e.target.closest('.sui-speed-dial-trigger');
4151
4226
  if (trigger) {
4152
- var dial = trigger.closest('.sui-speed-dial');
4227
+ const dial = trigger.closest('.sui-speed-dial');
4153
4228
  dial.classList.toggle('open');
4154
4229
  return;
4155
4230
  }
4156
- var action = e.target.closest('.sui-speed-dial-action');
4231
+ const action = e.target.closest('.sui-speed-dial-action');
4157
4232
  if (action) {
4158
- var dial = action.closest('.sui-speed-dial');
4233
+ const dial = action.closest('.sui-speed-dial');
4159
4234
  dial.classList.remove('open');
4160
4235
  return;
4161
4236
  }
@@ -4168,13 +4243,13 @@ const SoftUI = (() => {
4168
4243
  // Hover mode
4169
4244
  document.addEventListener('mouseenter', function(e) {
4170
4245
  if (!e.target.closest) return;
4171
- var dial = e.target.closest('.sui-speed-dial-hover');
4246
+ const dial = e.target.closest('.sui-speed-dial-hover');
4172
4247
  if (dial) dial.classList.add('open');
4173
4248
  }, true);
4174
4249
 
4175
4250
  document.addEventListener('mouseleave', function(e) {
4176
4251
  if (!e.target.closest) return;
4177
- var dial = e.target.closest('.sui-speed-dial-hover');
4252
+ const dial = e.target.closest('.sui-speed-dial-hover');
4178
4253
  if (dial) dial.classList.remove('open');
4179
4254
  }, true);
4180
4255
 
@@ -4182,11 +4257,11 @@ const SoftUI = (() => {
4182
4257
  // Tree View
4183
4258
  // =========================================
4184
4259
  document.addEventListener('click', function(e) {
4185
- var label = e.target.closest('.sui-tree-label');
4260
+ const label = e.target.closest('.sui-tree-label');
4186
4261
  if (!label) return;
4187
4262
  if (e.target.closest('.sui-checkbox')) return;
4188
- var item = label.closest('.sui-tree-item');
4189
- var children = item.querySelector('.sui-tree-children');
4263
+ const item = label.closest('.sui-tree-item');
4264
+ const children = item.querySelector('.sui-tree-children');
4190
4265
  if (children) {
4191
4266
  item.classList.toggle('expanded');
4192
4267
  }
@@ -4195,12 +4270,12 @@ const SoftUI = (() => {
4195
4270
  // Tree checkbox propagation
4196
4271
  document.addEventListener('change', function(e) {
4197
4272
  if (!e.target.closest('.sui-tree .sui-checkbox input')) return;
4198
- var checkbox = e.target;
4199
- var item = checkbox.closest('.sui-tree-item');
4200
- var checked = checkbox.checked;
4273
+ const checkbox = e.target;
4274
+ const item = checkbox.closest('.sui-tree-item');
4275
+ const checked = checkbox.checked;
4201
4276
 
4202
4277
  // Propagate down — check/uncheck all children
4203
- var childBoxes = item.querySelectorAll('.sui-tree-children .sui-checkbox input');
4278
+ const childBoxes = item.querySelectorAll('.sui-tree-children .sui-checkbox input');
4204
4279
  childBoxes.forEach(function(cb) {
4205
4280
  cb.checked = checked;
4206
4281
  cb.indeterminate = false;
@@ -4211,16 +4286,16 @@ const SoftUI = (() => {
4211
4286
  });
4212
4287
 
4213
4288
  function updateTreeParent(item) {
4214
- var parentChildren = item.closest('.sui-tree-children');
4289
+ const parentChildren = item.closest('.sui-tree-children');
4215
4290
  if (!parentChildren) return;
4216
- var parentItem = parentChildren.closest('.sui-tree-item');
4291
+ const parentItem = parentChildren.closest('.sui-tree-item');
4217
4292
  if (!parentItem) return;
4218
- var parentCb = parentItem.querySelector(':scope > .sui-tree-label .sui-checkbox input');
4293
+ const parentCb = parentItem.querySelector(':scope > .sui-tree-label .sui-checkbox input');
4219
4294
  if (!parentCb) return;
4220
4295
 
4221
- var siblings = parentChildren.querySelectorAll(':scope > .sui-tree-item > .sui-tree-label .sui-checkbox input');
4222
- var total = siblings.length;
4223
- var checkedCount = 0;
4296
+ const siblings = parentChildren.querySelectorAll(':scope > .sui-tree-item > .sui-tree-label .sui-checkbox input');
4297
+ const total = siblings.length;
4298
+ let checkedCount = 0;
4224
4299
  siblings.forEach(function(cb) { if (cb.checked) checkedCount++; });
4225
4300
 
4226
4301
  if (checkedCount === 0) {
@@ -4243,10 +4318,10 @@ const SoftUI = (() => {
4243
4318
  // =========================================
4244
4319
  function tour(steps, options) {
4245
4320
  options = options || {};
4246
- var currentStep = 0;
4247
- var overlay, backdrop, spotlight, tooltip;
4248
- var padding = options.padding || 8;
4249
- var noOverlay = options.noOverlay || false;
4321
+ let currentStep = 0;
4322
+ let overlay, backdrop, spotlight, tooltip;
4323
+ const padding = options.padding || 8;
4324
+ const noOverlay = options.noOverlay || false;
4250
4325
 
4251
4326
  function create() {
4252
4327
  overlay = document.createElement('div');
@@ -4265,12 +4340,12 @@ const SoftUI = (() => {
4265
4340
  backdrop.addEventListener('click', close);
4266
4341
  }
4267
4342
 
4268
- var firstShow = true;
4343
+ let firstShow = true;
4269
4344
 
4270
4345
  function show(idx) {
4271
4346
  currentStep = idx;
4272
- var step = steps[idx];
4273
- var target = document.querySelector(step.target);
4347
+ const step = steps[idx];
4348
+ const target = document.querySelector(step.target);
4274
4349
 
4275
4350
  // Only hide on first show to avoid flash between steps
4276
4351
  if (firstShow) {
@@ -4280,16 +4355,16 @@ const SoftUI = (() => {
4280
4355
  }
4281
4356
 
4282
4357
  // Scroll if element isn't comfortably in view (with margin for tooltip)
4283
- var needsScroll = false;
4358
+ let needsScroll = false;
4284
4359
  if (target) {
4285
- var r = target.getBoundingClientRect();
4286
- var margin = 120;
4360
+ const r = target.getBoundingClientRect();
4361
+ const margin = 120;
4287
4362
  needsScroll = r.top < margin || r.bottom > window.innerHeight - margin;
4288
4363
  if (needsScroll) target.scrollIntoView({ block: 'center', behavior: 'smooth' });
4289
4364
  }
4290
4365
  setTimeout(function() {
4291
4366
  if (target) {
4292
- var rect = target.getBoundingClientRect();
4367
+ const rect = target.getBoundingClientRect();
4293
4368
  spotlight.style.top = (rect.top - padding) + 'px';
4294
4369
  spotlight.style.left = (rect.left - padding) + 'px';
4295
4370
  spotlight.style.width = (rect.width + padding * 2) + 'px';
@@ -4297,10 +4372,10 @@ const SoftUI = (() => {
4297
4372
  }
4298
4373
 
4299
4374
  // Build tooltip content
4300
- var dotsHtml = '';
4375
+ let dotsHtml = '';
4301
4376
  if (steps.length > 1) {
4302
4377
  dotsHtml = '<div class="sui-tour-dots">';
4303
- for (var i = 0; i < steps.length; i++) {
4378
+ for (let i = 0; i < steps.length; i++) {
4304
4379
  dotsHtml += '<span class="sui-tour-dot' + (i === idx ? ' active' : '') + '"></span>';
4305
4380
  }
4306
4381
  dotsHtml += '</div>';
@@ -4318,10 +4393,10 @@ const SoftUI = (() => {
4318
4393
  '</div>';
4319
4394
 
4320
4395
  // Button handlers
4321
- var nextBtn = tooltip.querySelector('.sui-tour-next');
4322
- var prevBtn = tooltip.querySelector('.sui-tour-prev');
4323
- var skipBtn = tooltip.querySelector('.sui-tour-skip');
4324
- var doneBtn = tooltip.querySelector('.sui-tour-done');
4396
+ const nextBtn = tooltip.querySelector('.sui-tour-next');
4397
+ const prevBtn = tooltip.querySelector('.sui-tour-prev');
4398
+ const skipBtn = tooltip.querySelector('.sui-tour-skip');
4399
+ const doneBtn = tooltip.querySelector('.sui-tour-done');
4325
4400
  if (nextBtn) nextBtn.addEventListener('click', function() { show(currentStep + 1); });
4326
4401
  if (prevBtn) prevBtn.addEventListener('click', function() { show(currentStep - 1); });
4327
4402
  if (skipBtn) skipBtn.addEventListener('click', close);
@@ -4329,11 +4404,11 @@ const SoftUI = (() => {
4329
4404
 
4330
4405
  // Position tooltip after scroll settles
4331
4406
  if (target) {
4332
- var rect = target.getBoundingClientRect();
4333
- var pos = step.position || 'bottom';
4334
- var tooltipW = 300;
4335
- var centerX = rect.left + rect.width / 2 - tooltipW / 2;
4336
- var top, left;
4407
+ const rect = target.getBoundingClientRect();
4408
+ const pos = step.position || 'bottom';
4409
+ const tooltipW = 300;
4410
+ const centerX = rect.left + rect.width / 2 - tooltipW / 2;
4411
+ let top, left;
4337
4412
 
4338
4413
  tooltip.style.transform = '';
4339
4414