@pixelated-tech/components 3.2.13 → 3.3.0
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/README.COMPONENTS.md +435 -78
- package/README.md +59 -32
- package/dist/components/callout/callout.scss +0 -3
- package/dist/components/cms/flickr.js +8 -2
- package/dist/components/cms/google.reviews.components.js +1 -1
- package/dist/components/general/tab.css +105 -0
- package/dist/components/general/tab.js +26 -0
- package/dist/components/menu/menu-expando.js +7 -1
- package/dist/components/nerdjoke/nerdjoke.js +13 -7
- package/dist/components/seo/manifest.js +40 -0
- package/dist/components/seo/metadata.components.js +0 -19
- package/dist/components/seo/metadata.functions.js +111 -0
- package/dist/components/seo/schema-blogposting.functions.js +42 -0
- package/dist/components/seo/schema-blogposting.js +0 -46
- package/dist/components/seo/schema-localbusiness.js +46 -2
- package/dist/components/seo/schema-website.js +31 -2
- package/dist/components/seo/sitemap.js +4 -4
- package/dist/components/shoppingcart/shoppingcart.components.js +4 -4
- package/dist/components/sitebuilder/config/ConfigBuilder.css +266 -0
- package/dist/components/sitebuilder/config/ConfigBuilder.js +221 -0
- package/dist/components/{pagebuilder → sitebuilder}/form/form.css +55 -30
- package/dist/components/sitebuilder/form/formbuilder.js +106 -0
- package/dist/components/sitebuilder/form/formcomponents.js +356 -0
- package/dist/components/sitebuilder/form/formengine.js +82 -0
- package/dist/components/{pagebuilder/form/form.js → sitebuilder/form/formextractor.js} +10 -211
- package/dist/components/sitebuilder/form/formutils.js +206 -0
- package/dist/components/sitebuilder/form/formvalidator.js +123 -0
- package/dist/components/{pagebuilder → sitebuilder/page}/components/ComponentPropertiesForm.js +1 -1
- package/dist/components/{pagebuilder → sitebuilder/page}/components/PageBuilderUI.js +2 -2
- package/dist/components/{pagebuilder → sitebuilder/page}/components/PageEngine.js +1 -1
- package/dist/components/sitebuilder/page/documentation/api-examples/save-route-example.js +37 -0
- package/dist/components/{pagebuilder → sitebuilder/page}/lib/componentMap.js +3 -3
- package/dist/components/{pagebuilder → sitebuilder/page}/lib/componentMetadata.js +2 -2
- package/dist/components/{pagebuilder → sitebuilder/page}/lib/pageStorageContentful.js +2 -2
- package/dist/components/sitebuilder/page/lib/pageStorageTypes.js +1 -0
- package/dist/data/form.json +18 -18
- package/dist/data/routes.json +25 -0
- package/dist/data/routes2.json +25 -0
- package/dist/data/shipping.to.json +9 -9
- package/dist/data/siteinfo-form.json +200 -0
- package/dist/index.js +31 -23
- package/dist/index.server.js +24 -18
- package/dist/types/components/cms/flickr.d.ts.map +1 -1
- package/dist/types/components/cms/google.reviews.components.d.ts.map +1 -1
- package/dist/types/components/config/config.types.d.ts +30 -0
- package/dist/types/components/config/config.types.d.ts.map +1 -1
- package/dist/types/components/general/semantic.d.ts +3 -3
- package/dist/types/components/general/tab.d.ts +18 -0
- package/dist/types/components/general/tab.d.ts.map +1 -0
- package/dist/types/components/menu/menu-expando.d.ts.map +1 -1
- package/dist/types/components/nerdjoke/nerdjoke.d.ts.map +1 -1
- package/dist/types/components/seo/manifest.d.ts +19 -0
- package/dist/types/components/seo/manifest.d.ts.map +1 -0
- package/dist/types/components/seo/metadata.components.d.ts +0 -17
- package/dist/types/components/seo/metadata.components.d.ts.map +1 -1
- package/dist/types/components/seo/{metadata.d.ts → metadata.functions.d.ts} +15 -1
- package/dist/types/components/seo/metadata.functions.d.ts.map +1 -0
- package/dist/types/components/seo/schema-blogposting.d.ts +1 -25
- package/dist/types/components/seo/schema-blogposting.d.ts.map +1 -1
- package/dist/types/components/seo/schema-blogposting.functions.d.ts +26 -0
- package/dist/types/components/seo/schema-blogposting.functions.d.ts.map +1 -0
- package/dist/types/components/seo/schema-localbusiness.d.ts +22 -23
- package/dist/types/components/seo/schema-localbusiness.d.ts.map +1 -1
- package/dist/types/components/seo/schema-website.d.ts +17 -18
- package/dist/types/components/seo/schema-website.d.ts.map +1 -1
- package/dist/types/components/seo/sitemap.d.ts.map +1 -1
- package/dist/types/components/shoppingcart/shoppingcart.components.d.ts +1 -1
- package/dist/types/components/sitebuilder/config/ConfigBuilder.d.ts +86 -0
- package/dist/types/components/sitebuilder/config/ConfigBuilder.d.ts.map +1 -0
- package/dist/types/components/sitebuilder/form/formbuilder.d.ts +11 -0
- package/dist/types/components/sitebuilder/form/formbuilder.d.ts.map +1 -0
- package/dist/types/components/{pagebuilder → sitebuilder}/form/formcomponents.d.ts +12 -16
- package/dist/types/components/sitebuilder/form/formcomponents.d.ts.map +1 -0
- package/dist/types/components/{pagebuilder/form/form.submit.d.ts → sitebuilder/form/formemailer.d.ts} +1 -1
- package/dist/types/components/sitebuilder/form/formemailer.d.ts.map +1 -0
- package/dist/types/components/sitebuilder/form/formengine.d.ts +14 -0
- package/dist/types/components/sitebuilder/form/formengine.d.ts.map +1 -0
- package/dist/types/components/sitebuilder/form/formextractor.d.ts +25 -0
- package/dist/types/components/sitebuilder/form/formextractor.d.ts.map +1 -0
- package/dist/types/components/{pagebuilder/form/formvalidations.d.ts → sitebuilder/form/formfieldvalidations.d.ts} +1 -1
- package/dist/types/components/sitebuilder/form/formfieldvalidations.d.ts.map +1 -0
- package/dist/types/components/sitebuilder/form/formtypes.d.ts +66 -0
- package/dist/types/components/sitebuilder/form/formtypes.d.ts.map +1 -0
- package/dist/types/components/sitebuilder/form/formutils.d.ts +20 -0
- package/dist/types/components/sitebuilder/form/formutils.d.ts.map +1 -0
- package/dist/types/components/sitebuilder/form/formvalidator.d.ts +20 -0
- package/dist/types/components/sitebuilder/form/formvalidator.d.ts.map +1 -0
- package/dist/types/components/sitebuilder/page/components/ComponentPropertiesForm.d.ts.map +1 -0
- package/dist/types/components/sitebuilder/page/components/ComponentSelector.d.ts.map +1 -0
- package/dist/types/components/sitebuilder/page/components/ComponentTree.d.ts.map +1 -0
- package/dist/types/components/{pagebuilder → sitebuilder/page}/components/PageBuilderUI.d.ts +1 -1
- package/dist/types/components/sitebuilder/page/components/PageBuilderUI.d.ts.map +1 -0
- package/dist/types/components/sitebuilder/page/components/PageEngine.d.ts.map +1 -0
- package/dist/types/components/sitebuilder/page/components/SaveLoadSection.d.ts.map +1 -0
- package/dist/types/components/sitebuilder/page/documentation/api-examples/save-route-example.d.ts +6 -0
- package/dist/types/components/sitebuilder/page/documentation/api-examples/save-route-example.d.ts.map +1 -0
- package/dist/types/components/sitebuilder/page/lib/componentGeneration.d.ts.map +1 -0
- package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/componentMap.d.ts +3 -3
- package/dist/types/components/sitebuilder/page/lib/componentMap.d.ts.map +1 -0
- package/dist/types/components/sitebuilder/page/lib/componentMetadata.d.ts.map +1 -0
- package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/pageStorageContentful.d.ts +1 -1
- package/dist/types/components/sitebuilder/page/lib/pageStorageContentful.d.ts.map +1 -0
- package/dist/types/components/sitebuilder/page/lib/pageStorageLocal.d.ts.map +1 -0
- package/dist/types/components/sitebuilder/page/lib/pageStorageTypes.d.ts.map +1 -0
- package/dist/types/components/sitebuilder/page/lib/propTypeIntrospection.d.ts.map +1 -0
- package/dist/types/components/sitebuilder/page/lib/types.d.ts.map +1 -0
- package/dist/types/components/sitebuilder/page/lib/usePageBuilder.d.ts.map +1 -0
- package/dist/types/index.d.ts +30 -21
- package/dist/types/index.server.d.ts +23 -17
- package/dist/types/stories/general/tab.stories.d.ts +45 -0
- package/dist/types/stories/general/tab.stories.d.ts.map +1 -0
- package/dist/types/stories/seo/seo.googleanalytics.stories.d.ts.map +1 -1
- package/dist/types/stories/seo/seo.metadata.stories.d.ts +1 -1
- package/dist/types/stories/seo/seo.metadata.stories.d.ts.map +1 -1
- package/dist/types/stories/seo/seo.schema.stories.d.ts +23 -0
- package/dist/types/stories/seo/seo.schema.stories.d.ts.map +1 -0
- package/dist/types/stories/sitebuilder/configbuilder.stories.d.ts +48 -0
- package/dist/types/stories/sitebuilder/configbuilder.stories.d.ts.map +1 -0
- package/dist/types/stories/{pagebuilder → sitebuilder}/form-builder.stories.d.ts +1 -1
- package/dist/types/stories/sitebuilder/form-builder.stories.d.ts.map +1 -0
- package/dist/types/stories/{pagebuilder → sitebuilder}/form-engine.stories.d.ts +1 -1
- package/dist/types/stories/sitebuilder/form-engine.stories.d.ts.map +1 -0
- package/dist/types/stories/{pagebuilder → sitebuilder}/form-extractor.stories.d.ts +1 -1
- package/dist/types/stories/sitebuilder/form-extractor.stories.d.ts.map +1 -0
- package/dist/types/stories/{pagebuilder → sitebuilder}/pagebuilder.stories.d.ts +1 -1
- package/dist/types/stories/{pagebuilder → sitebuilder}/pagebuilder.stories.d.ts.map +1 -1
- package/dist/types/stories/{pagebuilder → sitebuilder}/pagebuilder.usageguide.stories.d.ts +1 -1
- package/dist/types/stories/{pagebuilder → sitebuilder}/pagebuilder.usageguide.stories.d.ts.map +1 -1
- package/dist/types/stories/{pagebuilder → sitebuilder}/pageengine.stories.d.ts +1 -1
- package/dist/types/stories/{pagebuilder → sitebuilder}/pageengine.stories.d.ts.map +1 -1
- package/dist/types/tests/configbuilder.test.d.ts +2 -0
- package/dist/types/tests/configbuilder.test.d.ts.map +1 -0
- package/dist/types/tests/manifest.test.d.ts +2 -0
- package/dist/types/tests/manifest.test.d.ts.map +1 -0
- package/dist/types/tests/tab.test.d.ts +2 -0
- package/dist/types/tests/tab.test.d.ts.map +1 -0
- package/package.json +6 -5
- package/dist/components/pagebuilder/form/formcomponents.js +0 -359
- package/dist/components/seo/metadata.js +0 -108
- package/dist/components/utilities/api.js +0 -36
- package/dist/types/components/pagebuilder/components/ComponentPropertiesForm.d.ts.map +0 -1
- package/dist/types/components/pagebuilder/components/ComponentSelector.d.ts.map +0 -1
- package/dist/types/components/pagebuilder/components/ComponentTree.d.ts.map +0 -1
- package/dist/types/components/pagebuilder/components/PageBuilderUI.d.ts.map +0 -1
- package/dist/types/components/pagebuilder/components/PageEngine.d.ts.map +0 -1
- package/dist/types/components/pagebuilder/components/SaveLoadSection.d.ts.map +0 -1
- package/dist/types/components/pagebuilder/form/form.d.ts +0 -46
- package/dist/types/components/pagebuilder/form/form.d.ts.map +0 -1
- package/dist/types/components/pagebuilder/form/form.submit.d.ts.map +0 -1
- package/dist/types/components/pagebuilder/form/formcomponents.d.ts.map +0 -1
- package/dist/types/components/pagebuilder/form/formvalidations.d.ts.map +0 -1
- package/dist/types/components/pagebuilder/lib/componentGeneration.d.ts.map +0 -1
- package/dist/types/components/pagebuilder/lib/componentMap.d.ts.map +0 -1
- package/dist/types/components/pagebuilder/lib/componentMetadata.d.ts.map +0 -1
- package/dist/types/components/pagebuilder/lib/pageStorageContentful.d.ts.map +0 -1
- package/dist/types/components/pagebuilder/lib/pageStorageLocal.d.ts.map +0 -1
- package/dist/types/components/pagebuilder/lib/pageStorageTypes.d.ts.map +0 -1
- package/dist/types/components/pagebuilder/lib/propTypeIntrospection.d.ts.map +0 -1
- package/dist/types/components/pagebuilder/lib/types.d.ts.map +0 -1
- package/dist/types/components/pagebuilder/lib/usePageBuilder.d.ts.map +0 -1
- package/dist/types/components/seo/metadata.d.ts.map +0 -1
- package/dist/types/components/utilities/api.d.ts +0 -16
- package/dist/types/components/utilities/api.d.ts.map +0 -1
- package/dist/types/stories/pagebuilder/form-builder.stories.d.ts.map +0 -1
- package/dist/types/stories/pagebuilder/form-engine.stories.d.ts.map +0 -1
- package/dist/types/stories/pagebuilder/form-extractor.stories.d.ts.map +0 -1
- package/dist/types/tests/api.test.d.ts +0 -2
- package/dist/types/tests/api.test.d.ts.map +0 -1
- /package/dist/components/{pagebuilder/form/form.submit.js → sitebuilder/form/formemailer.js} +0 -0
- /package/dist/components/{pagebuilder/form/formvalidations.js → sitebuilder/form/formfieldvalidations.js} +0 -0
- /package/dist/components/{pagebuilder/lib/pageStorageTypes.js → sitebuilder/form/formtypes.js} +0 -0
- /package/dist/components/{pagebuilder → sitebuilder/page}/components/ComponentSelector.js +0 -0
- /package/dist/components/{pagebuilder → sitebuilder/page}/components/ComponentTree.js +0 -0
- /package/dist/components/{pagebuilder → sitebuilder/page}/components/SaveLoadSection.js +0 -0
- /package/dist/components/{pagebuilder → sitebuilder/page}/components/pagebuilder.scss +0 -0
- /package/dist/components/{pagebuilder → sitebuilder/page}/lib/componentGeneration.js +0 -0
- /package/dist/components/{pagebuilder → sitebuilder/page}/lib/pageStorageLocal.js +0 -0
- /package/dist/components/{pagebuilder → sitebuilder/page}/lib/propTypeIntrospection.js +0 -0
- /package/dist/components/{pagebuilder → sitebuilder/page}/lib/types.js +0 -0
- /package/dist/components/{pagebuilder → sitebuilder/page}/lib/usePageBuilder.js +0 -0
- /package/dist/types/components/{pagebuilder → sitebuilder/page}/components/ComponentPropertiesForm.d.ts +0 -0
- /package/dist/types/components/{pagebuilder → sitebuilder/page}/components/ComponentSelector.d.ts +0 -0
- /package/dist/types/components/{pagebuilder → sitebuilder/page}/components/ComponentTree.d.ts +0 -0
- /package/dist/types/components/{pagebuilder → sitebuilder/page}/components/PageEngine.d.ts +0 -0
- /package/dist/types/components/{pagebuilder → sitebuilder/page}/components/SaveLoadSection.d.ts +0 -0
- /package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/componentGeneration.d.ts +0 -0
- /package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/componentMetadata.d.ts +0 -0
- /package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/pageStorageLocal.d.ts +0 -0
- /package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/pageStorageTypes.d.ts +0 -0
- /package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/propTypeIntrospection.d.ts +0 -0
- /package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/types.d.ts +0 -0
- /package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/usePageBuilder.d.ts +0 -0
|
@@ -59,43 +59,68 @@ form span {
|
|
|
59
59
|
vertical-align: middle;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
|
|
63
|
+
/* ========================================
|
|
64
|
+
============== TOOLTIP ==============
|
|
65
|
+
======================================== */
|
|
66
|
+
|
|
67
|
+
.tooltip-container {
|
|
68
|
+
display: inline-block;
|
|
65
69
|
position: relative;
|
|
70
|
+
margin-left: 5px;
|
|
71
|
+
vertical-align: middle;
|
|
66
72
|
}
|
|
67
73
|
|
|
68
|
-
.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
.tooltip-icon {
|
|
75
|
+
display: inline-flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
justify-content: center;
|
|
78
|
+
font-size: 1.0rem;
|
|
79
|
+
font-weight: bold;
|
|
80
|
+
cursor: pointer;
|
|
81
|
+
padding: 0 3px;
|
|
82
|
+
line-height: 1;
|
|
83
|
+
color: #00F;
|
|
75
84
|
}
|
|
76
85
|
|
|
77
|
-
.
|
|
78
|
-
|
|
79
|
-
margin: 5px 0 0 -10px;
|
|
80
|
-
background-color: #DDD;
|
|
81
|
-
border-radius: 5px;
|
|
86
|
+
.tooltip-icon-validate {
|
|
87
|
+
color: #d00;
|
|
82
88
|
}
|
|
83
89
|
|
|
84
|
-
.
|
|
85
|
-
display: none;
|
|
90
|
+
.tooltip-text {
|
|
86
91
|
position: absolute;
|
|
87
|
-
top: -
|
|
88
|
-
|
|
89
|
-
width: 200px;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
top: -15px;
|
|
93
|
+
left: 35px;
|
|
94
|
+
min-width: 200px;
|
|
95
|
+
max-width: 300px;
|
|
96
|
+
padding: 8px 12px;
|
|
97
|
+
background-color: #333;
|
|
98
|
+
color: white;
|
|
99
|
+
border-radius: 4px;
|
|
100
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
|
101
|
+
z-index: 1000;
|
|
102
|
+
font-size: 0.9em;
|
|
103
|
+
white-space: normal;
|
|
97
104
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
105
|
+
|
|
106
|
+
.tooltip-text::before {
|
|
107
|
+
content: '';
|
|
108
|
+
position: absolute;
|
|
109
|
+
top: 50%;
|
|
110
|
+
left: -8px;
|
|
111
|
+
transform: translateY(-50%);
|
|
112
|
+
width: 0;
|
|
113
|
+
height: 0;
|
|
114
|
+
border-top: 8px solid transparent;
|
|
115
|
+
border-bottom: 8px solid transparent;
|
|
116
|
+
border-right: 8px solid #333;
|
|
117
|
+
z-index: 1001;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.tooltip-text-item {
|
|
121
|
+
margin-bottom: 4px;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.tooltip-text-item:last-child {
|
|
125
|
+
margin-bottom: 0;
|
|
101
126
|
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import * as FC from './formcomponents';
|
|
5
|
+
import { mapTypeToComponent, generateTypeField, debug } from './formutils';
|
|
6
|
+
import { FormEngine } from './formengine';
|
|
7
|
+
/* ===== FORM BUILDER =====
|
|
8
|
+
Display all the components for a Form Builder -
|
|
9
|
+
Element Buttons, Element Details, and the Form */
|
|
10
|
+
export function FormBuilder() {
|
|
11
|
+
const [formData, setFormData] = useState({ fields: [] });
|
|
12
|
+
const [fieldFormData, setFieldFormData] = useState({ fields: [] });
|
|
13
|
+
function appendFormData(event) {
|
|
14
|
+
// APPEND JSON FOR NEW FIELD TO EXISTING JSON CONFIG OBJECT - EXPOSED EXTERNAL
|
|
15
|
+
if (debug)
|
|
16
|
+
console.log("Appending form Data...");
|
|
17
|
+
const props = {};
|
|
18
|
+
const target = event.target;
|
|
19
|
+
for (const prop in target) {
|
|
20
|
+
const thisProp = target[prop];
|
|
21
|
+
if (thisProp && thisProp.value && (thisProp.value !== Object(thisProp.value))) {
|
|
22
|
+
props[thisProp.name] = thisProp.value;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const field = {};
|
|
26
|
+
field.props = props;
|
|
27
|
+
field.component = mapTypeToComponent(field.props.type);
|
|
28
|
+
let fields = [];
|
|
29
|
+
if (Object.keys(formData).length > 0) {
|
|
30
|
+
fields = JSON.parse(JSON.stringify(formData.fields));
|
|
31
|
+
}
|
|
32
|
+
fields[fields.length] = field;
|
|
33
|
+
setFormData({ fields: fields });
|
|
34
|
+
setFieldFormData({ fields: [] });
|
|
35
|
+
return (field);
|
|
36
|
+
}
|
|
37
|
+
/* function getCircularReplacer() {
|
|
38
|
+
const seen = new WeakSet();
|
|
39
|
+
return (key, value) => {
|
|
40
|
+
if (typeof value === 'object' && value !== null) {
|
|
41
|
+
if (seen.has(value)) {
|
|
42
|
+
return ; // return undefined ;
|
|
43
|
+
}
|
|
44
|
+
seen.add(value);
|
|
45
|
+
}
|
|
46
|
+
return value;
|
|
47
|
+
};
|
|
48
|
+
} */
|
|
49
|
+
return (_jsxs("div", { className: "section-container", children: [_jsxs("div", { style: { display: 'inline-block', verticalAlign: 'top' }, children: [_jsx(FormBuild, { setFormData: setFieldFormData }), _jsx("div", {}), _jsx(FormEngine, { name: "field_props", id: "field_props", onSubmitHandler: appendFormData, formData: fieldFormData })] }), _jsx("div", { style: { display: 'inline-block', verticalAlign: 'top' }, children: _jsx("pre", { style: { width: '100%' }, children: JSON.stringify(formData, null, 2) }) }), _jsxs("div", { children: [_jsx("br", {}), _jsx("br", {}), _jsx("hr", {}), _jsx("br", {}), _jsx("br", {})] }), _jsx("div", { style: { display: 'block', verticalAlign: 'top' }, children: _jsx(FormEngine, { formData: formData || {} }) })] }));
|
|
50
|
+
}
|
|
51
|
+
/*
|
|
52
|
+
===== FORM BUILD =====
|
|
53
|
+
Dynamically generate, component by component, and prop by prop,
|
|
54
|
+
the JSON to create a form via FormEngine
|
|
55
|
+
*/
|
|
56
|
+
FormBuild.propTypes = {
|
|
57
|
+
setFormData: PropTypes.func.isRequired,
|
|
58
|
+
};
|
|
59
|
+
export function FormBuild(props) {
|
|
60
|
+
function generateFieldJSON(component, type) {
|
|
61
|
+
// GENERATE THE JSON TO DISPLAY A FORM TO ADD A FIELD - INTERNAL
|
|
62
|
+
if (debug)
|
|
63
|
+
console.log("Generating Form JSON for ", component, " Type : ", type);
|
|
64
|
+
const form = { fields: [] };
|
|
65
|
+
let i = 0;
|
|
66
|
+
for (const prop in FC[component].propTypes) {
|
|
67
|
+
const field = {};
|
|
68
|
+
field.component = 'FormInput';
|
|
69
|
+
const props = {};
|
|
70
|
+
props.label = prop;
|
|
71
|
+
props.name = prop;
|
|
72
|
+
props.id = prop;
|
|
73
|
+
props.type = 'text';
|
|
74
|
+
if (prop === 'type') {
|
|
75
|
+
props.disabled = true;
|
|
76
|
+
props.value = type;
|
|
77
|
+
props.list = 'inputTypes';
|
|
78
|
+
}
|
|
79
|
+
field.props = props;
|
|
80
|
+
form.fields[i] = field;
|
|
81
|
+
i++;
|
|
82
|
+
}
|
|
83
|
+
const addButton = {
|
|
84
|
+
component: 'FormButton',
|
|
85
|
+
props: {
|
|
86
|
+
label: 'Add ' + component,
|
|
87
|
+
type: 'submit',
|
|
88
|
+
id: 'Add ' + component,
|
|
89
|
+
text: 'Add ' + component
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
form.fields[i] = addButton;
|
|
93
|
+
return (form);
|
|
94
|
+
}
|
|
95
|
+
function handlePhaseOneSubmit(event) {
|
|
96
|
+
// GENERATE THE JSON TO DISPLAY A FORM TO ADD A FIELD - EXTERNAL
|
|
97
|
+
const target = event.target;
|
|
98
|
+
const typeElement = target.elements.namedItem('type');
|
|
99
|
+
const myType = typeElement ? typeElement.value : '';
|
|
100
|
+
const myComponent = mapTypeToComponent(myType);
|
|
101
|
+
const fieldJSON = generateFieldJSON(myComponent, myType);
|
|
102
|
+
props.setFormData(fieldJSON);
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
return (_jsx(_Fragment, { children: _jsx(FormEngine, { formData: generateTypeField(), onSubmitHandler: event => { handlePhaseOneSubmit(event); }, name: "build", id: "build", method: "post" }) }));
|
|
106
|
+
}
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React, { useState } from "react";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
import { validateField } from "./formvalidator";
|
|
5
|
+
import { useFormValidation } from "./formvalidator";
|
|
6
|
+
import * as FVF from "./formfieldvalidations";
|
|
7
|
+
import "./form.css";
|
|
8
|
+
/*
|
|
9
|
+
InferProps to generate Types
|
|
10
|
+
https://amanhimself.dev/blog/prop-types-in-react-and-typescript/#inferring-proptypes-in-typescript
|
|
11
|
+
*/
|
|
12
|
+
// Shared helper to setup input props for form components
|
|
13
|
+
const setupInputProps = (props, display) => {
|
|
14
|
+
// Clone props efficiently using destructuring instead of JSON.parse/stringify
|
|
15
|
+
let inputProps = { ...props };
|
|
16
|
+
// Remove props that shouldn't go to DOM
|
|
17
|
+
inputProps = Object.fromEntries(Object.entries(inputProps).filter(([key]) => !['display', 'label', 'listItems', 'validate', 'options', 'parent', 'text', 'checked'].includes(key)));
|
|
18
|
+
// Set className based on display mode
|
|
19
|
+
inputProps["className"] = (display == "vertical") ? "displayVertical" : "";
|
|
20
|
+
// Handle controlled vs uncontrolled inputs properly
|
|
21
|
+
// If value is provided, use it for controlled behavior
|
|
22
|
+
// Otherwise, use defaultValue for uncontrolled behavior
|
|
23
|
+
if (props.value !== undefined) {
|
|
24
|
+
inputProps["value"] = props.value;
|
|
25
|
+
// Remove defaultValue if value is present to avoid conflicts
|
|
26
|
+
delete inputProps["defaultValue"];
|
|
27
|
+
}
|
|
28
|
+
else if (props.defaultValue !== undefined) {
|
|
29
|
+
inputProps["defaultValue"] = props.defaultValue;
|
|
30
|
+
}
|
|
31
|
+
// For radio buttons, prioritize defaultChecked over checked to let browser handle state
|
|
32
|
+
// This prevents React's "both checked and defaultChecked" error
|
|
33
|
+
if (props.type === 'radio' || (props.parent && props.parent.type === 'radio')) {
|
|
34
|
+
if (props.checked !== undefined) {
|
|
35
|
+
delete inputProps["checked"];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return inputProps;
|
|
39
|
+
};
|
|
40
|
+
// Custom hook for common form component logic
|
|
41
|
+
const useFormComponent = (props) => {
|
|
42
|
+
const [validationState, setValidationState] = useState({ isValid: true, errors: [] });
|
|
43
|
+
const { validateField: validateFormField } = useFormValidation();
|
|
44
|
+
// Handle onChange for immediate feedback
|
|
45
|
+
const handleChange = (event) => {
|
|
46
|
+
// Determine the value (checkbox vs other)
|
|
47
|
+
const value = event.target.type === 'checkbox' ? (event.target.checked ? event.target.value : '') : event.target.value;
|
|
48
|
+
// Call custom onChange handler synchronously so controlled inputs update immediately
|
|
49
|
+
const customOnChange = props.onChange || (props.parent && props.parent.onChange);
|
|
50
|
+
if (customOnChange) {
|
|
51
|
+
try {
|
|
52
|
+
customOnChange(value);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// swallow handler errors to avoid breaking validation flow
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
// Handle onBlur for full validation
|
|
60
|
+
const handleBlur = (event) => {
|
|
61
|
+
validateField(props, event).then(result => {
|
|
62
|
+
setValidationState(result);
|
|
63
|
+
if (props.id) {
|
|
64
|
+
validateFormField(props.id, result.isValid, result.errors);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
const formValidate = _jsx(FormTooltip, { id: `${props.id}-validate`, mode: "validate", text: validationState.errors });
|
|
69
|
+
const inputProps = setupInputProps(props, props.display);
|
|
70
|
+
// Add validation event handlers
|
|
71
|
+
inputProps["onChange"] = handleChange;
|
|
72
|
+
inputProps["onBlur"] = handleBlur;
|
|
73
|
+
inputProps["onInput"] = handleChange; // Also handle onInput for tests
|
|
74
|
+
return { validationState, formValidate, inputProps, handleChange, handleBlur };
|
|
75
|
+
};
|
|
76
|
+
// Helper function for generating options (used by FormSelect, FormRadio, FormCheckbox)
|
|
77
|
+
const generateOptions = (options, parentProps, prefix, optionComponent) => {
|
|
78
|
+
let result = [];
|
|
79
|
+
for (let option in options) {
|
|
80
|
+
let key = option;
|
|
81
|
+
let thisOption = options[key];
|
|
82
|
+
// Create parent object without options to avoid circular references
|
|
83
|
+
const parentWithoutOptions = Object.fromEntries(Object.entries(parentProps).filter(([key]) => key !== 'options'));
|
|
84
|
+
thisOption.parent = { ...parentWithoutOptions };
|
|
85
|
+
let thisKey = prefix + "-" + parentProps.id + "-" + thisOption.value;
|
|
86
|
+
let newOption = React.createElement(optionComponent, { key: thisKey, ...thisOption });
|
|
87
|
+
result.push(newOption);
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
};
|
|
91
|
+
FormLabel.propTypes = {
|
|
92
|
+
id: PropTypes.string.isRequired,
|
|
93
|
+
label: PropTypes.string,
|
|
94
|
+
tooltip: PropTypes.string,
|
|
95
|
+
className: PropTypes.string,
|
|
96
|
+
};
|
|
97
|
+
FormLabel.defaultProps = {
|
|
98
|
+
id: "",
|
|
99
|
+
label: "",
|
|
100
|
+
tooltip: "",
|
|
101
|
+
};
|
|
102
|
+
function FormLabel(props) {
|
|
103
|
+
return (_jsxs(_Fragment, { children: [props.label && props.id
|
|
104
|
+
? _jsx("label", { className: props.className || '', id: `lbl-${props.id}`, htmlFor: props.id, children: props.label })
|
|
105
|
+
: "", props.tooltip
|
|
106
|
+
? _jsx(FormTooltip, { id: props.id, text: [props.tooltip] })
|
|
107
|
+
: ""] }));
|
|
108
|
+
}
|
|
109
|
+
FormTooltip.propTypes = {
|
|
110
|
+
id: PropTypes.string,
|
|
111
|
+
text: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
112
|
+
className: PropTypes.string,
|
|
113
|
+
mode: PropTypes.oneOf(['tooltip', 'validate']),
|
|
114
|
+
};
|
|
115
|
+
FormTooltip.defaultProps = {
|
|
116
|
+
id: "",
|
|
117
|
+
mode: 'tooltip',
|
|
118
|
+
};
|
|
119
|
+
function FormTooltip(props) {
|
|
120
|
+
const mode = props.mode || 'tooltip';
|
|
121
|
+
if (mode === 'validate' && props.text.length <= 0) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
const [showTooltip, setShowTooltip] = useState(false);
|
|
125
|
+
const content = props.text.map((item, index) => (_jsx("div", { className: "tooltip-text-item", children: item }, index)));
|
|
126
|
+
const toggleTooltip = () => setShowTooltip(!showTooltip);
|
|
127
|
+
const handleKeyDown = (event) => {
|
|
128
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
129
|
+
event.preventDefault();
|
|
130
|
+
toggleTooltip();
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
const modeIcons = {
|
|
134
|
+
tooltip: 'ⓘ', // U+24D8
|
|
135
|
+
validate: '❌' // U+274C
|
|
136
|
+
};
|
|
137
|
+
let icon = modeIcons[mode];
|
|
138
|
+
let mouseEvents = {
|
|
139
|
+
onMouseEnter: () => setShowTooltip(true),
|
|
140
|
+
onMouseLeave: () => setShowTooltip(false),
|
|
141
|
+
};
|
|
142
|
+
let clickHandler = toggleTooltip;
|
|
143
|
+
return (_jsx(_Fragment, { children: _jsxs("div", { id: mode + "-" + props.id, className: `tooltip-container ${props.className || ''}`, children: [_jsx("span", { className: "tooltip-icon tooltip-icon-" + mode, ...mouseEvents, onClick: clickHandler, onKeyDown: handleKeyDown, "aria-label": "Show more info", "aria-expanded": showTooltip, "aria-describedby": showTooltip ? `${props.id}-tooltip` : undefined, tabIndex: 0, role: "button", children: icon }), showTooltip && _jsx("div", { className: "tooltip-text", role: "tooltip", id: `${props.id}-tooltip`, children: content })] }) }));
|
|
144
|
+
}
|
|
145
|
+
FormInput.propTypes = {
|
|
146
|
+
type: PropTypes.string,
|
|
147
|
+
id: PropTypes.string.isRequired,
|
|
148
|
+
name: PropTypes.string,
|
|
149
|
+
defaultValue: PropTypes.string,
|
|
150
|
+
value: PropTypes.string,
|
|
151
|
+
list: PropTypes.string,
|
|
152
|
+
listItems: PropTypes.string, /* this one is mine */
|
|
153
|
+
size: PropTypes.string,
|
|
154
|
+
maxLength: PropTypes.string,
|
|
155
|
+
placeholder: PropTypes.string,
|
|
156
|
+
autoComplete: PropTypes.string,
|
|
157
|
+
"aria-label": PropTypes.string,
|
|
158
|
+
min: PropTypes.string,
|
|
159
|
+
max: PropTypes.string,
|
|
160
|
+
step: PropTypes.string,
|
|
161
|
+
// flag attributes
|
|
162
|
+
autoFocus: PropTypes.string,
|
|
163
|
+
disabled: PropTypes.string,
|
|
164
|
+
readOnly: PropTypes.string,
|
|
165
|
+
required: PropTypes.string,
|
|
166
|
+
// className,
|
|
167
|
+
// data-mapping, data-component-endpoint, data-testid
|
|
168
|
+
// aria-invalid, aria-describedby,
|
|
169
|
+
// ----- for calculations
|
|
170
|
+
display: PropTypes.string,
|
|
171
|
+
label: PropTypes.string,
|
|
172
|
+
tooltip: PropTypes.string,
|
|
173
|
+
className: PropTypes.string,
|
|
174
|
+
validate: PropTypes.string,
|
|
175
|
+
onChange: PropTypes.func,
|
|
176
|
+
};
|
|
177
|
+
export function FormInput(props) {
|
|
178
|
+
const { formValidate, inputProps } = useFormComponent(props);
|
|
179
|
+
let formDataList = props.list && props.list in FVF
|
|
180
|
+
? FVF[props.list]
|
|
181
|
+
: props.list && props.listItems
|
|
182
|
+
? props.listItems.split(',')
|
|
183
|
+
: undefined;
|
|
184
|
+
return (_jsxs("div", { children: [props.type == "checkbox" ? _jsx("input", { ...inputProps }) : "", _jsx(FormLabel, { id: props.id, label: props.label }, "label-" + props.id), props.tooltip ? _jsx(FormTooltip, { id: props.id, text: [props.tooltip] }) : "", props.display == "vertical" ? formValidate : "", props.type != "checkbox" ? _jsx("input", { ...inputProps }) : "", formDataList && Array.isArray(formDataList) ? _jsx(FormDataList, { id: props.list ?? '', items: formDataList }) : "", props.display != "vertical" ? formValidate : ""] }));
|
|
185
|
+
}
|
|
186
|
+
FormSelect.propTypes = {
|
|
187
|
+
id: PropTypes.string.isRequired,
|
|
188
|
+
name: PropTypes.string,
|
|
189
|
+
size: PropTypes.string,
|
|
190
|
+
autoComplete: PropTypes.string,
|
|
191
|
+
defaultValue: PropTypes.oneOfType([
|
|
192
|
+
PropTypes.string,
|
|
193
|
+
PropTypes.array
|
|
194
|
+
]),
|
|
195
|
+
// flag attributes
|
|
196
|
+
autoFocus: PropTypes.string,
|
|
197
|
+
disabled: PropTypes.string,
|
|
198
|
+
multiple: PropTypes.string,
|
|
199
|
+
readOnly: PropTypes.string,
|
|
200
|
+
required: PropTypes.string,
|
|
201
|
+
// selected: PropTypes.string, // not used
|
|
202
|
+
// ----- for calculations
|
|
203
|
+
options: PropTypes.array,
|
|
204
|
+
display: PropTypes.string,
|
|
205
|
+
label: PropTypes.string,
|
|
206
|
+
tooltip: PropTypes.string,
|
|
207
|
+
className: PropTypes.string,
|
|
208
|
+
validate: PropTypes.string,
|
|
209
|
+
onChange: PropTypes.func,
|
|
210
|
+
};
|
|
211
|
+
export function FormSelect(props) {
|
|
212
|
+
const { formValidate, inputProps } = useFormComponent(props);
|
|
213
|
+
const options = generateOptions(props.options || [], props, "select", FormSelectOption);
|
|
214
|
+
return (_jsxs("div", { children: [_jsx(FormLabel, { id: props.id, label: props.label }, "label-" + props.id), props.tooltip ? _jsx(FormTooltip, { id: props.id, text: [props.tooltip] }) : "", props.display == "vertical" ? formValidate : "", _jsx("select", { ...inputProps, suppressHydrationWarning: true, children: options }), props.display != "vertical" ? formValidate : ""] }));
|
|
215
|
+
}
|
|
216
|
+
FormSelectOption.propTypes = {
|
|
217
|
+
text: PropTypes.string,
|
|
218
|
+
value: PropTypes.string,
|
|
219
|
+
// flag attributes
|
|
220
|
+
disabled: PropTypes.bool,
|
|
221
|
+
// selected : PropTypes.string
|
|
222
|
+
};
|
|
223
|
+
function FormSelectOption(props) {
|
|
224
|
+
const { text, disabled, value, ...otherProps } = props;
|
|
225
|
+
const inputProps = {
|
|
226
|
+
...otherProps,
|
|
227
|
+
...(disabled !== null && disabled !== undefined ? { disabled } : {}),
|
|
228
|
+
...(value !== null && value !== undefined ? { value } : {})
|
|
229
|
+
};
|
|
230
|
+
return (_jsx("option", { ...inputProps, children: text }));
|
|
231
|
+
}
|
|
232
|
+
FormTextarea.propTypes = {
|
|
233
|
+
id: PropTypes.string.isRequired,
|
|
234
|
+
name: PropTypes.string,
|
|
235
|
+
rows: PropTypes.string,
|
|
236
|
+
cols: PropTypes.string,
|
|
237
|
+
defaultValue: PropTypes.string,
|
|
238
|
+
maxLength: PropTypes.number,
|
|
239
|
+
placeholder: PropTypes.string,
|
|
240
|
+
autoComplete: PropTypes.string,
|
|
241
|
+
// flag attributes
|
|
242
|
+
autoFocus: PropTypes.string,
|
|
243
|
+
disabled: PropTypes.string,
|
|
244
|
+
readOnly: PropTypes.string,
|
|
245
|
+
required: PropTypes.string,
|
|
246
|
+
// ----- for calculations
|
|
247
|
+
display: PropTypes.string,
|
|
248
|
+
label: PropTypes.string,
|
|
249
|
+
tooltip: PropTypes.string,
|
|
250
|
+
className: PropTypes.string,
|
|
251
|
+
validate: PropTypes.string,
|
|
252
|
+
onChange: PropTypes.func,
|
|
253
|
+
};
|
|
254
|
+
export function FormTextarea(props) {
|
|
255
|
+
const { formValidate, inputProps } = useFormComponent(props);
|
|
256
|
+
return (_jsxs("div", { children: [_jsx(FormLabel, { id: props.id, label: props.label }, "label-" + props.id), props.tooltip ? _jsx(FormTooltip, { id: props.id, text: [props.tooltip] }) : "", props.display == "vertical" ? formValidate : "", _jsx("textarea", { ...inputProps }), props.display != "vertical" ? formValidate : ""] }));
|
|
257
|
+
}
|
|
258
|
+
FormRadio.propTypes = {
|
|
259
|
+
id: PropTypes.string.isRequired, // not using?
|
|
260
|
+
name: PropTypes.string.isRequired,
|
|
261
|
+
options: PropTypes.array,
|
|
262
|
+
// flag attributes
|
|
263
|
+
autoFocus: PropTypes.string,
|
|
264
|
+
disabled: PropTypes.string,
|
|
265
|
+
readOnly: PropTypes.string,
|
|
266
|
+
required: PropTypes.string,
|
|
267
|
+
// ? selected: PropTypes.string,
|
|
268
|
+
// ----- for calculations
|
|
269
|
+
display: PropTypes.string,
|
|
270
|
+
label: PropTypes.string,
|
|
271
|
+
tooltip: PropTypes.string,
|
|
272
|
+
validate: PropTypes.string,
|
|
273
|
+
onChange: PropTypes.func,
|
|
274
|
+
};
|
|
275
|
+
export function FormRadio(props) {
|
|
276
|
+
const { formValidate } = useFormComponent(props);
|
|
277
|
+
const options = generateOptions(props.options || [], props, "radio", FormRadioOption);
|
|
278
|
+
return (_jsxs("div", { children: [_jsx(FormLabel, { id: props.name, label: props.label }, "label-" + props.id), props.tooltip ? _jsx(FormTooltip, { id: props.id, text: [props.tooltip] }) : "", props.display == "vertical" ? formValidate : "", options, props.display != "vertical" ? formValidate : ""] }));
|
|
279
|
+
}
|
|
280
|
+
FormRadioOption.propTypes = {
|
|
281
|
+
name: PropTypes.string,
|
|
282
|
+
text: PropTypes.string,
|
|
283
|
+
value: PropTypes.string.isRequired,
|
|
284
|
+
// flag attributes
|
|
285
|
+
checked: PropTypes.string,
|
|
286
|
+
// ----- for calculations
|
|
287
|
+
parent: PropTypes.any,
|
|
288
|
+
};
|
|
289
|
+
function FormRadioOption(props) {
|
|
290
|
+
const inputProps = setupInputProps(props);
|
|
291
|
+
return (_jsxs("span", { className: props.parent.display == "vertical" ? "displayVertical" : "", children: [_jsx("input", { type: "radio", id: `${props.parent.name}-${props.value}`, name: props.parent.name, value: props.value,
|
|
292
|
+
// defaultChecked={!!props.checked}
|
|
293
|
+
defaultChecked: props.checked, required: !!props.parent.required, ...inputProps }), _jsx("label", { htmlFor: `${props.parent.name}-${props.value}`, children: props.text })] }));
|
|
294
|
+
}
|
|
295
|
+
FormCheckbox.propTypes = {
|
|
296
|
+
id: PropTypes.string.isRequired,
|
|
297
|
+
name: PropTypes.string.isRequired,
|
|
298
|
+
options: PropTypes.array,
|
|
299
|
+
// flag attributes
|
|
300
|
+
autoFocus: PropTypes.string,
|
|
301
|
+
disabled: PropTypes.string,
|
|
302
|
+
readOnly: PropTypes.string,
|
|
303
|
+
// ----- for calculations
|
|
304
|
+
display: PropTypes.string,
|
|
305
|
+
label: PropTypes.string,
|
|
306
|
+
tooltip: PropTypes.string,
|
|
307
|
+
className: PropTypes.string,
|
|
308
|
+
validate: PropTypes.string,
|
|
309
|
+
onChange: PropTypes.func,
|
|
310
|
+
};
|
|
311
|
+
export function FormCheckbox(props) {
|
|
312
|
+
const { formValidate } = useFormComponent(props);
|
|
313
|
+
const options = generateOptions(props.options || [], props, "checkbox", FormCheckboxOption);
|
|
314
|
+
return (_jsxs("div", { children: [_jsx(FormLabel, { id: props.name, label: props.label }, "label-" + props.id), props.tooltip ? _jsx(FormTooltip, { id: props.id, text: [props.tooltip] }) : "", props.display == "vertical" ? formValidate : "", options, props.display != "vertical" ? formValidate : ""] }));
|
|
315
|
+
}
|
|
316
|
+
FormCheckboxOption.propTypes = {
|
|
317
|
+
text: PropTypes.string.isRequired,
|
|
318
|
+
value: PropTypes.string.isRequired,
|
|
319
|
+
// flag attributes
|
|
320
|
+
selected: PropTypes.string,
|
|
321
|
+
// ----- for calculations
|
|
322
|
+
parent: PropTypes.any,
|
|
323
|
+
};
|
|
324
|
+
function FormCheckboxOption(props) {
|
|
325
|
+
const inputProps = setupInputProps(props);
|
|
326
|
+
return (_jsxs("span", { className: props.parent.display == "vertical" ? "displayVertical" : "", children: [_jsx("input", { type: "checkbox", id: props.parent.name + "_" + props.text, name: props.text, value: props.value, ...inputProps }), _jsx("label", { htmlFor: props.text, children: props.text })] }));
|
|
327
|
+
}
|
|
328
|
+
FormButton.propTypes = {
|
|
329
|
+
type: PropTypes.string,
|
|
330
|
+
id: PropTypes.string.isRequired,
|
|
331
|
+
text: PropTypes.string,
|
|
332
|
+
// ----- for calculations
|
|
333
|
+
className: PropTypes.string,
|
|
334
|
+
onClick: PropTypes.func.isRequired
|
|
335
|
+
};
|
|
336
|
+
export function FormButton(props) {
|
|
337
|
+
return (_jsx("div", { children: _jsx("button", { type: props.type, id: props.id, className: props.className || "", onClick: props.onClick, children: props.text }) }));
|
|
338
|
+
}
|
|
339
|
+
FormDataList.propTypes = {
|
|
340
|
+
id: PropTypes.string.isRequired,
|
|
341
|
+
items: PropTypes.array,
|
|
342
|
+
};
|
|
343
|
+
export function FormDataList(props) {
|
|
344
|
+
const options = [];
|
|
345
|
+
for (const item in props.items) {
|
|
346
|
+
let key = item;
|
|
347
|
+
const thisItem = props.items[key];
|
|
348
|
+
const newOption = _jsx("option", { value: thisItem }, props.id + '-' + thisItem);
|
|
349
|
+
options.push(newOption);
|
|
350
|
+
}
|
|
351
|
+
return (_jsx("datalist", { id: props.id, children: options }));
|
|
352
|
+
}
|
|
353
|
+
FormFieldset.propTypes = {};
|
|
354
|
+
export function FormFieldset() {
|
|
355
|
+
return (_jsx(_Fragment, {}));
|
|
356
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import * as FC from './formcomponents';
|
|
5
|
+
import { generateKey } from './formutils';
|
|
6
|
+
import { FormValidationProvider, useFormValidation } from './formvalidator';
|
|
7
|
+
const debug = false;
|
|
8
|
+
/* ===== FORM ENGINE =====
|
|
9
|
+
Generate all the elements to display a form */
|
|
10
|
+
FormEngine.propTypes = {
|
|
11
|
+
name: PropTypes.string,
|
|
12
|
+
id: PropTypes.string,
|
|
13
|
+
method: PropTypes.string,
|
|
14
|
+
onSubmitHandler: PropTypes.func,
|
|
15
|
+
formData: PropTypes.object.isRequired
|
|
16
|
+
};
|
|
17
|
+
function FormEngineInner(props) {
|
|
18
|
+
const { validateAllFields } = useFormValidation();
|
|
19
|
+
function generateFormProps(props) {
|
|
20
|
+
// GENERATE PROPS TO RENDER THE FORM CONTAINER, INTERNAL FUNCTION
|
|
21
|
+
if (debug)
|
|
22
|
+
console.log("Generating Form Props");
|
|
23
|
+
const formProps = JSON.parse(JSON.stringify(props));
|
|
24
|
+
['formData', 'onSubmitHandler'].forEach(e => delete formProps[e]);
|
|
25
|
+
return formProps;
|
|
26
|
+
}
|
|
27
|
+
generateNewFields.propTypes = {
|
|
28
|
+
formData: PropTypes.any.isRequired,
|
|
29
|
+
};
|
|
30
|
+
function generateNewFields(props) {
|
|
31
|
+
// CORE OF THE FORM ENGINE - CONVERT JSON TO COMPONENTS - INTERNAL
|
|
32
|
+
if (debug)
|
|
33
|
+
console.log("Generating Form Fields");
|
|
34
|
+
const newFields = [];
|
|
35
|
+
const formFields = props.formData.fields;
|
|
36
|
+
if (props.formData && formFields) {
|
|
37
|
+
// const thisFields = formFields;
|
|
38
|
+
for (let field = 0; field < formFields.length; field++) {
|
|
39
|
+
const thisField = formFields[field];
|
|
40
|
+
// Shallow clone props to preserve function handlers (JSON stringify drops functions)
|
|
41
|
+
const newProps = { ...thisField.props };
|
|
42
|
+
newProps.key = thisField.props.id || generateKey();
|
|
43
|
+
// Convert string numeric props to numbers where needed
|
|
44
|
+
const numericProps = ['maxLength', 'minLength', 'rows', 'cols', 'size', 'step'];
|
|
45
|
+
numericProps.forEach(prop => {
|
|
46
|
+
if (newProps[prop] !== undefined &&
|
|
47
|
+
newProps[prop] !== null &&
|
|
48
|
+
newProps[prop] !== '') {
|
|
49
|
+
// Only convert if the value is not already a number
|
|
50
|
+
if (typeof newProps[prop] === 'string') {
|
|
51
|
+
const num = Number(newProps[prop]);
|
|
52
|
+
if (!isNaN(num)) {
|
|
53
|
+
newProps[prop] = num;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
const componentName = thisField.component;
|
|
59
|
+
const newElementType = FC[componentName];
|
|
60
|
+
const newElement = React.createElement(newElementType, newProps);
|
|
61
|
+
newFields.push(newElement);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return newFields;
|
|
65
|
+
}
|
|
66
|
+
function handleSubmit(event) {
|
|
67
|
+
// HANDLES THE FORM ACTION / FORM SUBMIT - EXPOSED EXTERNAL
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
// Check if form is valid before submission
|
|
70
|
+
if (!validateAllFields()) {
|
|
71
|
+
// Form has validation errors, don't submit
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
if (props.onSubmitHandler)
|
|
75
|
+
props.onSubmitHandler(event);
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
return (_jsx("form", { ...generateFormProps(props), onSubmit: (event) => { handleSubmit(event); }, suppressHydrationWarning: true, children: generateNewFields(props) }));
|
|
79
|
+
}
|
|
80
|
+
export function FormEngine(props) {
|
|
81
|
+
return (_jsx(FormValidationProvider, { children: _jsx(FormEngineInner, { ...props }) }));
|
|
82
|
+
}
|