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

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 (145) hide show
  1. package/Common/CheckboxGroup.d.ts +13 -0
  2. package/Common/CheckboxGroup.js +50 -0
  3. package/Common/ConditionalRuleEngine.d.ts +296 -0
  4. package/Common/ConditionalRuleEngine.js +566 -0
  5. package/Common/CustomTabComponent.d.ts +17 -0
  6. package/Common/CustomTabComponent.js +30 -0
  7. package/Common/CustomValidationEngine.d.ts +58 -0
  8. package/Common/CustomValidationEngine.js +146 -0
  9. package/Common/DataGridComponent.d.ts +20 -0
  10. package/Common/DataGridComponent.js +273 -0
  11. package/Common/FormValidator.d.ts +485 -0
  12. package/Common/FormValidator.js +575 -0
  13. package/Common/MinMaxRuleEngine.d.ts +105 -0
  14. package/Common/MinMaxRuleEngine.js +365 -0
  15. package/Common/StaticHtmlRenderer.d.ts +14 -0
  16. package/Common/StaticHtmlRenderer.js +24 -0
  17. package/Common/expressions/DependencyTracker.d.ts +30 -0
  18. package/Common/expressions/DependencyTracker.js +128 -0
  19. package/Common/expressions/ExpressionEngine.d.ts +95 -0
  20. package/Common/expressions/ExpressionEngine.js +506 -0
  21. package/Common/expressions/ExpressionParser.d.ts +41 -0
  22. package/Common/expressions/ExpressionParser.js +269 -0
  23. package/Common/expressions/useGridExpressions.d.ts +12 -0
  24. package/Common/expressions/useGridExpressions.js +71 -0
  25. package/Common/globalFormId.d.ts +26 -0
  26. package/Common/globalFormId.js +45 -0
  27. package/Common/json-converter.d.ts +444 -0
  28. package/Common/json-converter.js +1184 -0
  29. package/Common/parser.d.ts +98 -0
  30. package/Common/parser.js +753 -0
  31. package/Common/utils.d.ts +130 -0
  32. package/Common/utils.js +574 -0
  33. package/README.md +53 -0
  34. package/index.d.ts +6 -0
  35. package/index.js +6 -0
  36. package/license +13 -0
  37. package/package.json +54 -0
  38. package/src/FormBuilder.d.ts +211 -0
  39. package/src/FormBuilder.js +1299 -0
  40. package/src/components/Database/ConnectionForm.d.ts +8 -0
  41. package/src/components/Database/ConnectionForm.js +28 -0
  42. package/src/components/TopBar.d.ts +10 -0
  43. package/src/components/TopBar.js +7 -0
  44. package/src/components/icons/ComponentIcons.d.ts +44 -0
  45. package/src/components/icons/ComponentIcons.js +40 -0
  46. package/src/components/layout/FormFieldWrapper.d.ts +41 -0
  47. package/src/components/layout/FormFieldWrapper.js +112 -0
  48. package/src/components/layout/LeftPane.d.ts +23 -0
  49. package/src/components/layout/LeftPane.js +310 -0
  50. package/src/components/layout/ListBoxPane.d.ts +24 -0
  51. package/src/components/layout/ListBoxPane.js +94 -0
  52. package/src/components/layout/MainSection.d.ts +39 -0
  53. package/src/components/layout/MainSection.js +1766 -0
  54. package/src/components/layout/Preview.d.ts +12 -0
  55. package/src/components/layout/Preview.js +337 -0
  56. package/src/components/layout/RightPane.d.ts +22 -0
  57. package/src/components/layout/RightPane.js +370 -0
  58. package/src/components/theme/ThemeSwitcher.d.ts +14 -0
  59. package/src/components/theme/ThemeSwitcher.js +24 -0
  60. package/src/components/theme/theme-constant.d.ts +37 -0
  61. package/src/components/theme/theme-constant.js +23 -0
  62. package/src/config/component-properties.json +4929 -0
  63. package/src/config/form-setting.json +56 -0
  64. package/src/config/locale.d.ts +449 -0
  65. package/src/config/locale.js +449 -0
  66. package/src/context/FormSchemaContext.d.ts +32 -0
  67. package/src/context/FormSchemaContext.js +155 -0
  68. package/src/context/LocalizationContext.d.ts +27 -0
  69. package/src/context/LocalizationContext.js +43 -0
  70. package/src/context/ThemeContext.d.ts +23 -0
  71. package/src/context/ThemeContext.js +161 -0
  72. package/src/context/contexts.d.ts +6 -0
  73. package/src/context/contexts.js +6 -0
  74. package/src/context/defaultFormSchema.d.ts +6 -0
  75. package/src/context/defaultFormSchema.js +20 -0
  76. package/src/defaultPalette.d.ts +9 -0
  77. package/src/defaultPalette.js +45 -0
  78. package/src/defaultTemplate.d.ts +3 -0
  79. package/src/defaultTemplate.js +185 -0
  80. package/src/import/schemaImportUtils.d.ts +24 -0
  81. package/src/import/schemaImportUtils.js +121 -0
  82. package/src/index.d.ts +16 -0
  83. package/src/index.js +6 -0
  84. package/src/types/ActionButtons.d.ts +44 -0
  85. package/src/types/ActionButtons.js +27 -0
  86. package/src/types/ComponentProperties.d.ts +33 -0
  87. package/src/types/ComponentProperties.js +2 -0
  88. package/src/types/FormSchema.d.ts +585 -0
  89. package/src/types/FormSchema.js +147 -0
  90. package/src/types/connection.d.ts +19 -0
  91. package/src/types/connection.js +9 -0
  92. package/src/utils/AttributesEditor.d.ts +13 -0
  93. package/src/utils/AttributesEditor.js +117 -0
  94. package/src/utils/ButtonGroupEditor.d.ts +15 -0
  95. package/src/utils/ButtonGroupEditor.js +141 -0
  96. package/src/utils/ChoiceBasedFieldEditor.d.ts +16 -0
  97. package/src/utils/ChoiceBasedFieldEditor.js +87 -0
  98. package/src/utils/ClipboardHelper.d.ts +26 -0
  99. package/src/utils/ClipboardHelper.js +83 -0
  100. package/src/utils/ColumnBuilder.d.ts +11 -0
  101. package/src/utils/ColumnBuilder.js +153 -0
  102. package/src/utils/ColumnSpanEditor.d.ts +8 -0
  103. package/src/utils/ColumnSpanEditor.js +98 -0
  104. package/src/utils/ConditionDataEditor.d.ts +29 -0
  105. package/src/utils/ConditionDataEditor.js +177 -0
  106. package/src/utils/ConditionalEditorUtils.d.ts +27 -0
  107. package/src/utils/ConditionalEditorUtils.js +310 -0
  108. package/src/utils/ConditionsEditor.d.ts +20 -0
  109. package/src/utils/ConditionsEditor.js +571 -0
  110. package/src/utils/CustomAccordion.d.ts +13 -0
  111. package/src/utils/CustomAccordion.js +38 -0
  112. package/src/utils/CustomValidationEditor.d.ts +11 -0
  113. package/src/utils/CustomValidationEditor.js +208 -0
  114. package/src/utils/DataSourceCollector.d.ts +3 -0
  115. package/src/utils/DataSourceCollector.js +252 -0
  116. package/src/utils/ExpressionEditor.d.ts +13 -0
  117. package/src/utils/ExpressionEditor.js +99 -0
  118. package/src/utils/FormSettingsPanel.d.ts +9 -0
  119. package/src/utils/FormSettingsPanel.js +60 -0
  120. package/src/utils/LookupConfig.d.ts +11 -0
  121. package/src/utils/LookupConfig.js +159 -0
  122. package/src/utils/MinMaxValueEditor.d.ts +25 -0
  123. package/src/utils/MinMaxValueEditor.js +188 -0
  124. package/src/utils/OptionsEditor.d.ts +8 -0
  125. package/src/utils/OptionsEditor.js +79 -0
  126. package/src/utils/PropertyRenderer.d.ts +22 -0
  127. package/src/utils/PropertyRenderer.js +210 -0
  128. package/src/utils/ValueRenderer.d.ts +2 -0
  129. package/src/utils/ValueRenderer.js +91 -0
  130. package/src/utils/ej2DbAdaptor.d.ts +6 -0
  131. package/src/utils/ej2DbAdaptor.js +38 -0
  132. package/src/utils/mappingHeuristics.d.ts +10 -0
  133. package/src/utils/mappingHeuristics.js +33 -0
  134. package/src/utils/renderComponent.d.ts +18 -0
  135. package/src/utils/renderComponent.js +300 -0
  136. package/styles/tailwind3.css +67 -0
  137. package/styles/themes/action-buttons.css +110 -0
  138. package/styles/themes/datagrid.css +380 -0
  139. package/styles/themes/drag-reorder.css +248 -0
  140. package/styles/themes/import-dialog.css +292 -0
  141. package/styles/themes/index.css +658 -0
  142. package/styles/themes/left-pane.css +253 -0
  143. package/styles/themes/options-editor.css +63 -0
  144. package/styles/themes/preview.css +702 -0
  145. package/styles/themes/property-dialog.css +371 -0
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Represents the category of components in the Form Builder
3
+ * @private
4
+ */
5
+ export var ComponentCategory;
6
+ (function (ComponentCategory) {
7
+ /**
8
+ * Specifies the basic set of form components in the Form Builder
9
+ */
10
+ ComponentCategory["BASIC"] = "basic";
11
+ /**
12
+ * Specifies the advanced set of form components in the Form Builder
13
+ */
14
+ ComponentCategory["ADVANCED"] = "advanced";
15
+ /**
16
+ * Specifies the layout components in the Form Builder
17
+ */
18
+ ComponentCategory["LAYOUT"] = "layout";
19
+ })(ComponentCategory || (ComponentCategory = {}));
20
+ /**
21
+ * Represents the components supported in the Form Builder.
22
+ */
23
+ export var FormComponentType;
24
+ (function (FormComponentType) {
25
+ /**
26
+ * Specifies the text box component.
27
+ */
28
+ FormComponentType["TEXTBOX"] = "textbox";
29
+ /**
30
+ * Specifies the text area component.
31
+ */
32
+ FormComponentType["TEXTAREA"] = "textarea";
33
+ /**
34
+ * Specifies the check box component.
35
+ */
36
+ FormComponentType["CHECKBOX"] = "checkbox";
37
+ /**
38
+ * Specifies the radio button component.
39
+ */
40
+ FormComponentType["RADIO"] = "radio";
41
+ /**
42
+ * Specifies the numeric text box component.
43
+ */
44
+ FormComponentType["NUMBER"] = "number";
45
+ /**
46
+ * Specifies the masked text box component.
47
+ */
48
+ FormComponentType["INPUT_MASK"] = "inputMask";
49
+ /**
50
+ * Specifies the dropdown list component.
51
+ */
52
+ FormComponentType["DROPDOWN"] = "dropdown";
53
+ /**
54
+ * Specifies the date picker component.
55
+ */
56
+ FormComponentType["DATE"] = "date";
57
+ /**
58
+ * Specifies the button component.
59
+ */
60
+ FormComponentType["BUTTON"] = "button";
61
+ /**
62
+ * Specifies the check box component for multiple choice selection.
63
+ */
64
+ FormComponentType["CHECKBOX_GROUP"] = "checkboxGroup";
65
+ /**
66
+ * Specifies the multiselect component for multiple choice selection.
67
+ */
68
+ FormComponentType["MULTISELECT"] = "multiselect";
69
+ /**
70
+ * Specifies the date time picker component.
71
+ */
72
+ FormComponentType["DATE_TIME"] = "dateTime";
73
+ /**
74
+ * Specifies the time picker component.
75
+ */
76
+ FormComponentType["TIME"] = "time";
77
+ /**
78
+ * Specifies the date range picker component.
79
+ */
80
+ FormComponentType["DATE_RANGE"] = "dateRange";
81
+ /**
82
+ * Specifies the switch component.
83
+ */
84
+ FormComponentType["SWITCH"] = "switch";
85
+ /**
86
+ * Specifies the rating component.
87
+ */
88
+ FormComponentType["RATING"] = "rating";
89
+ /**
90
+ * Specifies the splitbutton component.
91
+ */
92
+ FormComponentType["SPLIT_BUTTON"] = "splitButton";
93
+ /**
94
+ * Specifies the range slider component.
95
+ */
96
+ FormComponentType["RANGE_SLIDER"] = "rangeSlider";
97
+ /**
98
+ * Specifies the signature component.
99
+ */
100
+ FormComponentType["SIGNATURE"] = "signature";
101
+ /**
102
+ * Specifies the image editor component.
103
+ */
104
+ FormComponentType["IMAGE_EDITOR"] = "imageEditor";
105
+ /**
106
+ * Specifies the file upload component.
107
+ */
108
+ FormComponentType["FILE_UPLOAD"] = "fileUpload";
109
+ FormComponentType["COLOR_PICKER"] = "colorPicker";
110
+ /**
111
+ * Specifies the rich text editor component.
112
+ */
113
+ FormComponentType["RICH_TEXT_EDITOR"] = "richTextEditor";
114
+ /**
115
+ * Specifies the data grid component.
116
+ */
117
+ FormComponentType["DATA_GRID"] = "dataGrid";
118
+ /**
119
+ * Specifies the message component.
120
+ */
121
+ FormComponentType["MESSAGE"] = "message";
122
+ /**
123
+ * Specifies the panel element for layouting.
124
+ */
125
+ FormComponentType["PANEL"] = "panel";
126
+ /**
127
+ * Specifies the table element for layouting.
128
+ */
129
+ FormComponentType["TABLE"] = "table";
130
+ /**
131
+ * Specifies the tab component for layouting.
132
+ */
133
+ FormComponentType["TABS"] = "tabs";
134
+ /**
135
+ * Specifies the card element for layouting.
136
+ */
137
+ FormComponentType["CARD"] = "card";
138
+ /**
139
+ * Specifies the lookup component for associating the data from other forms in the current form.
140
+ */
141
+ FormComponentType["LOOKUP"] = "lookup";
142
+ /**
143
+ * Specifies the HTML element that can be rendered in the form.
144
+ */
145
+ FormComponentType["STATIC_HTML"] = "staticHtml";
146
+ FormComponentType["AUTO_NUMBER"] = "autoNumber";
147
+ })(FormComponentType || (FormComponentType = {}));
@@ -0,0 +1,19 @@
1
+ export type ConnectionConfig = {
2
+ id?: string;
3
+ name?: string;
4
+ type?: 'postgres' | 'mysql' | 'mssql' | 'sqlite' | 'mongodb';
5
+ host: string;
6
+ port?: number;
7
+ database: string;
8
+ ssl?: {
9
+ require?: boolean;
10
+ ca?: string;
11
+ };
12
+ auth?: {
13
+ username?: string;
14
+ password?: string;
15
+ saveCredentials?: boolean;
16
+ };
17
+ timeout?: number;
18
+ };
19
+ export declare const defaultConnection: ConnectionConfig;
@@ -0,0 +1,9 @@
1
+ export const defaultConnection = {
2
+ type: 'postgres',
3
+ host: '',
4
+ port: 5432,
5
+ database: '',
6
+ ssl: { require: false },
7
+ auth: { username: '', password: '', saveCredentials: false },
8
+ timeout: 15,
9
+ };
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ interface AttributesEditorProps {
3
+ attributes: Array<{
4
+ key: string;
5
+ value: string;
6
+ }>;
7
+ onChange: (attributes: Array<{
8
+ key: string;
9
+ value: string;
10
+ }>) => void;
11
+ }
12
+ export declare const AttributesEditor: React.FC<AttributesEditorProps>;
13
+ export {};
@@ -0,0 +1,117 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect, useRef } from 'react';
3
+ import { ButtonComponent } from '@syncfusion/ej2-react-buttons';
4
+ import { GridComponent, ColumnsDirective, ColumnDirective, Inject, Page } from '@syncfusion/ej2-react-grids';
5
+ import { useLocalization } from '../context/LocalizationContext';
6
+ import { floatBlur, floatFocus } from '../components/layout/LeftPane';
7
+ export const AttributesEditor = ({ attributes, onChange }) => {
8
+ const [rows, setRows] = useState([]);
9
+ const gridRef = useRef(null);
10
+ const nextIdRef = useRef(0);
11
+ const { l10n } = useLocalization();
12
+ // Initialize rows from attributes
13
+ useEffect(() => {
14
+ let initialRows = [];
15
+ if (Array.isArray(attributes) && attributes.length > 0) {
16
+ initialRows = attributes.map((attr, idx) => {
17
+ const id = idx;
18
+ if (id >= nextIdRef.current) {
19
+ nextIdRef.current = id + 1;
20
+ }
21
+ return {
22
+ id,
23
+ index: idx + 1,
24
+ attrKey: attr.key || '',
25
+ value: attr.value || ''
26
+ };
27
+ });
28
+ }
29
+ else {
30
+ nextIdRef.current = 0;
31
+ initialRows = [];
32
+ }
33
+ setRows(initialRows);
34
+ }, []);
35
+ // Update parent when rows change
36
+ const updateAttributes = (newRows) => {
37
+ const updatedAttributes = newRows.map(row => ({
38
+ key: row.attrKey || '',
39
+ value: row.value || ''
40
+ }));
41
+ onChange(updatedAttributes);
42
+ };
43
+ const handleAddRow = () => {
44
+ const newId = nextIdRef.current++;
45
+ const newIndex = rows.length + 1;
46
+ const newRow = {
47
+ id: newId,
48
+ index: newIndex,
49
+ attrKey: '',
50
+ value: ''
51
+ };
52
+ const newRows = [...rows, newRow];
53
+ setRows(newRows);
54
+ updateAttributes(newRows);
55
+ };
56
+ /* istanbul ignore next */
57
+ const handleDeleteRow = (id) => {
58
+ const newRows = rows.filter(row => row.id !== id).map((row, idx) => ({
59
+ ...row,
60
+ index: idx + 1
61
+ }));
62
+ setRows(newRows);
63
+ updateAttributes(newRows);
64
+ };
65
+ /* istanbul ignore next */
66
+ const handleKeyChange = (id, newKey) => {
67
+ if (rows[id].attrKey === newKey) {
68
+ return;
69
+ }
70
+ const newRows = rows.map(row => row.id === id ? { ...row, attrKey: newKey } : row);
71
+ setRows(newRows);
72
+ updateAttributes(newRows);
73
+ };
74
+ /* istanbul ignore next */
75
+ const handleValueChange = (id, newValue) => {
76
+ if (rows[id].value === newValue) {
77
+ return;
78
+ }
79
+ const newRows = rows.map(row => row.id === id ? { ...row, value: newValue } : row);
80
+ setRows(newRows);
81
+ updateAttributes(newRows);
82
+ };
83
+ /* istanbul ignore next */
84
+ const actionTemplate = (props) => (_jsx("button", { onClick: () => handleDeleteRow(props.id), onKeyDown: (e) => {
85
+ if (e.code === "Enter") {
86
+ handleDeleteRow(props.id);
87
+ }
88
+ }, className: "e-btn e-flat", title: "Delete this attribute", "aria-label": "Delete this attribute", type: "button", children: _jsx("span", { className: "e-icons e-trash", style: {
89
+ cursor: 'pointer',
90
+ display: 'flex',
91
+ alignItems: 'center',
92
+ justifyContent: 'center',
93
+ flexShrink: 0,
94
+ } }) }));
95
+ /* istanbul ignore next */
96
+ const keyTemplate = (props) => (_jsx("div", { className: "e-input-group", children: _jsx("input", { type: "text", defaultValue: props.attrKey, onBlur: (e) => {
97
+ handleKeyChange(props.id, e.target.value);
98
+ floatBlur(e);
99
+ }, placeholder: "e.g., data-test, aria-label", tabIndex: 0, "aria-label": "Attribute name", onFocus: floatFocus, onKeyDown: (e) => {
100
+ if (e.ctrlKey && e.key === 'a') {
101
+ e.preventDefault();
102
+ e.currentTarget.select();
103
+ }
104
+ }, className: "e-input" }) }));
105
+ /* istanbul ignore next */
106
+ const valueTemplate = (props) => (_jsx("div", { className: "e-input-group", children: _jsx("input", { type: "text", defaultValue: props.value, onBlur: (e) => {
107
+ handleValueChange(props.id, e.target.value);
108
+ floatBlur(e);
109
+ }, placeholder: "e.g., value for the attribute", tabIndex: 0, "aria-label": "Attribute value", onFocus: floatFocus, onKeyDown: (e) => {
110
+ if (e.ctrlKey && e.key === 'a') {
111
+ e.preventDefault();
112
+ e.currentTarget.select();
113
+ }
114
+ }, className: "e-input" }) }));
115
+ const height = rows.length >= 3 ? 184 : rows.length === 2 ? 126 : rows.length <= 1 ? 68 : 184;
116
+ return (_jsxs("div", { className: "options-editor", children: [_jsxs(GridComponent, { ref: gridRef, dataSource: rows, allowPaging: false, height: height, cssClass: "options-grid", allowSelection: false, children: [_jsxs(ColumnsDirective, { children: [_jsx(ColumnDirective, { field: "attrKey", headerText: l10n.getConstant("attributeName"), width: "200px", template: keyTemplate, allowSorting: false }), _jsx(ColumnDirective, { field: "value", headerText: l10n.getConstant("attributeValue"), width: "200px", template: valueTemplate, allowSorting: false }), _jsx(ColumnDirective, { headerText: l10n.getConstant("action"), width: "80", textAlign: "Center", allowSorting: false, template: actionTemplate })] }), _jsx(Inject, { services: [Page] })] }), _jsx("div", { className: "options-actions", children: _jsx(ButtonComponent, { cssClass: "e-outlined", onClick: handleAddRow, type: "button", children: l10n.getConstant('addAttribute') }) })] }));
117
+ };
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ interface ButtonDef {
3
+ label?: string;
4
+ type?: 'submit' | 'reset' | 'button';
5
+ style?: 'primary' | 'flat' | 'information' | 'error' | 'success' | 'warning';
6
+ iconCss?: string;
7
+ iconPosition?: 'Left' | 'Right';
8
+ disabled?: boolean;
9
+ }
10
+ interface ButtonGroupEditorProps {
11
+ buttons?: ButtonDef[];
12
+ onChange: (buttons: ButtonDef[]) => void;
13
+ }
14
+ export declare const ButtonGroupEditor: React.FC<ButtonGroupEditorProps>;
15
+ export default ButtonGroupEditor;
@@ -0,0 +1,141 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect, useRef } from 'react';
3
+ import { ButtonComponent } from '@syncfusion/ej2-react-buttons';
4
+ import { GridComponent, ColumnsDirective, ColumnDirective, Inject, Page } from '@syncfusion/ej2-react-grids';
5
+ import { useLocalization } from '../context/LocalizationContext';
6
+ import { floatBlur, floatFocus } from '../components/layout/LeftPane';
7
+ export const ButtonGroupEditor = ({ buttons = [], onChange }) => {
8
+ const [rows, setRows] = useState([]);
9
+ const gridRef = useRef(null);
10
+ const { l10n } = useLocalization();
11
+ // Initialize rows from buttons array
12
+ useEffect(() => {
13
+ const initialRows = (buttons || []).map((btn, idx) => ({
14
+ id: idx,
15
+ index: idx + 1,
16
+ label: btn.label || '',
17
+ type: btn.type || 'button',
18
+ style: btn.style || 'primary',
19
+ iconCss: btn.iconCss || '',
20
+ iconPosition: btn.iconPosition || 'Left',
21
+ disabled: btn.disabled || false
22
+ }));
23
+ setRows(initialRows);
24
+ }, [buttons]);
25
+ // Update parent when rows change
26
+ const updateButtons = (newRows) => {
27
+ const updatedButtons = newRows.map(row => ({
28
+ label: row.label || '',
29
+ type: row.type || 'button',
30
+ style: row.style || 'primary',
31
+ iconCss: row.iconCss || '',
32
+ iconPosition: row.iconPosition || 'Left',
33
+ disabled: row.disabled || false
34
+ }));
35
+ onChange(updatedButtons);
36
+ };
37
+ const handleAddRow = () => {
38
+ const newId = rows.length > 0 ? Math.max(...rows.map(r => r.id)) + 1 : 0;
39
+ const newIndex = rows.length + 1;
40
+ const newRow = {
41
+ id: newId,
42
+ index: newIndex,
43
+ label: `Button ${newIndex}`,
44
+ type: 'button',
45
+ style: 'primary',
46
+ iconCss: '',
47
+ iconPosition: 'Left',
48
+ disabled: false
49
+ };
50
+ const newRows = [...rows, newRow];
51
+ setRows(newRows);
52
+ updateButtons(newRows);
53
+ };
54
+ const handleDeleteRow = (id) => {
55
+ const newRows = rows.filter(row => row.id !== id).map((row, idx) => ({
56
+ ...row,
57
+ index: idx + 1
58
+ }));
59
+ setRows(newRows);
60
+ updateButtons(newRows);
61
+ };
62
+ const handleLabelChange = (id, newLabel) => {
63
+ if (rows[id].label === newLabel) {
64
+ return;
65
+ }
66
+ const newRows = rows.map(row => row.id === id ? { ...row, label: newLabel } : row);
67
+ setRows(newRows);
68
+ updateButtons(newRows);
69
+ };
70
+ const handleTypeChange = (id, newType) => {
71
+ const newRows = rows.map(row => row.id === id ? { ...row, type: newType } : row);
72
+ setRows(newRows);
73
+ updateButtons(newRows);
74
+ };
75
+ const handleStyleChange = (id, newStyle) => {
76
+ const newRows = rows.map(row => row.id === id ? { ...row, style: newStyle } : row);
77
+ setRows(newRows);
78
+ updateButtons(newRows);
79
+ };
80
+ const handleIconCssChange = (id, newIconCss) => {
81
+ if (rows[id].iconCss === newIconCss) {
82
+ return;
83
+ }
84
+ const newRows = rows.map(row => row.id === id ? { ...row, iconCss: newIconCss } : row);
85
+ setRows(newRows);
86
+ updateButtons(newRows);
87
+ };
88
+ const handleIconPositionChange = (id, newPosition) => {
89
+ const newRows = rows.map(row => row.id === id ? { ...row, iconPosition: newPosition } : row);
90
+ setRows(newRows);
91
+ updateButtons(newRows);
92
+ };
93
+ const handleDisabledChange = (id, isDisabled) => {
94
+ const newRows = rows.map(row => row.id === id ? { ...row, disabled: isDisabled } : row);
95
+ setRows(newRows);
96
+ updateButtons(newRows);
97
+ };
98
+ // Template for label input column
99
+ const labelTemplate = (props) => (_jsx("div", { className: "e-input-group", children: _jsx("input", { type: "text", defaultValue: props.label, tabIndex: 0, "aria-label": "Button label", onFocus: floatFocus, onKeyDown: (e) => {
100
+ if (e.ctrlKey && e.key === 'a') {
101
+ e.preventDefault();
102
+ e.currentTarget.select();
103
+ }
104
+ }, onBlur: (e) => {
105
+ handleLabelChange(props.id, e.target.value);
106
+ floatBlur(e);
107
+ }, placeholder: "Enter button label", className: "e-input" }) }));
108
+ // Template for type select column (native select styled like Syncfusion input)
109
+ const typeTemplate = (props) => (_jsx("div", { className: "e-input-group", children: _jsxs("select", { defaultValue: props.type, className: "e-input", tabIndex: 0, "aria-label": "Button type", onFocus: floatFocus, onBlur: floatBlur, onChange: (e) => handleTypeChange(props.id, e.target.value), children: [_jsx("option", { value: "button", children: "Button" }), _jsx("option", { value: "submit", children: "Submit" }), _jsx("option", { value: "reset", children: "Reset" })] }) }));
110
+ // Template for style select column (native select styled like Syncfusion input)
111
+ const styleTemplate = (props) => (_jsx("div", { className: "e-input-group", children: _jsxs("select", { defaultValue: props.style, className: "e-input", tabIndex: 0, "aria-label": "Button style", onFocus: floatFocus, onBlur: floatBlur, onChange: (e) => handleStyleChange(props.id, e.target.value), children: [_jsx("option", { value: "primary", children: "Primary" }), _jsx("option", { value: "flat", children: "Flat" }), _jsx("option", { value: "information", children: "Information" }), _jsx("option", { value: "error", children: "Error" }), _jsx("option", { value: "success", children: "Success" }), _jsx("option", { value: "warning", children: "Warning" })] }) }));
112
+ // Template for icon CSS input column
113
+ const iconCssTemplate = (props) => (_jsx("div", { className: "e-input-group", children: _jsx("input", { type: "text", tabIndex: 0, "aria-label": "Icon CSS class", defaultValue: props.iconCss || '', onBlur: (e) => {
114
+ handleIconCssChange(props.id, e.target.value);
115
+ floatBlur(e);
116
+ }, placeholder: "e.g., e-icon-close", onFocus: floatFocus, onKeyDown: (e) => {
117
+ if (e.ctrlKey && e.key === 'a') {
118
+ e.preventDefault();
119
+ e.currentTarget.select();
120
+ }
121
+ }, className: "e-input" }) }));
122
+ // Template for icon position select column
123
+ const iconPositionTemplate = (props) => (_jsx("div", { className: "e-input-group", children: _jsxs("select", { defaultValue: props.iconPosition || 'Left', className: "e-input", tabIndex: 0, "aria-label": "Icon position", onFocus: floatFocus, onBlur: floatBlur, onChange: (e) => handleIconPositionChange(props.id, e.target.value), children: [_jsx("option", { value: "Left", children: "Left" }), _jsx("option", { value: "Right", children: "Right" })] }) }));
124
+ // Template for disabled checkbox column
125
+ const disabledTemplate = (props) => (_jsx("div", { className: 'e-checkbox-wrapper', children: _jsx("input", { type: "checkbox", defaultChecked: props.disabled || false, tabIndex: 0, "aria-label": "Disabled", onChange: (e) => handleDisabledChange(props.id, e.target.checked), className: "e-check-box", style: { cursor: 'pointer' } }) }));
126
+ // Template for delete action column
127
+ const actionTemplate = (props) => (_jsx("button", { onClick: () => handleDeleteRow(props.id), onKeyDown: (e) => {
128
+ if (e.code === "Enter") {
129
+ handleDeleteRow(props.id);
130
+ }
131
+ }, className: "e-btn e-flat", title: "Delete this button", "aria-label": "Delete this button", type: "button", children: _jsx("span", { className: "e-icons e-trash", style: {
132
+ cursor: 'pointer',
133
+ display: 'flex',
134
+ alignItems: 'center',
135
+ justifyContent: 'center',
136
+ flexShrink: 0,
137
+ } }) }));
138
+ const height = rows.length >= 3 ? 184 : rows.length === 2 ? 126 : rows.length <= 1 ? 68 : 184;
139
+ return (_jsxs("div", { className: "options-editor", children: [_jsxs(GridComponent, { ref: gridRef, dataSource: rows, allowPaging: false, height: height, allowSelection: false, cssClass: "options-grid", children: [_jsxs(ColumnsDirective, { children: [_jsx(ColumnDirective, { field: "label", headerText: l10n.getConstant("buttonLabel"), width: "150", template: labelTemplate, allowSorting: false }), _jsx(ColumnDirective, { field: "type", headerText: l10n.getConstant("buttonType"), width: "120", template: typeTemplate, allowSorting: false }), _jsx(ColumnDirective, { field: "style", headerText: l10n.getConstant("buttonStyle"), width: "120", template: styleTemplate, allowSorting: false }), _jsx(ColumnDirective, { field: "iconCss", headerText: l10n.getConstant("iconCss"), width: "130", template: iconCssTemplate, allowSorting: false }), _jsx(ColumnDirective, { field: "iconPosition", headerText: l10n.getConstant("iconPosition"), width: "110", template: iconPositionTemplate, allowSorting: false }), _jsx(ColumnDirective, { field: "disabled", headerText: l10n.getConstant("disabled"), width: "80", textAlign: "Center", template: disabledTemplate, allowSorting: false }), _jsx(ColumnDirective, { headerText: l10n.getConstant("action"), width: "80", textAlign: "Center", allowSorting: false, template: actionTemplate })] }), _jsx(Inject, { services: [Page] })] }), _jsx("div", { className: "options-actions", children: _jsx(ButtonComponent, { cssClass: "e-outlined", onClick: handleAddRow, type: "button", children: l10n.getConstant('addButton') }) })] }));
140
+ };
141
+ export default ButtonGroupEditor;
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import type { FormComponent, ChoiceBasedFieldRule } from '../types/FormSchema';
3
+ export interface ChoiceBasedFieldEditorProps {
4
+ componentForEdit: FormComponent;
5
+ componentFields: FormComponent[];
6
+ existingRule?: ChoiceBasedFieldRule;
7
+ onChange: (rule: ChoiceBasedFieldRule | undefined) => void;
8
+ }
9
+ /**
10
+ * ChoiceBasedFieldEditor Component - Simple 3-Step Flow
11
+ *
12
+ * STEP 1: Select primary field (Dropdown)
13
+ * STEP 2: Select primary option (Dropdown)
14
+ * STEP 3: Map dependent options (MultiSelect)
15
+ */
16
+ export declare const ChoiceBasedFieldEditor: React.FC<ChoiceBasedFieldEditorProps>;
@@ -0,0 +1,87 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo, useState, useRef } from 'react';
3
+ import { DropDownListComponent } from '@syncfusion/ej2-react-dropdowns';
4
+ import { MultiSelectComponent, Inject, CheckBoxSelection } from '@syncfusion/ej2-react-dropdowns';
5
+ import { ButtonComponent } from '@syncfusion/ej2-react-buttons';
6
+ import { useLocalization } from '../context/LocalizationContext';
7
+ import { useFormSchema } from '../context/FormSchemaContext';
8
+ import { useFetchOptions, resolveOptions, getComponentFromSchema } from '../../Common/utils';
9
+ /**
10
+ * ChoiceBasedFieldEditor Component - Simple 3-Step Flow
11
+ *
12
+ * STEP 1: Select primary field (Dropdown)
13
+ * STEP 2: Select primary option (Dropdown)
14
+ * STEP 3: Map dependent options (MultiSelect)
15
+ */
16
+ export const ChoiceBasedFieldEditor = ({ componentForEdit, componentFields, existingRule, onChange }) => {
17
+ const { l10n } = useLocalization();
18
+ const { formSchema } = useFormSchema();
19
+ const dropdownRef = useRef(null);
20
+ const [primaryFieldId, setPrimaryFieldId] = useState(existingRule?.primaryFieldId || null);
21
+ const [currentOptionIndex, setCurrentOptionIndex] = useState(0);
22
+ const [choiceMapping, setChoiceMapping] = useState(existingRule?.choiceMapping || {});
23
+ // Get live component with updated options from formSchema
24
+ const liveComponent = useMemo(() => {
25
+ return componentForEdit ? getComponentFromSchema(componentForEdit.id, formSchema) || componentForEdit : componentForEdit;
26
+ }, [componentForEdit, formSchema]);
27
+ // Filter available primary fields
28
+ const availablePrimaryFields = useMemo(() => {
29
+ const currentIndex = componentFields.findIndex(f => f.id === componentForEdit.id);
30
+ return componentFields
31
+ .slice(0, currentIndex)
32
+ .filter(f => f.type === 'dropdown' && f.id !== componentForEdit.id);
33
+ }, [componentFields, componentForEdit.id]);
34
+ // Find the selected primary field
35
+ const selectedPrimaryField = useMemo(() => {
36
+ return availablePrimaryFields.find(f => f.id === primaryFieldId);
37
+ }, [availablePrimaryFields, primaryFieldId]);
38
+ // Fetch and resolve primary field options
39
+ const primaryFieldFetchedOptions = useFetchOptions(selectedPrimaryField?.options || []);
40
+ const primaryFieldOptions = useMemo(() => {
41
+ if (!selectedPrimaryField)
42
+ return [];
43
+ const resolved = resolveOptions(selectedPrimaryField.options, primaryFieldFetchedOptions);
44
+ return Array.isArray(resolved) ? resolved : (Array.isArray(selectedPrimaryField.options) ? selectedPrimaryField.options : []);
45
+ }, [selectedPrimaryField, primaryFieldFetchedOptions]);
46
+ // Fetch and resolve dependent field options from live component
47
+ const dependentFieldFetchedOptions = useFetchOptions(liveComponent.options);
48
+ const dependentFieldOptions = useMemo(() => {
49
+ const resolved = resolveOptions(liveComponent.options, dependentFieldFetchedOptions);
50
+ return Array.isArray(resolved) ? resolved : (Array.isArray(liveComponent.options) ? liveComponent.options : []);
51
+ }, [liveComponent.options, dependentFieldFetchedOptions]);
52
+ const currentPrimaryOption = primaryFieldOptions[currentOptionIndex];
53
+ const handleSave = () => {
54
+ if (!primaryFieldId || Object.keys(choiceMapping).length === 0) {
55
+ return;
56
+ }
57
+ const rule = {
58
+ primaryFieldId,
59
+ choiceMapping,
60
+ showAllWhenNotMapped: false
61
+ };
62
+ onChange(rule);
63
+ };
64
+ const handleCancel = () => {
65
+ dropdownRef?.current?.setProperties({ value: null });
66
+ setPrimaryFieldId(null);
67
+ setCurrentOptionIndex(0);
68
+ setChoiceMapping({});
69
+ onChange(undefined);
70
+ };
71
+ return (_jsxs("div", { className: "choice-based-editor", style: { marginTop: '0.5rem' }, children: [_jsxs("div", { style: { marginBottom: '2rem', padding: '0 25px' }, children: [_jsx("label", { className: 'property-description', htmlFor: "cbfe-primary-field", children: l10n.getConstant('selectPrimaryField') }), _jsx(DropDownListComponent, { ref: dropdownRef, dataSource: availablePrimaryFields.map(f => ({
72
+ text: f.label || f.name || f.id,
73
+ value: f.id
74
+ })), fields: { text: 'text', value: 'value' }, placeholder: "Select a field", value: primaryFieldId || undefined, change: (e) => {
75
+ setPrimaryFieldId(e.value);
76
+ setCurrentOptionIndex(0);
77
+ setChoiceMapping({});
78
+ }, floatLabelType: "Never", htmlAttributes: { 'aria-label': l10n.getConstant('selectPrimaryField') } }, "step1-primary-field")] }), primaryFieldId && primaryFieldOptions.length > 0 && (_jsxs("div", { style: { marginBottom: '2rem', padding: '0 25px' }, children: [_jsxs("label", { className: 'property-description', htmlFor: "cbfe-primary-option", children: [l10n.getConstant('selectPrimaryOption'), " (", currentOptionIndex + 1, " of ", primaryFieldOptions.length, ")"] }), _jsx(DropDownListComponent, { dataSource: primaryFieldOptions.map(opt => ({ text: opt, value: opt })), fields: { text: 'text', value: 'value' }, value: currentPrimaryOption, change: (e) => {
79
+ const newIndex = primaryFieldOptions.indexOf(e.value);
80
+ setCurrentOptionIndex(newIndex);
81
+ }, floatLabelType: "Never", htmlAttributes: { 'aria-label': l10n.getConstant('selectPrimaryOption') } }, `step2-options-${primaryFieldId}`)] })), primaryFieldId && currentPrimaryOption && dependentFieldOptions.length > 0 && (_jsxs("div", { style: { marginBottom: '2rem', padding: '0 25px' }, children: [_jsxs("label", { className: 'property-description', htmlFor: "cbfe-dependent-options", children: [l10n.getConstant('selectDependentOptions'), " for \"", currentPrimaryOption, "\""] }), _jsx(MultiSelectComponent, { dataSource: dependentFieldOptions.map((opt) => ({ text: opt, value: opt })), fields: { text: 'text', value: 'value' }, placeholder: l10n.getConstant('selectOptions'), mode: "CheckBox", showSelectAll: true, value: choiceMapping[currentPrimaryOption] || [], change: (e) => {
82
+ setChoiceMapping({
83
+ ...choiceMapping,
84
+ [currentPrimaryOption]: e.value || []
85
+ });
86
+ }, floatLabelType: "Never", htmlAttributes: { 'aria-label': l10n.getConstant('selectDependentOptions') }, children: _jsx(Inject, { services: [CheckBoxSelection] }) }, `step3-multiselect-${primaryFieldId}-${currentOptionIndex}`), _jsxs("small", { style: { display: 'block', marginTop: '0.5rem', color: '#666' }, children: [choiceMapping[currentPrimaryOption]?.length || 0, " option(s) selected"] })] })), primaryFieldId && (_jsxs("div", { style: { display: 'flex', gap: '0.5rem', marginTop: '2rem', padding: '0 25px' }, children: [_jsx(ButtonComponent, { onClick: handleSave, cssClass: "e-primary", disabled: Object.keys(choiceMapping).length === 0, "aria-label": l10n.getConstant('save'), children: l10n.getConstant('save') }), _jsx(ButtonComponent, { onClick: handleCancel, cssClass: "e-outline", "aria-label": l10n.getConstant('cancel'), children: l10n.getConstant('cancel') })] }))] }));
87
+ };
@@ -0,0 +1,26 @@
1
+ import type { FormComponent } from '../types/FormSchema';
2
+ /**
3
+ * System clipboard helper for copying/pasting form field configurations
4
+ * Uses navigator.clipboard API for cross-tab and external tool integration
5
+ */
6
+ export declare class ClipboardHelper {
7
+ private static readonly CLIPBOARD_PREFIX;
8
+ /**
9
+ * Copy field configuration to system clipboard as JSON
10
+ */
11
+ static copyToClipboard(field: FormComponent): Promise<void>;
12
+ /**
13
+ * Read field configuration from system clipboard
14
+ * Returns null if clipboard doesn't contain valid field data
15
+ */
16
+ static readFromClipboard(): Promise<FormComponent | null>;
17
+ /**
18
+ * Check if clipboard contains valid form field data
19
+ */
20
+ static hasClipboardData(): Promise<boolean>;
21
+ /**
22
+ * Generate new unique field with modified ID and name
23
+ */
24
+ static createUniqueField(sourceField: FormComponent): FormComponent;
25
+ static clearData(): Promise<void>;
26
+ }