cynx-ui 1.2.26 → 1.2.27

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.
@@ -1,4 +1,5 @@
1
1
  import { useState, useRef, useEffect } from 'react';
2
+ import { createPortal } from 'react-dom';
2
3
  import { ChevronLeft, ChevronRight, Calendar } from 'lucide-react';
3
4
 
4
5
  // DatePicker — reusable date picker component
@@ -24,19 +25,41 @@ export function DatePicker({ value, onChange, placeholder = 'Select date', badge
24
25
  const [open, setOpen] = useState(false);
25
26
  const [view, setView] = useState('day'); // 'day' | 'month' | 'year'
26
27
  const [cursor, setCursor] = useState(() => value ? new Date(value) : new Date());
28
+ const [pos, setPos] = useState({ top: 0, left: 0, width: 0 });
27
29
  const ref = useRef(null);
30
+ const btnRef = useRef(null);
31
+ const dropRef = useRef(null);
28
32
 
29
33
  // Close on outside click
30
34
  useEffect(() => {
31
35
  if (!open) return;
32
- function close(e) { if (!ref.current?.contains(e.target)) { setOpen(false); setView('day'); } }
33
- document.addEventListener('mousedown', close);
34
- return () => document.removeEventListener('mousedown', close);
36
+ function close(e) {
37
+ if (ref.current?.contains(e.target)) return;
38
+ if (dropRef.current?.contains(e.target)) return;
39
+ setOpen(false); setView('day');
40
+ }
41
+ document.addEventListener('mousedown', close, true);
42
+ return () => document.removeEventListener('mousedown', close, true);
35
43
  }, [open]);
36
44
 
37
45
  // Sync cursor when value changes externally
38
46
  useEffect(() => { if (value) setCursor(new Date(value)); }, [value]);
39
47
 
48
+ // Compute button position when dropdown opens
49
+ useEffect(() => {
50
+ if (!open || !btnRef.current) return;
51
+ function updatePos() {
52
+ if (btnRef.current) {
53
+ const r = btnRef.current.getBoundingClientRect();
54
+ setPos({ top: r.bottom + 6, left: r.left, width: Math.max(r.width, 252) });
55
+ }
56
+ }
57
+ updatePos();
58
+ window.addEventListener('scroll', updatePos, true);
59
+ window.addEventListener('resize', updatePos);
60
+ return () => { window.removeEventListener('scroll', updatePos, true); window.removeEventListener('resize', updatePos); };
61
+ }, [open]);
62
+
40
63
  function pick(date) {
41
64
  onChange?.(date);
42
65
  setOpen(false);
@@ -196,7 +219,7 @@ export function DatePicker({ value, onChange, placeholder = 'Select date', badge
196
219
  return (
197
220
  <div ref={ref} style={{ position: 'relative', display: 'inline-block', ...style }}>
198
221
  {/* Trigger */}
199
- <button type="button" onClick={() => !disabled && setOpen(o => !o)} style={{
222
+ <button ref={btnRef} type="button" onClick={() => !disabled && setOpen(o => !o)} style={{
200
223
  display: 'flex', alignItems: 'center', gap: 7,
201
224
  padding: '5px 10px', borderRadius: 'var(--rads)',
202
225
  border: '1px solid var(--border)', background: 'var(--surface)',
@@ -220,13 +243,13 @@ export function DatePicker({ value, onChange, placeholder = 'Select date', badge
220
243
  )}
221
244
  </button>
222
245
 
223
- {/* Dropdown */}
224
- {open && (
225
- <div style={{
226
- position: 'absolute', top: 'calc(100% + 6px)', left: 0, zIndex: 999,
246
+ {/* Dropdown via portal */}
247
+ {open && createPortal(
248
+ <div ref={dropRef} style={{
249
+ position: 'fixed', top: pos.top, left: pos.left, width: pos.width, zIndex: 9000,
227
250
  background: 'var(--surface)', border: '1px solid var(--border)',
228
251
  borderRadius: 10, boxShadow: 'var(--shadow-lg)',
229
- padding: '10px 12px', width: 252,
252
+ padding: '10px 12px',
230
253
  animation: 'dp-in .12s ease',
231
254
  }}>
232
255
  {/* Calendar header */}
@@ -253,7 +276,8 @@ export function DatePicker({ value, onChange, placeholder = 'Select date', badge
253
276
  {view === 'month' && renderMonths()}
254
277
  {view === 'year' && renderYears()}
255
278
 
256
- </div>
279
+ </div>,
280
+ document.body
257
281
  )}
258
282
 
259
283
  <style>{`@keyframes dp-in { from { opacity:0; transform:translateY(-4px); } to { opacity:1; transform:none; } }`}</style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cynx-ui",
3
- "version": "1.2.26",
3
+ "version": "1.2.27",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./index.js"