tee3apps-cms-sdk-react 0.0.4 → 0.0.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.
@@ -0,0 +1,183 @@
1
+ import React, { useState, useRef, useEffect } from 'react';
2
+ import type { ComponentProps } from '../types';
3
+ import './Styles/SelectField.css';
4
+
5
+ interface SelectFieldProps {
6
+ props: ComponentProps;
7
+ value?: any;
8
+ onChange?: (code: string, value: any) => void;
9
+ }
10
+
11
+ const SelectField: React.FC<SelectFieldProps> = ({ props, value, onChange }) => {
12
+ const [isOpen, setIsOpen] = useState(false);
13
+ const [searchTerm, setSearchTerm] = useState('');
14
+ const dropdownRef = useRef<HTMLDivElement>(null);
15
+ const isMultiSelect = props.multiSelect || false;
16
+ const selectedValues = isMultiSelect ? (Array.isArray(value) ? value : []) : (value ? [value] : []);
17
+
18
+ // Get options from LOV
19
+ const options = props.lov || [];
20
+
21
+ // Filter options based on search term
22
+ const filteredOptions = options.filter(option =>
23
+ option.name.toLowerCase().includes(searchTerm.toLowerCase())
24
+ );
25
+
26
+ // Get selected option(s) display text
27
+ const getSelectedDisplay = () => {
28
+ if (isMultiSelect) {
29
+ if (selectedValues.length === 0) {
30
+ return props.helperText || 'Select...';
31
+ }
32
+ if (selectedValues.length === 1) {
33
+ const selected = options.find(opt => opt.code === selectedValues[0]);
34
+ return selected ? selected.name : selectedValues[0];
35
+ }
36
+ return `${selectedValues.length} selected`;
37
+ } else {
38
+ if (!value) return props.helperText || 'Select...';
39
+ const selected = options.find(opt => opt.code === value);
40
+ return selected ? selected.name : value;
41
+ }
42
+ };
43
+
44
+ const handleToggle = () => {
45
+ setIsOpen(!isOpen);
46
+ setSearchTerm('');
47
+ };
48
+
49
+ const handleSelect = (optionCode: string) => {
50
+ if (!onChange) return;
51
+
52
+ const code = props.code || `selectfield_${props.name?.all || 'field'}`;
53
+
54
+ if (isMultiSelect) {
55
+ const newValues = selectedValues.includes(optionCode)
56
+ ? selectedValues.filter(v => v !== optionCode)
57
+ : [...selectedValues, optionCode];
58
+ onChange(code, newValues);
59
+ } else {
60
+ onChange(code, optionCode);
61
+ setIsOpen(false);
62
+ }
63
+ };
64
+
65
+ const handleRemove = (optionCode: string, e: React.MouseEvent) => {
66
+ e.stopPropagation();
67
+ if (!onChange || !isMultiSelect) return;
68
+
69
+ const code = props.code || `selectfield_${props.name?.all || 'field'}`;
70
+ const newValues = selectedValues.filter(v => v !== optionCode);
71
+ onChange(code, newValues);
72
+ };
73
+
74
+ // Close dropdown when clicking outside
75
+ useEffect(() => {
76
+ const handleClickOutside = (event: MouseEvent) => {
77
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
78
+ setIsOpen(false);
79
+ setSearchTerm('');
80
+ }
81
+ };
82
+
83
+ document.addEventListener('mousedown', handleClickOutside);
84
+ return () => {
85
+ document.removeEventListener('mousedown', handleClickOutside);
86
+ };
87
+ }, []);
88
+
89
+ return (
90
+ <div className="select-field">
91
+ <label className="select-field__label">
92
+ {props.name?.all || 'Select Field'}
93
+ {props.required && <span className="select-field__required">*</span>}
94
+ </label>
95
+
96
+ <div className="select-field__wrapper" ref={dropdownRef}>
97
+ <div
98
+ className={`select-field__control ${isOpen ? 'select-field__control--is-focused' : ''} ${props.required && selectedValues.length === 0 ? 'select-field__control--is-invalid' : ''}`}
99
+ onClick={handleToggle}
100
+ >
101
+ <div className="select-field__value-container">
102
+ {isMultiSelect && selectedValues.length > 0 ? (
103
+ <div className="select-field__multi-value-container">
104
+ {selectedValues.map((val) => {
105
+ const option = options.find(opt => opt.code === val);
106
+ return (
107
+ <div key={val} className="select-field__multi-value">
108
+ <span className="select-field__multi-value__label">{option?.name || val}</span>
109
+ <span
110
+ className="select-field__multi-value__remove"
111
+ onClick={(e) => handleRemove(val, e)}
112
+ >
113
+ ×
114
+ </span>
115
+ </div>
116
+ );
117
+ })}
118
+ </div>
119
+ ) : (
120
+ <div className="select-field__single-value">{getSelectedDisplay()}</div>
121
+ )}
122
+ </div>
123
+ <div className="select-field__indicators">
124
+ <span className={`select-field__indicator ${isOpen ? 'select-field__indicator--open' : ''}`}>
125
+
126
+ </span>
127
+ </div>
128
+ </div>
129
+
130
+ {isOpen && (
131
+ <div className="select-field__menu">
132
+ {options.length > 5 && (
133
+ <div className="select-field__menu-search">
134
+ <input
135
+ type="text"
136
+ className="select-field__search-input"
137
+ placeholder="Search..."
138
+ value={searchTerm}
139
+ onChange={(e) => setSearchTerm(e.target.value)}
140
+ onClick={(e) => e.stopPropagation()}
141
+ />
142
+ </div>
143
+ )}
144
+ <div className="select-field__menu-list">
145
+ {filteredOptions.length === 0 ? (
146
+ <div className="select-field__option select-field__option--no-results">
147
+ No options found
148
+ </div>
149
+ ) : (
150
+ filteredOptions.map((option) => {
151
+ const isSelected = isMultiSelect
152
+ ? selectedValues.includes(option.code)
153
+ : value === option.code;
154
+
155
+ return (
156
+ <div
157
+ key={option.id}
158
+ className={`select-field__option ${isSelected ? 'select-field__option--is-selected' : ''}`}
159
+ onClick={() => handleSelect(option.code)}
160
+ >
161
+ {isMultiSelect && (
162
+ <span className="select-field__checkbox">
163
+ {isSelected ? '✓' : ''}
164
+ </span>
165
+ )}
166
+ <span>{option.name}</span>
167
+ </div>
168
+ );
169
+ })
170
+ )}
171
+ </div>
172
+ </div>
173
+ )}
174
+ </div>
175
+
176
+ {props.helperText && !isOpen && (
177
+ <div className="select-field__helper-text">{props.helperText}</div>
178
+ )}
179
+ </div>
180
+ );
181
+ };
182
+
183
+ export default SelectField;
@@ -0,0 +1,56 @@
1
+ .boolean-field {
2
+ margin-bottom: 1.5rem;
3
+ }
4
+
5
+ .boolean-field__container {
6
+ display: flex;
7
+ align-items: flex-start;
8
+ gap: 0.75rem;
9
+ }
10
+
11
+ .boolean-field__checkbox-wrapper {
12
+ display: flex;
13
+ align-items: center;
14
+ height: 1.25rem;
15
+ }
16
+
17
+ .boolean-field__checkbox {
18
+ width: 1rem;
19
+ height: 1rem;
20
+ border: 1px solid #d1d5db; /* gray-300 */
21
+ border-radius: 0.25rem;
22
+ accent-color: #2563eb; /* blue-600 */
23
+ }
24
+
25
+ .boolean-field__content {
26
+ flex: 1;
27
+ }
28
+
29
+ .boolean-field__label {
30
+ font-size: 0.875rem;
31
+ font-weight: 500;
32
+ color: #374151; /* gray-700 */
33
+ }
34
+
35
+ .boolean-field__required {
36
+ color: #ef4444; /* red-500 */
37
+ margin-left: 0.25rem;
38
+ }
39
+
40
+ .boolean-field__terms {
41
+ margin-top: 0.25rem;
42
+ font-size: 0.75rem;
43
+ color: #4b5563; /* gray-600 */
44
+ }
45
+
46
+ .boolean-field__helper-text {
47
+ margin-top: 0.25rem;
48
+ font-size: 0.75rem;
49
+ color: #6b7280; /* gray-500 */
50
+ }
51
+
52
+ .boolean-field__status-text {
53
+ margin-top: 0.25rem;
54
+ font-size: 0.75rem;
55
+ color: #9ca3af; /* gray-400 */
56
+ }
@@ -0,0 +1,44 @@
1
+ .date-field {
2
+ margin-bottom: 1.5rem;
3
+ }
4
+
5
+ .date-field__label {
6
+ display: block;
7
+ font-size: 0.875rem;
8
+ font-weight: 500;
9
+ color: #374151; /* gray-700 */
10
+ margin-bottom: 0.5rem;
11
+ }
12
+
13
+ .date-field__required {
14
+ color: #ef4444; /* red-500 */
15
+ margin-left: 0.25rem;
16
+ }
17
+
18
+ .date-field__input {
19
+ width: 99%;
20
+ padding: 0.5rem 0.75rem;
21
+ font-size: 1rem;
22
+ border: 1px solid #d1d5db; /* gray-300 */
23
+ border-radius: 0.375rem;
24
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
25
+ outline: none;
26
+ transition: border-color 0.2s, box-shadow 0.2s;
27
+ }
28
+
29
+ .date-field__input:focus {
30
+ border-color: #3b82f6; /* blue-500 */
31
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.5); /* blue-500 ring */
32
+ }
33
+
34
+ .date-field__helper-text {
35
+ margin-top: 0.25rem;
36
+ font-size: 0.75rem;
37
+ color: #6b7280; /* gray-500 */
38
+ }
39
+
40
+ .date-field__pattern {
41
+ margin-top: 0.25rem;
42
+ font-size: 0.75rem;
43
+ color: #9ca3af; /* gray-400 */
44
+ }
@@ -0,0 +1,50 @@
1
+ .input-field {
2
+ margin-bottom: 1.5rem;
3
+ }
4
+
5
+ .input-field__label {
6
+ display: block;
7
+ font-size: 0.875rem;
8
+ font-weight: 500;
9
+ color: #374151; /* gray-700 */
10
+ margin-bottom: 0.5rem;
11
+ }
12
+
13
+ .input-field__required {
14
+ color: #ef4444; /* red-500 */
15
+ margin-left: 0.25rem;
16
+ }
17
+
18
+ .input-field__input,
19
+ .input-field__textarea {
20
+ width: 99%;
21
+ padding: 0.5rem 0.75rem;
22
+ font-size: 1rem;
23
+ border: 1px solid #d1d5db; /* gray-300 */
24
+ border-radius: 0.375rem;
25
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
26
+ outline: none;
27
+ transition: border-color 0.2s, box-shadow 0.2s;
28
+ }
29
+
30
+ .input-field__input:focus,
31
+ .input-field__textarea:focus {
32
+ border-color: #3b82f6; /* blue-500 */
33
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.5);
34
+ }
35
+
36
+ .input-field__textarea {
37
+ resize: vertical;
38
+ }
39
+
40
+ .input-field__helper-text {
41
+ margin-top: 0.25rem;
42
+ font-size: 0.75rem;
43
+ color: #6b7280; /* gray-500 */
44
+ }
45
+
46
+ .input-field__status {
47
+ margin-top: 0.25rem;
48
+ font-size: 0.75rem;
49
+ color: #9ca3af; /* gray-400 */
50
+ }
@@ -0,0 +1,57 @@
1
+ .number-field {
2
+ margin-bottom: 1.5rem;
3
+ }
4
+
5
+ .number-field__label {
6
+ display: block;
7
+ font-size: 0.875rem;
8
+ font-weight: 500;
9
+ color: #374151; /* gray-700 */
10
+ margin-bottom: 0.5rem;
11
+ }
12
+
13
+ .number-field__required {
14
+ color: #ef4444; /* red-500 */
15
+ margin-left: 0.25rem;
16
+ }
17
+
18
+ .number-field__input-wrapper {
19
+ position: relative;
20
+ }
21
+
22
+ .number-field__input {
23
+ width: 100%;
24
+ padding: 0.5rem 2.75rem 0.5rem 0.75rem;
25
+ font-size: 1rem;
26
+ border: 1px solid #d1d5db; /* gray-300 */
27
+ border-radius: 0.375rem;
28
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
29
+ outline: none;
30
+ transition: border-color 0.2s, box-shadow 0.2s;
31
+ }
32
+
33
+ .number-field__input:focus {
34
+ border-color: #3b82f6; /* blue-500 */
35
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.5);
36
+ }
37
+
38
+ .number-field__suffix {
39
+ position: absolute;
40
+ right: 0.75rem;
41
+ top: 0.5rem;
42
+ font-size: 0.875rem;
43
+ color: #6b7280; /* gray-500 */
44
+ pointer-events: none;
45
+ }
46
+
47
+ .number-field__helper-text {
48
+ margin-top: 0.25rem;
49
+ font-size: 0.75rem;
50
+ color: #6b7280; /* gray-500 */
51
+ }
52
+
53
+ .number-field__status {
54
+ margin-top: 0.25rem;
55
+ font-size: 0.75rem;
56
+ color: #9ca3af; /* gray-400 */
57
+ }
@@ -0,0 +1,66 @@
1
+ .radio-field {
2
+ margin-bottom: 1.5rem;
3
+ }
4
+
5
+ .radio-field__label {
6
+ display: block;
7
+ font-size: 0.875rem;
8
+ font-weight: 500;
9
+ color: #374151; /* gray-700 */
10
+ margin-bottom: 0.5rem;
11
+ }
12
+
13
+ .radio-field__required {
14
+ color: #ef4444; /* red-500 */
15
+ margin-left: 0.25rem;
16
+ }
17
+
18
+ .radio-field__options {
19
+ display: flex;
20
+ flex-direction: column;
21
+ gap: 0.5rem;
22
+ }
23
+
24
+ .radio-field__option {
25
+ display: flex;
26
+ align-items: center;
27
+ gap: 0.5rem;
28
+ cursor: pointer;
29
+ padding: 0.5rem;
30
+ border-radius: 0.375rem;
31
+ transition: background-color 0.2s ease;
32
+ }
33
+
34
+ .radio-field__option:hover {
35
+ background-color: #f9fafb; /* gray-50 */
36
+ }
37
+
38
+ .radio-field__input {
39
+ width: 1rem;
40
+ height: 1rem;
41
+ accent-color: #2563eb; /* blue-600 */
42
+ }
43
+
44
+ .radio-field__option-label {
45
+ font-size: 0.875rem;
46
+ color: #374151; /* gray-700 */
47
+ }
48
+
49
+ .radio-field__empty {
50
+ font-size: 0.75rem;
51
+ color: #9ca3af; /* gray-400 */
52
+ font-style: italic;
53
+ padding: 0.5rem 0;
54
+ }
55
+
56
+ .radio-field__helper-text {
57
+ margin-top: 0.25rem;
58
+ font-size: 0.75rem;
59
+ color: #6b7280; /* gray-500 */
60
+ }
61
+
62
+ .radio-field__status {
63
+ margin-top: 0.25rem;
64
+ font-size: 0.75rem;
65
+ color: #9ca3af; /* gray-400 */
66
+ }
@@ -0,0 +1,264 @@
1
+ .select-field {
2
+ margin-bottom: 1.5rem;
3
+ position: relative;
4
+ }
5
+
6
+ .select-field__label {
7
+ display: block;
8
+ font-size: 0.875rem;
9
+ font-weight: 500;
10
+ color: #374151;
11
+ margin-bottom: 0.5rem;
12
+ }
13
+
14
+ .select-field__required {
15
+ color: #ef4444;
16
+ margin-left: 0.25rem;
17
+ }
18
+
19
+ .select-field__wrapper {
20
+ position: relative;
21
+ }
22
+
23
+ /* Control (main select box) */
24
+ .select-field__control {
25
+ min-height: 38px;
26
+ display: flex;
27
+ align-items: center;
28
+ justify-content: space-between;
29
+ width: 100%;
30
+ padding: 2px 8px;
31
+ border: 1px solid #d1d5db;
32
+ border-radius: 4px;
33
+ background-color: #ffffff;
34
+ cursor: pointer;
35
+ transition: all 0.2s ease;
36
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
37
+ }
38
+
39
+ .select-field__control:hover {
40
+ border-color: #9ca3af;
41
+ }
42
+
43
+ .select-field__control--is-focused {
44
+ border-color: #3b82f6;
45
+ box-shadow: 0 0 0 1px #3b82f6;
46
+ outline: none;
47
+ }
48
+
49
+ .select-field__control--is-invalid {
50
+ border-color: #ef4444;
51
+ }
52
+
53
+ /* Value Container */
54
+ .select-field__value-container {
55
+ flex: 1;
56
+ display: flex;
57
+ align-items: center;
58
+ flex-wrap: wrap;
59
+ gap: 4px;
60
+ padding: 2px 0;
61
+ min-height: 32px;
62
+ }
63
+
64
+ .select-field__single-value {
65
+ color: #1f2937;
66
+ font-size: 14px;
67
+ line-height: 1.5;
68
+ white-space: nowrap;
69
+ overflow: hidden;
70
+ text-overflow: ellipsis;
71
+ }
72
+
73
+ /* Multi-value container */
74
+ .select-field__multi-value-container {
75
+ display: flex;
76
+ flex-wrap: wrap;
77
+ gap: 4px;
78
+ width: 100%;
79
+ }
80
+
81
+ .select-field__multi-value {
82
+ display: flex;
83
+ align-items: center;
84
+ background-color: #e5e7eb;
85
+ border-radius: 2px;
86
+ padding: 2px 6px;
87
+ font-size: 13px;
88
+ line-height: 1.5;
89
+ max-width: 100%;
90
+ }
91
+
92
+ .select-field__multi-value__label {
93
+ color: #1f2937;
94
+ margin-right: 4px;
95
+ white-space: nowrap;
96
+ overflow: hidden;
97
+ text-overflow: ellipsis;
98
+ max-width: 150px;
99
+ }
100
+
101
+ .select-field__multi-value__remove {
102
+ color: #6b7280;
103
+ cursor: pointer;
104
+ font-size: 18px;
105
+ line-height: 1;
106
+ padding: 0 2px;
107
+ transition: color 0.2s;
108
+ }
109
+
110
+ .select-field__multi-value__remove:hover {
111
+ color: #ef4444;
112
+ }
113
+
114
+ /* Indicators (dropdown arrow) */
115
+ .select-field__indicators {
116
+ display: flex;
117
+ align-items: center;
118
+ padding-left: 8px;
119
+ }
120
+
121
+ .select-field__indicator {
122
+ color: #6b7280;
123
+ font-size: 12px;
124
+ transition: transform 0.2s;
125
+ display: flex;
126
+ align-items: center;
127
+ justify-content: center;
128
+ width: 20px;
129
+ height: 20px;
130
+ }
131
+
132
+ .select-field__indicator--open {
133
+ transform: rotate(180deg);
134
+ }
135
+
136
+ /* Menu (dropdown) */
137
+ .select-field__menu {
138
+ position: absolute;
139
+ top: 100%;
140
+ left: 0;
141
+ right: 0;
142
+ margin-top: 4px;
143
+ background-color: #ffffff;
144
+ border: 1px solid #d1d5db;
145
+ border-radius: 4px;
146
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
147
+ z-index: 1000;
148
+ max-height: 300px;
149
+ overflow: hidden;
150
+ display: flex;
151
+ flex-direction: column;
152
+ }
153
+
154
+ /* Search input */
155
+ .select-field__menu-search {
156
+ padding: 8px;
157
+ border-bottom: 1px solid #e5e7eb;
158
+ }
159
+
160
+ .select-field__search-input {
161
+ width: 100%;
162
+ padding: 6px 8px;
163
+ border: 1px solid #d1d5db;
164
+ border-radius: 4px;
165
+ font-size: 14px;
166
+ outline: none;
167
+ }
168
+
169
+ .select-field__search-input:focus {
170
+ border-color: #3b82f6;
171
+ box-shadow: 0 0 0 1px #3b82f6;
172
+ }
173
+
174
+ /* Menu list */
175
+ .select-field__menu-list {
176
+ overflow-y: auto;
177
+ max-height: 240px;
178
+ padding: 4px 0;
179
+ }
180
+
181
+ /* Option */
182
+ .select-field__option {
183
+ padding: 8px 12px;
184
+ cursor: pointer;
185
+ font-size: 14px;
186
+ color: #1f2937;
187
+ display: flex;
188
+ align-items: center;
189
+ gap: 8px;
190
+ transition: background-color 0.15s;
191
+ }
192
+
193
+ .select-field__option:hover {
194
+ background-color: #f3f4f6;
195
+ }
196
+
197
+ .select-field__option--is-selected {
198
+ background-color: #eff6ff;
199
+ color: #1e40af;
200
+ font-weight: 500;
201
+ }
202
+
203
+ .select-field__option--is-selected:hover {
204
+ background-color: #dbeafe;
205
+ }
206
+
207
+ .select-field__option--no-results {
208
+ padding: 12px;
209
+ text-align: center;
210
+ color: #6b7280;
211
+ font-style: italic;
212
+ cursor: default;
213
+ }
214
+
215
+ .select-field__option--no-results:hover {
216
+ background-color: transparent;
217
+ }
218
+
219
+ /* Checkbox for multi-select */
220
+ .select-field__checkbox {
221
+ display: inline-flex;
222
+ align-items: center;
223
+ justify-content: center;
224
+ width: 16px;
225
+ height: 16px;
226
+ border: 2px solid #d1d5db;
227
+ border-radius: 3px;
228
+ background-color: #ffffff;
229
+ font-size: 12px;
230
+ color: #3b82f6;
231
+ flex-shrink: 0;
232
+ }
233
+
234
+ .select-field__option--is-selected .select-field__checkbox {
235
+ background-color: #3b82f6;
236
+ border-color: #3b82f6;
237
+ color: #ffffff;
238
+ }
239
+
240
+ /* Helper text */
241
+ .select-field__helper-text {
242
+ margin-top: 0.25rem;
243
+ font-size: 0.75rem;
244
+ color: #6b7280;
245
+ }
246
+
247
+ /* Scrollbar styling for menu */
248
+ .select-field__menu-list::-webkit-scrollbar {
249
+ width: 8px;
250
+ }
251
+
252
+ .select-field__menu-list::-webkit-scrollbar-track {
253
+ background: #f9fafb;
254
+ border-radius: 4px;
255
+ }
256
+
257
+ .select-field__menu-list::-webkit-scrollbar-thumb {
258
+ background: #d1d5db;
259
+ border-radius: 4px;
260
+ }
261
+
262
+ .select-field__menu-list::-webkit-scrollbar-thumb:hover {
263
+ background: #9ca3af;
264
+ }