cynx-ui 1.2.32 → 1.2.33

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.
@@ -18,13 +18,23 @@ const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', '
18
18
 
19
19
  function pad(n) { return String(n).padStart(2, '0'); }
20
20
  function toKey(y, m, d) { return `${y}-${pad(m+1)}-${pad(d)}`; }
21
- function isSameDay(a, b) { return a && b && a.getFullYear()===b.getFullYear() && a.getMonth()===b.getMonth() && a.getDate()===b.getDate(); }
21
+ function toDate(val) {
22
+ if (!val) return null;
23
+ if (val instanceof Date) return isNaN(val.getTime()) ? null : val;
24
+ const d = new Date(val);
25
+ return isNaN(d.getTime()) ? null : d;
26
+ }
27
+
28
+ function isSameDay(a, b) {
29
+ const da = toDate(a), db = toDate(b);
30
+ return da && db && da.getFullYear()===db.getFullYear() && da.getMonth()===db.getMonth() && da.getDate()===db.getDate();
31
+ }
22
32
  function isToday(y, m, d) { const t = new Date(); return t.getFullYear()===y && t.getMonth()===m && t.getDate()===d; }
23
33
 
24
34
  export function DatePicker({ value, onChange, placeholder = 'Select date', badges = {}, minDate, maxDate, disabled, style }) {
25
35
  const [open, setOpen] = useState(false);
26
36
  const [view, setView] = useState('day'); // 'day' | 'month' | 'year'
27
- const [cursor, setCursor] = useState(() => value ? new Date(value) : new Date());
37
+ const [cursor, setCursor] = useState(() => toDate(value) || new Date());
28
38
  const [pos, setPos] = useState({ top: 0, left: 0, width: 0 });
29
39
  const ref = useRef(null);
30
40
  const btnRef = useRef(null);
@@ -43,7 +53,10 @@ export function DatePicker({ value, onChange, placeholder = 'Select date', badge
43
53
  }, [open]);
44
54
 
45
55
  // Sync cursor when value changes externally
46
- useEffect(() => { if (value) setCursor(new Date(value)); }, [value]);
56
+ useEffect(() => {
57
+ const d = toDate(value);
58
+ if (d) setCursor(d);
59
+ }, [value]);
47
60
 
48
61
  // Compute button position when dropdown opens
49
62
  useEffect(() => {
@@ -61,7 +74,14 @@ export function DatePicker({ value, onChange, placeholder = 'Select date', badge
61
74
  }, [open]);
62
75
 
63
76
  function pick(date) {
64
- onChange?.(date);
77
+ if (typeof value === 'string') {
78
+ const y = date.getFullYear();
79
+ const m = pad(date.getMonth() + 1);
80
+ const d = pad(date.getDate());
81
+ onChange?.(`${y}-${m}-${d}`);
82
+ } else {
83
+ onChange?.(date);
84
+ }
65
85
  setOpen(false);
66
86
  setView('day');
67
87
  }
@@ -78,9 +98,10 @@ export function DatePicker({ value, onChange, placeholder = 'Select date', badge
78
98
  setView(v => v === 'day' ? 'month' : v === 'month' ? 'year' : 'year');
79
99
  }
80
100
 
81
- const formatted = value
82
- ? `${MONTHS[value.getMonth()]} ${value.getDate()}, ${value.getFullYear()}`
83
- : null;
101
+ const valDate = toDate(value);
102
+ const formatted = valDate
103
+ ? `${MONTHS[valDate.getMonth()]} ${valDate.getDate()}, ${valDate.getFullYear()}`
104
+ : (typeof value === 'string' ? value : null);
84
105
 
85
106
  // ── Day grid ─────────────────────────────────────────────────
86
107
  function renderDays() {
@@ -14,7 +14,16 @@ export function SearchableDropdown({ value, onChange, options = [], placeholder
14
14
  const selected = options.find(o => o.value === value);
15
15
 
16
16
  const filtered = query
17
- ? options.filter(o => o.label.toLowerCase().includes(query.toLowerCase()))
17
+ ? options.filter(o => {
18
+ const q = query.toLowerCase().trim();
19
+ for (const [key, val] of Object.entries(o)) {
20
+ if (key === 'displayLabel') continue;
21
+ if (val != null && typeof val !== 'function' && typeof val !== 'object') {
22
+ if (String(val).toLowerCase().includes(q)) return true;
23
+ }
24
+ }
25
+ return false;
26
+ })
18
27
  : options;
19
28
 
20
29
  useEffect(() => {
@@ -68,7 +77,7 @@ export function SearchableDropdown({ value, onChange, options = [], placeholder
68
77
  onMouseEnter={e => { if (!open) e.currentTarget.style.borderColor = 'var(--accent)'; }}
69
78
  onMouseLeave={e => { if (!open) e.currentTarget.style.borderColor = 'var(--border)'; }}>
70
79
  <span style={{ flex: 1, textAlign: 'left', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
71
- {selected?.label || placeholder}
80
+ {selected ? (selected.displayLabel || selected.label) : placeholder}
72
81
  </span>
73
82
  <ChevronDown size={13} style={{ color: 'var(--muted)', flexShrink: 0 }} />
74
83
  </button>
@@ -105,7 +114,7 @@ export function SearchableDropdown({ value, onChange, options = [], placeholder
105
114
  }}
106
115
  onMouseEnter={e => { if (o.value !== value) e.currentTarget.style.background = 'var(--grey-1)'; }}
107
116
  onMouseLeave={e => { if (o.value !== value) e.currentTarget.style.background = 'transparent'; }}>
108
- <span>{o.label}</span>
117
+ <span>{o.displayLabel || o.label}</span>
109
118
  {o.value === value && <Check size={12} />}
110
119
  </div>
111
120
  ))}
@@ -66,8 +66,16 @@ export default function Select({
66
66
 
67
67
  const filtered = useMemo(() => {
68
68
  if (!query) return options;
69
- const q = query.toLowerCase();
70
- return options.filter(o => o.label.toLowerCase().includes(q));
69
+ const q = query.toLowerCase().trim();
70
+ return options.filter(o => {
71
+ for (const [key, val] of Object.entries(o)) {
72
+ if (key === 'displayLabel') continue;
73
+ if (val != null && typeof val !== 'function' && typeof val !== 'object') {
74
+ if (String(val).toLowerCase().includes(q)) return true;
75
+ }
76
+ }
77
+ return false;
78
+ });
71
79
  }, [options, query]);
72
80
 
73
81
  function handleOpen() {
@@ -101,7 +109,7 @@ export default function Select({
101
109
 
102
110
  function getLabel(optVal) {
103
111
  const opt = options.find(o => o.value === optVal);
104
- return opt ? opt.label : optVal;
112
+ return opt ? (opt.displayLabel || opt.label) : optVal;
105
113
  }
106
114
 
107
115
  const selectedValues = multi ? (Array.isArray(value) ? value : []) : (value ? [value] : []);
@@ -286,7 +294,7 @@ export default function Select({
286
294
  </span>
287
295
  )}
288
296
  <span style={{ flex: 1, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
289
- {opt.label}
297
+ {opt.displayLabel || opt.label}
290
298
  </span>
291
299
  {!multi && isSelected(opt.value) && (
292
300
  <Check size={13} style={{ flexShrink: 0, color: 'var(--accent)' }} />
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cynx-ui",
3
- "version": "1.2.32",
3
+ "version": "1.2.33",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./index.js"