softui-css 1.8.6 → 1.10.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
@@ -294,6 +294,12 @@ const SoftUI = (() => {
294
294
  // Charts
295
295
  initCharts();
296
296
 
297
+ // Styled Selects
298
+ initStyledSelects();
299
+
300
+ // Selectable pricing
301
+ initSelectablePricing();
302
+
297
303
  // Data Tables
298
304
  initDataTables();
299
305
 
@@ -2534,6 +2540,109 @@ const SoftUI = (() => {
2534
2540
  });
2535
2541
  }
2536
2542
 
2543
+ function initSelectablePricing() {
2544
+ document.querySelectorAll('.sui-pricing-selectable').forEach(function(container) {
2545
+ var cards = container.querySelectorAll('.sui-pricing-card');
2546
+ cards.forEach(function(card) {
2547
+ card.addEventListener('click', function() {
2548
+ cards.forEach(function(c) { c.classList.remove('selected'); });
2549
+ card.classList.add('selected');
2550
+ container.setAttribute('data-selected', card.getAttribute('data-plan') || '');
2551
+ container.dispatchEvent(new Event('change', { bubbles: true }));
2552
+ });
2553
+ });
2554
+ });
2555
+ }
2556
+
2557
+ function initStyledSelects() {
2558
+ 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;
2565
+
2566
+ if (!trigger || !menu) return;
2567
+
2568
+ // Set initial value
2569
+ var selected = sel.querySelector('.sui-styled-select-option.selected');
2570
+ if (selected && valueEl) {
2571
+ valueEl.textContent = selected.textContent;
2572
+ valueEl.classList.remove('sui-styled-select-placeholder');
2573
+ } else if (valueEl && placeholder) {
2574
+ valueEl.textContent = placeholder;
2575
+ valueEl.classList.add('sui-styled-select-placeholder');
2576
+ }
2577
+
2578
+ // Toggle menu
2579
+ trigger.addEventListener('click', function(e) {
2580
+ e.stopPropagation();
2581
+ // Close other open selects
2582
+ document.querySelectorAll('.sui-styled-select.open').forEach(function(s) {
2583
+ if (s !== sel) s.classList.remove('open');
2584
+ });
2585
+ sel.classList.toggle('open');
2586
+ if (sel.classList.contains('open')) {
2587
+ // Focus selected or first option
2588
+ focusIdx = -1;
2589
+ options.forEach(function(o, i) { if (o.classList.contains('selected')) focusIdx = i; });
2590
+ }
2591
+ });
2592
+
2593
+ // Option click
2594
+ options.forEach(function(opt, i) {
2595
+ opt.addEventListener('click', function() {
2596
+ options.forEach(function(o) { o.classList.remove('selected'); });
2597
+ opt.classList.add('selected');
2598
+ if (valueEl) {
2599
+ valueEl.textContent = opt.textContent;
2600
+ valueEl.classList.remove('sui-styled-select-placeholder');
2601
+ }
2602
+ sel.setAttribute('data-value', opt.getAttribute('data-value') || opt.textContent);
2603
+ sel.classList.remove('open');
2604
+ // Dispatch change event for datatable filter integration
2605
+ sel.dispatchEvent(new Event('change', { bubbles: true }));
2606
+ trigger.focus();
2607
+ });
2608
+ });
2609
+
2610
+ // Keyboard navigation
2611
+ trigger.addEventListener('keydown', function(e) {
2612
+ var isOpen = sel.classList.contains('open');
2613
+ if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
2614
+ e.preventDefault();
2615
+ if (!isOpen) { sel.classList.add('open'); focusIdx = -1; }
2616
+ if (e.key === 'ArrowDown') focusIdx = Math.min(focusIdx + 1, options.length - 1);
2617
+ else focusIdx = Math.max(focusIdx - 1, 0);
2618
+ options.forEach(function(o) { o.classList.remove('focused'); });
2619
+ options[focusIdx].classList.add('focused');
2620
+ options[focusIdx].scrollIntoView({ block: 'nearest' });
2621
+ } else if (e.key === 'Enter' || e.key === ' ') {
2622
+ e.preventDefault();
2623
+ if (isOpen && focusIdx >= 0) {
2624
+ options[focusIdx].click();
2625
+ } else {
2626
+ sel.classList.toggle('open');
2627
+ }
2628
+ } else if (e.key === 'Escape') {
2629
+ sel.classList.remove('open');
2630
+ options.forEach(function(o) { o.classList.remove('focused'); });
2631
+ }
2632
+ });
2633
+ });
2634
+
2635
+ // Close on outside click
2636
+ document.addEventListener('click', function(e) {
2637
+ if (!e.target.closest) return;
2638
+ if (!e.target.closest('.sui-styled-select')) {
2639
+ document.querySelectorAll('.sui-styled-select.open').forEach(function(s) {
2640
+ s.classList.remove('open');
2641
+ });
2642
+ }
2643
+ });
2644
+ }
2645
+
2537
2646
  function initDataTables() {
2538
2647
  document.querySelectorAll('.sui-datatable').forEach(function(dt) {
2539
2648
  var table = dt.querySelector('.sui-table');
@@ -2546,9 +2655,16 @@ const SoftUI = (() => {
2546
2655
  var filteredRows = allRows.slice();
2547
2656
  var currentPage = 1;
2548
2657
 
2549
- // Per-page selector
2550
- var perpageSelect = dt.querySelector('.sui-datatable-perpage select');
2551
- var perPage = perpageSelect ? parseInt(perpageSelect.value, 10) : allRows.length;
2658
+ // 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;
2662
+ function getPerpageValue() {
2663
+ if (perpageNative) return parseInt(perpageNative.value, 10);
2664
+ if (perpageStyled) return parseInt(perpageStyled.getAttribute('data-value') || '', 10);
2665
+ return allRows.length;
2666
+ }
2667
+ var perPage = perpageSelect ? getPerpageValue() : allRows.length;
2552
2668
 
2553
2669
  // Info & pagination elements
2554
2670
  var infoEl = dt.querySelector('.sui-datatable-info');
@@ -2634,56 +2750,108 @@ const SoftUI = (() => {
2634
2750
  }
2635
2751
  }
2636
2752
 
2637
- // Search / filter
2638
- if (searchInput) {
2639
- searchInput.addEventListener('input', function() {
2640
- var query = searchInput.value.toLowerCase().trim();
2641
- filteredRows = allRows.filter(function(row) {
2642
- return row.textContent.toLowerCase().indexOf(query) !== -1;
2753
+ // Filter elements (supports <select> and .sui-dropdown)
2754
+ var filterEls = dt.querySelectorAll('.sui-datatable-filter');
2755
+
2756
+ function getFilterValue(el) {
2757
+ if (el.tagName === 'SELECT') return el.value;
2758
+ // Dropdown-based filter: read from active item
2759
+ var active = el.querySelector('.sui-dropdown-item.active');
2760
+ return active ? (active.getAttribute('data-value') || '') : '';
2761
+ }
2762
+
2763
+ function applyFilters() {
2764
+ var query = searchInput ? searchInput.value.toLowerCase().trim() : '';
2765
+ filteredRows = allRows.filter(function(row) {
2766
+ if (query && row.textContent.toLowerCase().indexOf(query) === -1) return false;
2767
+ var pass = true;
2768
+ filterEls.forEach(function(el) {
2769
+ var attr = el.getAttribute('data-filter-attr') || 'data-status';
2770
+ var val = getFilterValue(el);
2771
+ if (val && row.getAttribute(attr) !== val) pass = false;
2643
2772
  });
2644
- currentPage = 1;
2645
- render();
2773
+ return pass;
2646
2774
  });
2775
+ currentPage = 1;
2776
+ render();
2777
+ }
2778
+
2779
+ // Search input
2780
+ if (searchInput) {
2781
+ searchInput.addEventListener('input', applyFilters);
2647
2782
  }
2648
2783
 
2784
+ // Wire up filters
2785
+ filterEls.forEach(function(el) {
2786
+ if (el.tagName === 'SELECT') {
2787
+ el.addEventListener('change', applyFilters);
2788
+ } else {
2789
+ // Dropdown-based filter
2790
+ el.querySelectorAll('.sui-dropdown-item').forEach(function(item) {
2791
+ item.addEventListener('click', function() {
2792
+ // Update active state
2793
+ el.querySelectorAll('.sui-dropdown-item').forEach(function(i) { i.classList.remove('active'); });
2794
+ item.classList.add('active');
2795
+ // Update label
2796
+ var label = el.querySelector('.sui-datatable-filter-label');
2797
+ if (label) label.textContent = item.textContent;
2798
+ // Close dropdown
2799
+ el.classList.remove('open');
2800
+ var toggle = el.querySelector('.sui-dropdown-toggle');
2801
+ if (toggle) toggle.setAttribute('aria-expanded', 'false');
2802
+ applyFilters();
2803
+ });
2804
+ });
2805
+ }
2806
+ });
2807
+
2649
2808
  // Per-page change
2650
2809
  if (perpageSelect) {
2651
2810
  perpageSelect.addEventListener('change', function() {
2652
- perPage = parseInt(perpageSelect.value, 10);
2811
+ perPage = getPerpageValue();
2653
2812
  currentPage = 1;
2654
2813
  render();
2655
2814
  });
2656
2815
  }
2657
2816
 
2658
- // Sortable headers
2817
+ // Sortable headers (unsorted → asc → desc → unsorted)
2659
2818
  var ths = table.querySelectorAll('th[data-sort]');
2660
2819
  ths.forEach(function(th) {
2661
2820
  th.addEventListener('click', function() {
2662
- var col = th.getAttribute('data-sort');
2663
2821
  var colIndex = Array.prototype.indexOf.call(th.parentElement.children, th);
2664
- var type = col; // 'string' or 'number'
2665
- var dir = 'asc';
2822
+ var type = th.getAttribute('data-sort');
2666
2823
 
2824
+ // Cycle: unsorted → asc → desc → unsorted
2825
+ var dir;
2667
2826
  if (th.classList.contains('sort-asc')) {
2668
2827
  dir = 'desc';
2828
+ } else if (th.classList.contains('sort-desc')) {
2829
+ dir = 'none';
2830
+ } else {
2831
+ dir = 'asc';
2669
2832
  }
2670
2833
 
2671
2834
  // Reset all headers
2672
2835
  ths.forEach(function(h) { h.classList.remove('sort-asc', 'sort-desc'); });
2673
- th.classList.add(dir === 'asc' ? 'sort-asc' : 'sort-desc');
2674
-
2675
- filteredRows.sort(function(a, b) {
2676
- var aText = a.children[colIndex] ? a.children[colIndex].textContent.trim() : '';
2677
- var bText = b.children[colIndex] ? b.children[colIndex].textContent.trim() : '';
2678
-
2679
- if (type === 'number') {
2680
- var aNum = parseFloat(aText.replace(/[^0-9.\-]/g, '')) || 0;
2681
- var bNum = parseFloat(bText.replace(/[^0-9.\-]/g, '')) || 0;
2682
- return dir === 'asc' ? aNum - bNum : bNum - aNum;
2683
- }
2684
2836
 
2685
- return dir === 'asc' ? aText.localeCompare(bText) : bText.localeCompare(aText);
2686
- });
2837
+ if (dir === 'none') {
2838
+ // Restore original order within filtered set
2839
+ filteredRows = allRows.filter(function(row) { return filteredRows.indexOf(row) !== -1; });
2840
+ } else {
2841
+ th.classList.add(dir === 'asc' ? 'sort-asc' : 'sort-desc');
2842
+ 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() : '';
2845
+
2846
+ if (type === 'number') {
2847
+ var aNum = parseFloat(aText.replace(/[^0-9.\-]/g, '')) || 0;
2848
+ var bNum = parseFloat(bText.replace(/[^0-9.\-]/g, '')) || 0;
2849
+ return dir === 'asc' ? aNum - bNum : bNum - aNum;
2850
+ }
2851
+
2852
+ return dir === 'asc' ? aText.localeCompare(bText) : bText.localeCompare(aText);
2853
+ });
2854
+ }
2687
2855
 
2688
2856
  // Re-append sorted rows to DOM
2689
2857
  filteredRows.forEach(function(row) { tbody.appendChild(row); });