@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,370 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import React, { useEffect, useRef, useState, useMemo } from 'react';
3
+ import { TabComponent, TabItemDirective, TabItemsDirective } from '@syncfusion/ej2-react-navigations';
4
+ import { PropertyGroupRenderer } from '../../utils/PropertyRenderer';
5
+ import { useLocalization } from '../../context/LocalizationContext';
6
+ import { FormSettingsPanel } from '../../utils/FormSettingsPanel';
7
+ import { useFormSchema } from '../../context/FormSchemaContext';
8
+ import { ConditionsEditor } from '../../utils/ConditionsEditor';
9
+ import { collectAvailableFieldNames, extractAllDescendantIds } from '../../../Common/utils';
10
+ /**
11
+ * Extract all form field names from components recursively
12
+ *
13
+ * Traverses the form schema and extracts all data field names (excluding layout containers).
14
+ * Used to populate the field list for conditional rule authoring.
15
+ *
16
+ * @spec CONDITIONAL-VALIDATION.md - PropertyDialog integration
17
+ * @requirement Task 7.3-7.5: Extract all form field names recursively
18
+ *
19
+ * @param components - Array of FormComponent from schema
20
+ * @returns Array of FormComponent objects representing data fields only
21
+ */
22
+ function extractAllFormFieldNames(components) {
23
+ const fields = [];
24
+ // Layout component types to exclude (non-data fields)
25
+ const layoutTypes = ['panel', 'table', 'tabs', 'button', 'message'];
26
+ function traverse(comps) {
27
+ for (const comp of comps) {
28
+ // Skip layout containers and non-data components
29
+ if (!layoutTypes.includes(comp.type)) {
30
+ fields.push(comp);
31
+ }
32
+ // Recursively traverse nested structures
33
+ if (comp.children && Array.isArray(comp.children)) {
34
+ traverse(comp.children);
35
+ }
36
+ // Handle table cells (3D array: [rows][columns][components])
37
+ if (comp.tableCells && Array.isArray(comp.tableCells)) {
38
+ comp.tableCells.forEach((row) => {
39
+ if (Array.isArray(row)) {
40
+ row.forEach((cell) => {
41
+ if (Array.isArray(cell)) {
42
+ traverse(cell);
43
+ }
44
+ });
45
+ }
46
+ });
47
+ }
48
+ // Handle tab items
49
+ if (comp.tabItems && Array.isArray(comp.tabItems)) {
50
+ comp.tabItems.forEach((tabItem) => {
51
+ if (tabItem.content && Array.isArray(tabItem.content)) {
52
+ traverse(tabItem.content);
53
+ }
54
+ });
55
+ }
56
+ }
57
+ }
58
+ traverse(components);
59
+ return fields;
60
+ }
61
+ const shouldShowPropertyInMode = (property, currentMode) => {
62
+ // If property requires developer mode and we're in Simple mode, hide it
63
+ if (property.mode === 'developer' && currentMode === 'Simple') {
64
+ return false;
65
+ }
66
+ return true;
67
+ };
68
+ /**
69
+ * RightPane Component
70
+ */
71
+ export const RightPane = ({ mode, componentType, configData, formSettingsConfig, existingValues = {}, formSettings, onSave, onPropertyChanged, onClose, builderMode }) => {
72
+ // Store ALL values in refs - ZERO state in this component
73
+ const propertyValuesRef = useRef({});
74
+ const initializedRef = useRef(false);
75
+ const savedButtonsGroupsRef = useRef(null);
76
+ const allowMultipleValueRef = useRef(undefined);
77
+ const columnsValueRef = useRef(null);
78
+ const { l10n } = useLocalization();
79
+ // Get form schema for extracting all field names (for Conditions tab)
80
+ const { formSchema } = useFormSchema();
81
+ // State to force re-render when needed (minimal)
82
+ const [, forceUpdate] = useState({});
83
+ // Get the component definition from the config
84
+ const componentDefinition = configData[componentType];
85
+ // Helper function to find current component in schema
86
+ const findCurrentComponent = (comps) => {
87
+ for (const comp of comps) {
88
+ if (comp.id === existingValues?.id)
89
+ return comp;
90
+ if (comp.children) {
91
+ const found = findCurrentComponent(comp.children);
92
+ if (found)
93
+ return found;
94
+ }
95
+ if (comp.tableCells) {
96
+ for (const row of comp.tableCells) {
97
+ for (const cell of row) {
98
+ const found = findCurrentComponent(cell);
99
+ if (found)
100
+ return found;
101
+ }
102
+ }
103
+ }
104
+ if (comp.tabItems) {
105
+ for (const tab of comp.tabItems) {
106
+ if (tab.content) {
107
+ const found = findCurrentComponent(tab.content);
108
+ if (found)
109
+ return found;
110
+ }
111
+ }
112
+ }
113
+ }
114
+ return null;
115
+ };
116
+ // Get current component and its child IDs for conditions editor
117
+ const currentComponent = useMemo(() => findCurrentComponent(formSchema.components), [formSchema.components, existingValues?.id]);
118
+ const excludeChildIds = useMemo(() => currentComponent ? extractAllDescendantIds(currentComponent) : [], [currentComponent]);
119
+ // Initialize property values with defaults - only when sidebar opens
120
+ useEffect(() => {
121
+ if (componentDefinition) {
122
+ const values = {};
123
+ componentDefinition.groups.forEach((group) => {
124
+ group.properties.forEach((property) => {
125
+ let value = property.default;
126
+ // Handle Validation group properties
127
+ if (group.name === 'Validation') {
128
+ if (existingValues?.[property.key] !== undefined) {
129
+ value = existingValues[property.key];
130
+ }
131
+ else {
132
+ value = property.default;
133
+ }
134
+ }
135
+ // Handle Layout group properties
136
+ else if (group.name === 'Layout') {
137
+ if (existingValues.layout?.[property.key] !== undefined) {
138
+ value = existingValues.layout[property.key];
139
+ }
140
+ else if (existingValues[property.key] !== undefined) {
141
+ value = existingValues[property.key];
142
+ }
143
+ else {
144
+ value = property.default;
145
+ }
146
+ }
147
+ // Handle all other groups
148
+ else {
149
+ if (existingValues[property.key] !== undefined) {
150
+ value = existingValues[property.key];
151
+ }
152
+ else if (property.key === 'value' && existingValues.defaultValue !== undefined) {
153
+ value = existingValues.defaultValue;
154
+ }
155
+ else if (property.key === 'rows' && existingValues.textareaRows !== undefined) {
156
+ value = existingValues.textareaRows;
157
+ }
158
+ else {
159
+ value = property.default;
160
+ }
161
+ }
162
+ values[property.key] = value;
163
+ });
164
+ });
165
+ // Store in refs only (NO state)
166
+ propertyValuesRef.current = values;
167
+ initializedRef.current = true;
168
+ // Initialize allowMultiple state for button component
169
+ if (componentType === 'button') {
170
+ const initialAllowMultiple = values.allowMultiple ?? false;
171
+ allowMultipleValueRef.current = initialAllowMultiple;
172
+ savedButtonsGroupsRef.current = values.buttonsGroups || [];
173
+ }
174
+ }
175
+ }, [componentType, componentDefinition, existingValues]);
176
+ // Determine which properties should be visible based on allowMultiple flag
177
+ const shouldShowProperty = (propKey, allowMultiple) => {
178
+ if (allowMultiple) {
179
+ const visibleKeys = ['allowMultiple', 'name', 'buttonsGroups', 'gap', 'cssClass', 'width', 'position', 'size', 'conditions'];
180
+ return visibleKeys.includes(propKey);
181
+ }
182
+ else {
183
+ const visibleKeys = ['allowMultiple', 'name', 'buttonType', 'cssClass', 'disabled', 'iconCss', 'iconPosition', 'label', 'position', 'style', 'size', 'conditions'];
184
+ return visibleKeys.includes(propKey);
185
+ }
186
+ };
187
+ // Filter groups and properties based on visibility rules
188
+ const getFilteredGroups = (groups, allowMultiple) => {
189
+ return groups.map(group => ({
190
+ ...group,
191
+ properties: group.properties.filter(prop => shouldShowProperty(prop.key, allowMultiple))
192
+ })).filter(group => group.properties.length > 0);
193
+ };
194
+ const applyModeFiltering = (groups) => {
195
+ return groups.map(group => ({
196
+ ...group,
197
+ properties: group.properties.filter(prop => shouldShowPropertyInMode(prop, builderMode))
198
+ })).filter(group => group.properties.length > 0);
199
+ };
200
+ /**
201
+ * Filter lookup properties based on displayFormat selection
202
+ * When displayFormat is Radio or CheckboxGroup, remove specific properties
203
+ */
204
+ const getFilteredLookupGroups = (groups, displayFormat) => {
205
+ const isRadioOrCheckbox = ['Radio', 'Checkbox', 'CheckboxGroup'].includes(displayFormat);
206
+ if (!isRadioOrCheckbox) {
207
+ return groups; // No filtering needed for Dropdown and MultiSelect
208
+ }
209
+ // Properties to remove for Radio/CheckboxGroup
210
+ const removeFromDisplay = ['placeholder', 'showClearButton', 'readOnly', 'enableVirtualization', 'floatLabelType', 'popupHeight', 'popupWidth'];
211
+ const removeFromValidation = ['validateOn'];
212
+ return groups.map(group => {
213
+ let filteredProps = group.properties;
214
+ if (group.name === 'Display') {
215
+ filteredProps = filteredProps.filter(prop => !removeFromDisplay.includes(prop.key));
216
+ }
217
+ else if (group.name === 'Validation') {
218
+ filteredProps = filteredProps.filter(prop => !removeFromValidation.includes(prop.key));
219
+ }
220
+ else if (group.name === 'Conditions') {
221
+ // For Conditions, keep only conditionalReadonly if it exists
222
+ filteredProps = filteredProps.map(prop => {
223
+ if (prop.key === 'conditions') {
224
+ return {
225
+ ...prop,
226
+ supportedConditions: [
227
+ "visibleWhen",
228
+ "hideWhen",
229
+ "requiredWhen",
230
+ "setValueWhen",
231
+ "disabledWhen"
232
+ ]
233
+ };
234
+ }
235
+ return prop;
236
+ });
237
+ }
238
+ return {
239
+ ...group,
240
+ properties: filteredProps
241
+ };
242
+ }).filter(group => group.properties.length > 0);
243
+ };
244
+ // Real-time property change - saves immediately on change
245
+ const handlePropertyChange = (key, value) => {
246
+ // Update ref immediately
247
+ propertyValuesRef.current = {
248
+ ...propertyValuesRef.current,
249
+ [key]: value,
250
+ };
251
+ // Handle allowMultiple toggle for button component
252
+ if (key === 'allowMultiple' && componentType === 'button') {
253
+ const prevAllowMultiple = allowMultipleValueRef.current;
254
+ allowMultipleValueRef.current = value;
255
+ if (value === false) {
256
+ savedButtonsGroupsRef.current = propertyValuesRef.current.buttonsGroups;
257
+ propertyValuesRef.current.buttonsGroups = [];
258
+ }
259
+ else {
260
+ propertyValuesRef.current.buttonsGroups = savedButtonsGroupsRef.current || [];
261
+ }
262
+ // Only re-render if visibility actually changed
263
+ if (prevAllowMultiple !== value) {
264
+ forceUpdate({});
265
+ }
266
+ }
267
+ // Track buttonsGroups changes when allowMultiple is true
268
+ if (key === 'buttonsGroups' && componentType === 'button' && allowMultipleValueRef.current === true) {
269
+ savedButtonsGroupsRef.current = value;
270
+ }
271
+ // Handle columns change for table component
272
+ if (key === 'columns' && componentType === 'table') {
273
+ const prevColumns = columnsValueRef.current;
274
+ columnsValueRef.current = value;
275
+ propertyValuesRef.current.columns = value;
276
+ // Only re-render if columns actually changed
277
+ if (prevColumns !== value) {
278
+ forceUpdate({});
279
+ }
280
+ }
281
+ const propertyDef = componentDefinition?.groups
282
+ .flatMap(g => g.properties)
283
+ .find(p => p.key === key);
284
+ const fieldLabel = propertyDef?.label || key;
285
+ const label = propertyValuesRef.current.label || '';
286
+ // FIRE the property changed event
287
+ if (onPropertyChanged) {
288
+ onPropertyChanged({
289
+ field: label,
290
+ property: fieldLabel,
291
+ value,
292
+ name: existingValues?.name
293
+ });
294
+ }
295
+ // AUTO-SAVE: Save to parent immediately on every change
296
+ // This triggers real-time updates to the form schema
297
+ const valuesToSave = { ...propertyValuesRef.current };
298
+ onSave(valuesToSave);
299
+ };
300
+ /**
301
+ * Handle conditions change from ConditionsEditor
302
+ *
303
+ * @spec CONDITIONAL-VALIDATION.md - PropertyDialog integration
304
+ * @requirement Task 7.10-7.11: Store condition changes and call preview callback
305
+ */
306
+ const handleConditionsChange = (conditions) => {
307
+ // Store conditions in the ref first
308
+ propertyValuesRef.current = {
309
+ ...propertyValuesRef.current,
310
+ conditions,
311
+ };
312
+ onSave(propertyValuesRef.current);
313
+ };
314
+ const handleClose = () => {
315
+ propertyValuesRef.current = {};
316
+ initializedRef.current = false;
317
+ savedButtonsGroupsRef.current = null;
318
+ allowMultipleValueRef.current = false;
319
+ columnsValueRef.current = null;
320
+ onClose();
321
+ };
322
+ // If component definition doesn't exist, show error
323
+ if (!componentDefinition && mode === 'component') {
324
+ return (_jsxs("div", { className: "right-sidebar-content", children: [_jsxs("div", { className: "sidebar-header", children: [_jsx("h3", { children: "Error" }), _jsx("button", { "aria-label": "Close panel", className: "close-btn", onClick: handleClose, title: "Close (Esc)", children: "\u2715" })] }), _jsx("div", { className: "sidebar-body", children: _jsxs("p", { style: { color: 'var(--sf-error-color, red)', padding: '1rem' }, children: ["Component definition not found for: ", componentType] }) })] }));
325
+ }
326
+ if (mode === 'formSettings') {
327
+ return (_jsx(FormSettingsPanel, { settings: formSettings, formSettingsConfig: formSettingsConfig, onSave: onSave }));
328
+ }
329
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "sidebar-header", children: [_jsx("h3", { children: l10n.getConstant('fieldSettings') }), _jsx("button", { "aria-label": "Close panel", className: "close-btn", onClick: handleClose, title: "Close (Esc)", children: "\u2715" })] }), _jsx("div", { className: `rightpane-property-content single-view`, role: "region", "aria-labelledby": "field-settings-heading", children: _jsx("div", { className: "properties-section", "aria-label": l10n.getConstant('fieldSettings'), children: _jsx("div", { className: "property-tabs-wrapper", children: _jsx(TabComponent, { className: "property-tabs", heightAdjustMode: "Auto", "aria-label": l10n.getConstant('fieldSettings'), animation: { previous: { effect: 'None' }, next: { effect: 'None' } }, children: _jsx(TabItemsDirective, { children: useMemo(() => {
330
+ let groupsToRender = componentDefinition.groups;
331
+ // Filter for button component based on allowMultiple
332
+ if (componentType === 'button') {
333
+ groupsToRender = getFilteredGroups(componentDefinition.groups, (allowMultipleValueRef.current || existingValues?.allowMultiple));
334
+ }
335
+ // Filter for lookup component based on displayFormat
336
+ else if (componentType === 'lookup') {
337
+ const displayFormat = propertyValuesRef.current?.lookUpConfig?.displayFormat || 'Dropdown';
338
+ groupsToRender = getFilteredLookupGroups(componentDefinition.groups, displayFormat);
339
+ }
340
+ groupsToRender = applyModeFiltering(groupsToRender);
341
+ return groupsToRender.map((group, index) => (_jsx(TabItemDirective, { header: { text: l10n.getConstant(group.name.toLowerCase()) }, content: () => {
342
+ // Special handling for Conditions group
343
+ if (group.name === 'Conditions') {
344
+ // Extract all form fields dynamically
345
+ const allFormFields = extractAllFormFieldNames(formSchema.components);
346
+ const conditionsProperty = group.properties.find(p => p.key === 'conditions');
347
+ const supportedConditions = conditionsProperty?.supportedConditions ||
348
+ ['visibleWhen', 'readOnlyWhen', 'disabledWhen'];
349
+ return (_jsxs("div", { children: [_jsx(ConditionsEditor, { componentFields: allFormFields, excludeFieldId: existingValues?.id, componentForEdit: currentComponent || undefined, excludeChildIds: excludeChildIds, existingConditions: existingValues?.conditions, onChange: handleConditionsChange, supportedConditions: supportedConditions, builderMode: builderMode }, currentComponent?.options?.length), _jsx("input", { style: { opacity: 0, pointerEvents: 'none', width: 0, height: 0 }, "aria-hidden": "true", tabIndex: 0, onFocus: (e) => {
350
+ const input = e.currentTarget;
351
+ // Only forward focus the first time, then remove from tab order
352
+ if (input.dataset.forwarded === 'true') {
353
+ return;
354
+ }
355
+ input.dataset.forwarded = 'true';
356
+ input.tabIndex = -1;
357
+ const btn = input.parentElement?.querySelector('.conditions-editor-wrapper button, .conditions-editor-wrapper [role="button"]');
358
+ if (btn) {
359
+ btn.focus();
360
+ }
361
+ }, type: "text" })] }));
362
+ }
363
+ // Default: render PropertyGroupRenderer
364
+ return (_jsx(PropertyGroupRenderer, { groupName: group.name, properties: group.properties, existingValues: existingValues, initialValues: { ...propertyValuesRef.current,
365
+ availableFields: collectAvailableFieldNames(formSchema.components)
366
+ }, onChange: handlePropertyChange, mode: builderMode }));
367
+ } }, `tab-${componentType}-${index}`)));
368
+ }, [componentType, allowMultipleValueRef.current, propertyValuesRef.current?.lookUpConfig?.displayFormat, componentDefinition.groups]) }) }, `tabs-${componentType}-${columnsValueRef.current}-${propertyValuesRef.current}`) }) }) })] }));
369
+ };
370
+ export default React.memo(RightPane);
@@ -0,0 +1,14 @@
1
+ /**
2
+ * ThemeSwitcher - Dropdown component for selecting Syncfusion theme variants
3
+ */
4
+ import React from 'react';
5
+ import type { ThemeVariant } from './theme-constant';
6
+ interface ThemeSwitcherProps {
7
+ currentTheme: ThemeVariant;
8
+ setTheme: (variant: ThemeVariant) => void;
9
+ }
10
+ /**
11
+ * ThemeSwitcher component renders a DropDownButton for theme selection
12
+ */
13
+ export declare function ThemeSwitcher({ currentTheme, setTheme }: ThemeSwitcherProps): React.JSX.Element;
14
+ export {};
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { DropDownButtonComponent } from '@syncfusion/ej2-react-splitbuttons';
3
+ import { AVAILABLE_THEMES } from './theme-constant';
4
+ /**
5
+ * ThemeSwitcher component renders a DropDownButton for theme selection
6
+ */
7
+ export function ThemeSwitcher({ currentTheme, setTheme }) {
8
+ // Convert theme options to DropDownButton items
9
+ const items = AVAILABLE_THEMES.map(theme => ({
10
+ id: theme.id,
11
+ text: theme.text,
12
+ iconCss: currentTheme === theme.id ? 'e-icons e-check' : ''
13
+ }));
14
+ /**
15
+ * Handle theme selection from dropdown
16
+ */
17
+ const handleSelect = (args) => {
18
+ const selectedTheme = AVAILABLE_THEMES.find(t => t.id === args.item.id);
19
+ if (selectedTheme) {
20
+ setTheme(selectedTheme.id);
21
+ }
22
+ };
23
+ return (_jsx("div", { className: "theme-switcher", style: { display: 'inline-block', marginRight: '10px' }, children: _jsx(DropDownButtonComponent, { cssClass: "action-btn theme-dropdown", items: items, select: handleSelect, content: "Choose Theme", title: "Choose theme" }) }));
24
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Theme configuration constants for Syncfusion EJ2 components
3
+ */
4
+ /**
5
+ * Supported theme variants
6
+ */
7
+ export type ThemeVariant = 'material3' | 'tailwind3' | 'bootstrap5.3' | 'fluent2';
8
+ /**
9
+ * Theme mode (light or dark)
10
+ */
11
+ export type ThemeMode = 'light' | 'dark';
12
+ /**
13
+ * Complete theme configuration
14
+ */
15
+ export interface ThemeConfig {
16
+ variant: ThemeVariant;
17
+ mode: ThemeMode;
18
+ }
19
+ /**
20
+ * Theme option for dropdown selection
21
+ */
22
+ export interface ThemeOption {
23
+ id: ThemeVariant;
24
+ text: string;
25
+ }
26
+ /**
27
+ * Available theme options for theme switcher dropdown
28
+ */
29
+ export declare const AVAILABLE_THEMES: ThemeOption[];
30
+ /**
31
+ * Default theme configuration
32
+ */
33
+ export declare const DEFAULT_THEME: ThemeConfig;
34
+ /**
35
+ * LocalStorage key for theme persistence
36
+ */
37
+ export declare const THEME_STORAGE_KEY = "form-builder-theme";
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Theme configuration constants for Syncfusion EJ2 components
3
+ */
4
+ /**
5
+ * Available theme options for theme switcher dropdown
6
+ */
7
+ export const AVAILABLE_THEMES = [
8
+ { id: 'material3', text: 'Material 3' },
9
+ { id: 'tailwind3', text: 'Tailwind 3' },
10
+ { id: 'bootstrap5.3', text: 'Bootstrap 5.3' },
11
+ { id: 'fluent2', text: 'Fluent 2' }
12
+ ];
13
+ /**
14
+ * Default theme configuration
15
+ */
16
+ export const DEFAULT_THEME = {
17
+ variant: 'tailwind3',
18
+ mode: 'light'
19
+ };
20
+ /**
21
+ * LocalStorage key for theme persistence
22
+ */
23
+ export const THEME_STORAGE_KEY = 'form-builder-theme';