@syncfusion/ej2-react-form-builder 0.1.0-beta → 35.1.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/README.md +81 -36
  2. package/diConfig.json +148 -0
  3. package/dist/ej2-react-form-builder.min.js +10 -0
  4. package/dist/ej2-react-form-builder.umd.min.js +11 -0
  5. package/dist/ej2-react-form-builder.umd.min.js.map +1 -0
  6. package/dist/es6/ej2-react-form-builder.es2015.js +60 -0
  7. package/dist/es6/ej2-react-form-builder.es2015.js.map +1 -0
  8. package/form-builder.d.ts +4 -0
  9. package/form-builder.js +4 -0
  10. package/index.d.ts +2 -4
  11. package/index.js +2 -4
  12. package/license +6 -13
  13. package/package.json +19 -43
  14. package/src/form-builder/formbuilder.component.d.ts +37 -0
  15. package/src/form-builder/formbuilder.component.js +50 -0
  16. package/src/form-builder/index.d.ts +2 -0
  17. package/src/form-builder/index.js +2 -0
  18. package/src/form-builder/toolboxitems-directive.d.ts +28 -0
  19. package/src/form-builder/toolboxitems-directive.js +47 -0
  20. package/src/global.js +1 -0
  21. package/src/index.d.ts +2 -16
  22. package/src/index.js +2 -6
  23. package/Common/CheckboxGroup.d.ts +0 -13
  24. package/Common/CheckboxGroup.js +0 -50
  25. package/Common/ConditionalRuleEngine.d.ts +0 -296
  26. package/Common/ConditionalRuleEngine.js +0 -566
  27. package/Common/CustomTabComponent.d.ts +0 -17
  28. package/Common/CustomTabComponent.js +0 -30
  29. package/Common/CustomValidationEngine.d.ts +0 -58
  30. package/Common/CustomValidationEngine.js +0 -146
  31. package/Common/DataGridComponent.d.ts +0 -20
  32. package/Common/DataGridComponent.js +0 -273
  33. package/Common/FormValidator.d.ts +0 -485
  34. package/Common/FormValidator.js +0 -575
  35. package/Common/MinMaxRuleEngine.d.ts +0 -105
  36. package/Common/MinMaxRuleEngine.js +0 -365
  37. package/Common/StaticHtmlRenderer.d.ts +0 -14
  38. package/Common/StaticHtmlRenderer.js +0 -24
  39. package/Common/expressions/DependencyTracker.d.ts +0 -30
  40. package/Common/expressions/DependencyTracker.js +0 -128
  41. package/Common/expressions/ExpressionEngine.d.ts +0 -95
  42. package/Common/expressions/ExpressionEngine.js +0 -506
  43. package/Common/expressions/ExpressionParser.d.ts +0 -41
  44. package/Common/expressions/ExpressionParser.js +0 -269
  45. package/Common/expressions/useGridExpressions.d.ts +0 -12
  46. package/Common/expressions/useGridExpressions.js +0 -71
  47. package/Common/globalFormId.d.ts +0 -26
  48. package/Common/globalFormId.js +0 -45
  49. package/Common/json-converter.d.ts +0 -444
  50. package/Common/json-converter.js +0 -1184
  51. package/Common/parser.d.ts +0 -98
  52. package/Common/parser.js +0 -753
  53. package/Common/utils.d.ts +0 -130
  54. package/Common/utils.js +0 -574
  55. package/src/FormBuilder.d.ts +0 -211
  56. package/src/FormBuilder.js +0 -1299
  57. package/src/components/Database/ConnectionForm.d.ts +0 -8
  58. package/src/components/Database/ConnectionForm.js +0 -28
  59. package/src/components/TopBar.d.ts +0 -10
  60. package/src/components/TopBar.js +0 -7
  61. package/src/components/icons/ComponentIcons.d.ts +0 -44
  62. package/src/components/icons/ComponentIcons.js +0 -40
  63. package/src/components/layout/FormFieldWrapper.d.ts +0 -41
  64. package/src/components/layout/FormFieldWrapper.js +0 -112
  65. package/src/components/layout/LeftPane.d.ts +0 -23
  66. package/src/components/layout/LeftPane.js +0 -310
  67. package/src/components/layout/ListBoxPane.d.ts +0 -24
  68. package/src/components/layout/ListBoxPane.js +0 -94
  69. package/src/components/layout/MainSection.d.ts +0 -39
  70. package/src/components/layout/MainSection.js +0 -1766
  71. package/src/components/layout/Preview.d.ts +0 -12
  72. package/src/components/layout/Preview.js +0 -337
  73. package/src/components/layout/RightPane.d.ts +0 -22
  74. package/src/components/layout/RightPane.js +0 -370
  75. package/src/components/theme/ThemeSwitcher.d.ts +0 -14
  76. package/src/components/theme/ThemeSwitcher.js +0 -24
  77. package/src/components/theme/theme-constant.d.ts +0 -37
  78. package/src/components/theme/theme-constant.js +0 -23
  79. package/src/config/component-properties.json +0 -4929
  80. package/src/config/form-setting.json +0 -56
  81. package/src/config/locale.d.ts +0 -449
  82. package/src/config/locale.js +0 -449
  83. package/src/context/FormSchemaContext.d.ts +0 -32
  84. package/src/context/FormSchemaContext.js +0 -155
  85. package/src/context/LocalizationContext.d.ts +0 -27
  86. package/src/context/LocalizationContext.js +0 -43
  87. package/src/context/ThemeContext.d.ts +0 -23
  88. package/src/context/ThemeContext.js +0 -161
  89. package/src/context/contexts.d.ts +0 -6
  90. package/src/context/contexts.js +0 -6
  91. package/src/context/defaultFormSchema.d.ts +0 -6
  92. package/src/context/defaultFormSchema.js +0 -20
  93. package/src/defaultPalette.d.ts +0 -9
  94. package/src/defaultPalette.js +0 -45
  95. package/src/defaultTemplate.d.ts +0 -3
  96. package/src/defaultTemplate.js +0 -185
  97. package/src/import/schemaImportUtils.d.ts +0 -24
  98. package/src/import/schemaImportUtils.js +0 -121
  99. package/src/types/ActionButtons.d.ts +0 -44
  100. package/src/types/ActionButtons.js +0 -27
  101. package/src/types/ComponentProperties.d.ts +0 -33
  102. package/src/types/ComponentProperties.js +0 -2
  103. package/src/types/FormSchema.d.ts +0 -585
  104. package/src/types/FormSchema.js +0 -147
  105. package/src/types/connection.d.ts +0 -19
  106. package/src/types/connection.js +0 -9
  107. package/src/utils/AttributesEditor.d.ts +0 -13
  108. package/src/utils/AttributesEditor.js +0 -117
  109. package/src/utils/ButtonGroupEditor.d.ts +0 -15
  110. package/src/utils/ButtonGroupEditor.js +0 -141
  111. package/src/utils/ChoiceBasedFieldEditor.d.ts +0 -16
  112. package/src/utils/ChoiceBasedFieldEditor.js +0 -87
  113. package/src/utils/ClipboardHelper.d.ts +0 -26
  114. package/src/utils/ClipboardHelper.js +0 -83
  115. package/src/utils/ColumnBuilder.d.ts +0 -11
  116. package/src/utils/ColumnBuilder.js +0 -153
  117. package/src/utils/ColumnSpanEditor.d.ts +0 -8
  118. package/src/utils/ColumnSpanEditor.js +0 -98
  119. package/src/utils/ConditionDataEditor.d.ts +0 -29
  120. package/src/utils/ConditionDataEditor.js +0 -177
  121. package/src/utils/ConditionalEditorUtils.d.ts +0 -27
  122. package/src/utils/ConditionalEditorUtils.js +0 -310
  123. package/src/utils/ConditionsEditor.d.ts +0 -20
  124. package/src/utils/ConditionsEditor.js +0 -571
  125. package/src/utils/CustomAccordion.d.ts +0 -13
  126. package/src/utils/CustomAccordion.js +0 -38
  127. package/src/utils/CustomValidationEditor.d.ts +0 -11
  128. package/src/utils/CustomValidationEditor.js +0 -208
  129. package/src/utils/DataSourceCollector.d.ts +0 -3
  130. package/src/utils/DataSourceCollector.js +0 -252
  131. package/src/utils/ExpressionEditor.d.ts +0 -13
  132. package/src/utils/ExpressionEditor.js +0 -99
  133. package/src/utils/FormSettingsPanel.d.ts +0 -9
  134. package/src/utils/FormSettingsPanel.js +0 -60
  135. package/src/utils/LookupConfig.d.ts +0 -11
  136. package/src/utils/LookupConfig.js +0 -159
  137. package/src/utils/MinMaxValueEditor.d.ts +0 -25
  138. package/src/utils/MinMaxValueEditor.js +0 -188
  139. package/src/utils/OptionsEditor.d.ts +0 -8
  140. package/src/utils/OptionsEditor.js +0 -79
  141. package/src/utils/PropertyRenderer.d.ts +0 -22
  142. package/src/utils/PropertyRenderer.js +0 -210
  143. package/src/utils/ValueRenderer.d.ts +0 -2
  144. package/src/utils/ValueRenderer.js +0 -91
  145. package/src/utils/ej2DbAdaptor.d.ts +0 -6
  146. package/src/utils/ej2DbAdaptor.js +0 -38
  147. package/src/utils/mappingHeuristics.d.ts +0 -10
  148. package/src/utils/mappingHeuristics.js +0 -33
  149. package/src/utils/renderComponent.d.ts +0 -18
  150. package/src/utils/renderComponent.js +0 -300
  151. package/styles/tailwind3.css +0 -67
  152. package/styles/themes/action-buttons.css +0 -110
  153. package/styles/themes/datagrid.css +0 -380
  154. package/styles/themes/drag-reorder.css +0 -248
  155. package/styles/themes/import-dialog.css +0 -292
  156. package/styles/themes/index.css +0 -658
  157. package/styles/themes/left-pane.css +0 -253
  158. package/styles/themes/options-editor.css +0 -63
  159. package/styles/themes/preview.css +0 -702
  160. package/styles/themes/property-dialog.css +0 -371
package/Common/parser.js DELETED
@@ -1,753 +0,0 @@
1
- import { VALIDATION_REGEX } from './utils';
2
- /**
3
- * @private
4
- */
5
- const DEFAULT_CONFIG = {
6
- strict: false,
7
- maxInputSize: 5 * 1024 * 1024,
8
- defaultTextType: 'textbox',
9
- inferTypeFromValue: true,
10
- generateIds: true,
11
- includeMetadata: true,
12
- customTypeMap: {},
13
- validateUnknownFields: false
14
- };
15
- /**
16
- * @private
17
- */
18
- export class UniversalJsonParser {
19
- config;
20
- constructor(customConfig) {
21
- this.config = { ...DEFAULT_CONFIG, ...customConfig };
22
- }
23
- parse(input) {
24
- const errors = [];
25
- const warnings = [];
26
- // Handle string input
27
- if (typeof input === 'string') {
28
- if (input.length > this.config.maxInputSize) {
29
- return this.formatError(errors, warnings, `Input exceeds maximum size of ${this.config.maxInputSize} bytes`);
30
- }
31
- const jsonValidation = validateJSON(input);
32
- if (!jsonValidation.valid) {
33
- return this.formatError(errors, warnings, jsonValidation.errors?.[0] || 'Invalid JSON');
34
- }
35
- try {
36
- input = JSON.parse(input);
37
- }
38
- catch (error) {
39
- return this.formatError(errors, warnings, `Failed to parse JSON: ${error.message}`);
40
- }
41
- }
42
- // Detect format
43
- const detection = this.detectFormat(input);
44
- // Add warnings for ambiguous format detection
45
- if (detection.ambiguousFormats && detection.ambiguousFormats.length > 0) {
46
- warnings.push(`Ambiguous format detected.`);
47
- warnings.push(`Using ${detection.format} format (${detection.format === 'json-schema' ? 'properties priority' : 'pages priority'})`);
48
- }
49
- // Parse based on format
50
- let components = [];
51
- switch (detection.format) {
52
- case 'configured-schema':
53
- // Already in our standard format, just use directly
54
- components = input.properties || [];
55
- break;
56
- case 'json-schema': {
57
- const jsonSchemaResult = this.parseJsonSchema(input);
58
- components = jsonSchemaResult.components;
59
- if (jsonSchemaResult.warnings) {
60
- warnings.push(...jsonSchemaResult.warnings);
61
- }
62
- break;
63
- }
64
- case 'surveyjs':
65
- components = this.parseSurveyJS(input);
66
- break;
67
- case 'openapi':
68
- components = this.parseOpenAPI(input);
69
- break;
70
- case 'data-inference':
71
- components = this.parseDataInference(input);
72
- // Only warn about inferred types if components were actually created
73
- if (components.length > 0) {
74
- warnings.push('Field types inferred from data; please verify');
75
- }
76
- break;
77
- default:
78
- errors.push('Could not determine input format');
79
- }
80
- if (components.length === 0) {
81
- errors.push('No form fields could be extracted from input');
82
- }
83
- // Add default submit button if not already present
84
- const hasSubmitButton = components.some((comp) => comp.type === 'button' && comp.buttonType === 'submit');
85
- if (!hasSubmitButton && components.length > 0) {
86
- components.push({
87
- id: this.generateComponentId('button'),
88
- type: 'button',
89
- label: 'Submit',
90
- style: 'primary',
91
- buttonType: 'submit',
92
- });
93
- }
94
- const schema = {
95
- version: input.version || "0.1.0",
96
- components
97
- };
98
- const schemaValidation = validateFormSchema(schema);
99
- if (!schemaValidation.valid && schemaValidation.errors) {
100
- errors.push(...schemaValidation.errors);
101
- }
102
- if (schemaValidation.warnings) {
103
- warnings.push(...schemaValidation.warnings);
104
- }
105
- if (this.config.strict && warnings.length > 0) {
106
- errors.push(...warnings);
107
- warnings.length = 0;
108
- }
109
- const sanitized = sanitizeFormSchema(schema);
110
- return {
111
- success: errors.length === 0,
112
- schema: sanitized,
113
- errors,
114
- warnings,
115
- metadata: {
116
- detectedFormat: detection.format,
117
- confidence: detection.confidence,
118
- sourceHints: detection.hints
119
- }
120
- };
121
- }
122
- formatError(errors, warnings, message) {
123
- errors.push(message);
124
- return {
125
- success: false,
126
- schema: null,
127
- errors,
128
- warnings,
129
- metadata: {
130
- detectedFormat: 'unknown',
131
- confidence: 'low',
132
- sourceHints: []
133
- }
134
- };
135
- }
136
- detectFormat(input) {
137
- const hints = [];
138
- const ambiguousFormats = [];
139
- if (!input || typeof input !== 'object') {
140
- return {
141
- format: 'unknown',
142
- confidence: 'low',
143
- hints: ['Input is not a valid object']
144
- };
145
- }
146
- // Check for JSON Schema indicators
147
- if (input.$schema && typeof input.$schema === 'string' && input.$schema.includes('json-schema')) {
148
- hints.push('$schema property detected');
149
- return { format: 'json-schema', confidence: 'high', hints };
150
- }
151
- // Detect ambiguous formats (Test Case 8)
152
- const hasProperties = input.properties && typeof input.properties === 'object';
153
- const hasPages = input.pages && Array.isArray(input.pages);
154
- const hasElements = input.elements && Array.isArray(input.elements);
155
- if (hasProperties && hasPages) {
156
- hints.push('Multiple format patterns detected');
157
- hints.push('Selected based on priority rules');
158
- ambiguousFormats.push('properties format', 'pages format');
159
- // JSON Schema has priority
160
- return {
161
- format: 'json-schema',
162
- confidence: 'medium',
163
- hints,
164
- ambiguousFormats
165
- };
166
- }
167
- // Check for SurveyJS with pages
168
- if (hasPages) {
169
- hints.push('pages array detected');
170
- if (input.pages[0]?.elements)
171
- hints.push('elements structure matched');
172
- return { format: 'surveyjs', confidence: 'high', hints };
173
- }
174
- // Check for SurveyJS with elements at root
175
- if (hasElements && !hasProperties) {
176
- hints.push('elements array at root level');
177
- return { format: 'surveyjs', confidence: 'medium', hints };
178
- }
179
- // Check for JSON Schema with properties
180
- if (hasProperties) {
181
- hints.push('properties object detected');
182
- if (input.type === 'object')
183
- hints.push('type: object matched');
184
- if (input.required && Array.isArray(input.required))
185
- hints.push('required array found');
186
- // Check for API schema indicators (example fields pattern)
187
- if (this.hasExampleFields(input.properties)) {
188
- hints.push('example fields detected (API schema pattern)');
189
- return { format: 'openapi', confidence: 'high', hints };
190
- }
191
- return { format: 'json-schema', confidence: 'high', hints };
192
- }
193
- // Check for Data Inference
194
- if (this.isDataInferenceCandidate(input)) {
195
- hints.push('Simple object with primitive values');
196
- hints.push('No schema indicators found');
197
- return { format: 'data-inference', confidence: 'medium', hints };
198
- }
199
- return {
200
- format: 'unknown',
201
- confidence: 'low',
202
- hints: ['Could not identify format']
203
- };
204
- }
205
- hasExampleFields(properties) {
206
- if (!properties || typeof properties !== 'object')
207
- return false;
208
- return Object.values(properties).some((prop) => 'example' in prop);
209
- }
210
- isDataInferenceCandidate(input) {
211
- if (!input || typeof input !== 'object')
212
- return false;
213
- if (input.$schema || input.properties || input.pages || input.elements)
214
- return false;
215
- return Object.values(input).every((val) => {
216
- if (val === null || val === undefined)
217
- return true;
218
- if (typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean')
219
- return true;
220
- if (Array.isArray(val)) {
221
- return val.every((item) => typeof item === 'string' || typeof item === 'number' || typeof item === 'boolean');
222
- }
223
- return false;
224
- });
225
- }
226
- inferComponentType(fieldName, value) {
227
- const valueType = this.inferTypeFromValue(fieldName, value);
228
- if (valueType.type) {
229
- return { ...valueType, confidence: 'high', method: 'value-type' };
230
- }
231
- const patternMatch = this.matchFieldNamePattern(fieldName);
232
- if (patternMatch.type) {
233
- return { ...patternMatch, confidence: 'high', method: 'name-pattern' };
234
- }
235
- return {
236
- type: this.config.defaultTextType,
237
- confidence: 'low',
238
- method: 'fallback'
239
- };
240
- }
241
- inferTypeFromValue(fieldName, value) {
242
- if (!this.config.inferTypeFromValue)
243
- return { type: 'textbox' };
244
- if (value === null || value === undefined) {
245
- // Even with null/undefined values, check field name patterns
246
- return this.matchFieldNamePattern(fieldName);
247
- }
248
- if (typeof value === 'string') {
249
- // Check value-based patterns first (highest confidence)
250
- if (value.includes('@') && value.includes('.')) {
251
- return { type: 'textbox', textboxType: 'email' };
252
- }
253
- if (/^(https?:\/\/)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}/.test(value)) {
254
- return { type: 'textbox', textboxType: 'url' };
255
- }
256
- if (/^\d{4}-\d{2}-\d{2}/.test(value)) {
257
- return { type: 'date' };
258
- }
259
- // For generic strings, check field name patterns
260
- const fieldNameMatch = this.matchFieldNamePattern(fieldName);
261
- if (fieldNameMatch.type !== 'textbox' || fieldNameMatch.textboxType) {
262
- return fieldNameMatch;
263
- }
264
- return { type: this.config.defaultTextType };
265
- }
266
- if (typeof value === 'number') {
267
- return { type: 'number' };
268
- }
269
- if (typeof value === 'boolean') {
270
- return { type: 'checkbox' };
271
- }
272
- if (Array.isArray(value)) {
273
- return { type: 'multiselect' };
274
- }
275
- return { type: 'textbox' };
276
- }
277
- matchFieldNamePattern(fieldName) {
278
- if (this.config.customTypeMap) {
279
- const customType = this.config.customTypeMap[fieldName.toLowerCase()];
280
- if (customType)
281
- return { type: customType };
282
- }
283
- const lower = fieldName.toLowerCase();
284
- const normalized = lower.replace(/[_\-\s]+/g, ' ');
285
- if (/email|mail|e-?mail/.test(normalized))
286
- return { type: 'textbox', textboxType: 'email' };
287
- if (/date|birth|birthday|dob|created|updated/.test(normalized))
288
- return { type: 'date' };
289
- if (/phone|tel|mobile|cell|cellphone|msisdn/.test(normalized))
290
- return { type: 'textbox', textboxType: 'number' };
291
- if (/url|link|uri|website/.test(normalized))
292
- return { type: 'textbox', textboxType: 'url' };
293
- if (/password|pwd|pass|security|secret|key|token/.test(normalized))
294
- return { type: 'textbox', textboxType: 'password' };
295
- if (/\b(?:age|count|qty|quantity|amount|price|cost|salary|fee|rate|number|num)\b/.test(normalized))
296
- return { type: 'number' };
297
- if (/active|enabled|visible|deleted|archived|approved|confirmed/.test(normalized))
298
- return { type: 'checkbox' };
299
- if (/select|choice|option|type|status|category|kind/.test(normalized))
300
- return { type: 'dropdown' };
301
- if (/description|comment|note|bio|biography|about|message|content|summary|details/.test(normalized))
302
- return { type: 'textarea' };
303
- return { type: 'textbox' };
304
- }
305
- parseJsonSchema(schema) {
306
- const components = [];
307
- const warnings = [];
308
- if (!schema.properties || typeof schema.properties !== 'object')
309
- return { components, warnings };
310
- const flattenedProperties = this.flattenNestedProperties(schema.properties, '', warnings);
311
- Object.entries(flattenedProperties).forEach(([key, propDef]) => {
312
- const typeInfo = this.mapJsonSchemaType(propDef);
313
- components.push(this.createComponent(key, propDef, typeInfo, { required: schema.required?.includes(key) || false }));
314
- });
315
- return { components, warnings };
316
- }
317
- flattenNestedProperties(properties, prefix, warnings) {
318
- const flattened = {};
319
- let hasNested = false;
320
- Object.entries(properties).forEach(([key, propDef]) => {
321
- const fullKey = prefix ? `${prefix}_${key}` : key;
322
- // Check if this is a nested object (Test Case 9)
323
- if (propDef.type === 'object' && propDef.properties && typeof propDef.properties === 'object') {
324
- hasNested = true;
325
- const nestedFlattened = this.flattenNestedProperties(propDef.properties, fullKey, warnings);
326
- Object.assign(flattened, nestedFlattened);
327
- }
328
- else {
329
- flattened[fullKey] = propDef;
330
- }
331
- });
332
- // Add warnings for nested objects
333
- if (hasNested && !prefix) {
334
- warnings.push('NESTED_OBJECTS_FLATTENED: Nested objects converted to flat structure');
335
- Object.entries(properties).forEach(([key, propDef]) => {
336
- if (propDef.type === 'object' && propDef.properties) {
337
- this.collectFlattenedFieldNames(propDef.properties, key, warnings);
338
- }
339
- });
340
- }
341
- return flattened;
342
- }
343
- collectFlattenedFieldNames(properties, prefix, warnings) {
344
- Object.entries(properties).forEach(([key, propDef]) => {
345
- const fullKey = `${prefix}_${key}`;
346
- warnings.push(`Field '${prefix}.${key}' flattened to '${fullKey}'`);
347
- if (propDef.type === 'object' && propDef.properties) {
348
- this.collectFlattenedFieldNames(propDef.properties, fullKey, warnings);
349
- }
350
- });
351
- }
352
- parseSurveyJS(schema) {
353
- const components = [];
354
- if (schema.pages && Array.isArray(schema.pages)) {
355
- schema.pages.forEach((page) => {
356
- if (page.elements && Array.isArray(page.elements)) {
357
- page.elements.forEach((element) => {
358
- components.push(this.createComponentFromSurveyJS(element));
359
- });
360
- }
361
- });
362
- }
363
- if (!components.length && schema.elements && Array.isArray(schema.elements)) {
364
- schema.elements.forEach((element) => {
365
- components.push(this.createComponentFromSurveyJS(element));
366
- });
367
- }
368
- return components;
369
- }
370
- createComponentFromSurveyJS(element) {
371
- const type = this.mapSurveyJSType(element.type || 'text');
372
- const validation = {};
373
- // Only add required if explicitly true
374
- if (element.isRequired === true) {
375
- validation.required = true;
376
- }
377
- if (element.validators && Array.isArray(element.validators)) {
378
- element.validators.forEach((validator) => {
379
- if (validator.type === 'numeric') {
380
- if (validator.minValue !== undefined)
381
- validation.minValue = validator.minValue;
382
- if (validator.maxValue !== undefined)
383
- validation.maxValue = validator.maxValue;
384
- }
385
- if (validator.type === 'text') {
386
- if (validator.minLength !== undefined)
387
- validation.minLength = validator.minLength;
388
- if (validator.maxLength !== undefined)
389
- validation.maxLength = validator.maxLength;
390
- }
391
- });
392
- }
393
- const component = {
394
- id: this.generateComponentId(type),
395
- type: type,
396
- label: element.title || element.label || this.humanizeFieldName(element.name),
397
- defaultValue: element.defaultValue,
398
- expressionValue: element.expressionValue,
399
- ...Object.keys(validation).length > 0 ? validation : undefined
400
- };
401
- if (type === 'dropdown' || type === 'multiselect' || type === 'radio' || type === 'splitButton') {
402
- if (element.choices && Array.isArray(element.choices)) {
403
- component.options = element.choices;
404
- }
405
- }
406
- return component;
407
- }
408
- mapSurveyJSType(surveyType) {
409
- const typeMap = {
410
- text: 'textbox',
411
- email: 'email',
412
- number: 'number',
413
- checkbox: 'checkbox',
414
- radiogroup: 'radio',
415
- dropdown: 'dropdown',
416
- tagbox: 'multiselect',
417
- multiselect: 'multiselect',
418
- rating: 'number',
419
- comment: 'textarea',
420
- date: 'date'
421
- };
422
- return typeMap[surveyType] || 'textbox';
423
- }
424
- parseOpenAPI(schema) {
425
- const components = [];
426
- if (!schema.properties || typeof schema.properties !== 'object')
427
- return components;
428
- Object.entries(schema.properties).forEach(([key, propDef]) => {
429
- const typeInfo = this.mapJsonSchemaType(propDef);
430
- components.push(this.createComponent(key, propDef, typeInfo, { required: schema.required?.includes(key) || false }));
431
- });
432
- return components;
433
- }
434
- parseDataInference(data) {
435
- const components = [];
436
- Object.entries(data).forEach(([key, value]) => {
437
- const typeInference = this.inferComponentType(key, value);
438
- const component = {
439
- id: this.generateComponentId(typeInference.type),
440
- type: typeInference.type,
441
- label: this.humanizeFieldName(key),
442
- defaultValue: value,
443
- expressionValue: undefined
444
- };
445
- if (typeInference.textboxType) {
446
- component.textboxType = typeInference.textboxType;
447
- // Add default regex patterns for email and url
448
- if (typeInference.textboxType === 'email') {
449
- component.regex = VALIDATION_REGEX['EMAIL'];
450
- }
451
- else if (typeInference.textboxType === 'url') {
452
- component.regex = VALIDATION_REGEX['URL'];
453
- }
454
- }
455
- if (typeInference.type === 'date' && component.defaultValue !== undefined && component.defaultValue !== null) {
456
- const formatted = this.formatDateTimeValue(component.defaultValue);
457
- if (formatted)
458
- component.defaultValue = formatted;
459
- }
460
- if (Array.isArray(value) && value.length > 0) {
461
- component.options = value;
462
- }
463
- components.push(component);
464
- });
465
- return components;
466
- }
467
- createComponent(fieldName, propDef, typeInfo, context) {
468
- const validation = {};
469
- // Only add required if explicitly true
470
- const isRequired = propDef.required !== undefined ? propDef.required : context.required;
471
- if (isRequired === true) {
472
- validation.required = true;
473
- }
474
- if (propDef.minLength)
475
- validation.minLength = propDef.minLength;
476
- if (propDef.maxLength)
477
- validation.maxLength = propDef.maxLength;
478
- if (propDef.minDate)
479
- validation.minDate = propDef.minDate;
480
- if (propDef.maxDate)
481
- validation.maxDate = propDef.maxDate;
482
- if (propDef.minTime)
483
- validation.minTime = propDef.minTime;
484
- if (propDef.minValue)
485
- validation.minValue = propDef.minValue;
486
- if (propDef.maxValue)
487
- validation.maxValue = propDef.maxValue;
488
- if (propDef.pattern) {
489
- validation.pattern = propDef.pattern;
490
- }
491
- const component = {
492
- id: propDef.id || this.generateComponentId(typeInfo.type),
493
- name: propDef.name,
494
- type: typeInfo.type,
495
- label: propDef.title || propDef.label || this.humanizeFieldName(fieldName),
496
- defaultValue: propDef.default || propDef.defaultValue || undefined,
497
- expressionValue: propDef.expressionValue || undefined,
498
- content: propDef.content,
499
- buttonType: propDef.buttonType,
500
- textboxType: propDef.textboxType,
501
- ...Object.keys(validation).length > 0 ? validation : undefined
502
- };
503
- if (typeInfo.textboxType) {
504
- component.textboxType = typeInfo.textboxType;
505
- // Add default regex patterns for email and url
506
- if (typeInfo.textboxType === 'email') {
507
- if (!validation.pattern) {
508
- validation.regex = '^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$'; // Standard email regex
509
- validation.validationMessage = 'Please enter a valid email address';
510
- }
511
- }
512
- else if (typeInfo.textboxType === 'url') {
513
- if (!validation.pattern) {
514
- validation.regex = '^(https?:\\/\\/)?(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&//=]*)$';
515
- validation.validationMessage = 'Please enter a valid URL';
516
- }
517
- }
518
- }
519
- if (typeInfo.type === 'date' && component.defaultValue !== undefined && component.defaultValue !== null) {
520
- const formatted = this.formatDateTimeValue(component.defaultValue);
521
- if (formatted)
522
- component.defaultValue = formatted;
523
- }
524
- if ((typeInfo.type === 'splitButton' || typeInfo.type === 'dropdown' || typeInfo.type === 'multiselect' || typeInfo.type === 'radio') && (propDef.enum || propDef.options)) {
525
- component.options = propDef.enum || propDef.options;
526
- }
527
- return component;
528
- }
529
- parseDateFromValue(value) {
530
- if (value == null)
531
- return null;
532
- // numeric timestamps
533
- if (typeof value === 'number') {
534
- // assume seconds if small
535
- if (value < 1e12)
536
- return new Date(value * 1000);
537
- return new Date(value);
538
- }
539
- if (typeof value === 'string') {
540
- const s = value.trim();
541
- // Try ISO / RFC parse
542
- const dIso = new Date(s);
543
- if (!isNaN(dIso.getTime()))
544
- return dIso;
545
- // yyyy-mm-dd[ T hh:mm(:ss)]
546
- const m = s.match(/^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2}))?)?/);
547
- if (m)
548
- return new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]), Number(m[4] || 0), Number(m[5] || 0), Number(m[6] || 0));
549
- // mm/dd/yyyy or m/d/yyyy
550
- const m2 = s.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})(?:[ T](\d{1,2}):(\d{2})(?:[:](\d{2}))?)?/);
551
- if (m2)
552
- return new Date(Number(m2[3]), Number(m2[1]) - 1, Number(m2[2]), Number(m2[4] || 0), Number(m2[5] || 0), Number(m2[6] || 0));
553
- }
554
- return null;
555
- }
556
- formatDate(d) {
557
- const mm = (d.getMonth() + 1).toString().padStart(2, '0');
558
- const dd = d.getDate().toString().padStart(2, '0');
559
- const yyyy = d.getFullYear();
560
- return `${mm}/${dd}/${yyyy}`;
561
- }
562
- // private formatTime(d: Date): string {
563
- // let hours = d.getHours();
564
- // const minutes = d.getMinutes().toString().padStart(2, '0');
565
- // const ampm = hours >= 12 ? 'PM' : 'AM';
566
- // hours = hours % 12;
567
- // if (hours === 0) hours = 12;
568
- // return `${hours.toString().padStart(2, '0')}:${minutes} ${ampm}`;
569
- // }
570
- formatDateTimeValue(value) {
571
- const d = this.parseDateFromValue(value);
572
- if (!d)
573
- return undefined;
574
- const datePart = this.formatDate(d);
575
- const hasTime = d.getHours() !== 0 || d.getMinutes() !== 0 || /\dT\d{2}:\d{2}/.test(String(value));
576
- if (hasTime)
577
- return `${datePart}`;
578
- return datePart;
579
- }
580
- mapJsonSchemaType(propDef) {
581
- const schemaType = propDef.type;
582
- if (schemaType === 'string') {
583
- const fmt = (propDef.format || '').toString().toLowerCase();
584
- const text = ((propDef.title || propDef.label || '') + ' ' + (propDef.description || '')).toLowerCase();
585
- if (/email|mail|e-?mail/.test(fmt) || /email|mail|e-?mail/.test(text))
586
- return { type: 'textbox', textboxType: 'email' };
587
- if (/uri|url|link|website/.test(fmt) || /uri|url|link|website/.test(text))
588
- return { type: 'textbox', textboxType: 'url' };
589
- if (/phone|tel|mobile|cell|msisdn/.test(fmt) || /phone|tel|mobile|cell|msisdn/.test(text))
590
- return { type: 'textbox', textboxType: 'number' };
591
- if (/password|pwd|pass|security|secret|key/.test(fmt) || /password|pwd|pass|security|secret|key/.test(text))
592
- return { type: 'textbox', textboxType: 'password' };
593
- if (fmt === 'date' || fmt === 'date-time' || /date|birthday|dob/.test(text))
594
- return { type: 'date' };
595
- if (propDef.enum)
596
- return { type: 'dropdown' };
597
- if (propDef.options)
598
- return { type: 'dropdown' };
599
- return { type: this.config.defaultTextType };
600
- }
601
- if (schemaType === 'number' || schemaType === 'integer')
602
- return { type: 'number' };
603
- if (schemaType === 'boolean')
604
- return { type: 'checkbox' };
605
- if (schemaType === 'array')
606
- return { type: 'multiselect' };
607
- // Presentation / custom widget types
608
- if (schemaType === 'message')
609
- return { type: 'message' };
610
- if (schemaType === 'button')
611
- return { type: 'button' };
612
- if (schemaType === 'signature' || schemaType === 'sign')
613
- return { type: 'signature' };
614
- if (schemaType === 'image' || schemaType === 'imageEditor')
615
- return { type: 'imageEditor' };
616
- if (schemaType === 'file' || schemaType === 'fileUpload')
617
- return { type: 'fileUpload' };
618
- if (schemaType === 'color' || schemaType === 'colorPicker')
619
- return { type: 'colorPicker' };
620
- if (schemaType === 'splitButton')
621
- return { type: 'splitButton' };
622
- return { type: this.config.defaultTextType };
623
- }
624
- generateComponentId(type) {
625
- if (!this.config.generateIds)
626
- return '';
627
- const timestamp = Date.now();
628
- const random = Math.floor(Math.random() * 1000);
629
- return `${type}_${timestamp}_${random}`;
630
- }
631
- humanizeFieldName(fieldName) {
632
- let result = fieldName.replace(/_/g, ' ');
633
- result = result.replace(/([a-z])([A-Z])/g, '$1 $2');
634
- return result;
635
- }
636
- }
637
- /**
638
- * @private
639
- */
640
- export const validateJSON = (jsonString) => {
641
- try {
642
- JSON.parse(jsonString);
643
- return { valid: true };
644
- }
645
- catch (error) {
646
- return {
647
- valid: false,
648
- errors: [`Invalid JSON syntax: ${error.message}`]
649
- };
650
- }
651
- };
652
- /**
653
- * @private
654
- */
655
- export const validateFormSchema = (schema) => {
656
- const errors = [];
657
- const warnings = [];
658
- if (!schema.components) {
659
- errors.push('Missing required property: components');
660
- }
661
- else if (!Array.isArray(schema.components)) {
662
- errors.push('Property "components" must be an array');
663
- }
664
- // Validate component structure
665
- if (Array.isArray(schema.components)) {
666
- const componentIds = new Set();
667
- const validateComponent = (comp, path = 'root') => {
668
- if (!comp.id) {
669
- errors.push(`Component at ${path} is missing "id" property`);
670
- }
671
- else {
672
- if (componentIds.has(comp.id)) {
673
- errors.push(`Duplicate component ID found: ${comp.id}`);
674
- }
675
- componentIds.add(comp.id);
676
- }
677
- if (!comp.type) {
678
- errors.push(`Component "${comp.id || 'unknown'}" is missing "type" property`);
679
- }
680
- if (comp.children && Array.isArray(comp.children)) {
681
- comp.children.forEach((child, index) => {
682
- validateComponent(child, `${path}.children[${index}]`);
683
- });
684
- }
685
- if (comp.tabItems && Array.isArray(comp.tabItems)) {
686
- comp.tabItems.forEach((tab, tabIndex) => {
687
- if (tab.content && Array.isArray(tab.content)) {
688
- tab.content.forEach((child, index) => {
689
- validateComponent(child, `${path}.tabItems[${tabIndex}].content[${index}]`);
690
- });
691
- }
692
- });
693
- }
694
- if (comp.tableCells && Array.isArray(comp.tableCells)) {
695
- comp.tableCells.forEach((row, rowIndex) => {
696
- if (Array.isArray(row)) {
697
- row.forEach((cell, colIndex) => {
698
- if (Array.isArray(cell)) {
699
- cell.forEach((child, index) => {
700
- validateComponent(child, `${path}.tableCells[${rowIndex}][${colIndex}][${index}]`);
701
- });
702
- }
703
- });
704
- }
705
- });
706
- }
707
- };
708
- schema.components.forEach((comp, index) => {
709
- validateComponent(comp, `components[${index}]`);
710
- });
711
- }
712
- return {
713
- valid: errors.length === 0,
714
- errors: errors.length > 0 ? errors : undefined,
715
- warnings: warnings.length > 0 ? warnings : undefined
716
- };
717
- };
718
- /**
719
- * @private
720
- */
721
- export const sanitizeFormSchema = (schema) => {
722
- const sanitized = {
723
- version: schema.version || "0.1.0",
724
- components: schema.components || []
725
- };
726
- const sanitizeComponent = (comp) => {
727
- const sanitizedComp = {
728
- ...comp,
729
- _configured: true
730
- };
731
- if (comp.children && Array.isArray(comp.children)) {
732
- sanitizedComp.children = comp.children.map(sanitizeComponent);
733
- }
734
- if (comp.tabItems && Array.isArray(comp.tabItems)) {
735
- sanitizedComp.tabItems = comp.tabItems.map((tab) => ({
736
- ...tab,
737
- content: tab.content ? tab.content.map(sanitizeComponent) : []
738
- }));
739
- }
740
- if (comp.tableCells && Array.isArray(comp.tableCells)) {
741
- sanitizedComp.tableCells = comp.tableCells.map((row) => row.map((cell) => Array.isArray(cell) ? cell.map(sanitizeComponent) : []));
742
- }
743
- return sanitizedComp;
744
- };
745
- sanitized.components = sanitized.components.map(sanitizeComponent);
746
- return sanitized;
747
- };
748
- /**
749
- * @private
750
- */
751
- export const exportFormSchema = (schema) => {
752
- return JSON.stringify(schema, null, 2);
753
- };