cynx-ui 1.2.38 → 1.2.40
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/Badge.jsx +33 -2
- package/components/DatePicker.jsx +99 -24
- package/package.json +1 -1
package/components/Badge.jsx
CHANGED
|
@@ -41,8 +41,9 @@ function ensureStyles() {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
const SIZE_MAP = {
|
|
44
|
+
xs: { padding: '1.5px 6px', fontSize: '9px' },
|
|
44
45
|
sm: { padding: '2px 7px', fontSize: '10px' },
|
|
45
|
-
md: { padding: '3px 9px', fontSize: '
|
|
46
|
+
md: { padding: '3px 9px', fontSize: '11px' },
|
|
46
47
|
lg: { padding: '4px 12px', fontSize: '12px' },
|
|
47
48
|
};
|
|
48
49
|
|
|
@@ -53,9 +54,31 @@ const SHAPE_STYLE = {
|
|
|
53
54
|
semicircle:(c) => ({ width: 7, height: 7, borderRadius: '50%', border: 'none', borderTop: '2px solid var(--bg,#fff)', background: c }),
|
|
54
55
|
};
|
|
55
56
|
|
|
57
|
+
const COLOR_MAP = {
|
|
58
|
+
green: '#10b981', passed: '#10b981', ok: '#10b981',
|
|
59
|
+
yellow: '#f97316', violation: '#f97316', warn: '#f97316', amber: '#f97316', orange: '#f97316',
|
|
60
|
+
red: '#ef4444', failed: '#ef4444', err: '#ef4444', danger: '#ef4444',
|
|
61
|
+
blue: '#3b82f6', conditional: '#3b82f6',
|
|
62
|
+
purple: '#8b5cf6',
|
|
63
|
+
indigo: '#6366f1',
|
|
64
|
+
cyan: '#06b6d4',
|
|
65
|
+
teal: '#14b8a6',
|
|
66
|
+
pink: '#ec4899',
|
|
67
|
+
grey: '#6b7280', gray: '#6b7280', dim: '#6b7280',
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
function resolveBadgeColor(cStr) {
|
|
71
|
+
if (!cStr) return null;
|
|
72
|
+
const c = cStr.toLowerCase();
|
|
73
|
+
if (COLOR_MAP[c]) return COLOR_MAP[c];
|
|
74
|
+
if (c.startsWith('#') || c.startsWith('rgb') || c.startsWith('hsl') || c.startsWith('var(')) return cStr;
|
|
75
|
+
return '#3b82f6';
|
|
76
|
+
}
|
|
77
|
+
|
|
56
78
|
export default function Badge({
|
|
57
79
|
children,
|
|
58
80
|
variant = 'dim',
|
|
81
|
+
color = null,
|
|
59
82
|
size = 'md',
|
|
60
83
|
dot = false,
|
|
61
84
|
shape = null,
|
|
@@ -69,7 +92,14 @@ export default function Badge({
|
|
|
69
92
|
const v = VARIANTS[variant] || VARIANTS.dim;
|
|
70
93
|
const sz = SIZE_MAP[size] || SIZE_MAP.md;
|
|
71
94
|
const shapeType = (shape === 'none' || shape === false) ? null : (shape || SHAPES[variant] || null);
|
|
72
|
-
|
|
95
|
+
|
|
96
|
+
const customHex = resolveBadgeColor(color);
|
|
97
|
+
const colorStyle = customHex ? {
|
|
98
|
+
color: customHex,
|
|
99
|
+
background: `${customHex}18`,
|
|
100
|
+
border: `1px solid ${customHex}33`,
|
|
101
|
+
} : {};
|
|
102
|
+
const dotColor = customHex || v.dot || v.color;
|
|
73
103
|
|
|
74
104
|
return (
|
|
75
105
|
<span
|
|
@@ -88,6 +118,7 @@ export default function Badge({
|
|
|
88
118
|
whiteSpace: 'nowrap',
|
|
89
119
|
transition: 'all .15s',
|
|
90
120
|
cursor: onClick ? 'pointer' : undefined,
|
|
121
|
+
...colorStyle,
|
|
91
122
|
...style,
|
|
92
123
|
}}
|
|
93
124
|
onClick={onClick}
|
|
@@ -42,7 +42,7 @@ function isSameDay(a, b) {
|
|
|
42
42
|
}
|
|
43
43
|
function isToday(y, m, d) { const t = new Date(); return t.getFullYear()===y && t.getMonth()===m && t.getDate()===d; }
|
|
44
44
|
|
|
45
|
-
export function DatePicker({ value, onChange, placeholder = 'Select date', badges = {}, minDate, maxDate, disabled, style }) {
|
|
45
|
+
export function DatePicker({ value, onChange, placeholder = 'Select date', badges = {}, minDate, maxDate, disabled, showStepper = true, stepperPosition = 'around', style }) {
|
|
46
46
|
const [open, setOpen] = useState(false);
|
|
47
47
|
const [view, setView] = useState('day'); // 'day' | 'month' | 'year'
|
|
48
48
|
const [cursor, setCursor] = useState(() => toDate(value) || new Date());
|
|
@@ -51,6 +51,24 @@ export function DatePicker({ value, onChange, placeholder = 'Select date', badge
|
|
|
51
51
|
const btnRef = useRef(null);
|
|
52
52
|
const dropRef = useRef(null);
|
|
53
53
|
|
|
54
|
+
const changeDay = (delta) => {
|
|
55
|
+
if (disabled) return;
|
|
56
|
+
const current = toDate(value) || new Date();
|
|
57
|
+
const next = new Date(current.getFullYear(), current.getMonth(), current.getDate() + delta);
|
|
58
|
+
|
|
59
|
+
if (minDate && next < new Date(new Date(minDate).setHours(0, 0, 0, 0))) return;
|
|
60
|
+
if (maxDate && next > new Date(new Date(maxDate).setHours(23, 59, 59, 999))) return;
|
|
61
|
+
|
|
62
|
+
if (typeof value === 'string') {
|
|
63
|
+
const y = next.getFullYear();
|
|
64
|
+
const m = pad(next.getMonth() + 1);
|
|
65
|
+
const d = pad(next.getDate());
|
|
66
|
+
onChange?.(`${y}-${m}-${d}`);
|
|
67
|
+
} else {
|
|
68
|
+
onChange?.(next);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
54
72
|
// Close on outside click
|
|
55
73
|
useEffect(() => {
|
|
56
74
|
if (!open) return;
|
|
@@ -272,32 +290,89 @@ export function DatePicker({ value, onChange, placeholder = 'Select date', badge
|
|
|
272
290
|
? `${cursor.getFullYear()}`
|
|
273
291
|
: `${Math.floor(cursor.getFullYear()/12)*12} – ${Math.floor(cursor.getFullYear()/12)*12+11}`;
|
|
274
292
|
|
|
275
|
-
|
|
276
|
-
<
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
293
|
+
const renderPrevBtn = () => (
|
|
294
|
+
<button
|
|
295
|
+
type="button"
|
|
296
|
+
disabled={disabled}
|
|
297
|
+
onClick={(e) => { e.stopPropagation(); changeDay(-1); }}
|
|
298
|
+
style={{
|
|
299
|
+
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
300
|
+
width: 32, height: 32, borderRadius: 'var(--rads)',
|
|
301
|
+
border: '1px solid var(--border)', background: 'var(--surface)',
|
|
302
|
+
cursor: disabled ? 'not-allowed' : 'pointer', opacity: disabled ? 0.5 : 1,
|
|
303
|
+
color: 'var(--muted)', transition: 'all .15s', boxShadow: 'var(--shadow)',
|
|
304
|
+
flexShrink: 0
|
|
305
|
+
}}
|
|
306
|
+
title="Previous day (-1)"
|
|
307
|
+
onMouseEnter={e => { if (!disabled) { e.currentTarget.style.borderColor = 'var(--accent)'; e.currentTarget.style.color = 'var(--accent)'; } }}
|
|
308
|
+
onMouseLeave={e => { e.currentTarget.style.borderColor = 'var(--border)'; e.currentTarget.style.color = 'var(--muted)'; }}
|
|
309
|
+
>
|
|
310
|
+
<ChevronLeft size={15} />
|
|
311
|
+
</button>
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
const renderNextBtn = () => (
|
|
315
|
+
<button
|
|
316
|
+
type="button"
|
|
317
|
+
disabled={disabled}
|
|
318
|
+
onClick={(e) => { e.stopPropagation(); changeDay(1); }}
|
|
319
|
+
style={{
|
|
320
|
+
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
321
|
+
width: 32, height: 32, borderRadius: 'var(--rads)',
|
|
281
322
|
border: '1px solid var(--border)', background: 'var(--surface)',
|
|
282
323
|
cursor: disabled ? 'not-allowed' : 'pointer', opacity: disabled ? 0.5 : 1,
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
whiteSpace: 'nowrap', minWidth: 140, height: 32, boxShadow: 'var(--shadow)',
|
|
324
|
+
color: 'var(--muted)', transition: 'all .15s', boxShadow: 'var(--shadow)',
|
|
325
|
+
flexShrink: 0
|
|
286
326
|
}}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
327
|
+
title="Next day (+1)"
|
|
328
|
+
onMouseEnter={e => { if (!disabled) { e.currentTarget.style.borderColor = 'var(--accent)'; e.currentTarget.style.color = 'var(--accent)'; } }}
|
|
329
|
+
onMouseLeave={e => { e.currentTarget.style.borderColor = 'var(--border)'; e.currentTarget.style.color = 'var(--muted)'; }}
|
|
330
|
+
>
|
|
331
|
+
<ChevronRight size={15} />
|
|
332
|
+
</button>
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
const renderTriggerBtn = () => (
|
|
336
|
+
<button ref={btnRef} type="button" onClick={toggleOpen} style={{
|
|
337
|
+
display: 'flex', alignItems: 'center', gap: 7,
|
|
338
|
+
padding: '5px 10px', borderRadius: 'var(--rads)',
|
|
339
|
+
border: '1px solid var(--border)', background: 'var(--surface)',
|
|
340
|
+
cursor: disabled ? 'not-allowed' : 'pointer', opacity: disabled ? 0.5 : 1,
|
|
341
|
+
fontSize: '.8rem', color: formatted ? 'var(--primary-text)' : 'var(--muted)',
|
|
342
|
+
fontFamily: 'var(--sans)', transition: 'border-color .15s',
|
|
343
|
+
whiteSpace: 'nowrap', minWidth: 140, height: 32, boxShadow: 'var(--shadow)',
|
|
344
|
+
}}
|
|
345
|
+
onMouseEnter={e => { if (!disabled) e.currentTarget.style.borderColor = 'var(--accent)'; }}
|
|
346
|
+
onMouseLeave={e => { e.currentTarget.style.borderColor = 'var(--border)'; }}>
|
|
347
|
+
<Calendar size={13} style={{ color: 'var(--muted)', flexShrink: 0 }} />
|
|
348
|
+
<span style={{ flex: 1, textAlign: 'left' }}>{formatted || placeholder}</span>
|
|
349
|
+
{value && (
|
|
350
|
+
<span
|
|
351
|
+
onClick={e => { e.stopPropagation(); onChange?.(null); }}
|
|
352
|
+
style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--muted)', cursor: 'pointer', flexShrink: 0, fontSize: 13, lineHeight: 1, padding: '0 2px' }}
|
|
353
|
+
onMouseEnter={e => { e.currentTarget.style.color = 'var(--danger)'; }}
|
|
354
|
+
onMouseLeave={e => { e.currentTarget.style.color = 'var(--muted)'; }}>
|
|
355
|
+
✕
|
|
356
|
+
</span>
|
|
357
|
+
)}
|
|
358
|
+
</button>
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
const posMode = stepperPosition === 'left' || stepperPosition === 'start' ? 'left'
|
|
362
|
+
: stepperPosition === 'right' || stepperPosition === 'end' ? 'right'
|
|
363
|
+
: 'around';
|
|
364
|
+
|
|
365
|
+
return (
|
|
366
|
+
<div ref={ref} style={{ position: 'relative', display: 'inline-flex', alignItems: 'center', gap: 4, ...style }}>
|
|
367
|
+
{showStepper && posMode === 'around' && renderPrevBtn()}
|
|
368
|
+
{showStepper && posMode === 'left' && renderPrevBtn()}
|
|
369
|
+
{showStepper && posMode === 'left' && renderNextBtn()}
|
|
370
|
+
|
|
371
|
+
{renderTriggerBtn()}
|
|
372
|
+
|
|
373
|
+
{showStepper && posMode === 'around' && renderNextBtn()}
|
|
374
|
+
{showStepper && posMode === 'right' && renderPrevBtn()}
|
|
375
|
+
{showStepper && posMode === 'right' && renderNextBtn()}
|
|
301
376
|
|
|
302
377
|
{/* Dropdown via portal */}
|
|
303
378
|
{open && createPortal(
|