cynx-ui 1.2.37 → 1.2.39
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/CircularProgress.jsx +64 -11
- package/components/LineProgress.jsx +25 -1
- 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}
|
|
@@ -10,14 +10,16 @@ const SIZES = {
|
|
|
10
10
|
|
|
11
11
|
export default function CircularProgress({
|
|
12
12
|
value = 0,
|
|
13
|
+
segments,
|
|
13
14
|
size = 'md',
|
|
14
15
|
customSize,
|
|
15
16
|
color = 'var(--blue)',
|
|
16
17
|
trackColor = 'var(--bd2, #e0e0e4)',
|
|
17
18
|
label = '',
|
|
19
|
+
centerText,
|
|
18
20
|
showPercent = true,
|
|
19
21
|
animated = true,
|
|
20
|
-
rounded =
|
|
22
|
+
rounded = true,
|
|
21
23
|
indeterminate = false,
|
|
22
24
|
speed = 1,
|
|
23
25
|
strokeWidth,
|
|
@@ -35,38 +37,36 @@ export default function CircularProgress({
|
|
|
35
37
|
const c = 2 * Math.PI * r;
|
|
36
38
|
const pct = Math.min(100, Math.max(0, value));
|
|
37
39
|
const BIG = c * 4;
|
|
38
|
-
const
|
|
40
|
+
const isRounded = rounded !== false;
|
|
41
|
+
const cap = isRounded ? 'round' : 'butt';
|
|
39
42
|
|
|
40
|
-
const userGap = gap !== undefined ? gap : (
|
|
41
|
-
const capOverrun =
|
|
43
|
+
const userGap = gap !== undefined ? gap : (isRounded ? c * 0.05 : c * 0.03);
|
|
44
|
+
const capOverrun = isRounded ? stroke : 0;
|
|
42
45
|
const gapSize = userGap + capOverrun;
|
|
43
46
|
const total = c - 2 * gapSize;
|
|
44
47
|
|
|
45
48
|
const isFull = pct >= 100;
|
|
46
49
|
const isEmpty = pct <= 0;
|
|
47
50
|
|
|
51
|
+
const hasSegments = Array.isArray(segments) && segments.length > 0;
|
|
52
|
+
const rawSum = hasSegments ? segments.reduce((sum, s) => sum + (Number(s.value) || 0), 0) : 0;
|
|
53
|
+
|
|
48
54
|
let indDash, indOffset, trackDash, trackOffset, indOpacity;
|
|
49
|
-
let indStartDeg, indEndDeg, trackStartDeg, trackEndDeg;
|
|
50
55
|
|
|
51
56
|
if (isEmpty) {
|
|
52
57
|
indDash = `0,${BIG}`; indOffset = 0;
|
|
53
58
|
trackDash = `${c},${BIG}`; trackOffset = 0;
|
|
54
59
|
indOpacity = 0;
|
|
55
|
-
indStartDeg = indEndDeg = trackStartDeg = trackEndDeg = 0;
|
|
56
60
|
} else if (isFull) {
|
|
57
61
|
indDash = `${c + stroke * 2},${BIG}`; indOffset = 0;
|
|
58
62
|
trackDash = `0,${BIG}`; trackOffset = 0;
|
|
59
63
|
indOpacity = 1;
|
|
60
|
-
indStartDeg = 0; indEndDeg = 360;
|
|
61
|
-
trackStartDeg = trackEndDeg = 0;
|
|
62
64
|
} else {
|
|
63
65
|
const activeLen = (pct / 100) * total;
|
|
64
66
|
const inactiveLen = total - activeLen;
|
|
65
67
|
indDash = `${activeLen},${BIG}`; indOffset = 0;
|
|
66
68
|
trackDash = `${inactiveLen},${BIG}`; trackOffset = -(activeLen + gapSize);
|
|
67
69
|
indOpacity = 1;
|
|
68
|
-
indStartDeg = 0; indEndDeg = (pct / 100) * 360;
|
|
69
|
-
trackStartDeg = (pct / 100) * 360; trackEndDeg = 360;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
const gRef = useRef(null);
|
|
@@ -118,6 +118,59 @@ export default function CircularProgress({
|
|
|
118
118
|
fill="none" stroke={color} strokeWidth={stroke} strokeLinecap={cap}
|
|
119
119
|
/>
|
|
120
120
|
</g>
|
|
121
|
+
) : hasSegments ? (
|
|
122
|
+
<>
|
|
123
|
+
{rawSum <= 0 ? (
|
|
124
|
+
<circle
|
|
125
|
+
cx={cx} cy={cy} r={r}
|
|
126
|
+
fill="none" stroke={trackColor} strokeWidth={stroke}
|
|
127
|
+
/>
|
|
128
|
+
) : (
|
|
129
|
+
(() => {
|
|
130
|
+
const activeSegs = segments.filter((seg) => (Number(seg.value) || 0) > 0);
|
|
131
|
+
const segCount = activeSegs.length;
|
|
132
|
+
if (segCount === 0) {
|
|
133
|
+
return (
|
|
134
|
+
<circle
|
|
135
|
+
cx={cx} cy={cy} r={r}
|
|
136
|
+
fill="none" stroke={trackColor} strokeWidth={stroke}
|
|
137
|
+
/>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const gapPx = gap !== undefined ? gap : 4;
|
|
142
|
+
const capOverrun = isRounded ? stroke : 0;
|
|
143
|
+
const gapArc = segCount > 1 ? (gapPx + capOverrun) : 0;
|
|
144
|
+
const availableC = Math.max(0, c - segCount * gapArc);
|
|
145
|
+
|
|
146
|
+
let accumulatedArc = isRounded ? stroke / 2 : 0;
|
|
147
|
+
|
|
148
|
+
return activeSegs.map((seg, idx) => {
|
|
149
|
+
const val = Number(seg.value) || 0;
|
|
150
|
+
const segLen = (val / rawSum) * availableC;
|
|
151
|
+
const currentOffset = accumulatedArc;
|
|
152
|
+
accumulatedArc += segLen + gapArc;
|
|
153
|
+
|
|
154
|
+
return (
|
|
155
|
+
<circle
|
|
156
|
+
key={idx}
|
|
157
|
+
cx={cx} cy={cy} r={r}
|
|
158
|
+
fill="none" stroke={seg.color || color} strokeWidth={stroke}
|
|
159
|
+
strokeLinecap={cap}
|
|
160
|
+
strokeDasharray={`${Math.max(0.1, segLen).toFixed(2)}, ${BIG}`}
|
|
161
|
+
strokeDashoffset={(-currentOffset).toFixed(2)}
|
|
162
|
+
style={{
|
|
163
|
+
transform: 'rotate(-90deg)', transformOrigin: '50% 50%',
|
|
164
|
+
transition: animated ? 'stroke-dasharray .5s cubic-bezier(0.4,0,0.2,1), stroke-dashoffset .5s cubic-bezier(0.4,0,0.2,1)' : 'none',
|
|
165
|
+
}}
|
|
166
|
+
>
|
|
167
|
+
{seg.label && <title>{`${seg.label}: ${val}`}</title>}
|
|
168
|
+
</circle>
|
|
169
|
+
);
|
|
170
|
+
});
|
|
171
|
+
})()
|
|
172
|
+
)}
|
|
173
|
+
</>
|
|
121
174
|
) : (
|
|
122
175
|
<>
|
|
123
176
|
<circle
|
|
@@ -151,7 +204,7 @@ export default function CircularProgress({
|
|
|
151
204
|
fontSize, fontWeight: 600, color: 'var(--primary-text)',
|
|
152
205
|
fontVariantNumeric: 'tabular-nums',
|
|
153
206
|
}}>
|
|
154
|
-
{Math.round(pct)}
|
|
207
|
+
{centerText !== undefined ? centerText : hasSegments ? rawSum : `${Math.round(pct)}%`}
|
|
155
208
|
</div>
|
|
156
209
|
)}
|
|
157
210
|
</div>
|
|
@@ -10,6 +10,7 @@ const SIZES = {
|
|
|
10
10
|
|
|
11
11
|
export default function LineProgress({
|
|
12
12
|
value = 0,
|
|
13
|
+
segments,
|
|
13
14
|
size = 'md',
|
|
14
15
|
customSize,
|
|
15
16
|
color = 'var(--blue)',
|
|
@@ -57,13 +58,15 @@ export default function LineProgress({
|
|
|
57
58
|
}, [indeterminate, speed]);
|
|
58
59
|
|
|
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;
|
|
60
63
|
|
|
61
64
|
return (
|
|
62
65
|
<div className={className} style={{ display: 'inline-flex', flexDirection: 'column', gap: labelGap, width: '100%', ...style }}>
|
|
63
66
|
{(label || showPercent) && (
|
|
64
67
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
|
|
65
68
|
{label && <span style={{ fontSize, fontWeight: 600, color: 'var(--primary-text, #1a1a2e)' }}>{label}</span>}
|
|
66
|
-
{showPercent && !indeterminate && (
|
|
69
|
+
{showPercent && !indeterminate && !hasSegments && (
|
|
67
70
|
<span style={{ fontSize, fontWeight: 600, color: 'var(--muted, #9ca3af)', fontVariantNumeric: 'tabular-nums', marginLeft: label ? 0 : 'auto' }}>
|
|
68
71
|
{Math.round(pct)}%
|
|
69
72
|
</span>
|
|
@@ -87,6 +90,27 @@ export default function LineProgress({
|
|
|
87
90
|
}}
|
|
88
91
|
/>
|
|
89
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
|
+
</>
|
|
90
114
|
) : (
|
|
91
115
|
<>
|
|
92
116
|
<div ref={barRef} style={{
|