cynx-ui 1.3.5 → 1.3.7
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 +205 -205
- package/components/LinearProgress.jsx +132 -0
- package/components/Select.jsx +50 -32
- package/index.js +99 -99
- package/package.json +1 -1
|
@@ -1,205 +1,205 @@
|
|
|
1
|
-
import { useState, useRef } from 'react';
|
|
2
|
-
import { UploadCloud, Paperclip, FileCheck, X, Eye, Trash2 } from 'lucide-react';
|
|
3
|
-
import
|
|
4
|
-
import Badge from './Badge.jsx';
|
|
5
|
-
import Button from './Button.jsx';
|
|
6
|
-
|
|
7
|
-
function formatBytes(bytes, decimals = 1) {
|
|
8
|
-
if (!bytes || bytes === 0) return '0 B';
|
|
9
|
-
const k = 1024;
|
|
10
|
-
const dm = decimals < 0 ? 0 : decimals;
|
|
11
|
-
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
12
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
13
|
-
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function FilePicker({
|
|
17
|
-
onFiles,
|
|
18
|
-
onFile,
|
|
19
|
-
accept = 'image/*,application/pdf,.*',
|
|
20
|
-
multiple = true,
|
|
21
|
-
hint = 'Поддерживаются изображения, PDF документы и любые файлы',
|
|
22
|
-
uploadingFiles = [],
|
|
23
|
-
existingFiles = [],
|
|
24
|
-
onRemoveUploading,
|
|
25
|
-
onRemoveExisting,
|
|
26
|
-
onPreview,
|
|
27
|
-
label = 'Прикрепить файлы (Фото, PDF, документы)',
|
|
28
|
-
}) {
|
|
29
|
-
const [over, setOver] = useState(false);
|
|
30
|
-
const inputRef = useRef(null);
|
|
31
|
-
|
|
32
|
-
const handleFiles = (fileList) => {
|
|
33
|
-
const arr = Array.from(fileList || []);
|
|
34
|
-
if (arr.length === 0) return;
|
|
35
|
-
if (onFiles) onFiles(arr);
|
|
36
|
-
else if (onFile) arr.forEach(f => onFile(f));
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const handleDrop = (e) => {
|
|
40
|
-
e.preventDefault();
|
|
41
|
-
e.stopPropagation();
|
|
42
|
-
setOver(false);
|
|
43
|
-
if (e.dataTransfer && e.dataTransfer.files) {
|
|
44
|
-
handleFiles(e.dataTransfer.files);
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
return (
|
|
49
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
50
|
-
{label && (
|
|
51
|
-
<label style={{ fontSize: 11, fontWeight: 600, color: 'var(--t2)', textTransform: 'uppercase', letterSpacing: '.08em', display: 'block' }}>
|
|
52
|
-
{label}
|
|
53
|
-
</label>
|
|
54
|
-
)}
|
|
55
|
-
|
|
56
|
-
<div
|
|
57
|
-
className={`file-picker${over ? ' over' : ''}`}
|
|
58
|
-
onDragOver={e => { e.preventDefault(); e.stopPropagation(); setOver(true); }}
|
|
59
|
-
onDragLeave={e => { e.preventDefault(); e.stopPropagation(); setOver(false); }}
|
|
60
|
-
onDrop={handleDrop}
|
|
61
|
-
onClick={() => inputRef.current?.click()}
|
|
62
|
-
style={{
|
|
63
|
-
display: 'flex',
|
|
64
|
-
flexDirection: 'column',
|
|
65
|
-
alignItems: 'center',
|
|
66
|
-
justifyContent: 'center',
|
|
67
|
-
padding: '20px 20px',
|
|
68
|
-
border: `2px dashed ${over ? 'var(--accent, #3b82f6)' : 'var(--border-light)'}`,
|
|
69
|
-
borderRadius: 'var(--rad)',
|
|
70
|
-
cursor: 'pointer',
|
|
71
|
-
background: over ? 'rgba(59, 130, 246, 0.08)' : 'var(--grey-1)',
|
|
72
|
-
transition: 'all .2s ease',
|
|
73
|
-
textAlign: 'center',
|
|
74
|
-
}}
|
|
75
|
-
>
|
|
76
|
-
<input
|
|
77
|
-
ref={inputRef}
|
|
78
|
-
type="file"
|
|
79
|
-
accept={accept}
|
|
80
|
-
multiple={multiple}
|
|
81
|
-
onChange={e => {
|
|
82
|
-
handleFiles(e.target.files);
|
|
83
|
-
e.target.value = '';
|
|
84
|
-
}}
|
|
85
|
-
style={{ display: 'none' }}
|
|
86
|
-
/>
|
|
87
|
-
<UploadCloud size={24} color="var(--accent, #3b82f6)" style={{ marginBottom: 6, pointerEvents: 'none' }} />
|
|
88
|
-
<span style={{ fontSize: '.82rem', fontWeight: 600, pointerEvents: 'none' }}>
|
|
89
|
-
Выберите файлы, перетащите их сюда или вставьте через Ctrl+V
|
|
90
|
-
</span>
|
|
91
|
-
{hint && <span style={{ fontSize: '.72rem', color: 'var(--muted)', marginTop: 2, pointerEvents: 'none' }}>{hint}</span>}
|
|
92
|
-
</div>
|
|
93
|
-
|
|
94
|
-
{/* Uploading progress items */}
|
|
95
|
-
{uploadingFiles.length > 0 && (
|
|
96
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
|
97
|
-
{uploadingFiles.map((item) => (
|
|
98
|
-
<div
|
|
99
|
-
key={item.tempId || item.name}
|
|
100
|
-
style={{
|
|
101
|
-
display: 'flex',
|
|
102
|
-
flexDirection: 'column',
|
|
103
|
-
gap: 4,
|
|
104
|
-
padding: '8px 10px',
|
|
105
|
-
background: 'var(--surface)',
|
|
106
|
-
border: '1px solid var(--border-light)',
|
|
107
|
-
borderRadius: 'var(--rads)',
|
|
108
|
-
fontSize: '.8rem',
|
|
109
|
-
}}
|
|
110
|
-
>
|
|
111
|
-
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
|
112
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: 6, minWidth: 0 }}>
|
|
113
|
-
{item.status === 'uploading' && <Paperclip size={14} color="var(--accent)" />}
|
|
114
|
-
{item.status === 'done' && <FileCheck size={14} color="var(--success, #22c55e)" />}
|
|
115
|
-
{item.status === 'error' && <X size={14} color="var(--err, #ef4444)" />}
|
|
116
|
-
<span style={{ textOverflow: 'ellipsis', overflow: 'hidden', whiteSpace: 'nowrap', fontWeight: 500 }}>
|
|
117
|
-
{item.name} <span style={{ color: 'var(--muted)', fontSize: '.72rem' }}>({formatBytes(item.size)})</span>
|
|
118
|
-
</span>
|
|
119
|
-
</div>
|
|
120
|
-
|
|
121
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
|
122
|
-
{item.status === 'uploading' && (
|
|
123
|
-
<span style={{ fontSize: '.75rem', fontWeight: 600, color: 'var(--accent, #3b82f6)' }}>
|
|
124
|
-
{item.progress}%
|
|
125
|
-
</span>
|
|
126
|
-
)}
|
|
127
|
-
{item.status === 'done' && <Badge variant="ok" size="xs">Загружен</Badge>}
|
|
128
|
-
{item.status === 'error' && <Badge variant="err" size="xs">{item.error || 'Ошибка'}</Badge>}
|
|
129
|
-
{onRemoveUploading && (
|
|
130
|
-
<button
|
|
131
|
-
onClick={(e) => { e.stopPropagation(); onRemoveUploading(item.tempId); }}
|
|
132
|
-
style={{ border: 'none', background: 'none', cursor: 'pointer', padding: 2, color: 'var(--muted)' }}
|
|
133
|
-
>
|
|
134
|
-
<X size={14} />
|
|
135
|
-
</button>
|
|
136
|
-
)}
|
|
137
|
-
</div>
|
|
138
|
-
</div>
|
|
139
|
-
|
|
140
|
-
{item.status === 'uploading' && (
|
|
141
|
-
<
|
|
142
|
-
value={item.progress}
|
|
143
|
-
color="var(--accent, #3b82f6)"
|
|
144
|
-
height={4}
|
|
145
|
-
showPercent={false}
|
|
146
|
-
style={{ marginTop: 2 }}
|
|
147
|
-
/>
|
|
148
|
-
)}
|
|
149
|
-
</div>
|
|
150
|
-
))}
|
|
151
|
-
</div>
|
|
152
|
-
)}
|
|
153
|
-
|
|
154
|
-
{/* Existing saved files list */}
|
|
155
|
-
{existingFiles.length > 0 && (
|
|
156
|
-
<div style={{ marginTop: 4 }}>
|
|
157
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
|
158
|
-
{existingFiles.map((f) => (
|
|
159
|
-
<div
|
|
160
|
-
key={f.id}
|
|
161
|
-
style={{
|
|
162
|
-
display: 'flex',
|
|
163
|
-
alignItems: 'center',
|
|
164
|
-
justifyContent: 'space-between',
|
|
165
|
-
padding: '6px 10px',
|
|
166
|
-
background: 'var(--surface)',
|
|
167
|
-
border: '1px solid var(--border-light)',
|
|
168
|
-
borderRadius: 'var(--rads)',
|
|
169
|
-
fontSize: '.8rem',
|
|
170
|
-
}}
|
|
171
|
-
>
|
|
172
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: 6, minWidth: 0 }}>
|
|
173
|
-
<Paperclip size={14} color="var(--accent)" />
|
|
174
|
-
<span style={{ textOverflow: 'ellipsis', overflow: 'hidden', whiteSpace: 'nowrap', fontWeight: 500 }}>
|
|
175
|
-
{f.original_name || f.originalName} <span style={{ color: 'var(--muted)', fontSize: '.72rem' }}>({formatBytes(f.file_size || f.fileSize)})</span>
|
|
176
|
-
</span>
|
|
177
|
-
</div>
|
|
178
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
|
|
179
|
-
{onPreview && (
|
|
180
|
-
<Button
|
|
181
|
-
variant="ghost"
|
|
182
|
-
size="xs"
|
|
183
|
-
icon={<Eye size={13} />}
|
|
184
|
-
onClick={(e) => { e.stopPropagation(); onPreview(f); }}
|
|
185
|
-
/>
|
|
186
|
-
)}
|
|
187
|
-
{onRemoveExisting && (
|
|
188
|
-
<Button
|
|
189
|
-
variant="ghost"
|
|
190
|
-
size="xs"
|
|
191
|
-
icon={<Trash2 size={13} color="var(--err)" />}
|
|
192
|
-
onClick={(e) => { e.stopPropagation(); onRemoveExisting(f.id); }}
|
|
193
|
-
/>
|
|
194
|
-
)}
|
|
195
|
-
</div>
|
|
196
|
-
</div>
|
|
197
|
-
))}
|
|
198
|
-
</div>
|
|
199
|
-
</div>
|
|
200
|
-
)}
|
|
201
|
-
</div>
|
|
202
|
-
);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
export default FilePicker;
|
|
1
|
+
import { useState, useRef } from 'react';
|
|
2
|
+
import { UploadCloud, Paperclip, FileCheck, X, Eye, Trash2 } from 'lucide-react';
|
|
3
|
+
import LinearProgress from './LinearProgress.jsx';
|
|
4
|
+
import Badge from './Badge.jsx';
|
|
5
|
+
import Button from './Button.jsx';
|
|
6
|
+
|
|
7
|
+
function formatBytes(bytes, decimals = 1) {
|
|
8
|
+
if (!bytes || bytes === 0) return '0 B';
|
|
9
|
+
const k = 1024;
|
|
10
|
+
const dm = decimals < 0 ? 0 : decimals;
|
|
11
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
12
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
13
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function FilePicker({
|
|
17
|
+
onFiles,
|
|
18
|
+
onFile,
|
|
19
|
+
accept = 'image/*,application/pdf,.*',
|
|
20
|
+
multiple = true,
|
|
21
|
+
hint = 'Поддерживаются изображения, PDF документы и любые файлы',
|
|
22
|
+
uploadingFiles = [],
|
|
23
|
+
existingFiles = [],
|
|
24
|
+
onRemoveUploading,
|
|
25
|
+
onRemoveExisting,
|
|
26
|
+
onPreview,
|
|
27
|
+
label = 'Прикрепить файлы (Фото, PDF, документы)',
|
|
28
|
+
}) {
|
|
29
|
+
const [over, setOver] = useState(false);
|
|
30
|
+
const inputRef = useRef(null);
|
|
31
|
+
|
|
32
|
+
const handleFiles = (fileList) => {
|
|
33
|
+
const arr = Array.from(fileList || []);
|
|
34
|
+
if (arr.length === 0) return;
|
|
35
|
+
if (onFiles) onFiles(arr);
|
|
36
|
+
else if (onFile) arr.forEach(f => onFile(f));
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const handleDrop = (e) => {
|
|
40
|
+
e.preventDefault();
|
|
41
|
+
e.stopPropagation();
|
|
42
|
+
setOver(false);
|
|
43
|
+
if (e.dataTransfer && e.dataTransfer.files) {
|
|
44
|
+
handleFiles(e.dataTransfer.files);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
50
|
+
{label && (
|
|
51
|
+
<label style={{ fontSize: 11, fontWeight: 600, color: 'var(--t2)', textTransform: 'uppercase', letterSpacing: '.08em', display: 'block' }}>
|
|
52
|
+
{label}
|
|
53
|
+
</label>
|
|
54
|
+
)}
|
|
55
|
+
|
|
56
|
+
<div
|
|
57
|
+
className={`file-picker${over ? ' over' : ''}`}
|
|
58
|
+
onDragOver={e => { e.preventDefault(); e.stopPropagation(); setOver(true); }}
|
|
59
|
+
onDragLeave={e => { e.preventDefault(); e.stopPropagation(); setOver(false); }}
|
|
60
|
+
onDrop={handleDrop}
|
|
61
|
+
onClick={() => inputRef.current?.click()}
|
|
62
|
+
style={{
|
|
63
|
+
display: 'flex',
|
|
64
|
+
flexDirection: 'column',
|
|
65
|
+
alignItems: 'center',
|
|
66
|
+
justifyContent: 'center',
|
|
67
|
+
padding: '20px 20px',
|
|
68
|
+
border: `2px dashed ${over ? 'var(--accent, #3b82f6)' : 'var(--border-light)'}`,
|
|
69
|
+
borderRadius: 'var(--rad)',
|
|
70
|
+
cursor: 'pointer',
|
|
71
|
+
background: over ? 'rgba(59, 130, 246, 0.08)' : 'var(--grey-1)',
|
|
72
|
+
transition: 'all .2s ease',
|
|
73
|
+
textAlign: 'center',
|
|
74
|
+
}}
|
|
75
|
+
>
|
|
76
|
+
<input
|
|
77
|
+
ref={inputRef}
|
|
78
|
+
type="file"
|
|
79
|
+
accept={accept}
|
|
80
|
+
multiple={multiple}
|
|
81
|
+
onChange={e => {
|
|
82
|
+
handleFiles(e.target.files);
|
|
83
|
+
e.target.value = '';
|
|
84
|
+
}}
|
|
85
|
+
style={{ display: 'none' }}
|
|
86
|
+
/>
|
|
87
|
+
<UploadCloud size={24} color="var(--accent, #3b82f6)" style={{ marginBottom: 6, pointerEvents: 'none' }} />
|
|
88
|
+
<span style={{ fontSize: '.82rem', fontWeight: 600, pointerEvents: 'none' }}>
|
|
89
|
+
Выберите файлы, перетащите их сюда или вставьте через Ctrl+V
|
|
90
|
+
</span>
|
|
91
|
+
{hint && <span style={{ fontSize: '.72rem', color: 'var(--muted)', marginTop: 2, pointerEvents: 'none' }}>{hint}</span>}
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
{/* Uploading progress items */}
|
|
95
|
+
{uploadingFiles.length > 0 && (
|
|
96
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
|
97
|
+
{uploadingFiles.map((item) => (
|
|
98
|
+
<div
|
|
99
|
+
key={item.tempId || item.name}
|
|
100
|
+
style={{
|
|
101
|
+
display: 'flex',
|
|
102
|
+
flexDirection: 'column',
|
|
103
|
+
gap: 4,
|
|
104
|
+
padding: '8px 10px',
|
|
105
|
+
background: 'var(--surface)',
|
|
106
|
+
border: '1px solid var(--border-light)',
|
|
107
|
+
borderRadius: 'var(--rads)',
|
|
108
|
+
fontSize: '.8rem',
|
|
109
|
+
}}
|
|
110
|
+
>
|
|
111
|
+
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
|
112
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 6, minWidth: 0 }}>
|
|
113
|
+
{item.status === 'uploading' && <Paperclip size={14} color="var(--accent)" />}
|
|
114
|
+
{item.status === 'done' && <FileCheck size={14} color="var(--success, #22c55e)" />}
|
|
115
|
+
{item.status === 'error' && <X size={14} color="var(--err, #ef4444)" />}
|
|
116
|
+
<span style={{ textOverflow: 'ellipsis', overflow: 'hidden', whiteSpace: 'nowrap', fontWeight: 500 }}>
|
|
117
|
+
{item.name} <span style={{ color: 'var(--muted)', fontSize: '.72rem' }}>({formatBytes(item.size)})</span>
|
|
118
|
+
</span>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
|
122
|
+
{item.status === 'uploading' && (
|
|
123
|
+
<span style={{ fontSize: '.75rem', fontWeight: 600, color: 'var(--accent, #3b82f6)' }}>
|
|
124
|
+
{item.progress}%
|
|
125
|
+
</span>
|
|
126
|
+
)}
|
|
127
|
+
{item.status === 'done' && <Badge variant="ok" size="xs">Загружен</Badge>}
|
|
128
|
+
{item.status === 'error' && <Badge variant="err" size="xs">{item.error || 'Ошибка'}</Badge>}
|
|
129
|
+
{onRemoveUploading && (
|
|
130
|
+
<button
|
|
131
|
+
onClick={(e) => { e.stopPropagation(); onRemoveUploading(item.tempId); }}
|
|
132
|
+
style={{ border: 'none', background: 'none', cursor: 'pointer', padding: 2, color: 'var(--muted)' }}
|
|
133
|
+
>
|
|
134
|
+
<X size={14} />
|
|
135
|
+
</button>
|
|
136
|
+
)}
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
|
|
140
|
+
{item.status === 'uploading' && (
|
|
141
|
+
<LinearProgress
|
|
142
|
+
value={item.progress}
|
|
143
|
+
color="var(--accent, #3b82f6)"
|
|
144
|
+
height={4}
|
|
145
|
+
showPercent={false}
|
|
146
|
+
style={{ marginTop: 2 }}
|
|
147
|
+
/>
|
|
148
|
+
)}
|
|
149
|
+
</div>
|
|
150
|
+
))}
|
|
151
|
+
</div>
|
|
152
|
+
)}
|
|
153
|
+
|
|
154
|
+
{/* Existing saved files list */}
|
|
155
|
+
{existingFiles.length > 0 && (
|
|
156
|
+
<div style={{ marginTop: 4 }}>
|
|
157
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
|
158
|
+
{existingFiles.map((f) => (
|
|
159
|
+
<div
|
|
160
|
+
key={f.id}
|
|
161
|
+
style={{
|
|
162
|
+
display: 'flex',
|
|
163
|
+
alignItems: 'center',
|
|
164
|
+
justifyContent: 'space-between',
|
|
165
|
+
padding: '6px 10px',
|
|
166
|
+
background: 'var(--surface)',
|
|
167
|
+
border: '1px solid var(--border-light)',
|
|
168
|
+
borderRadius: 'var(--rads)',
|
|
169
|
+
fontSize: '.8rem',
|
|
170
|
+
}}
|
|
171
|
+
>
|
|
172
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 6, minWidth: 0 }}>
|
|
173
|
+
<Paperclip size={14} color="var(--accent)" />
|
|
174
|
+
<span style={{ textOverflow: 'ellipsis', overflow: 'hidden', whiteSpace: 'nowrap', fontWeight: 500 }}>
|
|
175
|
+
{f.original_name || f.originalName} <span style={{ color: 'var(--muted)', fontSize: '.72rem' }}>({formatBytes(f.file_size || f.fileSize)})</span>
|
|
176
|
+
</span>
|
|
177
|
+
</div>
|
|
178
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
|
|
179
|
+
{onPreview && (
|
|
180
|
+
<Button
|
|
181
|
+
variant="ghost"
|
|
182
|
+
size="xs"
|
|
183
|
+
icon={<Eye size={13} />}
|
|
184
|
+
onClick={(e) => { e.stopPropagation(); onPreview(f); }}
|
|
185
|
+
/>
|
|
186
|
+
)}
|
|
187
|
+
{onRemoveExisting && (
|
|
188
|
+
<Button
|
|
189
|
+
variant="ghost"
|
|
190
|
+
size="xs"
|
|
191
|
+
icon={<Trash2 size={13} color="var(--err)" />}
|
|
192
|
+
onClick={(e) => { e.stopPropagation(); onRemoveExisting(f.id); }}
|
|
193
|
+
/>
|
|
194
|
+
)}
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
))}
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
)}
|
|
201
|
+
</div>
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export default FilePicker;
|
|
@@ -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/components/Select.jsx
CHANGED
|
@@ -24,7 +24,9 @@ export default function Select({
|
|
|
24
24
|
searchable = false,
|
|
25
25
|
clearable = false,
|
|
26
26
|
onClear,
|
|
27
|
+
size = 'md',
|
|
27
28
|
style = {},
|
|
29
|
+
rounded = false,
|
|
28
30
|
}) {
|
|
29
31
|
const [open, setOpen] = useState(false);
|
|
30
32
|
const [query, setQuery] = useState('');
|
|
@@ -116,14 +118,26 @@ export default function Select({
|
|
|
116
118
|
|
|
117
119
|
const selectedValues = multi ? (Array.isArray(value) ? value : []) : (value !== undefined && value !== null ? [value] : []);
|
|
118
120
|
|
|
121
|
+
const heightVal = size === 'xs' ? 24 : size === 'sm' ? 28 : 32;
|
|
122
|
+
const paddingVal = size === 'xs' ? '0 6px' : size === 'sm' ? '0 8px' : '0 10px';
|
|
123
|
+
const fontSizeVal = size === 'xs' ? '11px' : size === 'sm' ? '11.5px' : '13px';
|
|
124
|
+
|
|
125
|
+
let radiusVal = rounded
|
|
126
|
+
? (typeof rounded === 'string'
|
|
127
|
+
? (rounded.endsWith('px') || rounded.endsWith('rem') || rounded.startsWith('var') ? rounded : `${rounded}px`)
|
|
128
|
+
: typeof rounded === 'number'
|
|
129
|
+
? `${rounded}px`
|
|
130
|
+
: '6px')
|
|
131
|
+
: (size === 'xs' ? '4px' : size === 'sm' ? '6px' : 'var(--rmd,var(--rads))');
|
|
132
|
+
|
|
119
133
|
return (
|
|
120
134
|
<div
|
|
121
135
|
ref={wrapRef}
|
|
122
136
|
style={{
|
|
123
137
|
position: 'relative',
|
|
124
138
|
width: '100%',
|
|
139
|
+
boxSizing: 'border-box',
|
|
125
140
|
fontFamily: 'var(--f,var(--sans))',
|
|
126
|
-
fontSize: 'var(--fsz,13px)',
|
|
127
141
|
opacity: disabled ? 0.5 : 1,
|
|
128
142
|
pointerEvents: disabled ? 'none' : undefined,
|
|
129
143
|
...style,
|
|
@@ -133,19 +147,20 @@ export default function Select({
|
|
|
133
147
|
onClick={handleOpen}
|
|
134
148
|
style={{
|
|
135
149
|
width: '100%',
|
|
150
|
+
boxSizing: 'border-box',
|
|
136
151
|
display: 'flex',
|
|
137
152
|
alignItems: 'center',
|
|
138
153
|
justifyContent: 'space-between',
|
|
139
|
-
gap:
|
|
140
|
-
padding:
|
|
141
|
-
height:
|
|
154
|
+
gap: 4,
|
|
155
|
+
padding: paddingVal,
|
|
156
|
+
height: heightVal,
|
|
142
157
|
background: 'var(--sf,var(--surface))',
|
|
143
|
-
border: `
|
|
144
|
-
borderRadius:
|
|
158
|
+
border: `1px solid ${open ? 'var(--accent)' : 'var(--bd2,var(--border))'}`,
|
|
159
|
+
borderRadius: radiusVal,
|
|
145
160
|
cursor: 'pointer',
|
|
146
161
|
fontFamily: 'var(--f,var(--sans,inherit))',
|
|
147
|
-
fontSize:
|
|
148
|
-
lineHeight: 1,
|
|
162
|
+
fontSize: fontSizeVal,
|
|
163
|
+
lineHeight: 1.2,
|
|
149
164
|
color: 'var(--t,var(--primary-text))',
|
|
150
165
|
outline: 'none',
|
|
151
166
|
transition: 'border-color var(--tr,.15s), box-shadow var(--tr,.15s)',
|
|
@@ -182,7 +197,7 @@ export default function Select({
|
|
|
182
197
|
</div>
|
|
183
198
|
) : !multi && selectedValues.length > 0 ? (
|
|
184
199
|
<span style={{ fontWeight: 400, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
|
185
|
-
{getLabel(selectedValues[0])}
|
|
200
|
+
{getLabel(selectedValues[0]) || '\u00A0'}
|
|
186
201
|
</span>
|
|
187
202
|
) : (
|
|
188
203
|
<span style={{ color: 'var(--t3,var(--grey-3))', fontWeight: 400 }}>{placeholder}</span>
|
|
@@ -209,11 +224,11 @@ export default function Select({
|
|
|
209
224
|
onMouseEnter={(e) => { e.currentTarget.style.color = 'var(--danger, #ef4444)'; }}
|
|
210
225
|
onMouseLeave={(e) => { e.currentTarget.style.color = 'var(--muted, #64748b)'; }}
|
|
211
226
|
>
|
|
212
|
-
<X size={
|
|
227
|
+
<X size={12} />
|
|
213
228
|
</span>
|
|
214
229
|
) : (
|
|
215
230
|
<ChevronDown
|
|
216
|
-
size={
|
|
231
|
+
size={12}
|
|
217
232
|
style={{
|
|
218
233
|
flexShrink: 0,
|
|
219
234
|
color: 'var(--t3,var(--grey-3))',
|
|
@@ -233,33 +248,35 @@ export default function Select({
|
|
|
233
248
|
top: pos.top,
|
|
234
249
|
left: pos.left,
|
|
235
250
|
width: pos.width,
|
|
251
|
+
boxSizing: 'border-box',
|
|
236
252
|
background: 'var(--sf,var(--surface))',
|
|
237
253
|
border: '1px solid var(--bd,var(--border))',
|
|
238
|
-
borderRadius:
|
|
254
|
+
borderRadius: radiusVal,
|
|
239
255
|
boxShadow: 'var(--shadow,0 1px 3px rgba(20,22,41,.1))',
|
|
240
|
-
padding:
|
|
256
|
+
padding: 3,
|
|
241
257
|
animation: 'selFadeIn .15s ease',
|
|
242
258
|
overflow: 'hidden',
|
|
243
259
|
}}
|
|
244
260
|
>
|
|
245
261
|
{searchable && (
|
|
246
|
-
<div style={{ padding:
|
|
262
|
+
<div style={{ padding: 3 }}>
|
|
247
263
|
<input
|
|
248
264
|
ref={inputRef}
|
|
249
265
|
type="text"
|
|
250
|
-
placeholder="
|
|
266
|
+
placeholder="Поиск..."
|
|
251
267
|
value={query}
|
|
252
268
|
onChange={e => setQuery(e.target.value)}
|
|
253
269
|
onMouseDown={e => e.stopPropagation()}
|
|
254
270
|
style={{
|
|
255
271
|
width: '100%',
|
|
256
|
-
|
|
257
|
-
|
|
272
|
+
boxSizing: 'border-box',
|
|
273
|
+
padding: size === 'xs' ? '2px 5px' : size === 'sm' ? '3px 6px' : '7px 10px',
|
|
274
|
+
height: size === 'xs' ? 22 : size === 'sm' ? 26 : 32,
|
|
258
275
|
background: 'var(--sf2,var(--grey-1))',
|
|
259
276
|
border: '1.5px solid var(--bd2,var(--border))',
|
|
260
|
-
borderRadius: 'var(--rmd,var(--rads))',
|
|
277
|
+
borderRadius: size === 'xs' ? '3px' : size === 'sm' ? '4px' : 'var(--rmd,var(--rads))',
|
|
261
278
|
fontFamily: 'var(--f,var(--sans))',
|
|
262
|
-
fontSize: '12.5px',
|
|
279
|
+
fontSize: size === 'xs' ? '10.5px' : size === 'sm' ? '11.5px' : '12.5px',
|
|
263
280
|
lineHeight: 1,
|
|
264
281
|
color: 'var(--t,var(--primary-text))',
|
|
265
282
|
outline: 'none',
|
|
@@ -270,8 +287,8 @@ export default function Select({
|
|
|
270
287
|
)}
|
|
271
288
|
<div style={{ maxHeight: 200, overflowY: 'auto' }}>
|
|
272
289
|
{filtered.length === 0 ? (
|
|
273
|
-
<div style={{ padding: '
|
|
274
|
-
|
|
290
|
+
<div style={{ padding: '10px 8px', textAlign: 'center', color: 'var(--t3,var(--grey-3))', fontSize: 11 }}>
|
|
291
|
+
Ничего не найдено
|
|
275
292
|
</div>
|
|
276
293
|
) : (
|
|
277
294
|
filtered.map(opt => (
|
|
@@ -281,13 +298,14 @@ export default function Select({
|
|
|
281
298
|
style={{
|
|
282
299
|
display: 'flex',
|
|
283
300
|
alignItems: 'center',
|
|
284
|
-
gap:
|
|
285
|
-
|
|
286
|
-
|
|
301
|
+
gap: 6,
|
|
302
|
+
minHeight: size === 'xs' ? 18 : size === 'sm' ? 22 : 28,
|
|
303
|
+
padding: size === 'xs' ? '3px 5px' : size === 'sm' ? '4.5px 7px' : '8px 10px',
|
|
304
|
+
borderRadius: size === 'xs' ? '3px' : size === 'sm' ? '4px' : 'var(--rmd,var(--rads))',
|
|
287
305
|
cursor: opt.disabled ? 'not-allowed' : 'pointer',
|
|
288
306
|
color: isSelected(opt.value) ? 'var(--accent)' : 'var(--t,var(--primary-text))',
|
|
289
307
|
background: isSelected(opt.value) ? 'var(--pd,var(--accent-bg))' : undefined,
|
|
290
|
-
fontSize: '12.5px',
|
|
308
|
+
fontSize: size === 'xs' ? '10.5px' : size === 'sm' ? '11.5px' : '12.5px',
|
|
291
309
|
fontWeight: 400,
|
|
292
310
|
transition: 'background .1s',
|
|
293
311
|
userSelect: 'none',
|
|
@@ -297,8 +315,8 @@ export default function Select({
|
|
|
297
315
|
>
|
|
298
316
|
{multi && (
|
|
299
317
|
<span style={{
|
|
300
|
-
width:
|
|
301
|
-
height:
|
|
318
|
+
width: 13,
|
|
319
|
+
height: 13,
|
|
302
320
|
border: `1.5px solid ${isSelected(opt.value) ? 'var(--accent)' : 'var(--bd2,var(--border))'}`,
|
|
303
321
|
borderRadius: 3,
|
|
304
322
|
flexShrink: 0,
|
|
@@ -311,8 +329,8 @@ export default function Select({
|
|
|
311
329
|
position: 'absolute',
|
|
312
330
|
top: 1,
|
|
313
331
|
left: 3.5,
|
|
314
|
-
width: 4
|
|
315
|
-
height:
|
|
332
|
+
width: 4,
|
|
333
|
+
height: 6,
|
|
316
334
|
border: 'solid var(--def-btn-text,#fff)',
|
|
317
335
|
borderWidth: '0 1.5px 1.5px 0',
|
|
318
336
|
transform: 'rotate(45deg)',
|
|
@@ -320,11 +338,11 @@ export default function Select({
|
|
|
320
338
|
)}
|
|
321
339
|
</span>
|
|
322
340
|
)}
|
|
323
|
-
<span style={{ flex: 1, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
|
324
|
-
{opt.displayLabel || opt.label}
|
|
341
|
+
<span style={{ flex: 1, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', minHeight: '1em' }}>
|
|
342
|
+
{opt.displayLabel || opt.label || '\u00A0'}
|
|
325
343
|
</span>
|
|
326
344
|
{!multi && isSelected(opt.value) && (
|
|
327
|
-
<Check size={
|
|
345
|
+
<Check size={12} style={{ flexShrink: 0, color: 'var(--accent)' }} />
|
|
328
346
|
)}
|
|
329
347
|
</div>
|
|
330
348
|
))
|
package/index.js
CHANGED
|
@@ -1,99 +1,99 @@
|
|
|
1
|
-
// cynx-ui — shared component library
|
|
2
|
-
import { default as _Button } from './components/Button.jsx';
|
|
3
|
-
import { default as _Input } from './components/Input.jsx';
|
|
4
|
-
import { default as _Toggle } from './components/Toggle.jsx';
|
|
5
|
-
import { default as _Spinner, Spinner as _SpinnerNamed } from './components/Spinner.jsx';
|
|
6
|
-
import { default as _TabBar } from './components/TabBar.jsx';
|
|
7
|
-
import { default as _Dropdown } from './components/Dropdown.jsx';
|
|
8
|
-
import { default as _MultiSelect } from './components/MultiSelect.jsx';
|
|
9
|
-
import { default as _ImageUpload } from './components/ImageUpload.jsx';
|
|
10
|
-
import { default as _ChipSelect } from './components/ChipSelect.jsx';
|
|
11
|
-
import { default as _Checkbox } from './components/Checkbox.jsx';
|
|
12
|
-
import { default as _AuthOverlay } from './components/AuthOverlay.jsx';
|
|
13
|
-
import { default as _UptimeBar } from './components/UptimeBar.jsx';
|
|
14
|
-
import { default as
|
|
15
|
-
import { default as _StatBarCard } from './components/StatBarCard.jsx';
|
|
16
|
-
import { default as _StatsCard } from './components/StatsCard.jsx';
|
|
17
|
-
import { default as _ActivityCard } from './components/ActivityCard.jsx';
|
|
18
|
-
import { default as _HoverCard } from './components/HoverCard.jsx';
|
|
19
|
-
import { default as _StatsBar } from './components/StatsBar.jsx';
|
|
20
|
-
import { default as _Select } from './components/Select.jsx';
|
|
21
|
-
import { default as _Badge } from './components/Badge.jsx';
|
|
22
|
-
import { default as _PulseDot } from './components/PulseDot.jsx';
|
|
23
|
-
import { default as _CircularProgress } from './components/CircularProgress.jsx';
|
|
24
|
-
import { default as _Skeleton } from './components/Skeleton.jsx';
|
|
25
|
-
import { Modal as _Modal, Confirm as _Confirm } from './components/Modal.jsx';
|
|
26
|
-
import { toast as _toast, useToast as _useToast, Toast as _Toast } from './components/Toast.jsx';
|
|
27
|
-
import { ConfirmRoot as _ConfirmRoot, confirm as _confirm } from './components/Confirm.jsx';
|
|
28
|
-
import { SearchableDropdown as _SearchableDropdown } from './components/SearchableDropdown.jsx';
|
|
29
|
-
import { Pagination as _Pagination } from './components/Pagination.jsx';
|
|
30
|
-
import { DatePicker as _DatePicker } from './components/DatePicker.jsx';
|
|
31
|
-
import { TimePicker as _TimePicker } from './components/TimePicker.jsx';
|
|
32
|
-
import { ActionsMenu as _ActionsMenu } from './components/ActionsMenu.jsx';
|
|
33
|
-
import { BrandingCard as _BrandingCard } from './components/BrandingCard.jsx';
|
|
34
|
-
import { Dropzone as _Dropzone } from './components/Dropzone.jsx';
|
|
35
|
-
import { Tabs as _Tabs } from './components/Tabs.jsx';
|
|
36
|
-
import { SkeletonText as _SkeletonText, SkeletonCard as _SkeletonCard, SkeletonRows as _SkeletonRows, SkeletonStats as _SkeletonStats } from './components/Skeleton.jsx';
|
|
37
|
-
import { XTable as _XTable, XThead as _XThead, XTbody as _XTbody, XTr as _XTr, XTh as _XTh, XTd as _XTd, SkeletonTable as _SkeletonTable } from './components/XTable.jsx';
|
|
38
|
-
import { PageShell as _PageShell, TableCard as _TableCard, TableCardFilters as _TableCardFilters, TableCardBody as _TableCardBody, TableCardFooter as _TableCardFooter, CardHdr as _CardHdr, StatCard as _StatCard, StatCardSkeleton as _StatCardSkeleton } from './components/Card.jsx';
|
|
39
|
-
|
|
40
|
-
import { default as _FilePicker } from './components/FilePicker.jsx';
|
|
41
|
-
|
|
42
|
-
export const Button = _Button;
|
|
43
|
-
export const FilePicker = _FilePicker;
|
|
44
|
-
export const Input = _Input;
|
|
45
|
-
export const Toggle = _Toggle;
|
|
46
|
-
export const Spinner = _Spinner || _SpinnerNamed;
|
|
47
|
-
export const TabBar = _TabBar;
|
|
48
|
-
export const Dropdown = _Dropdown;
|
|
49
|
-
export const MultiSelect = _MultiSelect;
|
|
50
|
-
export const ImageUpload = _ImageUpload;
|
|
51
|
-
export const ChipSelect = _ChipSelect;
|
|
52
|
-
export const Checkbox = _Checkbox;
|
|
53
|
-
export const AuthOverlay = _AuthOverlay;
|
|
54
|
-
export const UptimeBar = _UptimeBar;
|
|
55
|
-
export const
|
|
56
|
-
export const StatBarCard = _StatBarCard;
|
|
57
|
-
export const StatsCard = _StatsCard;
|
|
58
|
-
export const ActivityCard = _ActivityCard;
|
|
59
|
-
export const HoverCard = _HoverCard;
|
|
60
|
-
export const StatsBar = _StatsBar;
|
|
61
|
-
export const Select = _Select;
|
|
62
|
-
export const Badge = _Badge;
|
|
63
|
-
export const PulseDot = _PulseDot;
|
|
64
|
-
export const CircularProgress = _CircularProgress;
|
|
65
|
-
export const Skeleton = _Skeleton;
|
|
66
|
-
export const Modal = _Modal;
|
|
67
|
-
export const Confirm = _Confirm;
|
|
68
|
-
export const toast = _toast;
|
|
69
|
-
export const useToast = _useToast;
|
|
70
|
-
export const Toast = _Toast;
|
|
71
|
-
export const ConfirmRoot = _ConfirmRoot;
|
|
72
|
-
export const confirm = _confirm;
|
|
73
|
-
export const SearchableDropdown = _SearchableDropdown;
|
|
74
|
-
export const Pagination = _Pagination;
|
|
75
|
-
export const DatePicker = _DatePicker;
|
|
76
|
-
export const TimePicker = _TimePicker;
|
|
77
|
-
export const ActionsMenu = _ActionsMenu;
|
|
78
|
-
export const BrandingCard = _BrandingCard;
|
|
79
|
-
export const Dropzone = _Dropzone;
|
|
80
|
-
export const Tabs = _Tabs;
|
|
81
|
-
export const SkeletonText = _SkeletonText;
|
|
82
|
-
export const SkeletonCard = _SkeletonCard;
|
|
83
|
-
export const SkeletonRows = _SkeletonRows;
|
|
84
|
-
export const SkeletonStats = _SkeletonStats;
|
|
85
|
-
export const XTable = _XTable;
|
|
86
|
-
export const XThead = _XThead;
|
|
87
|
-
export const XTbody = _XTbody;
|
|
88
|
-
export const XTr = _XTr;
|
|
89
|
-
export const XTh = _XTh;
|
|
90
|
-
export const XTd = _XTd;
|
|
91
|
-
export const SkeletonTable = _SkeletonTable;
|
|
92
|
-
export const PageShell = _PageShell;
|
|
93
|
-
export const TableCard = _TableCard;
|
|
94
|
-
export const TableCardFilters = _TableCardFilters;
|
|
95
|
-
export const TableCardBody = _TableCardBody;
|
|
96
|
-
export const TableCardFooter = _TableCardFooter;
|
|
97
|
-
export const CardHdr = _CardHdr;
|
|
98
|
-
export const StatCard = _StatCard;
|
|
99
|
-
export const StatCardSkeleton = _StatCardSkeleton;
|
|
1
|
+
// cynx-ui — shared component library
|
|
2
|
+
import { default as _Button } from './components/Button.jsx';
|
|
3
|
+
import { default as _Input } from './components/Input.jsx';
|
|
4
|
+
import { default as _Toggle } from './components/Toggle.jsx';
|
|
5
|
+
import { default as _Spinner, Spinner as _SpinnerNamed } from './components/Spinner.jsx';
|
|
6
|
+
import { default as _TabBar } from './components/TabBar.jsx';
|
|
7
|
+
import { default as _Dropdown } from './components/Dropdown.jsx';
|
|
8
|
+
import { default as _MultiSelect } from './components/MultiSelect.jsx';
|
|
9
|
+
import { default as _ImageUpload } from './components/ImageUpload.jsx';
|
|
10
|
+
import { default as _ChipSelect } from './components/ChipSelect.jsx';
|
|
11
|
+
import { default as _Checkbox } from './components/Checkbox.jsx';
|
|
12
|
+
import { default as _AuthOverlay } from './components/AuthOverlay.jsx';
|
|
13
|
+
import { default as _UptimeBar } from './components/UptimeBar.jsx';
|
|
14
|
+
import { default as _LinearProgress } from './components/LinearProgress.jsx';
|
|
15
|
+
import { default as _StatBarCard } from './components/StatBarCard.jsx';
|
|
16
|
+
import { default as _StatsCard } from './components/StatsCard.jsx';
|
|
17
|
+
import { default as _ActivityCard } from './components/ActivityCard.jsx';
|
|
18
|
+
import { default as _HoverCard } from './components/HoverCard.jsx';
|
|
19
|
+
import { default as _StatsBar } from './components/StatsBar.jsx';
|
|
20
|
+
import { default as _Select } from './components/Select.jsx';
|
|
21
|
+
import { default as _Badge } from './components/Badge.jsx';
|
|
22
|
+
import { default as _PulseDot } from './components/PulseDot.jsx';
|
|
23
|
+
import { default as _CircularProgress } from './components/CircularProgress.jsx';
|
|
24
|
+
import { default as _Skeleton } from './components/Skeleton.jsx';
|
|
25
|
+
import { Modal as _Modal, Confirm as _Confirm } from './components/Modal.jsx';
|
|
26
|
+
import { toast as _toast, useToast as _useToast, Toast as _Toast } from './components/Toast.jsx';
|
|
27
|
+
import { ConfirmRoot as _ConfirmRoot, confirm as _confirm } from './components/Confirm.jsx';
|
|
28
|
+
import { SearchableDropdown as _SearchableDropdown } from './components/SearchableDropdown.jsx';
|
|
29
|
+
import { Pagination as _Pagination } from './components/Pagination.jsx';
|
|
30
|
+
import { DatePicker as _DatePicker } from './components/DatePicker.jsx';
|
|
31
|
+
import { TimePicker as _TimePicker } from './components/TimePicker.jsx';
|
|
32
|
+
import { ActionsMenu as _ActionsMenu } from './components/ActionsMenu.jsx';
|
|
33
|
+
import { BrandingCard as _BrandingCard } from './components/BrandingCard.jsx';
|
|
34
|
+
import { Dropzone as _Dropzone } from './components/Dropzone.jsx';
|
|
35
|
+
import { Tabs as _Tabs } from './components/Tabs.jsx';
|
|
36
|
+
import { SkeletonText as _SkeletonText, SkeletonCard as _SkeletonCard, SkeletonRows as _SkeletonRows, SkeletonStats as _SkeletonStats } from './components/Skeleton.jsx';
|
|
37
|
+
import { XTable as _XTable, XThead as _XThead, XTbody as _XTbody, XTr as _XTr, XTh as _XTh, XTd as _XTd, SkeletonTable as _SkeletonTable } from './components/XTable.jsx';
|
|
38
|
+
import { PageShell as _PageShell, TableCard as _TableCard, TableCardFilters as _TableCardFilters, TableCardBody as _TableCardBody, TableCardFooter as _TableCardFooter, CardHdr as _CardHdr, StatCard as _StatCard, StatCardSkeleton as _StatCardSkeleton } from './components/Card.jsx';
|
|
39
|
+
|
|
40
|
+
import { default as _FilePicker } from './components/FilePicker.jsx';
|
|
41
|
+
|
|
42
|
+
export const Button = _Button;
|
|
43
|
+
export const FilePicker = _FilePicker;
|
|
44
|
+
export const Input = _Input;
|
|
45
|
+
export const Toggle = _Toggle;
|
|
46
|
+
export const Spinner = _Spinner || _SpinnerNamed;
|
|
47
|
+
export const TabBar = _TabBar;
|
|
48
|
+
export const Dropdown = _Dropdown;
|
|
49
|
+
export const MultiSelect = _MultiSelect;
|
|
50
|
+
export const ImageUpload = _ImageUpload;
|
|
51
|
+
export const ChipSelect = _ChipSelect;
|
|
52
|
+
export const Checkbox = _Checkbox;
|
|
53
|
+
export const AuthOverlay = _AuthOverlay;
|
|
54
|
+
export const UptimeBar = _UptimeBar;
|
|
55
|
+
export const LinearProgress = _LinearProgress;
|
|
56
|
+
export const StatBarCard = _StatBarCard;
|
|
57
|
+
export const StatsCard = _StatsCard;
|
|
58
|
+
export const ActivityCard = _ActivityCard;
|
|
59
|
+
export const HoverCard = _HoverCard;
|
|
60
|
+
export const StatsBar = _StatsBar;
|
|
61
|
+
export const Select = _Select;
|
|
62
|
+
export const Badge = _Badge;
|
|
63
|
+
export const PulseDot = _PulseDot;
|
|
64
|
+
export const CircularProgress = _CircularProgress;
|
|
65
|
+
export const Skeleton = _Skeleton;
|
|
66
|
+
export const Modal = _Modal;
|
|
67
|
+
export const Confirm = _Confirm;
|
|
68
|
+
export const toast = _toast;
|
|
69
|
+
export const useToast = _useToast;
|
|
70
|
+
export const Toast = _Toast;
|
|
71
|
+
export const ConfirmRoot = _ConfirmRoot;
|
|
72
|
+
export const confirm = _confirm;
|
|
73
|
+
export const SearchableDropdown = _SearchableDropdown;
|
|
74
|
+
export const Pagination = _Pagination;
|
|
75
|
+
export const DatePicker = _DatePicker;
|
|
76
|
+
export const TimePicker = _TimePicker;
|
|
77
|
+
export const ActionsMenu = _ActionsMenu;
|
|
78
|
+
export const BrandingCard = _BrandingCard;
|
|
79
|
+
export const Dropzone = _Dropzone;
|
|
80
|
+
export const Tabs = _Tabs;
|
|
81
|
+
export const SkeletonText = _SkeletonText;
|
|
82
|
+
export const SkeletonCard = _SkeletonCard;
|
|
83
|
+
export const SkeletonRows = _SkeletonRows;
|
|
84
|
+
export const SkeletonStats = _SkeletonStats;
|
|
85
|
+
export const XTable = _XTable;
|
|
86
|
+
export const XThead = _XThead;
|
|
87
|
+
export const XTbody = _XTbody;
|
|
88
|
+
export const XTr = _XTr;
|
|
89
|
+
export const XTh = _XTh;
|
|
90
|
+
export const XTd = _XTd;
|
|
91
|
+
export const SkeletonTable = _SkeletonTable;
|
|
92
|
+
export const PageShell = _PageShell;
|
|
93
|
+
export const TableCard = _TableCard;
|
|
94
|
+
export const TableCardFilters = _TableCardFilters;
|
|
95
|
+
export const TableCardBody = _TableCardBody;
|
|
96
|
+
export const TableCardFooter = _TableCardFooter;
|
|
97
|
+
export const CardHdr = _CardHdr;
|
|
98
|
+
export const StatCard = _StatCard;
|
|
99
|
+
export const StatCardSkeleton = _StatCardSkeleton;
|