cynx-lib 1.3.28

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.
Files changed (46) hide show
  1. package/index.js +3 -0
  2. package/package.json +36 -0
  3. package/ui/components/ActionsMenu.jsx +158 -0
  4. package/ui/components/ActivityCard.jsx +184 -0
  5. package/ui/components/AuthOverlay.jsx +94 -0
  6. package/ui/components/Badge.jsx +186 -0
  7. package/ui/components/BrandingCard.jsx +146 -0
  8. package/ui/components/Button.jsx +148 -0
  9. package/ui/components/Card.jsx +132 -0
  10. package/ui/components/Checkbox.jsx +59 -0
  11. package/ui/components/ChipSelect.jsx +54 -0
  12. package/ui/components/CircularProgress.jsx +216 -0
  13. package/ui/components/Confirm.jsx +79 -0
  14. package/ui/components/DatePicker.jsx +605 -0
  15. package/ui/components/Divider.jsx +96 -0
  16. package/ui/components/Dropzone.jsx +203 -0
  17. package/ui/components/FilePicker.jsx +205 -0
  18. package/ui/components/HoverCard.jsx +81 -0
  19. package/ui/components/ImageUpload.jsx +147 -0
  20. package/ui/components/InfoBadge.jsx +204 -0
  21. package/ui/components/Input.jsx +229 -0
  22. package/ui/components/LineProgress.jsx +133 -0
  23. package/ui/components/LinearProgress.jsx +132 -0
  24. package/ui/components/Modal.jsx +212 -0
  25. package/ui/components/Pagination.jsx +52 -0
  26. package/ui/components/PulseDot.jsx +77 -0
  27. package/ui/components/Select.jsx +415 -0
  28. package/ui/components/Skeleton.jsx +118 -0
  29. package/ui/components/Spinner.jsx +108 -0
  30. package/ui/components/StatBarCard.jsx +25 -0
  31. package/ui/components/StatsBar.jsx +88 -0
  32. package/ui/components/StatsCard.jsx +171 -0
  33. package/ui/components/TabBar.jsx +404 -0
  34. package/ui/components/Tabs.jsx +29 -0
  35. package/ui/components/TimePicker.jsx +1069 -0
  36. package/ui/components/Toast.jsx +106 -0
  37. package/ui/components/Toggle.jsx +67 -0
  38. package/ui/components/UptimeBar.jsx +135 -0
  39. package/ui/components/XTable.jsx +44 -0
  40. package/ui/index.js +97 -0
  41. package/utils/index.js +11 -0
  42. package/utils/utils/oidc.js +209 -0
  43. package/utils/utils/realtime.js +1040 -0
  44. package/utils/utils/storage.js +393 -0
  45. package/utils/utils/tz.js +625 -0
  46. package/utils/utils/uid.js +252 -0
@@ -0,0 +1,216 @@
1
+ import { useEffect, useRef } from 'react';
2
+
3
+ const SIZES = {
4
+ xs: { svg: 48, stroke: 4, fontSize: '.7rem' },
5
+ sm: { svg: 64, stroke: 5, fontSize: '.85rem' },
6
+ md: { svg: 80, stroke: 6, fontSize: '1rem' },
7
+ lg: { svg: 100, stroke: 7, fontSize: '1.15rem' },
8
+ xl: { svg: 120, stroke: 8, fontSize: '1.3rem' },
9
+ };
10
+
11
+ export default function CircularProgress({
12
+ value = 0,
13
+ segments,
14
+ size = 'md',
15
+ customSize,
16
+ color = 'var(--blue, #2782e4)',
17
+ trackColor = 'var(--bd2, #e0e0e4)',
18
+ label = '',
19
+ centerText,
20
+ showPercent = true,
21
+ animated = true,
22
+ rounded = true,
23
+ indeterminate = false,
24
+ speed = 1,
25
+ strokeWidth,
26
+ gap = 4,
27
+ className = '',
28
+ style = {},
29
+ }) {
30
+ const base = SIZES[size] || SIZES.md;
31
+ const svg = customSize || base.svg;
32
+ const stroke = strokeWidth ?? (customSize ? Math.max(3, svg * 0.06) : base.stroke);
33
+ const fontSize = customSize ? `${Math.max(11, svg * 0.13)}px` : base.fontSize;
34
+ const cx = svg / 2;
35
+ const cy = svg / 2;
36
+ const r = (svg - stroke) / 2;
37
+ const c = 2 * Math.PI * r;
38
+ const pct = Math.min(100, Math.max(0, value));
39
+ const BIG = c * 4;
40
+ const isRounded = rounded !== false;
41
+ const cap = isRounded ? 'round' : 'butt';
42
+
43
+ const userGap = gap !== undefined ? gap : (isRounded ? c * 0.05 : c * 0.03);
44
+ const capOverrun = isRounded ? stroke : 0;
45
+ const gapSize = userGap + capOverrun;
46
+ const total = c - 2 * gapSize;
47
+
48
+ const isFull = pct >= 100;
49
+ const isEmpty = pct <= 0;
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
+
54
+ let indDash, indOffset, trackDash, trackOffset, indOpacity;
55
+
56
+ if (isEmpty) {
57
+ indDash = `0,${BIG}`; indOffset = 0;
58
+ trackDash = `${c},${BIG}`; trackOffset = 0;
59
+ indOpacity = 0;
60
+ } else if (isFull) {
61
+ indDash = `${c + stroke * 2},${BIG}`; indOffset = 0;
62
+ trackDash = `0,${BIG}`; trackOffset = 0;
63
+ indOpacity = 1;
64
+ } else {
65
+ const activeLen = (pct / 100) * total;
66
+ const inactiveLen = total - activeLen;
67
+ indDash = `${activeLen},${BIG}`; indOffset = 0;
68
+ trackDash = `${inactiveLen},${BIG}`; trackOffset = -(activeLen + gapSize);
69
+ indOpacity = 1;
70
+ }
71
+
72
+ const gRef = useRef(null);
73
+ const indRef = useRef(null);
74
+ const trackRef = useRef(null);
75
+
76
+ useEffect(() => {
77
+ if (!indeterminate) return;
78
+ const capRad = (180 / Math.PI) * (stroke / r) / 2;
79
+ const gapRad = (180 / Math.PI) * (gapSize / r);
80
+ const gapArcLen = (capRad + gapRad) / 360 * c;
81
+ let angle = -90, arcFrac = 0.45, arcDir = 1, raf;
82
+ let lastTime = performance.now();
83
+ function tick(now) {
84
+ const dt = (now - lastTime) / 16.67;
85
+ lastTime = now;
86
+ angle = (angle + 3.6 * speed * dt) % 360;
87
+ arcFrac += 0.006 * arcDir * speed * dt;
88
+ if (arcFrac >= 0.72) { arcFrac = 0.72; arcDir = -1; }
89
+ if (arcFrac <= 0.18) { arcFrac = 0.18; arcDir = 1; }
90
+ const indLen = c * arcFrac;
91
+ const trackLen = Math.max(0, c - indLen - gapArcLen * 2);
92
+ if (gRef.current) gRef.current.style.transform = `rotate(${angle}deg)`;
93
+ if (indRef.current) indRef.current.setAttribute('stroke-dasharray', `${indLen.toFixed(2)},9999`);
94
+ if (trackRef.current) {
95
+ trackRef.current.setAttribute('stroke-dasharray', `${trackLen.toFixed(2)},9999`);
96
+ trackRef.current.setAttribute('stroke-dashoffset', `${(-(indLen + gapArcLen)).toFixed(2)}`);
97
+ }
98
+ raf = requestAnimationFrame(tick);
99
+ }
100
+ raf = requestAnimationFrame(tick);
101
+ return () => cancelAnimationFrame(raf);
102
+ }, [indeterminate, c, stroke, r, gapSize, speed]);
103
+
104
+ return (
105
+ <div className={className} style={{ display: 'inline-flex', flexDirection: 'column', alignItems: 'center', gap: 4, ...style }}>
106
+ <div style={{ position: 'relative', width: svg, height: svg }}>
107
+ <svg width={svg} height={svg} viewBox={`0 0 ${svg} ${svg}`}>
108
+ {indeterminate ? (
109
+ <g ref={gRef} style={{ transformOrigin: `${cx}px ${cy}px` }}>
110
+ <circle
111
+ ref={trackRef}
112
+ cx={cx} cy={cy} r={r}
113
+ fill="none" stroke={trackColor} strokeWidth={stroke} strokeLinecap={cap}
114
+ />
115
+ <circle
116
+ ref={indRef}
117
+ cx={cx} cy={cy} r={r}
118
+ fill="none" stroke={color} strokeWidth={stroke} strokeLinecap={cap}
119
+ />
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
+ </>
174
+ ) : (
175
+ <>
176
+ <circle
177
+ cx={cx} cy={cy} r={r}
178
+ fill="none" stroke={trackColor} strokeWidth={stroke}
179
+ strokeLinecap={cap}
180
+ strokeDasharray={trackDash} strokeDashoffset={trackOffset}
181
+ style={{
182
+ transform: 'rotate(-90deg)', transformOrigin: '50% 50%',
183
+ 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',
184
+ }}
185
+ />
186
+ <circle
187
+ cx={cx} cy={cy} r={r}
188
+ fill="none" stroke={color} strokeWidth={stroke}
189
+ strokeLinecap={cap}
190
+ strokeDasharray={indDash} strokeDashoffset={indOffset}
191
+ style={{
192
+ transform: 'rotate(-90deg)', transformOrigin: '50% 50%',
193
+ opacity: indOpacity,
194
+ transition: animated ? 'stroke-dasharray .5s cubic-bezier(0.4,0,0.2,1), opacity .2s' : 'none',
195
+ }}
196
+ />
197
+ </>
198
+ )}
199
+ </svg>
200
+ {showPercent && !indeterminate && (
201
+ <div style={{
202
+ position: 'absolute', inset: 0,
203
+ display: 'flex', alignItems: 'center', justifyContent: 'center',
204
+ fontSize, fontWeight: 600, color: 'var(--primary-text, #0f172a)',
205
+ fontVariantNumeric: 'tabular-nums',
206
+ }}>
207
+ {centerText !== undefined ? centerText : hasSegments ? rawSum : `${Math.round(pct)}%`}
208
+ </div>
209
+ )}
210
+ </div>
211
+ {label && (
212
+ <span style={{ fontSize: '.6rem', color: 'var(--muted, #797b8d)', fontWeight: 500 }}>{label}</span>
213
+ )}
214
+ </div>
215
+ );
216
+ }
@@ -0,0 +1,79 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { AlertTriangle } from 'lucide-react';
3
+ import Button from './Button.jsx';
4
+
5
+ let _show = null;
6
+
7
+ export function ConfirmRoot() {
8
+ const [state, setState] = useState(null);
9
+ const [closing, setClosing] = useState(false);
10
+
11
+ useEffect(() => {
12
+ _show = (opts) => new Promise(resolve => {
13
+ setClosing(false);
14
+ setState({ ...opts, resolve });
15
+ });
16
+ return () => { _show = null; };
17
+ }, []);
18
+
19
+ if (!state) return null;
20
+
21
+ function answer(val) {
22
+ setClosing(true);
23
+ setTimeout(() => { state.resolve(val); setState(null); }, 250);
24
+ }
25
+
26
+ return (
27
+ <div
28
+ style={{
29
+ position: 'fixed', inset: 0,
30
+ background: 'rgba(20,22,41,.5)',
31
+ display: 'flex', alignItems: 'center', justifyContent: 'center',
32
+ zIndex: 99999, padding: 20,
33
+ animation: `${closing ? 'backdropClose' : 'backdropFade'} .25s ease forwards`,
34
+ }}
35
+ onClick={e => e.target === e.currentTarget && answer(false)}
36
+ >
37
+ <div style={{
38
+ background: 'var(--surface, #ffffff)', border: '1px solid var(--border, #e2e8f0)',
39
+ borderRadius: 14, width: '100%', maxWidth: 400,
40
+ boxShadow: 'var(--slg, 0 20px 60px rgba(0,0,0,.4))',
41
+ animation: `${closing ? 'modalClose' : 'modalPop'} .25s cubic-bezier(.4,0,.2,1) forwards`,
42
+ }}>
43
+ <div style={{ padding: '20px 20px 0', display: 'flex', gap: 14, alignItems: 'flex-start' }}>
44
+ <div style={{
45
+ width: 36, height: 36, borderRadius: '50%', flexShrink: 0,
46
+ background: state.danger ? 'var(--dd, rgba(246,71,71,.1))' : 'var(--pd, rgba(249,158,44,.1))',
47
+ display: 'flex', alignItems: 'center', justifyContent: 'center',
48
+ }}>
49
+ <AlertTriangle size={17} color={state.danger ? 'var(--danger, #f64747)' : 'var(--warning, #f59e0b)'} />
50
+ </div>
51
+ <div>
52
+ <div style={{ fontWeight: 700, fontSize: '.9rem', marginBottom: 6 }}>
53
+ {state.title || 'Are you sure?'}
54
+ </div>
55
+ <div style={{ fontSize: '.8rem', color: 'var(--muted, #797b8d)', lineHeight: 1.6 }}>
56
+ {state.message}
57
+ </div>
58
+ </div>
59
+ </div>
60
+ <div style={{
61
+ display: 'flex', justifyContent: 'flex-end', gap: 8,
62
+ padding: '16px 20px', marginTop: 18,
63
+ borderTop: '1px solid var(--border-light, #ebebf0)',
64
+ background: 'var(--grey-1, #f5f5f8)', borderRadius: '0 0 14px 14px',
65
+ }}>
66
+ <Button variant="ghost" size="sm" onClick={() => answer(false)}>Cancel</Button>
67
+ <Button variant={state.danger ? 'ghost' : 'primary'} size="sm" style={state.danger ? { background: 'var(--danger, #f64747)', color: 'var(--def-btn-text,#fff)', border: 'none' } : {}} onClick={() => answer(true)}>
68
+ {state.confirmLabel || 'Confirm'}
69
+ </Button>
70
+ </div>
71
+ </div>
72
+ </div>
73
+ );
74
+ }
75
+
76
+ export function confirm(opts) {
77
+ if (!_show) return Promise.resolve(false);
78
+ return _show(typeof opts === 'string' ? { message: opts } : opts);
79
+ }