cynx-ui 1.3.5 → 1.3.6
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/FilePicker.jsx +2 -2
- package/components/LineProgress.jsx +133 -133
- package/components/LinearProgress.jsx +132 -0
- package/index.js +2 -2
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState, useRef } from 'react';
|
|
2
2
|
import { UploadCloud, Paperclip, FileCheck, X, Eye, Trash2 } from 'lucide-react';
|
|
3
|
-
import
|
|
3
|
+
import LinearProgress from './LinearProgress.jsx';
|
|
4
4
|
import Badge from './Badge.jsx';
|
|
5
5
|
import Button from './Button.jsx';
|
|
6
6
|
|
|
@@ -138,7 +138,7 @@ export function FilePicker({
|
|
|
138
138
|
</div>
|
|
139
139
|
|
|
140
140
|
{item.status === 'uploading' && (
|
|
141
|
-
<
|
|
141
|
+
<LinearProgress
|
|
142
142
|
value={item.progress}
|
|
143
143
|
color="var(--accent, #3b82f6)"
|
|
144
144
|
height={4}
|
|
@@ -1,133 +1,133 @@
|
|
|
1
|
-
import { useEffect, useRef } from 'react';
|
|
2
|
-
|
|
3
|
-
const SIZES = {
|
|
4
|
-
xs: { height: 4, fontSize: '.68rem' },
|
|
5
|
-
sm: { height: 5, fontSize: '.75rem' },
|
|
6
|
-
md: { height: 6, fontSize: '.82rem' },
|
|
7
|
-
lg: { height: 8, fontSize: '.9rem' },
|
|
8
|
-
xl: { height: 10, fontSize: '1rem' },
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export default function LineProgress({
|
|
12
|
-
value = 0,
|
|
13
|
-
segments,
|
|
14
|
-
size = 'md',
|
|
15
|
-
customSize,
|
|
16
|
-
color = 'var(--blue)',
|
|
17
|
-
trackColor = 'var(--bd2, #e0e0e4)',
|
|
18
|
-
label = '',
|
|
19
|
-
showPercent = true,
|
|
20
|
-
animated = true,
|
|
21
|
-
rounded = true,
|
|
22
|
-
indeterminate = false,
|
|
23
|
-
speed = 1,
|
|
24
|
-
height,
|
|
25
|
-
gap = 5,
|
|
26
|
-
labelGap = 2,
|
|
27
|
-
className = '',
|
|
28
|
-
style = {},
|
|
29
|
-
}) {
|
|
30
|
-
const base = SIZES[size] || SIZES.md;
|
|
31
|
-
const barHeight = height ?? (customSize || base.height);
|
|
32
|
-
const fontSize = customSize ? `${Math.max(11, customSize * 1.6)}px` : base.fontSize;
|
|
33
|
-
const pct = Math.min(100, Math.max(0, value));
|
|
34
|
-
const radius = rounded ? barHeight / 2 : 0;
|
|
35
|
-
|
|
36
|
-
const barRef = useRef(null);
|
|
37
|
-
|
|
38
|
-
// Indeterminate animation
|
|
39
|
-
useEffect(() => {
|
|
40
|
-
if (!indeterminate || !barRef.current) return;
|
|
41
|
-
let pos = -40, direction = 1, raf;
|
|
42
|
-
let lastTime = performance.now();
|
|
43
|
-
|
|
44
|
-
function tick(now) {
|
|
45
|
-
const dt = (now - lastTime) / 16.67;
|
|
46
|
-
lastTime = now;
|
|
47
|
-
pos += direction * 1.2 * speed * dt;
|
|
48
|
-
if (pos > 100) { pos = 100; direction = -1; }
|
|
49
|
-
if (pos < -40) { pos = -40; direction = 1; }
|
|
50
|
-
if (barRef.current) {
|
|
51
|
-
barRef.current.style.left = pos + '%';
|
|
52
|
-
barRef.current.style.width = '40%';
|
|
53
|
-
}
|
|
54
|
-
raf = requestAnimationFrame(tick);
|
|
55
|
-
}
|
|
56
|
-
raf = requestAnimationFrame(tick);
|
|
57
|
-
return () => cancelAnimationFrame(raf);
|
|
58
|
-
}, [indeterminate, speed]);
|
|
59
|
-
|
|
60
|
-
const transitionStyle = animated ? 'flex .5s cubic-bezier(0.4,0,0.2,1)' : 'none';
|
|
61
|
-
const hasSegments = Array.isArray(segments) && segments.length > 0;
|
|
62
|
-
const rawSum = hasSegments ? segments.reduce((sum, s) => sum + (Number(s.value) || 0), 0) : 0;
|
|
63
|
-
|
|
64
|
-
return (
|
|
65
|
-
<div className={className} style={{ display: 'inline-flex', flexDirection: 'column', gap: labelGap, width: '100%', ...style }}>
|
|
66
|
-
{(label || showPercent) && (
|
|
67
|
-
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
|
|
68
|
-
{label && <span style={{ fontSize, fontWeight: 600, color: 'var(--primary-text, #1a1a2e)' }}>{label}</span>}
|
|
69
|
-
{showPercent && !indeterminate && !hasSegments && (
|
|
70
|
-
<span style={{ fontSize, fontWeight: 600, color: 'var(--muted, #9ca3af)', fontVariantNumeric: 'tabular-nums', marginLeft: label ? 0 : 'auto' }}>
|
|
71
|
-
{Math.round(pct)}%
|
|
72
|
-
</span>
|
|
73
|
-
)}
|
|
74
|
-
</div>
|
|
75
|
-
)}
|
|
76
|
-
<div style={{
|
|
77
|
-
height: barHeight, borderRadius: radius,
|
|
78
|
-
background: 'transparent', position: 'relative',
|
|
79
|
-
display: 'flex', gap: gap, overflow: 'hidden',
|
|
80
|
-
}}>
|
|
81
|
-
{indeterminate ? (
|
|
82
|
-
<>
|
|
83
|
-
<div style={{ flex: 1, borderRadius: radius, background: trackColor }} />
|
|
84
|
-
<div
|
|
85
|
-
ref={barRef}
|
|
86
|
-
style={{
|
|
87
|
-
position: 'absolute', top: 0, height: '100%',
|
|
88
|
-
width: '40%', borderRadius: radius,
|
|
89
|
-
background: color, opacity: 0.85,
|
|
90
|
-
}}
|
|
91
|
-
/>
|
|
92
|
-
</>
|
|
93
|
-
) : hasSegments && rawSum > 0 ? (
|
|
94
|
-
<>
|
|
95
|
-
{segments.map((seg, idx) => {
|
|
96
|
-
const segVal = Number(seg.value) || 0;
|
|
97
|
-
if (segVal <= 0) return null;
|
|
98
|
-
const ratioPct = (segVal / rawSum) * 100;
|
|
99
|
-
return (
|
|
100
|
-
<div
|
|
101
|
-
key={idx}
|
|
102
|
-
title={seg.label ? `${seg.label}: ${segVal}` : undefined}
|
|
103
|
-
style={{
|
|
104
|
-
flex: ratioPct,
|
|
105
|
-
height: '100%',
|
|
106
|
-
borderRadius: radius,
|
|
107
|
-
background: seg.color || color,
|
|
108
|
-
transition: transitionStyle,
|
|
109
|
-
}}
|
|
110
|
-
/>
|
|
111
|
-
);
|
|
112
|
-
})}
|
|
113
|
-
</>
|
|
114
|
-
) : (
|
|
115
|
-
<>
|
|
116
|
-
<div ref={barRef} style={{
|
|
117
|
-
flex: pct > 0 ? pct : 0, minWidth: pct > 0 ? barHeight : 0,
|
|
118
|
-
borderRadius: radius, background: color,
|
|
119
|
-
transition: transitionStyle,
|
|
120
|
-
}} />
|
|
121
|
-
{pct < 100 && (
|
|
122
|
-
<div style={{
|
|
123
|
-
flex: 100 - pct,
|
|
124
|
-
borderRadius: radius, background: trackColor,
|
|
125
|
-
transition: transitionStyle,
|
|
126
|
-
}} />
|
|
127
|
-
)}
|
|
128
|
-
</>
|
|
129
|
-
)}
|
|
130
|
-
</div>
|
|
131
|
-
</div>
|
|
132
|
-
);
|
|
133
|
-
}
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
const SIZES = {
|
|
4
|
+
xs: { height: 4, fontSize: '.68rem' },
|
|
5
|
+
sm: { height: 5, fontSize: '.75rem' },
|
|
6
|
+
md: { height: 6, fontSize: '.82rem' },
|
|
7
|
+
lg: { height: 8, fontSize: '.9rem' },
|
|
8
|
+
xl: { height: 10, fontSize: '1rem' },
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default function LineProgress({
|
|
12
|
+
value = 0,
|
|
13
|
+
segments,
|
|
14
|
+
size = 'md',
|
|
15
|
+
customSize,
|
|
16
|
+
color = 'var(--blue)',
|
|
17
|
+
trackColor = 'var(--bd2, #e0e0e4)',
|
|
18
|
+
label = '',
|
|
19
|
+
showPercent = true,
|
|
20
|
+
animated = true,
|
|
21
|
+
rounded = true,
|
|
22
|
+
indeterminate = false,
|
|
23
|
+
speed = 1,
|
|
24
|
+
height,
|
|
25
|
+
gap = 5,
|
|
26
|
+
labelGap = 2,
|
|
27
|
+
className = '',
|
|
28
|
+
style = {},
|
|
29
|
+
}) {
|
|
30
|
+
const base = SIZES[size] || SIZES.md;
|
|
31
|
+
const barHeight = height ?? (customSize || base.height);
|
|
32
|
+
const fontSize = customSize ? `${Math.max(11, customSize * 1.6)}px` : base.fontSize;
|
|
33
|
+
const pct = Math.min(100, Math.max(0, value));
|
|
34
|
+
const radius = rounded ? barHeight / 2 : 0;
|
|
35
|
+
|
|
36
|
+
const barRef = useRef(null);
|
|
37
|
+
|
|
38
|
+
// Indeterminate animation
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (!indeterminate || !barRef.current) return;
|
|
41
|
+
let pos = -40, direction = 1, raf;
|
|
42
|
+
let lastTime = performance.now();
|
|
43
|
+
|
|
44
|
+
function tick(now) {
|
|
45
|
+
const dt = (now - lastTime) / 16.67;
|
|
46
|
+
lastTime = now;
|
|
47
|
+
pos += direction * 1.2 * speed * dt;
|
|
48
|
+
if (pos > 100) { pos = 100; direction = -1; }
|
|
49
|
+
if (pos < -40) { pos = -40; direction = 1; }
|
|
50
|
+
if (barRef.current) {
|
|
51
|
+
barRef.current.style.left = pos + '%';
|
|
52
|
+
barRef.current.style.width = '40%';
|
|
53
|
+
}
|
|
54
|
+
raf = requestAnimationFrame(tick);
|
|
55
|
+
}
|
|
56
|
+
raf = requestAnimationFrame(tick);
|
|
57
|
+
return () => cancelAnimationFrame(raf);
|
|
58
|
+
}, [indeterminate, speed]);
|
|
59
|
+
|
|
60
|
+
const transitionStyle = animated ? 'flex .5s cubic-bezier(0.4,0,0.2,1)' : 'none';
|
|
61
|
+
const hasSegments = Array.isArray(segments) && segments.length > 0;
|
|
62
|
+
const rawSum = hasSegments ? segments.reduce((sum, s) => sum + (Number(s.value) || 0), 0) : 0;
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<div className={className} style={{ display: 'inline-flex', flexDirection: 'column', gap: labelGap, width: '100%', ...style }}>
|
|
66
|
+
{(label || showPercent) && (
|
|
67
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
|
|
68
|
+
{label && <span style={{ fontSize, fontWeight: 600, color: 'var(--primary-text, #1a1a2e)' }}>{label}</span>}
|
|
69
|
+
{showPercent && !indeterminate && !hasSegments && (
|
|
70
|
+
<span style={{ fontSize, fontWeight: 600, color: 'var(--muted, #9ca3af)', fontVariantNumeric: 'tabular-nums', marginLeft: label ? 0 : 'auto' }}>
|
|
71
|
+
{Math.round(pct)}%
|
|
72
|
+
</span>
|
|
73
|
+
)}
|
|
74
|
+
</div>
|
|
75
|
+
)}
|
|
76
|
+
<div style={{
|
|
77
|
+
height: barHeight, borderRadius: radius,
|
|
78
|
+
background: 'transparent', position: 'relative',
|
|
79
|
+
display: 'flex', gap: gap, overflow: 'hidden',
|
|
80
|
+
}}>
|
|
81
|
+
{indeterminate ? (
|
|
82
|
+
<>
|
|
83
|
+
<div style={{ flex: 1, borderRadius: radius, background: trackColor }} />
|
|
84
|
+
<div
|
|
85
|
+
ref={barRef}
|
|
86
|
+
style={{
|
|
87
|
+
position: 'absolute', top: 0, height: '100%',
|
|
88
|
+
width: '40%', borderRadius: radius,
|
|
89
|
+
background: color, opacity: 0.85,
|
|
90
|
+
}}
|
|
91
|
+
/>
|
|
92
|
+
</>
|
|
93
|
+
) : hasSegments && rawSum > 0 ? (
|
|
94
|
+
<>
|
|
95
|
+
{segments.map((seg, idx) => {
|
|
96
|
+
const segVal = Number(seg.value) || 0;
|
|
97
|
+
if (segVal <= 0) return null;
|
|
98
|
+
const ratioPct = (segVal / rawSum) * 100;
|
|
99
|
+
return (
|
|
100
|
+
<div
|
|
101
|
+
key={idx}
|
|
102
|
+
title={seg.label ? `${seg.label}: ${segVal}` : undefined}
|
|
103
|
+
style={{
|
|
104
|
+
flex: ratioPct,
|
|
105
|
+
height: '100%',
|
|
106
|
+
borderRadius: radius,
|
|
107
|
+
background: seg.color || color,
|
|
108
|
+
transition: transitionStyle,
|
|
109
|
+
}}
|
|
110
|
+
/>
|
|
111
|
+
);
|
|
112
|
+
})}
|
|
113
|
+
</>
|
|
114
|
+
) : (
|
|
115
|
+
<>
|
|
116
|
+
<div ref={barRef} style={{
|
|
117
|
+
flex: pct > 0 ? pct : 0, minWidth: pct > 0 ? barHeight : 0,
|
|
118
|
+
borderRadius: radius, background: color,
|
|
119
|
+
transition: transitionStyle,
|
|
120
|
+
}} />
|
|
121
|
+
{pct < 100 && (
|
|
122
|
+
<div style={{
|
|
123
|
+
flex: 100 - pct,
|
|
124
|
+
borderRadius: radius, background: trackColor,
|
|
125
|
+
transition: transitionStyle,
|
|
126
|
+
}} />
|
|
127
|
+
)}
|
|
128
|
+
</>
|
|
129
|
+
)}
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
const SIZES = {
|
|
4
|
+
xs: { height: 4, fontSize: '.68rem' },
|
|
5
|
+
sm: { height: 5, fontSize: '.75rem' },
|
|
6
|
+
md: { height: 6, fontSize: '.82rem' },
|
|
7
|
+
lg: { height: 8, fontSize: '.9rem' },
|
|
8
|
+
xl: { height: 10, fontSize: '1rem' },
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default function LinearProgress({
|
|
12
|
+
value = 0,
|
|
13
|
+
segments,
|
|
14
|
+
size = 'md',
|
|
15
|
+
customSize,
|
|
16
|
+
color = 'var(--blue, #3b82f6)',
|
|
17
|
+
trackColor = 'var(--bd2, #e0e0e4)',
|
|
18
|
+
label = '',
|
|
19
|
+
showPercent = true,
|
|
20
|
+
animated = true,
|
|
21
|
+
rounded = true,
|
|
22
|
+
indeterminate = false,
|
|
23
|
+
speed = 1,
|
|
24
|
+
height,
|
|
25
|
+
gap = 5,
|
|
26
|
+
labelGap = 2,
|
|
27
|
+
className = '',
|
|
28
|
+
style = {},
|
|
29
|
+
}) {
|
|
30
|
+
const base = SIZES[size] || SIZES.md;
|
|
31
|
+
const barHeight = height ?? (customSize || base.height);
|
|
32
|
+
const fontSize = customSize ? `${Math.max(11, customSize * 1.6)}px` : base.fontSize;
|
|
33
|
+
const pct = Math.min(100, Math.max(0, value));
|
|
34
|
+
const radius = rounded ? barHeight / 2 : 0;
|
|
35
|
+
|
|
36
|
+
const barRef = useRef(null);
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (!indeterminate || !barRef.current) return;
|
|
40
|
+
let pos = -40, direction = 1, raf;
|
|
41
|
+
let lastTime = performance.now();
|
|
42
|
+
|
|
43
|
+
function tick(now) {
|
|
44
|
+
const dt = (now - lastTime) / 16.67;
|
|
45
|
+
lastTime = now;
|
|
46
|
+
pos += direction * 1.2 * speed * dt;
|
|
47
|
+
if (pos > 100) { pos = 100; direction = -1; }
|
|
48
|
+
if (pos < -40) { pos = -40; direction = 1; }
|
|
49
|
+
if (barRef.current) {
|
|
50
|
+
barRef.current.style.left = pos + '%';
|
|
51
|
+
barRef.current.style.width = '40%';
|
|
52
|
+
}
|
|
53
|
+
raf = requestAnimationFrame(tick);
|
|
54
|
+
}
|
|
55
|
+
raf = requestAnimationFrame(tick);
|
|
56
|
+
return () => cancelAnimationFrame(raf);
|
|
57
|
+
}, [indeterminate, speed]);
|
|
58
|
+
|
|
59
|
+
const transitionStyle = animated ? 'flex .5s cubic-bezier(0.4,0,0.2,1)' : 'none';
|
|
60
|
+
const hasSegments = Array.isArray(segments) && segments.length > 0;
|
|
61
|
+
const rawSum = hasSegments ? segments.reduce((sum, s) => sum + (Number(s.value) || 0), 0) : 0;
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<div className={className} style={{ display: 'inline-flex', flexDirection: 'column', gap: labelGap, width: '100%', ...style }}>
|
|
65
|
+
{(label || showPercent) && (
|
|
66
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
|
|
67
|
+
{label && <span style={{ fontSize, fontWeight: 600, color: 'var(--primary-text, #1a1a2e)' }}>{label}</span>}
|
|
68
|
+
{showPercent && !indeterminate && !hasSegments && (
|
|
69
|
+
<span style={{ fontSize, fontWeight: 600, color: 'var(--muted, #9ca3af)', fontVariantNumeric: 'tabular-nums', marginLeft: label ? 0 : 'auto' }}>
|
|
70
|
+
{Math.round(pct)}%
|
|
71
|
+
</span>
|
|
72
|
+
)}
|
|
73
|
+
</div>
|
|
74
|
+
)}
|
|
75
|
+
<div style={{
|
|
76
|
+
height: barHeight, borderRadius: radius,
|
|
77
|
+
background: 'transparent', position: 'relative',
|
|
78
|
+
display: 'flex', gap: gap, overflow: 'hidden',
|
|
79
|
+
}}>
|
|
80
|
+
{indeterminate ? (
|
|
81
|
+
<>
|
|
82
|
+
<div style={{ flex: 1, borderRadius: radius, background: trackColor }} />
|
|
83
|
+
<div
|
|
84
|
+
ref={barRef}
|
|
85
|
+
style={{
|
|
86
|
+
position: 'absolute', top: 0, height: '100%',
|
|
87
|
+
width: '40%', borderRadius: radius,
|
|
88
|
+
background: color, opacity: 0.85,
|
|
89
|
+
}}
|
|
90
|
+
/>
|
|
91
|
+
</>
|
|
92
|
+
) : hasSegments && rawSum > 0 ? (
|
|
93
|
+
<>
|
|
94
|
+
{segments.map((seg, idx) => {
|
|
95
|
+
const segVal = Number(seg.value) || 0;
|
|
96
|
+
if (segVal <= 0) return null;
|
|
97
|
+
const ratioPct = (segVal / rawSum) * 100;
|
|
98
|
+
return (
|
|
99
|
+
<div
|
|
100
|
+
key={idx}
|
|
101
|
+
title={seg.label ? `${seg.label}: ${segVal}` : undefined}
|
|
102
|
+
style={{
|
|
103
|
+
flex: ratioPct,
|
|
104
|
+
height: '100%',
|
|
105
|
+
borderRadius: radius,
|
|
106
|
+
background: seg.color || color,
|
|
107
|
+
transition: transitionStyle,
|
|
108
|
+
}}
|
|
109
|
+
/>
|
|
110
|
+
);
|
|
111
|
+
})}
|
|
112
|
+
</>
|
|
113
|
+
) : (
|
|
114
|
+
<>
|
|
115
|
+
<div ref={barRef} style={{
|
|
116
|
+
flex: pct > 0 ? pct : 0, minWidth: pct > 0 ? barHeight : 0,
|
|
117
|
+
borderRadius: radius, background: color,
|
|
118
|
+
transition: transitionStyle,
|
|
119
|
+
}} />
|
|
120
|
+
{pct < 100 && (
|
|
121
|
+
<div style={{
|
|
122
|
+
flex: 100 - pct,
|
|
123
|
+
borderRadius: radius, background: trackColor,
|
|
124
|
+
transition: transitionStyle,
|
|
125
|
+
}} />
|
|
126
|
+
)}
|
|
127
|
+
</>
|
|
128
|
+
)}
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
);
|
|
132
|
+
}
|
package/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import { default as _ChipSelect } from './components/ChipSelect.jsx';
|
|
|
11
11
|
import { default as _Checkbox } from './components/Checkbox.jsx';
|
|
12
12
|
import { default as _AuthOverlay } from './components/AuthOverlay.jsx';
|
|
13
13
|
import { default as _UptimeBar } from './components/UptimeBar.jsx';
|
|
14
|
-
import { default as
|
|
14
|
+
import { default as _LinearProgress } from './components/LinearProgress.jsx';
|
|
15
15
|
import { default as _StatBarCard } from './components/StatBarCard.jsx';
|
|
16
16
|
import { default as _StatsCard } from './components/StatsCard.jsx';
|
|
17
17
|
import { default as _ActivityCard } from './components/ActivityCard.jsx';
|
|
@@ -52,7 +52,7 @@ export const ChipSelect = _ChipSelect;
|
|
|
52
52
|
export const Checkbox = _Checkbox;
|
|
53
53
|
export const AuthOverlay = _AuthOverlay;
|
|
54
54
|
export const UptimeBar = _UptimeBar;
|
|
55
|
-
export const
|
|
55
|
+
export const LinearProgress = _LinearProgress;
|
|
56
56
|
export const StatBarCard = _StatBarCard;
|
|
57
57
|
export const StatsCard = _StatsCard;
|
|
58
58
|
export const ActivityCard = _ActivityCard;
|