albinasoft-ui-package 1.0.1 → 1.0.3
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/dist/Button/button.d.ts +7 -0
- package/dist/Button/button.js +11 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -0
- package/package.json +18 -34
- package/readme.md +10 -0
- package/dist/index.cjs +0 -2
- package/dist/index.cjs.map +0 -1
- package/dist/index.modern.js +0 -2
- package/dist/index.modern.js.map +0 -1
- package/dist/index.module.js +0 -2
- package/dist/index.module.js.map +0 -1
- package/dist/index.umd.js +0 -2
- package/dist/index.umd.js.map +0 -1
- package/src/assets/css/custom/components.css +0 -20
- package/src/assets/css/custom/layout.css +0 -89
- package/src/assets/css/dark.css +0 -2145
- package/src/assets/css/main.css +0 -26263
- package/src/assets/data/countryCodes.json +0 -1466
- package/src/assets/images/albinasoft/albinasoft_logo.ico +0 -0
- package/src/assets/images/albinasoft/albinasoft_logo.png +0 -0
- package/src/assets/images/albinasoft/albinasoft_logo.svg +0 -22
- package/src/assets/images/albinasoft/albinasoft_logo_blue.svg +0 -7
- package/src/assets/images/albinasoft/albinasoft_logo_spin.gif +0 -0
- package/src/assets/images/albinasoft/albinasoft_logo_white.svg +0 -1
- package/src/assets/images/header/top-header.png +0 -0
- package/src/assets/images/header/top-image.jpg +0 -0
- package/src/components/CustomButton.tsx +0 -73
- package/src/components/CustomCheckbox.tsx +0 -122
- package/src/components/CustomDateTimePicker.tsx +0 -127
- package/src/components/CustomDivider.tsx +0 -90
- package/src/components/CustomForm.tsx +0 -327
- package/src/components/CustomInput.tsx +0 -180
- package/src/components/CustomModal.tsx +0 -43
- package/src/components/CustomRadioButton.tsx +0 -119
- package/src/components/CustomRichTextbox.tsx +0 -123
- package/src/components/CustomSelect.tsx +0 -134
- package/src/components/CustomText.tsx +0 -199
- package/src/components/CustomTextarea.tsx +0 -131
- package/src/components/CustomUploader.tsx +0 -125
- package/src/components/layout/FullScreenToggle.tsx +0 -72
- package/src/components/layout/LoginDropdown.tsx +0 -39
- package/src/components/layout/ThemeToggle.tsx +0 -59
- package/src/index.js +0 -26
@@ -1,123 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import ReactQuill, { Quill } from 'react-quill';
|
3
|
-
import 'react-quill/dist/quill.snow.css'; // Quill'in varsayılan stil dosyası
|
4
|
-
import { OverlayTrigger, Tooltip } from 'react-bootstrap';
|
5
|
-
import { FaExclamationTriangle, FaInfoCircle } from 'react-icons/fa';
|
6
|
-
|
7
|
-
interface CustomRichTextboxProps {
|
8
|
-
id?: string;
|
9
|
-
name?: string;
|
10
|
-
label: string;
|
11
|
-
value: string;
|
12
|
-
placeholder?: string;
|
13
|
-
required?: boolean;
|
14
|
-
errorMessage?: string;
|
15
|
-
conditionalErrorVisible?: boolean;
|
16
|
-
conditionalErrorMessage?: string;
|
17
|
-
description?: string | null;
|
18
|
-
tooltip?: string;
|
19
|
-
disabled?: boolean;
|
20
|
-
style?: React.CSSProperties;
|
21
|
-
className?: string;
|
22
|
-
onChange: (value: string) => void; // Zengin metin değeri döndürür
|
23
|
-
}
|
24
|
-
|
25
|
-
const CustomRichTextbox: React.FC<CustomRichTextboxProps> = ({
|
26
|
-
id,
|
27
|
-
name,
|
28
|
-
label,
|
29
|
-
value,
|
30
|
-
placeholder,
|
31
|
-
required = false,
|
32
|
-
errorMessage,
|
33
|
-
conditionalErrorVisible = false,
|
34
|
-
conditionalErrorMessage,
|
35
|
-
description,
|
36
|
-
tooltip,
|
37
|
-
disabled = false,
|
38
|
-
style = {border:'none'},
|
39
|
-
className,
|
40
|
-
onChange,
|
41
|
-
}) => {
|
42
|
-
const handleChange = (content: string) => {
|
43
|
-
onChange(content);
|
44
|
-
};
|
45
|
-
|
46
|
-
return (
|
47
|
-
<div className={className}>
|
48
|
-
<div className="form-group">
|
49
|
-
<label htmlFor={id} className="form-label">
|
50
|
-
{label}
|
51
|
-
</label>
|
52
|
-
<div className="position-relative">
|
53
|
-
{tooltip && (
|
54
|
-
<OverlayTrigger
|
55
|
-
placement="bottom"
|
56
|
-
overlay={<Tooltip id={`tooltip-${id}`}>{tooltip}</Tooltip>}
|
57
|
-
>
|
58
|
-
<div>
|
59
|
-
<ReactQuill
|
60
|
-
id={id}
|
61
|
-
value={value}
|
62
|
-
placeholder={placeholder}
|
63
|
-
readOnly={disabled}
|
64
|
-
onChange={handleChange}
|
65
|
-
className="form-control"
|
66
|
-
style={style}
|
67
|
-
theme="snow" // Kullanılan tema
|
68
|
-
/>
|
69
|
-
</div>
|
70
|
-
</OverlayTrigger>
|
71
|
-
)}
|
72
|
-
{!tooltip && (
|
73
|
-
<ReactQuill
|
74
|
-
id={id}
|
75
|
-
value={value}
|
76
|
-
placeholder={placeholder}
|
77
|
-
readOnly={disabled}
|
78
|
-
onChange={handleChange}
|
79
|
-
className="form-control"
|
80
|
-
style={style}
|
81
|
-
theme="snow"
|
82
|
-
/>
|
83
|
-
)}
|
84
|
-
|
85
|
-
{required && !value && (
|
86
|
-
<div className="invalid-feedback text-danger mt-2">
|
87
|
-
<div className="description-icon">
|
88
|
-
<FaExclamationTriangle />
|
89
|
-
</div>
|
90
|
-
<div className="description-text">
|
91
|
-
<span>{errorMessage || 'Bu alan boş bırakılamaz.'}</span>
|
92
|
-
</div>
|
93
|
-
</div>
|
94
|
-
)}
|
95
|
-
|
96
|
-
{conditionalErrorVisible && (
|
97
|
-
<div className="conditional-error-message text-warning mt-2">
|
98
|
-
<div className="description-icon">
|
99
|
-
<FaExclamationTriangle />
|
100
|
-
</div>
|
101
|
-
<div className="description-text">
|
102
|
-
<span>{conditionalErrorMessage}</span>
|
103
|
-
</div>
|
104
|
-
</div>
|
105
|
-
)}
|
106
|
-
|
107
|
-
{description && (
|
108
|
-
<div className="form-description text-secondary mt-2">
|
109
|
-
<div className="description-icon">
|
110
|
-
<FaInfoCircle />
|
111
|
-
</div>
|
112
|
-
<div className="description-text">
|
113
|
-
<span>{description}</span>
|
114
|
-
</div>
|
115
|
-
</div>
|
116
|
-
)}
|
117
|
-
</div>
|
118
|
-
</div>
|
119
|
-
</div>
|
120
|
-
);
|
121
|
-
};
|
122
|
-
|
123
|
-
export default CustomRichTextbox;
|
@@ -1,134 +0,0 @@
|
|
1
|
-
import React, { ChangeEvent, useRef } from 'react';
|
2
|
-
import { OverlayTrigger, Tooltip } from 'react-bootstrap';
|
3
|
-
import { FaExclamationTriangle, FaInfoCircle } from 'react-icons/fa';
|
4
|
-
|
5
|
-
interface CustomSelectOption {
|
6
|
-
id: string;
|
7
|
-
label: string;
|
8
|
-
value: string;
|
9
|
-
}
|
10
|
-
|
11
|
-
interface CustomSelectProps {
|
12
|
-
id?: string;
|
13
|
-
name?: string;
|
14
|
-
label: string;
|
15
|
-
options: CustomSelectOption[];
|
16
|
-
value: string | string[];
|
17
|
-
required?: boolean;
|
18
|
-
multiple?: boolean;
|
19
|
-
errorMessage?: string;
|
20
|
-
conditionalErrorVisible?: boolean;
|
21
|
-
conditionalErrorMessage?: string;
|
22
|
-
tooltip?: string;
|
23
|
-
description?: string | null;
|
24
|
-
disabled?: boolean;
|
25
|
-
className?: string;
|
26
|
-
style?: React.CSSProperties;
|
27
|
-
onChange: (value: string | string[]) => void; // Updated
|
28
|
-
}
|
29
|
-
|
30
|
-
const CustomSelect: React.FC<CustomSelectProps> = ({
|
31
|
-
id,
|
32
|
-
name,
|
33
|
-
label,
|
34
|
-
options,
|
35
|
-
value,
|
36
|
-
required = false,
|
37
|
-
multiple = false,
|
38
|
-
errorMessage,
|
39
|
-
conditionalErrorVisible,
|
40
|
-
conditionalErrorMessage,
|
41
|
-
tooltip,
|
42
|
-
description,
|
43
|
-
disabled = false,
|
44
|
-
className,
|
45
|
-
style,
|
46
|
-
onChange,
|
47
|
-
}) => {
|
48
|
-
|
49
|
-
const handleChange = (e: ChangeEvent<HTMLSelectElement>) => {
|
50
|
-
const selectedOptions = multiple
|
51
|
-
? Array.from(e.target.selectedOptions).map((option) => option.value)
|
52
|
-
: e.target.value;
|
53
|
-
|
54
|
-
onChange(selectedOptions); // Pass string or string[] as needed
|
55
|
-
};
|
56
|
-
|
57
|
-
return (
|
58
|
-
<div className={className} style={style}>
|
59
|
-
<div className="form-group">
|
60
|
-
<label htmlFor={id} className="form-label">
|
61
|
-
{label}
|
62
|
-
</label>
|
63
|
-
<div className="select-container">
|
64
|
-
<OverlayTrigger
|
65
|
-
placement="bottom"
|
66
|
-
overlay={
|
67
|
-
tooltip ? (
|
68
|
-
<Tooltip id={`tooltip-${id || name}`}>
|
69
|
-
{tooltip}
|
70
|
-
</Tooltip>
|
71
|
-
) : <></>
|
72
|
-
}
|
73
|
-
>
|
74
|
-
<select
|
75
|
-
id={id}
|
76
|
-
name={name}
|
77
|
-
className="form-select"
|
78
|
-
value={value}
|
79
|
-
multiple={multiple}
|
80
|
-
required={required}
|
81
|
-
disabled={disabled}
|
82
|
-
onChange={handleChange} // Yeni handleChange
|
83
|
-
>
|
84
|
-
{!multiple && (
|
85
|
-
<option value="" disabled>
|
86
|
-
Seçiniz
|
87
|
-
</option>
|
88
|
-
)}
|
89
|
-
{options.map((option) => (
|
90
|
-
<option key={option.id} value={option.value}>
|
91
|
-
{option.label}
|
92
|
-
</option>
|
93
|
-
))}
|
94
|
-
</select>
|
95
|
-
</OverlayTrigger>
|
96
|
-
|
97
|
-
<div className="invalid-feedback text-danger mt-2">
|
98
|
-
<div className="description-icon">
|
99
|
-
<FaExclamationTriangle />
|
100
|
-
</div>
|
101
|
-
<div className="description-text">
|
102
|
-
<span>{errorMessage || 'Lütfen bir seçim yapınız.'}</span>
|
103
|
-
</div>
|
104
|
-
</div>
|
105
|
-
|
106
|
-
{conditionalErrorVisible && (
|
107
|
-
<div className="conditional-error-message text-warning mt-2">
|
108
|
-
<div className="description-icon">
|
109
|
-
<FaExclamationTriangle />
|
110
|
-
</div>
|
111
|
-
<div className="description-text">
|
112
|
-
<span>{conditionalErrorMessage}</span>
|
113
|
-
</div>
|
114
|
-
</div>
|
115
|
-
)}
|
116
|
-
|
117
|
-
{description && (
|
118
|
-
<div className="form-description text-secondary mt-2">
|
119
|
-
<div className="description-icon">
|
120
|
-
<FaInfoCircle />
|
121
|
-
</div>
|
122
|
-
<div className="description-text">
|
123
|
-
<span>{description}</span>
|
124
|
-
</div>
|
125
|
-
</div>
|
126
|
-
)}
|
127
|
-
</div>
|
128
|
-
</div>
|
129
|
-
</div>
|
130
|
-
);
|
131
|
-
};
|
132
|
-
|
133
|
-
|
134
|
-
export default CustomSelect;
|
@@ -1,199 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
|
3
|
-
enum TextType {
|
4
|
-
BOLDHEAD = 'boldhead',
|
5
|
-
HEADING = 'heading',
|
6
|
-
SUBHEADING = 'subheading',
|
7
|
-
PARAGRAPH = 'paragraph',
|
8
|
-
|
9
|
-
}
|
10
|
-
|
11
|
-
enum TextAlign {
|
12
|
-
START = 'start',
|
13
|
-
CENTER = 'center',
|
14
|
-
END = 'end',
|
15
|
-
}
|
16
|
-
|
17
|
-
enum FontSize {
|
18
|
-
XXXXL = '96px',
|
19
|
-
XXXL = '72px',
|
20
|
-
XXL = '48px',
|
21
|
-
XL = '32px',
|
22
|
-
L = '24px',
|
23
|
-
M = '20px',
|
24
|
-
S = '18px',
|
25
|
-
XS = '16px',
|
26
|
-
XXS = '14px',
|
27
|
-
XXXS = '12px',
|
28
|
-
XXXXS = '10px',
|
29
|
-
}
|
30
|
-
|
31
|
-
enum LineHeight {
|
32
|
-
XXL = '5',
|
33
|
-
XL = '4',
|
34
|
-
L = '3',
|
35
|
-
M = '2.5',
|
36
|
-
S = '2',
|
37
|
-
XS = '1.5',
|
38
|
-
XXS = '1',
|
39
|
-
}
|
40
|
-
|
41
|
-
enum FontWeight {
|
42
|
-
BOLD = '800',
|
43
|
-
NORMAL = '600',
|
44
|
-
THIN = '400',
|
45
|
-
}
|
46
|
-
|
47
|
-
enum Color {
|
48
|
-
DEFAULT = "currentColor",
|
49
|
-
PRIMARY = 'text-primary',
|
50
|
-
SECONDARY = 'text-secondary',
|
51
|
-
SUCCESS = 'text-success',
|
52
|
-
WARNING = 'text-warning',
|
53
|
-
DANGER = 'text-danger',
|
54
|
-
INFO = 'text-info',
|
55
|
-
DARK = 'text-dark',
|
56
|
-
LIGHT = 'text-light',
|
57
|
-
}
|
58
|
-
|
59
|
-
interface CustomTextProps {
|
60
|
-
id?: string;
|
61
|
-
value: string;
|
62
|
-
className?: string;
|
63
|
-
style?: React.CSSProperties;
|
64
|
-
textType?: TextType;
|
65
|
-
color?: Color;
|
66
|
-
textAlign?: TextAlign;
|
67
|
-
fontSize?: FontSize;
|
68
|
-
lineHeight?: LineHeight;
|
69
|
-
fontWeight?: FontWeight;
|
70
|
-
underline?: boolean;
|
71
|
-
overline?: boolean;
|
72
|
-
linethrough?: boolean;
|
73
|
-
italic?: boolean;
|
74
|
-
textAlignClass?: string;
|
75
|
-
linkText?: string;
|
76
|
-
}
|
77
|
-
|
78
|
-
const CustomText: React.FC<CustomTextProps> = ({
|
79
|
-
id,
|
80
|
-
value,
|
81
|
-
className = '',
|
82
|
-
style = {},
|
83
|
-
textType = TextType.PARAGRAPH, // Varsayılan olarak 'paragraph'
|
84
|
-
color,
|
85
|
-
textAlign,
|
86
|
-
fontSize,
|
87
|
-
lineHeight,
|
88
|
-
fontWeight,
|
89
|
-
underline = false,
|
90
|
-
overline = false,
|
91
|
-
linethrough = false,
|
92
|
-
italic = false,
|
93
|
-
textAlignClass,
|
94
|
-
linkText,
|
95
|
-
}) => {
|
96
|
-
// Varsayılan stiller
|
97
|
-
const defaultStyles: Record<TextType, React.CSSProperties> = {
|
98
|
-
[TextType.BOLDHEAD]: {
|
99
|
-
fontSize: FontSize.XL,
|
100
|
-
lineHeight: LineHeight.S,
|
101
|
-
fontWeight: FontWeight.BOLD,
|
102
|
-
},
|
103
|
-
[TextType.HEADING]: {
|
104
|
-
fontSize: FontSize.XL,
|
105
|
-
lineHeight: LineHeight.S,
|
106
|
-
fontWeight: FontWeight.NORMAL,
|
107
|
-
},
|
108
|
-
[TextType.SUBHEADING]: {
|
109
|
-
fontSize: FontSize.L,
|
110
|
-
lineHeight: LineHeight.XS,
|
111
|
-
fontWeight: FontWeight.NORMAL,
|
112
|
-
},
|
113
|
-
[TextType.PARAGRAPH]: {
|
114
|
-
fontSize: FontSize.XS,
|
115
|
-
lineHeight: LineHeight.XXS,
|
116
|
-
fontWeight: FontWeight.THIN,
|
117
|
-
},
|
118
|
-
};
|
119
|
-
|
120
|
-
// Dinamik sınıf ekleme
|
121
|
-
if (textType == TextType.BOLDHEAD && !color) {
|
122
|
-
color = Color.DARK;
|
123
|
-
} else if (textType == TextType.HEADING && !color) {
|
124
|
-
color = Color.DARK;
|
125
|
-
} else if (textType == TextType.SUBHEADING && !color) {
|
126
|
-
color = Color.DARK;
|
127
|
-
} else if (textType == TextType.PARAGRAPH && !color) {
|
128
|
-
color = Color.DEFAULT;
|
129
|
-
}
|
130
|
-
|
131
|
-
if (textAlign == TextAlign.START) {
|
132
|
-
textAlignClass = "text-start";
|
133
|
-
} else if (textAlign == TextAlign.CENTER) {
|
134
|
-
textAlignClass = "text-center";
|
135
|
-
} else if (textAlign == TextAlign.END) {
|
136
|
-
textAlignClass = "text-end";
|
137
|
-
}
|
138
|
-
|
139
|
-
// Text decoration birleştirme
|
140
|
-
const textDecoration = [
|
141
|
-
underline ? 'underline' : '',
|
142
|
-
overline ? 'overline' : '',
|
143
|
-
linethrough ? 'line-through' : '',
|
144
|
-
]
|
145
|
-
.filter(Boolean)
|
146
|
-
.join(' ');
|
147
|
-
|
148
|
-
// Varsayılan stil ile kullanıcı stilini birleştir
|
149
|
-
const combinedStyle: React.CSSProperties = {
|
150
|
-
...defaultStyles[textType],
|
151
|
-
textAlign: textAlign ?? defaultStyles[textType].textAlign,
|
152
|
-
fontSize: fontSize ?? defaultStyles[textType].fontSize,
|
153
|
-
lineHeight: lineHeight ?? defaultStyles[textType].lineHeight,
|
154
|
-
fontWeight: fontWeight ?? defaultStyles[textType].fontWeight,
|
155
|
-
textDecoration: textDecoration || undefined,
|
156
|
-
fontStyle: italic ? 'italic' : undefined,
|
157
|
-
...style, // Kullanıcıdan gelen stil
|
158
|
-
};
|
159
|
-
|
160
|
-
const combinedClassName = `${color} ${className}`.trim();
|
161
|
-
|
162
|
-
// URL'leri tıklanabilir hale getirme
|
163
|
-
const renderWithLinks = (text: string, linkText?: string) => {
|
164
|
-
const urlRegex = /(https?:\/\/[^\s]+|www\.[^\s]+)/g; // URL'leri algılayan RegEx
|
165
|
-
const parts = text.split(urlRegex);
|
166
|
-
|
167
|
-
return parts.map((part, index) =>
|
168
|
-
urlRegex.test(part) ? (
|
169
|
-
<a
|
170
|
-
key={index}
|
171
|
-
href={part.startsWith('http') ? part : `https://${part}`} // Eğer http yoksa başına ekle
|
172
|
-
target="_blank"
|
173
|
-
rel="noopener noreferrer"
|
174
|
-
>
|
175
|
-
{linkText || part.replace(/https?:\/\/|www\.|\/$/g, '')} {/* linkText varsa kullan */}
|
176
|
-
</a>
|
177
|
-
) : (
|
178
|
-
part
|
179
|
-
)
|
180
|
-
);
|
181
|
-
};
|
182
|
-
|
183
|
-
return (
|
184
|
-
<div
|
185
|
-
className={`${textAlignClass}`} //TextAlign sınıf olarak ekleniyor
|
186
|
-
>
|
187
|
-
<span
|
188
|
-
id={id || `custom-text-${Date.now()}`}
|
189
|
-
className={`custom-text ${combinedClassName}`} // Color sınıf olarak ekleniyor
|
190
|
-
style={combinedStyle} // Geri kalan stiller
|
191
|
-
>
|
192
|
-
{renderWithLinks(value, linkText)} {/* URL'ler işlenerek render ediliyor */}
|
193
|
-
</span>
|
194
|
-
</div>
|
195
|
-
);
|
196
|
-
};
|
197
|
-
|
198
|
-
export { CustomText, TextType, TextAlign, FontSize, LineHeight, FontWeight, Color };
|
199
|
-
export default CustomText;
|
@@ -1,131 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import { OverlayTrigger, Tooltip } from 'react-bootstrap';
|
3
|
-
import { FaExclamationTriangle, FaInfoCircle } from 'react-icons/fa';
|
4
|
-
|
5
|
-
interface CustomTextareaProps {
|
6
|
-
id?: string;
|
7
|
-
name?: string;
|
8
|
-
label: string;
|
9
|
-
value: string;
|
10
|
-
placeholder?: string;
|
11
|
-
required?: boolean;
|
12
|
-
errorMessage?: string;
|
13
|
-
conditionalErrorVisible?: boolean;
|
14
|
-
conditionalErrorMessage?: string;
|
15
|
-
disabled?: boolean;
|
16
|
-
readOnly?: boolean;
|
17
|
-
rows?: number;
|
18
|
-
description?: string | null;
|
19
|
-
tooltip?: string;
|
20
|
-
style?: React.CSSProperties;
|
21
|
-
className?: string;
|
22
|
-
onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
|
23
|
-
}
|
24
|
-
|
25
|
-
const CustomTextarea: React.FC<CustomTextareaProps> = ({
|
26
|
-
id,
|
27
|
-
name,
|
28
|
-
label,
|
29
|
-
value,
|
30
|
-
placeholder,
|
31
|
-
required = false,
|
32
|
-
errorMessage,
|
33
|
-
conditionalErrorVisible = false,
|
34
|
-
conditionalErrorMessage,
|
35
|
-
disabled = false,
|
36
|
-
readOnly = false,
|
37
|
-
rows = 3,
|
38
|
-
description,
|
39
|
-
tooltip,
|
40
|
-
style,
|
41
|
-
className,
|
42
|
-
onChange,
|
43
|
-
}) => {
|
44
|
-
return (
|
45
|
-
<div className={className}>
|
46
|
-
<div className="form-group">
|
47
|
-
<label htmlFor={id} className="form-label">
|
48
|
-
{label}
|
49
|
-
</label>
|
50
|
-
<div className="position-relative">
|
51
|
-
{tooltip ? (
|
52
|
-
<OverlayTrigger
|
53
|
-
placement="bottom"
|
54
|
-
overlay={<Tooltip id={`tooltip-${tooltip}`}>{tooltip}</Tooltip>}
|
55
|
-
>
|
56
|
-
<textarea
|
57
|
-
id={id}
|
58
|
-
name={name}
|
59
|
-
value={value}
|
60
|
-
placeholder={placeholder}
|
61
|
-
onChange={onChange}
|
62
|
-
className="form-control"
|
63
|
-
style={style}
|
64
|
-
required={required}
|
65
|
-
disabled={disabled}
|
66
|
-
readOnly={readOnly}
|
67
|
-
rows={rows}
|
68
|
-
/>
|
69
|
-
</OverlayTrigger>
|
70
|
-
) : (
|
71
|
-
<textarea
|
72
|
-
id={id}
|
73
|
-
name={name}
|
74
|
-
value={value}
|
75
|
-
placeholder={placeholder}
|
76
|
-
onChange={onChange}
|
77
|
-
className="form-control"
|
78
|
-
style={style}
|
79
|
-
required={required}
|
80
|
-
disabled={disabled}
|
81
|
-
readOnly={readOnly}
|
82
|
-
rows={rows}
|
83
|
-
/>
|
84
|
-
)}
|
85
|
-
|
86
|
-
{required && (
|
87
|
-
<div className="invalid-feedback text-danger">
|
88
|
-
<div className="description-icon">
|
89
|
-
<FaExclamationTriangle />
|
90
|
-
</div>
|
91
|
-
<div className="description-text">
|
92
|
-
<span>
|
93
|
-
{errorMessage || 'Bu alan boş bırakılamaz.'}
|
94
|
-
</span>
|
95
|
-
</div>
|
96
|
-
</div>
|
97
|
-
)}
|
98
|
-
|
99
|
-
{conditionalErrorVisible && (
|
100
|
-
<div className="conditional-error-message text-warning">
|
101
|
-
<div className="description-icon">
|
102
|
-
<FaExclamationTriangle />
|
103
|
-
</div>
|
104
|
-
<div className="description-text">
|
105
|
-
<span>
|
106
|
-
{conditionalErrorMessage}
|
107
|
-
</span>
|
108
|
-
</div>
|
109
|
-
</div>
|
110
|
-
)}
|
111
|
-
|
112
|
-
{description && (
|
113
|
-
<div className="form-description text-secondary">
|
114
|
-
<div className="description-icon">
|
115
|
-
<FaInfoCircle />
|
116
|
-
</div>
|
117
|
-
<div className="description-text">
|
118
|
-
<span>
|
119
|
-
{description}
|
120
|
-
</span>
|
121
|
-
</div>
|
122
|
-
</div>
|
123
|
-
)}
|
124
|
-
|
125
|
-
</div>
|
126
|
-
</div>
|
127
|
-
</div>
|
128
|
-
);
|
129
|
-
};
|
130
|
-
|
131
|
-
export default CustomTextarea;
|
@@ -1,125 +0,0 @@
|
|
1
|
-
import React, { useState } from 'react';
|
2
|
-
import { FaCloudUploadAlt, FaTimes } from 'react-icons/fa';
|
3
|
-
import { toast } from 'react-toastify';
|
4
|
-
|
5
|
-
export enum UploadError {
|
6
|
-
INVALID_TYPE = 'INVALID_TYPE',
|
7
|
-
EXCEEDS_SIZE = 'EXCEEDS_SIZE',
|
8
|
-
}
|
9
|
-
|
10
|
-
interface FileInfo {
|
11
|
-
name: string;
|
12
|
-
size: number; // bytes
|
13
|
-
type: string;
|
14
|
-
}
|
15
|
-
|
16
|
-
interface CustomUploaderProps {
|
17
|
-
id: string;
|
18
|
-
label: string;
|
19
|
-
fileTypes: string[]; // Yüklenebilir dosya türleri
|
20
|
-
maxFileSize: number; // Maksimum dosya boyutu (MB)
|
21
|
-
multiple?: boolean; // Çoklu dosya yükleme seçeneği
|
22
|
-
required?: boolean;
|
23
|
-
description?: string;
|
24
|
-
tooltip?: string;
|
25
|
-
value?: FileInfo[]; // Yüklü dosyalar
|
26
|
-
onChange: (files: FileInfo[]) => void;
|
27
|
-
onError?: (errorType: UploadError, file?: File) => void; // Hata işleyici
|
28
|
-
}
|
29
|
-
|
30
|
-
const CustomUploader: React.FC<CustomUploaderProps> = ({
|
31
|
-
id,
|
32
|
-
label,
|
33
|
-
fileTypes,
|
34
|
-
maxFileSize,
|
35
|
-
multiple = false,
|
36
|
-
required = false,
|
37
|
-
description,
|
38
|
-
tooltip,
|
39
|
-
value = [],
|
40
|
-
onChange,
|
41
|
-
onError,
|
42
|
-
}) => {
|
43
|
-
const [files, setFiles] = useState<FileInfo[]>(value);
|
44
|
-
|
45
|
-
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
46
|
-
const selectedFiles = Array.from(e.target.files || []);
|
47
|
-
const newFiles: FileInfo[] = [];
|
48
|
-
const errors: { type: UploadError; file?: File }[] = [];
|
49
|
-
|
50
|
-
selectedFiles.forEach((file) => {
|
51
|
-
const fileType = file.type;
|
52
|
-
const fileSizeMB = file.size / (1024 * 1024);
|
53
|
-
|
54
|
-
if (!fileTypes.includes(fileType)) {
|
55
|
-
errors.push({ type: UploadError.INVALID_TYPE, file });
|
56
|
-
onError?.(UploadError.INVALID_TYPE, file);
|
57
|
-
} else if (fileSizeMB > maxFileSize) {
|
58
|
-
errors.push({ type: UploadError.EXCEEDS_SIZE, file });
|
59
|
-
onError?.(UploadError.EXCEEDS_SIZE, file);
|
60
|
-
} else {
|
61
|
-
newFiles.push({
|
62
|
-
name: file.name,
|
63
|
-
size: file.size,
|
64
|
-
type: file.type,
|
65
|
-
});
|
66
|
-
}
|
67
|
-
});
|
68
|
-
|
69
|
-
if (errors.length > 0) {
|
70
|
-
errors.forEach(({ type, file }) => {
|
71
|
-
const fileName = file?.name || 'Unknown';
|
72
|
-
if (type === UploadError.INVALID_TYPE) {
|
73
|
-
toast.error(`Invalid file type: ${fileName}`);
|
74
|
-
} else if (type === UploadError.EXCEEDS_SIZE) {
|
75
|
-
toast.error(`File exceeds size limit: ${fileName}`);
|
76
|
-
}
|
77
|
-
});
|
78
|
-
}
|
79
|
-
|
80
|
-
const updatedFiles = multiple ? [...files, ...newFiles] : newFiles;
|
81
|
-
setFiles(updatedFiles);
|
82
|
-
onChange(updatedFiles);
|
83
|
-
};
|
84
|
-
|
85
|
-
const handleRemoveFile = (fileName: string) => {
|
86
|
-
const updatedFiles = files.filter((file) => file.name !== fileName);
|
87
|
-
setFiles(updatedFiles);
|
88
|
-
onChange(updatedFiles);
|
89
|
-
};
|
90
|
-
|
91
|
-
return (
|
92
|
-
<div className="form-group">
|
93
|
-
<label htmlFor={id} className="form-label">
|
94
|
-
{label} {tooltip && <span className="ms-2" title={tooltip}>ℹ️</span>}
|
95
|
-
</label>
|
96
|
-
<div className="uploader-container">
|
97
|
-
<input
|
98
|
-
id={id}
|
99
|
-
type="file"
|
100
|
-
multiple={multiple}
|
101
|
-
onChange={handleFileChange}
|
102
|
-
className="form-control"
|
103
|
-
/>
|
104
|
-
{description && <small className="form-text text-muted">{description}</small>}
|
105
|
-
{required && files.length === 0 && (
|
106
|
-
<div className="invalid-feedback">This field is required.</div>
|
107
|
-
)}
|
108
|
-
<div className="file-preview mt-3">
|
109
|
-
{files.map((file) => (
|
110
|
-
<div key={file.name} className="file-item">
|
111
|
-
<FaCloudUploadAlt className="me-2" />
|
112
|
-
<span>{file.name} ({(file.size / (1024 * 1024)).toFixed(2)} MB)</span>
|
113
|
-
<FaTimes
|
114
|
-
className="remove-icon text-danger ms-3"
|
115
|
-
onClick={() => handleRemoveFile(file.name)}
|
116
|
-
/>
|
117
|
-
</div>
|
118
|
-
))}
|
119
|
-
</div>
|
120
|
-
</div>
|
121
|
-
</div>
|
122
|
-
);
|
123
|
-
};
|
124
|
-
|
125
|
-
export default CustomUploader;
|