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.
- package/components/DatePicker.jsx +34 -10
- package/package.json +1 -1
|
@@ -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) {
|
|
33
|
-
|
|
34
|
-
|
|
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: '
|
|
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',
|
|
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>
|