tee3apps-cms-sdk-react 0.0.10 → 0.0.12
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/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/PageFormComponents/BoxRenderer.tsx +75 -10
- package/src/PageFormComponents/Button.tsx +6 -2
- package/src/PageFormComponents/PageForm.tsx +32 -6
- package/src/PageFormComponents/RowComponent.tsx +67 -6
package/package.json
CHANGED
|
@@ -10,6 +10,7 @@ import RadioField from './RadioField';
|
|
|
10
10
|
import SelectField from './SelectField';
|
|
11
11
|
import ImageComponent from './ImageComponent';
|
|
12
12
|
import TermsAndCondition from './TermsAndCondition';
|
|
13
|
+
import { Linodeurl } from '../const';
|
|
13
14
|
|
|
14
15
|
interface BoxRendererProps {
|
|
15
16
|
box: Box;
|
|
@@ -64,7 +65,7 @@ const BoxRenderer: React.FC<BoxRendererProps> = ({
|
|
|
64
65
|
return box.props.mode.tablet || {};
|
|
65
66
|
case 'web':
|
|
66
67
|
default:
|
|
67
|
-
return box.props.mode.web;
|
|
68
|
+
return box.props.mode.web || {};
|
|
68
69
|
}
|
|
69
70
|
};
|
|
70
71
|
|
|
@@ -80,6 +81,58 @@ const BoxRenderer: React.FC<BoxRendererProps> = ({
|
|
|
80
81
|
const currentMode = getCurrentMode();
|
|
81
82
|
const widthPercentage = (colspan / totalColumns) * 100;
|
|
82
83
|
|
|
84
|
+
// Handle content justification with proper CSS values (from BoxComponent)
|
|
85
|
+
const getJustifyContent = (justify: string): React.CSSProperties['justifyContent'] => {
|
|
86
|
+
if (justify === 'initial') return 'initial';
|
|
87
|
+
|
|
88
|
+
const justifyMap: Record<string, React.CSSProperties['justifyContent']> = {
|
|
89
|
+
around: 'space-around',
|
|
90
|
+
between: 'space-between',
|
|
91
|
+
evenly: 'space-evenly',
|
|
92
|
+
start: 'flex-start',
|
|
93
|
+
end: 'flex-end',
|
|
94
|
+
center: 'center',
|
|
95
|
+
'flex-start': 'flex-start',
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
return justifyMap[justify] || justify || 'flex-start';
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// Handle alignment with proper CSS values (from BoxComponent)
|
|
102
|
+
const getAlignItems = (align: string): React.CSSProperties['alignItems'] => {
|
|
103
|
+
if (align === 'initial') return 'initial';
|
|
104
|
+
|
|
105
|
+
const alignMap: Record<string, React.CSSProperties['alignItems']> = {
|
|
106
|
+
start: 'flex-start',
|
|
107
|
+
end: 'flex-end',
|
|
108
|
+
center: 'center',
|
|
109
|
+
stretch: 'stretch',
|
|
110
|
+
baseline: 'baseline',
|
|
111
|
+
'flex-start': 'flex-start',
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
return (typeof align === 'string' && alignMap[align]) || align || 'flex-start';
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// Build background image URL with Linode URL prefix
|
|
118
|
+
const getBackgroundImageUrl = () => {
|
|
119
|
+
if (!box.props.bgImage) return 'none';
|
|
120
|
+
|
|
121
|
+
const bgImage = box.props.bgImage;
|
|
122
|
+
// If the image URL already starts with http:// or https://, use it as is
|
|
123
|
+
if (typeof bgImage === 'string' && (bgImage.startsWith('http://') || bgImage.startsWith('https://'))) {
|
|
124
|
+
return `url(${bgImage})`;
|
|
125
|
+
}
|
|
126
|
+
// Otherwise, prepend the Linode URL
|
|
127
|
+
return `url(${Linodeurl}${bgImage})`;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// Determine if flexbox should be used
|
|
131
|
+
const useFlexView = currentMode.flexView !== false; // Default to true if not specified
|
|
132
|
+
|
|
133
|
+
// Handle overflow/scroll
|
|
134
|
+
const overflowStyle = currentMode.isScroll ? 'auto' : 'hidden';
|
|
135
|
+
|
|
83
136
|
// Dynamic height calculation based on content type
|
|
84
137
|
const getBoxHeight = () => {
|
|
85
138
|
if (!autoAdjustForImages) {
|
|
@@ -101,28 +154,40 @@ const BoxRenderer: React.FC<BoxRendererProps> = ({
|
|
|
101
154
|
<div
|
|
102
155
|
style={{
|
|
103
156
|
backgroundColor: box.props.bgColor,
|
|
104
|
-
|
|
157
|
+
backgroundImage: getBackgroundImageUrl(),
|
|
105
158
|
backgroundSize: box.props.bgsize || 'cover',
|
|
159
|
+
backgroundPosition: 'center',
|
|
160
|
+
display: currentMode.isVisible !== false ? (useFlexView ? 'flex' : 'block') : 'none',
|
|
106
161
|
height: getBoxHeight(),
|
|
107
162
|
minHeight: autoAdjustForImages ? 'auto' : (currentMode.height || 'auto'),
|
|
108
163
|
borderRadius: currentMode.radius || '0px',
|
|
109
164
|
width: `${widthPercentage}%`,
|
|
110
165
|
float: 'left',
|
|
111
166
|
boxSizing: 'border-box',
|
|
112
|
-
padding: '
|
|
167
|
+
padding: '5px',
|
|
113
168
|
margin: 0,
|
|
114
|
-
|
|
115
|
-
|
|
169
|
+
marginTop: currentMode.top,
|
|
170
|
+
marginBottom: currentMode.bottom,
|
|
171
|
+
marginLeft: currentMode.left,
|
|
172
|
+
marginRight: currentMode.right,
|
|
173
|
+
overflow: overflowStyle,
|
|
174
|
+
position: 'relative',
|
|
175
|
+
// Flexbox properties when flexView is enabled
|
|
176
|
+
...(useFlexView && {
|
|
177
|
+
flexDirection: 'column',
|
|
178
|
+
alignItems: box.props.align === 'initial' ? 'initial' : getAlignItems(box.props.align),
|
|
179
|
+
justifyContent: box.props.justify === 'initial' ? 'initial' : getJustifyContent(box.props.justify),
|
|
180
|
+
}),
|
|
116
181
|
}}
|
|
117
182
|
>
|
|
118
183
|
<div style={{
|
|
119
184
|
width: '100%',
|
|
120
185
|
height: autoAdjustForImages ? 'auto' : '100%',
|
|
121
|
-
display: 'flex',
|
|
186
|
+
display: useFlexView ? 'flex' : 'block',
|
|
122
187
|
flexDirection: 'column',
|
|
123
|
-
alignItems: box.props.align
|
|
124
|
-
justifyContent: box.props.justify
|
|
125
|
-
padding:
|
|
188
|
+
alignItems: useFlexView ? (box.props.align === 'initial' ? 'initial' : getAlignItems(box.props.align)) : undefined,
|
|
189
|
+
justifyContent: useFlexView ? (box.props.justify === 'initial' ? 'initial' : getJustifyContent(box.props.justify)) : undefined,
|
|
190
|
+
padding: '5px',
|
|
126
191
|
margin: 0,
|
|
127
192
|
boxSizing: 'border-box'
|
|
128
193
|
}}>
|
|
@@ -218,7 +283,7 @@ const BoxRenderer: React.FC<BoxRendererProps> = ({
|
|
|
218
283
|
);
|
|
219
284
|
} else if (component.name === 'TextComponent') {
|
|
220
285
|
return <TextComponent key={index} props={component.props} />;
|
|
221
|
-
} else if (component.name === 'TermsAndCondition'
|
|
286
|
+
} else if (component.name === 'TermsAndCondition' ) {
|
|
222
287
|
const code = getFieldCode(component);
|
|
223
288
|
const hasError = validationErrors[code];
|
|
224
289
|
return (
|
|
@@ -85,7 +85,11 @@ const Button: React.FC<ButtonProps> = ({
|
|
|
85
85
|
};
|
|
86
86
|
|
|
87
87
|
// Handle button click based on button type
|
|
88
|
-
const handleClick = () => {
|
|
88
|
+
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
89
|
+
// Prevent default form submission behavior
|
|
90
|
+
e.preventDefault();
|
|
91
|
+
e.stopPropagation();
|
|
92
|
+
|
|
89
93
|
const buttonType = props.buttonType || 'submit';
|
|
90
94
|
|
|
91
95
|
// Handle different button types
|
|
@@ -145,7 +149,7 @@ const Button: React.FC<ButtonProps> = ({
|
|
|
145
149
|
<div className="mb-6">
|
|
146
150
|
<button
|
|
147
151
|
onClick={handleClick}
|
|
148
|
-
type=
|
|
152
|
+
type="button"
|
|
149
153
|
disabled={props.disabled || (isSubmitting && props.buttonType === 'submit')}
|
|
150
154
|
style={{
|
|
151
155
|
backgroundColor: currentMode.bgColor || '#3498db',
|
|
@@ -209,7 +209,8 @@ const PageForm: React.FC<PageFormProps> = ({ jsonData, onSubmit, isUrl = false,
|
|
|
209
209
|
const transformedData = transformFormValuesToNames();
|
|
210
210
|
|
|
211
211
|
// Check if we should send to URL or use onSubmit callback
|
|
212
|
-
|
|
212
|
+
// If URL is provided, automatically use it for API submission
|
|
213
|
+
if (url && url.trim() !== '') {
|
|
213
214
|
// Send data to API URL
|
|
214
215
|
setIsSubmitting(true);
|
|
215
216
|
try {
|
|
@@ -223,13 +224,17 @@ const PageForm: React.FC<PageFormProps> = ({ jsonData, onSubmit, isUrl = false,
|
|
|
223
224
|
|
|
224
225
|
// For GET requests, append data as query parameters
|
|
225
226
|
// For POST requests, send data in the body
|
|
226
|
-
let requestUrl = url;
|
|
227
|
+
let requestUrl = url.trim();
|
|
227
228
|
if (httpMethod === 'GET') {
|
|
228
229
|
const queryParams = new URLSearchParams();
|
|
229
230
|
Object.keys(transformedData).forEach((key) => {
|
|
230
|
-
|
|
231
|
+
const value = transformedData[key];
|
|
232
|
+
if (value !== null && value !== undefined && value !== '') {
|
|
233
|
+
queryParams.append(key, String(value));
|
|
234
|
+
}
|
|
231
235
|
});
|
|
232
|
-
|
|
236
|
+
const queryString = queryParams.toString();
|
|
237
|
+
requestUrl = queryString ? `${requestUrl}?${queryString}` : requestUrl;
|
|
233
238
|
} else {
|
|
234
239
|
// POST, PUT, PATCH, etc.
|
|
235
240
|
requestOptions.body = JSON.stringify(transformedData);
|
|
@@ -237,11 +242,28 @@ const PageForm: React.FC<PageFormProps> = ({ jsonData, onSubmit, isUrl = false,
|
|
|
237
242
|
|
|
238
243
|
const response = await fetch(requestUrl, requestOptions);
|
|
239
244
|
|
|
245
|
+
// Read response as text first (can only read body once)
|
|
246
|
+
const responseText = await response.text().catch(() => '');
|
|
247
|
+
|
|
240
248
|
if (!response.ok) {
|
|
241
|
-
|
|
249
|
+
const errorText = responseText || response.statusText || `HTTP ${response.status}`;
|
|
250
|
+
throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`);
|
|
242
251
|
}
|
|
243
252
|
|
|
244
|
-
|
|
253
|
+
// Try to parse as JSON, fallback to text if not JSON
|
|
254
|
+
let responseData;
|
|
255
|
+
const contentType = response.headers.get('content-type');
|
|
256
|
+
if (contentType && contentType.includes('application/json') && responseText) {
|
|
257
|
+
try {
|
|
258
|
+
responseData = JSON.parse(responseText);
|
|
259
|
+
} catch {
|
|
260
|
+
// If JSON parsing fails, use text as message
|
|
261
|
+
responseData = { message: responseText };
|
|
262
|
+
}
|
|
263
|
+
} else {
|
|
264
|
+
// Not JSON or empty response, use text as message
|
|
265
|
+
responseData = responseText ? { message: responseText } : { message: 'Success' };
|
|
266
|
+
}
|
|
245
267
|
|
|
246
268
|
setIsSubmitting(false);
|
|
247
269
|
showToast('Form submitted successfully!', 'success');
|
|
@@ -253,6 +275,7 @@ const PageForm: React.FC<PageFormProps> = ({ jsonData, onSubmit, isUrl = false,
|
|
|
253
275
|
} catch (error) {
|
|
254
276
|
setIsSubmitting(false);
|
|
255
277
|
const errorMessage = error instanceof Error ? error.message : 'Failed to submit form';
|
|
278
|
+
console.error('Form submission error:', error);
|
|
256
279
|
showToast(`Error submitting form: ${errorMessage}`, 'error');
|
|
257
280
|
|
|
258
281
|
// Still call onSubmit with error data if provided
|
|
@@ -264,6 +287,9 @@ const PageForm: React.FC<PageFormProps> = ({ jsonData, onSubmit, isUrl = false,
|
|
|
264
287
|
// Send transformed data back to parent component via onSubmit callback
|
|
265
288
|
if (onSubmit) {
|
|
266
289
|
onSubmit(transformedData);
|
|
290
|
+
} else {
|
|
291
|
+
// If neither URL nor onSubmit is provided, show a warning
|
|
292
|
+
showToast('No submission handler configured. Please provide either a URL or onSubmit callback.', 'warning');
|
|
267
293
|
}
|
|
268
294
|
}
|
|
269
295
|
}, [formValues, validateForm, getRequiredFields, transformFormValuesToNames, onSubmit, showToast, isUrl, method, url]);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import BoxRenderer from './BoxRenderer';
|
|
3
3
|
import type { Row } from '../types';
|
|
4
|
+
import { Linodeurl } from '../const';
|
|
4
5
|
|
|
5
6
|
interface RowComponentProps {
|
|
6
7
|
row: Row;
|
|
@@ -35,18 +36,72 @@ const RowComponent: React.FC<RowComponentProps> = ({
|
|
|
35
36
|
return row.props.mode.tablet || {};
|
|
36
37
|
case 'web':
|
|
37
38
|
default:
|
|
38
|
-
return row.props.mode.web;
|
|
39
|
+
return row.props.mode.web || {};
|
|
39
40
|
}
|
|
40
41
|
};
|
|
41
42
|
|
|
42
43
|
const currentMode = getCurrentMode();
|
|
44
|
+
|
|
45
|
+
// Determine if flexbox should be used
|
|
46
|
+
const useFlexView = currentMode.flexView !== false; // Default to true if not specified
|
|
47
|
+
|
|
48
|
+
// Handle overflow/scroll
|
|
49
|
+
const overflowStyle = currentMode.isScroll ? 'auto' : 'hidden';
|
|
50
|
+
|
|
51
|
+
// Handle content justification with proper CSS values (from BoxComponent)
|
|
52
|
+
const getJustifyContent = (justify: string): React.CSSProperties['justifyContent'] => {
|
|
53
|
+
if (justify === 'initial') return 'initial';
|
|
54
|
+
|
|
55
|
+
const justifyMap: Record<string, React.CSSProperties['justifyContent']> = {
|
|
56
|
+
around: 'space-around',
|
|
57
|
+
between: 'space-between',
|
|
58
|
+
evenly: 'space-evenly',
|
|
59
|
+
start: 'flex-start',
|
|
60
|
+
end: 'flex-end',
|
|
61
|
+
center: 'center',
|
|
62
|
+
'flex-start': 'flex-start',
|
|
63
|
+
'flex-end': 'flex-end',
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return justifyMap[justify] || justify || 'flex-start';
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Handle alignment with proper CSS values (from BoxComponent)
|
|
70
|
+
const getAlignItems = (align: string): React.CSSProperties['alignItems'] => {
|
|
71
|
+
if (align === 'initial') return 'initial';
|
|
72
|
+
|
|
73
|
+
const alignMap: Record<string, React.CSSProperties['alignItems']> = {
|
|
74
|
+
start: 'flex-start',
|
|
75
|
+
end: 'flex-end',
|
|
76
|
+
center: 'center',
|
|
77
|
+
stretch: 'stretch',
|
|
78
|
+
baseline: 'baseline',
|
|
79
|
+
'flex-start': 'flex-start',
|
|
80
|
+
'flex-end': 'flex-end',
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
return (typeof align === 'string' && alignMap[align]) || align || 'flex-start';
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// Build background image URL with Linode URL prefix
|
|
87
|
+
const getBackgroundImageUrl = () => {
|
|
88
|
+
if (!row.props.bgImage) return 'none';
|
|
89
|
+
|
|
90
|
+
const bgImage = row.props.bgImage;
|
|
91
|
+
// If the image URL already starts with http:// or https://, use it as is
|
|
92
|
+
if (typeof bgImage === 'string' && (bgImage.startsWith('http://') || bgImage.startsWith('https://'))) {
|
|
93
|
+
return `url(${bgImage})`;
|
|
94
|
+
}
|
|
95
|
+
// Otherwise, prepend the Linode URL
|
|
96
|
+
return `url(${Linodeurl}${bgImage})`;
|
|
97
|
+
};
|
|
43
98
|
|
|
44
99
|
return (
|
|
45
100
|
<div
|
|
46
101
|
style={{
|
|
47
102
|
minHeight: row.props.minHeight,
|
|
48
103
|
backgroundColor: row.props.bgColor,
|
|
49
|
-
backgroundImage:
|
|
104
|
+
backgroundImage: getBackgroundImageUrl(),
|
|
50
105
|
backgroundSize: row.props.bgsize,
|
|
51
106
|
backgroundPosition: 'center',
|
|
52
107
|
width: '100%',
|
|
@@ -54,15 +109,21 @@ const RowComponent: React.FC<RowComponentProps> = ({
|
|
|
54
109
|
border: '1px solid #e0e0e0',
|
|
55
110
|
borderRadius: '4px',
|
|
56
111
|
boxSizing: 'border-box',
|
|
57
|
-
overflow:
|
|
112
|
+
overflow: overflowStyle,
|
|
58
113
|
position: 'relative',
|
|
59
114
|
display: currentMode.isVisible !== false ? 'block' : 'none',
|
|
115
|
+
top: currentMode.top,
|
|
116
|
+
bottom: currentMode.bottom,
|
|
60
117
|
}}
|
|
61
118
|
>
|
|
62
119
|
{/* Row Content */}
|
|
63
120
|
<div style={{
|
|
64
121
|
width: '100%',
|
|
65
|
-
display: 'block',
|
|
122
|
+
display: useFlexView ? 'flex' : 'block',
|
|
123
|
+
flexDirection: 'row',
|
|
124
|
+
alignItems: useFlexView ? (row.props.align === 'initial' ? 'initial' : getAlignItems(row.props.align || 'flex-start')) : undefined,
|
|
125
|
+
justifyContent: useFlexView ? (row.props.justify === 'initial' ? 'initial' : getJustifyContent(row.props.justify || 'flex-start')) : undefined,
|
|
126
|
+
flexWrap: useFlexView ? (row.props.nowrap === true ? 'nowrap' : 'wrap') : undefined,
|
|
66
127
|
position: 'relative',
|
|
67
128
|
padding: '5px',
|
|
68
129
|
boxSizing: 'border-box'
|
|
@@ -81,8 +142,8 @@ const RowComponent: React.FC<RowComponentProps> = ({
|
|
|
81
142
|
isSubmitting={isSubmitting}
|
|
82
143
|
/>
|
|
83
144
|
))}
|
|
84
|
-
{/* Clear float after all children */}
|
|
85
|
-
<div style={{ clear: 'both' }} />
|
|
145
|
+
{/* Clear float after all children (only needed when not using flexbox) */}
|
|
146
|
+
{!useFlexView && <div style={{ clear: 'both' }} />}
|
|
86
147
|
</div>
|
|
87
148
|
</div>
|
|
88
149
|
);
|