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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/README.md +81 -36
  2. package/diConfig.json +148 -0
  3. package/dist/ej2-react-form-builder.min.js +10 -0
  4. package/dist/ej2-react-form-builder.umd.min.js +11 -0
  5. package/dist/ej2-react-form-builder.umd.min.js.map +1 -0
  6. package/dist/es6/ej2-react-form-builder.es2015.js +60 -0
  7. package/dist/es6/ej2-react-form-builder.es2015.js.map +1 -0
  8. package/form-builder.d.ts +4 -0
  9. package/form-builder.js +4 -0
  10. package/index.d.ts +2 -4
  11. package/index.js +2 -4
  12. package/license +6 -13
  13. package/package.json +19 -43
  14. package/src/form-builder/formbuilder.component.d.ts +37 -0
  15. package/src/form-builder/formbuilder.component.js +50 -0
  16. package/src/form-builder/index.d.ts +2 -0
  17. package/src/form-builder/index.js +2 -0
  18. package/src/form-builder/toolboxitems-directive.d.ts +28 -0
  19. package/src/form-builder/toolboxitems-directive.js +47 -0
  20. package/src/global.js +1 -0
  21. package/src/index.d.ts +2 -16
  22. package/src/index.js +2 -6
  23. package/Common/CheckboxGroup.d.ts +0 -13
  24. package/Common/CheckboxGroup.js +0 -50
  25. package/Common/ConditionalRuleEngine.d.ts +0 -296
  26. package/Common/ConditionalRuleEngine.js +0 -566
  27. package/Common/CustomTabComponent.d.ts +0 -17
  28. package/Common/CustomTabComponent.js +0 -30
  29. package/Common/CustomValidationEngine.d.ts +0 -58
  30. package/Common/CustomValidationEngine.js +0 -146
  31. package/Common/DataGridComponent.d.ts +0 -20
  32. package/Common/DataGridComponent.js +0 -273
  33. package/Common/FormValidator.d.ts +0 -485
  34. package/Common/FormValidator.js +0 -575
  35. package/Common/MinMaxRuleEngine.d.ts +0 -105
  36. package/Common/MinMaxRuleEngine.js +0 -365
  37. package/Common/StaticHtmlRenderer.d.ts +0 -14
  38. package/Common/StaticHtmlRenderer.js +0 -24
  39. package/Common/expressions/DependencyTracker.d.ts +0 -30
  40. package/Common/expressions/DependencyTracker.js +0 -128
  41. package/Common/expressions/ExpressionEngine.d.ts +0 -95
  42. package/Common/expressions/ExpressionEngine.js +0 -506
  43. package/Common/expressions/ExpressionParser.d.ts +0 -41
  44. package/Common/expressions/ExpressionParser.js +0 -269
  45. package/Common/expressions/useGridExpressions.d.ts +0 -12
  46. package/Common/expressions/useGridExpressions.js +0 -71
  47. package/Common/globalFormId.d.ts +0 -26
  48. package/Common/globalFormId.js +0 -45
  49. package/Common/json-converter.d.ts +0 -444
  50. package/Common/json-converter.js +0 -1184
  51. package/Common/parser.d.ts +0 -98
  52. package/Common/parser.js +0 -753
  53. package/Common/utils.d.ts +0 -130
  54. package/Common/utils.js +0 -574
  55. package/src/FormBuilder.d.ts +0 -211
  56. package/src/FormBuilder.js +0 -1299
  57. package/src/components/Database/ConnectionForm.d.ts +0 -8
  58. package/src/components/Database/ConnectionForm.js +0 -28
  59. package/src/components/TopBar.d.ts +0 -10
  60. package/src/components/TopBar.js +0 -7
  61. package/src/components/icons/ComponentIcons.d.ts +0 -44
  62. package/src/components/icons/ComponentIcons.js +0 -40
  63. package/src/components/layout/FormFieldWrapper.d.ts +0 -41
  64. package/src/components/layout/FormFieldWrapper.js +0 -112
  65. package/src/components/layout/LeftPane.d.ts +0 -23
  66. package/src/components/layout/LeftPane.js +0 -310
  67. package/src/components/layout/ListBoxPane.d.ts +0 -24
  68. package/src/components/layout/ListBoxPane.js +0 -94
  69. package/src/components/layout/MainSection.d.ts +0 -39
  70. package/src/components/layout/MainSection.js +0 -1766
  71. package/src/components/layout/Preview.d.ts +0 -12
  72. package/src/components/layout/Preview.js +0 -337
  73. package/src/components/layout/RightPane.d.ts +0 -22
  74. package/src/components/layout/RightPane.js +0 -370
  75. package/src/components/theme/ThemeSwitcher.d.ts +0 -14
  76. package/src/components/theme/ThemeSwitcher.js +0 -24
  77. package/src/components/theme/theme-constant.d.ts +0 -37
  78. package/src/components/theme/theme-constant.js +0 -23
  79. package/src/config/component-properties.json +0 -4929
  80. package/src/config/form-setting.json +0 -56
  81. package/src/config/locale.d.ts +0 -449
  82. package/src/config/locale.js +0 -449
  83. package/src/context/FormSchemaContext.d.ts +0 -32
  84. package/src/context/FormSchemaContext.js +0 -155
  85. package/src/context/LocalizationContext.d.ts +0 -27
  86. package/src/context/LocalizationContext.js +0 -43
  87. package/src/context/ThemeContext.d.ts +0 -23
  88. package/src/context/ThemeContext.js +0 -161
  89. package/src/context/contexts.d.ts +0 -6
  90. package/src/context/contexts.js +0 -6
  91. package/src/context/defaultFormSchema.d.ts +0 -6
  92. package/src/context/defaultFormSchema.js +0 -20
  93. package/src/defaultPalette.d.ts +0 -9
  94. package/src/defaultPalette.js +0 -45
  95. package/src/defaultTemplate.d.ts +0 -3
  96. package/src/defaultTemplate.js +0 -185
  97. package/src/import/schemaImportUtils.d.ts +0 -24
  98. package/src/import/schemaImportUtils.js +0 -121
  99. package/src/types/ActionButtons.d.ts +0 -44
  100. package/src/types/ActionButtons.js +0 -27
  101. package/src/types/ComponentProperties.d.ts +0 -33
  102. package/src/types/ComponentProperties.js +0 -2
  103. package/src/types/FormSchema.d.ts +0 -585
  104. package/src/types/FormSchema.js +0 -147
  105. package/src/types/connection.d.ts +0 -19
  106. package/src/types/connection.js +0 -9
  107. package/src/utils/AttributesEditor.d.ts +0 -13
  108. package/src/utils/AttributesEditor.js +0 -117
  109. package/src/utils/ButtonGroupEditor.d.ts +0 -15
  110. package/src/utils/ButtonGroupEditor.js +0 -141
  111. package/src/utils/ChoiceBasedFieldEditor.d.ts +0 -16
  112. package/src/utils/ChoiceBasedFieldEditor.js +0 -87
  113. package/src/utils/ClipboardHelper.d.ts +0 -26
  114. package/src/utils/ClipboardHelper.js +0 -83
  115. package/src/utils/ColumnBuilder.d.ts +0 -11
  116. package/src/utils/ColumnBuilder.js +0 -153
  117. package/src/utils/ColumnSpanEditor.d.ts +0 -8
  118. package/src/utils/ColumnSpanEditor.js +0 -98
  119. package/src/utils/ConditionDataEditor.d.ts +0 -29
  120. package/src/utils/ConditionDataEditor.js +0 -177
  121. package/src/utils/ConditionalEditorUtils.d.ts +0 -27
  122. package/src/utils/ConditionalEditorUtils.js +0 -310
  123. package/src/utils/ConditionsEditor.d.ts +0 -20
  124. package/src/utils/ConditionsEditor.js +0 -571
  125. package/src/utils/CustomAccordion.d.ts +0 -13
  126. package/src/utils/CustomAccordion.js +0 -38
  127. package/src/utils/CustomValidationEditor.d.ts +0 -11
  128. package/src/utils/CustomValidationEditor.js +0 -208
  129. package/src/utils/DataSourceCollector.d.ts +0 -3
  130. package/src/utils/DataSourceCollector.js +0 -252
  131. package/src/utils/ExpressionEditor.d.ts +0 -13
  132. package/src/utils/ExpressionEditor.js +0 -99
  133. package/src/utils/FormSettingsPanel.d.ts +0 -9
  134. package/src/utils/FormSettingsPanel.js +0 -60
  135. package/src/utils/LookupConfig.d.ts +0 -11
  136. package/src/utils/LookupConfig.js +0 -159
  137. package/src/utils/MinMaxValueEditor.d.ts +0 -25
  138. package/src/utils/MinMaxValueEditor.js +0 -188
  139. package/src/utils/OptionsEditor.d.ts +0 -8
  140. package/src/utils/OptionsEditor.js +0 -79
  141. package/src/utils/PropertyRenderer.d.ts +0 -22
  142. package/src/utils/PropertyRenderer.js +0 -210
  143. package/src/utils/ValueRenderer.d.ts +0 -2
  144. package/src/utils/ValueRenderer.js +0 -91
  145. package/src/utils/ej2DbAdaptor.d.ts +0 -6
  146. package/src/utils/ej2DbAdaptor.js +0 -38
  147. package/src/utils/mappingHeuristics.d.ts +0 -10
  148. package/src/utils/mappingHeuristics.js +0 -33
  149. package/src/utils/renderComponent.d.ts +0 -18
  150. package/src/utils/renderComponent.js +0 -300
  151. package/styles/tailwind3.css +0 -67
  152. package/styles/themes/action-buttons.css +0 -110
  153. package/styles/themes/datagrid.css +0 -380
  154. package/styles/themes/drag-reorder.css +0 -248
  155. package/styles/themes/import-dialog.css +0 -292
  156. package/styles/themes/index.css +0 -658
  157. package/styles/themes/left-pane.css +0 -253
  158. package/styles/themes/options-editor.css +0 -63
  159. package/styles/themes/preview.css +0 -702
  160. package/styles/themes/property-dialog.css +0 -371
@@ -1,83 +0,0 @@
1
- /**
2
- * System clipboard helper for copying/pasting form field configurations
3
- * Uses navigator.clipboard API for cross-tab and external tool integration
4
- */
5
- export class ClipboardHelper {
6
- static CLIPBOARD_PREFIX = 'FORMBUILDER_FIELD:';
7
- /**
8
- * Copy field configuration to system clipboard as JSON
9
- */
10
- static async copyToClipboard(field) {
11
- try {
12
- const jsonString = JSON.stringify(field, null, 2);
13
- const clipboardData = `${this.CLIPBOARD_PREFIX}${jsonString}`;
14
- await navigator.clipboard.writeText(clipboardData);
15
- //console.log('Field copied to clipboard:', field.id);
16
- }
17
- catch (error) {
18
- console.error('Failed to copy to clipboard:', error);
19
- throw new Error(`Unable to copy field: ${error}`);
20
- }
21
- }
22
- /**
23
- * Read field configuration from system clipboard
24
- * Returns null if clipboard doesn't contain valid field data
25
- */
26
- static async readFromClipboard() {
27
- try {
28
- const clipboardText = await navigator.clipboard.readText();
29
- // Check if clipboard contains our prefixed data
30
- if (!clipboardText.startsWith(this.CLIPBOARD_PREFIX)) {
31
- console.warn('Clipboard does not contain form field data');
32
- return null;
33
- }
34
- // Remove prefix and parse JSON
35
- const jsonString = clipboardText.substring(this.CLIPBOARD_PREFIX.length);
36
- const field = JSON.parse(jsonString);
37
- //console.log('Field read from clipboard:', field.id);
38
- return field;
39
- }
40
- catch (error) {
41
- console.error('Failed to read from clipboard:', error);
42
- return null;
43
- }
44
- }
45
- /**
46
- * Check if clipboard contains valid form field data
47
- */
48
- static async hasClipboardData() {
49
- try {
50
- const clipboardText = await navigator.clipboard.readText();
51
- return clipboardText.startsWith(this.CLIPBOARD_PREFIX);
52
- }
53
- catch (error) {
54
- // Silently return false when document is not focused (expected behavior)
55
- // or clipboard permissions are denied
56
- return false;
57
- }
58
- }
59
- /**
60
- * Generate new unique field with modified ID and name
61
- */
62
- static createUniqueField(sourceField) {
63
- const timestamp = Date.now();
64
- const random = Math.floor(Math.random() * 1000);
65
- return {
66
- ...sourceField,
67
- id: `${sourceField.type}_${timestamp}_${random}`
68
- };
69
- }
70
- static async clearData() {
71
- // Overwrite the system clipboard with empty text unconditionally.
72
- //NOTE: We deliberately do NOT call navigator.clipboard.readText() here
73
- // because doing so triggers the browser's native paste UI prompt in
74
- // Firefox on first invocation. Writing alone is sufficient to clear
75
- // any form-builder data we previously wrote.
76
- try {
77
- await navigator.clipboard.writeText('');
78
- }
79
- catch (error) {
80
- // Silently ignore failures (e.g. document not focused)
81
- }
82
- }
83
- }
@@ -1,11 +0,0 @@
1
- import React from 'react';
2
- import type { GridColumn } from '../types/FormSchema';
3
- interface ColumnBuilderProps {
4
- columns: GridColumn[];
5
- onChange: (columns: GridColumn[]) => void;
6
- name?: any;
7
- isConditionData?: boolean;
8
- label?: string;
9
- }
10
- export declare const ColumnBuilder: React.FC<ColumnBuilderProps>;
11
- export {};
@@ -1,153 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useState, useEffect, useRef } from 'react';
3
- import { ButtonComponent, CheckBoxComponent } from '@syncfusion/ej2-react-buttons';
4
- import { AutoCompleteComponent, DropDownListComponent } from '@syncfusion/ej2-react-dropdowns';
5
- import { NumericTextBoxComponent, TextBoxComponent } from '@syncfusion/ej2-react-inputs';
6
- import { DatePickerComponent } from '@syncfusion/ej2-react-calendars';
7
- import { useLocalization } from '../context/LocalizationContext';
8
- import { useFetchOptions } from '../../Common/utils';
9
- import { useFormSchema } from '../context/FormSchemaContext';
10
- import { ColumnExpressionEditor } from './ExpressionEditor';
11
- import { CustomValidationEditor } from './CustomValidationEditor';
12
- import { AccordionWrapper } from './CustomAccordion';
13
- export const ColumnBuilder = ({ columns, onChange, name, isConditionData, label }) => {
14
- const [rows, setRows] = useState([]);
15
- const [expandedId, setExpandedId] = useState(null);
16
- const nextIdRef = useRef(0);
17
- const { l10n, locale } = useLocalization();
18
- const { formSchema } = useFormSchema();
19
- const componentForEdit = formSchema.components.find(comp => comp.name === name);
20
- const dataSource = componentForEdit?.dataSource || [];
21
- const fetchedData = useFetchOptions(dataSource);
22
- const extractDataSourceKeys = (dataSource) => {
23
- if (Array.isArray(dataSource) && dataSource.length > 0) {
24
- return Object.keys(dataSource[0]);
25
- }
26
- return [];
27
- };
28
- const dataSourceKeys = extractDataSourceKeys(Array.isArray(dataSource) ? dataSource : fetchedData);
29
- const columnsWithKeys = columns.map(col => col.field).filter(field => field);
30
- const mapTypeToEditType = (type) => {
31
- switch (type) {
32
- case 'number':
33
- return 'numericedit';
34
- case 'date':
35
- return 'datepickeredit';
36
- case 'boolean':
37
- return 'booleanedit';
38
- default:
39
- return 'textbox';
40
- }
41
- };
42
- // Initialize rows from columns
43
- useEffect(() => {
44
- const initialRows = (columns || []).map((col, idx) => {
45
- const id = idx;
46
- if (id >= nextIdRef.current) {
47
- nextIdRef.current = id + 1;
48
- }
49
- return {
50
- id,
51
- index: idx + 1,
52
- ...col,
53
- allowEditing: col.allowEditing !== undefined ? col.allowEditing : true // Add this line
54
- };
55
- });
56
- setRows(initialRows);
57
- }, []);
58
- // Update parent when columns change
59
- const updateColumns = (newRows) => {
60
- const updatedColumns = newRows.map(({ id, index, ...col }) => col);
61
- onChange(updatedColumns);
62
- };
63
- const handleAddColumn = () => {
64
- const newId = nextIdRef.current++;
65
- const newIndex = rows.length + 1;
66
- const newColumn = {
67
- id: newId,
68
- index: newIndex,
69
- field: `column_${Date.now()}`,
70
- headerText: `${l10n.getConstant('column')} ${newIndex}`,
71
- type: 'string',
72
- editType: mapTypeToEditType('string'),
73
- width: '120',
74
- allowEditing: true,
75
- validationRules: {}
76
- };
77
- const newRows = [...rows, newColumn];
78
- setRows(newRows);
79
- updateColumns(newRows);
80
- };
81
- const handleDeleteColumn = (id) => {
82
- const newRows = rows.filter(row => row.id !== id).map((row, idx) => ({
83
- ...row,
84
- index: idx + 1
85
- }));
86
- setRows(newRows);
87
- updateColumns(newRows);
88
- };
89
- const handleColumnUpdate = (id, updates) => {
90
- const newRows = rows.map(row => row.id === id ? { ...row, ...updates } : row);
91
- setRows(newRows);
92
- updateColumns(newRows);
93
- };
94
- const handleValidationRuleChange = (id, ruleName, value) => {
95
- const newRows = rows.map(row => {
96
- if (row.id === id) {
97
- const newValidationRules = { ...row.validationRules };
98
- if (value === undefined || value === '' || value === null) {
99
- delete newValidationRules[ruleName];
100
- }
101
- else {
102
- newValidationRules[ruleName] = value;
103
- }
104
- return { ...row, validationRules: newValidationRules };
105
- }
106
- return row;
107
- });
108
- setRows(newRows);
109
- updateColumns(newRows);
110
- };
111
- const handlePrimaryKeyChange = (id, checked) => {
112
- const newRows = rows.map(row => {
113
- if (row.id === id) {
114
- // Set this column as primary key
115
- return { ...row, isPrimaryKey: checked };
116
- }
117
- else if (checked) {
118
- // Clear primary key from all other columns when setting a new one
119
- return { ...row, isPrimaryKey: false };
120
- }
121
- return row;
122
- });
123
- setRows(newRows);
124
- updateColumns(newRows);
125
- };
126
- const hasOtherPrimaryKey = (currentId) => {
127
- return rows.some(row => row.id !== currentId && row.isPrimaryKey === true);
128
- };
129
- return (_jsx(AccordionWrapper, { title: l10n.getConstant(label), children: _jsxs("div", { className: "column-builder", children: [_jsx("div", { className: "column-list", style: rows.length ? undefined : { display: 'none' }, children: rows.map((col) => (_jsxs("div", { className: "column-item", children: [_jsxs("div", { className: `column-header ${isConditionData ? 'no-border-radius' : ''}`, children: [_jsx("span", { className: "column-field", children: col.headerText }), _jsxs("div", { style: { display: 'flex', gap: '8px' }, children: [_jsx(ButtonComponent, { onClick: () => setExpandedId(expandedId === col.id ? null : col.id), cssClass: 'e-flat', iconCss: "e-icons e-edit", title: l10n.getConstant('edit'), "aria-label": l10n.getConstant('edit') }), _jsx(ButtonComponent, { onClick: (e) => {
130
- e.stopPropagation();
131
- handleDeleteColumn(col.id);
132
- }, cssClass: 'e-flat', iconCss: "e-icons e-trash", title: l10n.getConstant('delete'), "aria-label": l10n.getConstant('delete') })] })] }), expandedId === col.id && (_jsxs("div", { className: !isConditionData ? 'right-sidebar-column' : 'conditional-column', children: [!isConditionData &&
133
- _jsxs("div", { className: "column-sidebar-header", children: [_jsx("h3", { children: l10n.getConstant('columnSettings') }), _jsx("button", { className: "close-btn", onClick: () => setExpandedId(expandedId === col.id ? null : col.id), title: "Close (Esc)", "aria-label": l10n.getConstant('close'), children: "\u2715" })] }, 'column settings'), _jsxs("div", { className: 'column-details', children: [_jsxs("div", { className: "form-group", children: [_jsx("label", { className: 'property-label', style: { fontWeight: 600, marginBottom: '0.5rem', fontSize: '14px' }, children: l10n.getConstant('basic') }), _jsx("label", { htmlFor: `col-field-${col.id}`, children: l10n.getConstant('fieldName') }), dataSourceKeys.length > 0 ? (_jsx(AutoCompleteComponent, { dataSource: dataSourceKeys.map(key => ({ text: key, value: key })), fields: { text: 'text', value: 'value' }, value: col.field, change: (e) => handleColumnUpdate(col.id, { field: e.value }), htmlAttributes: { 'aria-label': l10n.getConstant('fieldName') } })) : (_jsx(TextBoxComponent, { type: "text", value: col.field, onChange: (e) => handleColumnUpdate(col.id, { field: e.value }), htmlAttributes: { 'aria-label': l10n.getConstant('fieldName') } }))] }), _jsxs("div", { className: "form-group", children: [_jsx("label", { htmlFor: `col-header-${col.id}`, children: l10n.getConstant('headerText') }), _jsx(TextBoxComponent, { type: "text", value: col.headerText, onChange: (e) => handleColumnUpdate(col.id, { headerText: e.value }), placeholder: l10n.getConstant('columnHeaderDisplay'), className: "e-input-group e-control-wrapper e-textbox ", htmlAttributes: { 'aria-label': l10n.getConstant('headerText') } })] }), _jsxs("div", { className: "form-group", children: [_jsx("label", { htmlFor: `col-type-${col.id}`, children: l10n.getConstant('type') }), _jsx(DropDownListComponent, { dataSource: [
134
- { text: l10n.getConstant('string'), value: 'string' },
135
- { text: l10n.getConstant('number'), value: 'number' },
136
- { text: l10n.getConstant('date'), value: 'date' },
137
- { text: l10n.getConstant('boolean'), value: 'boolean' }
138
- ], fields: { text: 'text', value: 'value' }, value: col.type || 'string', change: (e) => {
139
- const newType = e.value || 'string';
140
- handleColumnUpdate(col.id, {
141
- type: newType,
142
- editType: mapTypeToEditType(newType),
143
- validationRules: {}
144
- });
145
- }, placeholder: l10n.getConstant('selectType'), cssClass: "e-textbox", htmlAttributes: { 'aria-label': l10n.getConstant('type') } })] }), _jsxs("div", { className: "form-group", children: [_jsx("label", { htmlFor: `col-width-${col.id}`, children: l10n.getConstant('width') }), _jsx(TextBoxComponent, { type: "text", value: `${col.width}` || '120', onChange: (e) => handleColumnUpdate(col.id, { width: e.value }), placeholder: l10n.getConstant('columnWidthPlaceholder'), className: "e-textbox", htmlAttributes: { 'aria-label': l10n.getConstant('width') } })] }), _jsxs("div", { className: "form-group", children: [_jsx("label", { htmlFor: `col-format-${col.id}`, children: l10n.getConstant('format') }), _jsx(TextBoxComponent, { type: "text", value: col.format || '', onChange: (e) => handleColumnUpdate(col.id, { format: e.value }), className: "e-textbox", htmlAttributes: { 'aria-label': l10n.getConstant('format') } })] }), _jsx("div", { style: { height: '0.5px', borderBottom: '0.5px solid rgb(var(--sf-color-line-color))' } }), _jsxs("div", { className: "form-group checkbox-group", children: [_jsx("label", { className: 'property-label', style: { fontWeight: 600, marginBottom: '0.5rem', fontSize: '14px' }, children: l10n.getConstant('behaviour') }), _jsxs("label", { children: [_jsx(CheckBoxComponent, { type: "checkbox", checked: col.isPrimaryKey || false, disabled: !col.isPrimaryKey && hasOtherPrimaryKey(col.id), change: (e) => handlePrimaryKeyChange(col.id, e.checked || false), htmlAttributes: { 'aria-label': l10n.getConstant('primaryKey') } }), l10n.getConstant('primaryKey'), !col.isPrimaryKey &&
146
- hasOtherPrimaryKey(col.id) &&
147
- ` (${l10n.getConstant('anotherPrimaryKeySet')})`] })] }), _jsx("div", { className: "form-group checkbox-group", children: _jsxs("label", { children: [_jsx(CheckBoxComponent, { type: "checkbox", checked: col.allowEditing !== false, change: (e) => handleColumnUpdate(col.id, { allowEditing: e.checked || false }), htmlAttributes: { 'aria-label': l10n.getConstant('allowEditing') || 'Allow Editing' } }), l10n.getConstant('allowEditing') || 'Allow Editing'] }) }), _jsx("div", { style: { height: '0.5px', borderBottom: '0.5px solid rgb(var(--sf-color-line-color))' } }), _jsx("div", { children: _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: '1rem' }, children: [_jsx("label", { style: { fontWeight: 600, fontSize: '14px' }, id: `validation-rules-label-${col.id}`, children: l10n.getConstant('validationRulesFor') }), _jsx("div", { className: "form-group checkbox-group", children: _jsxs("label", { children: [_jsx(CheckBoxComponent, { type: "checkbox", checked: col.validationRules?.required || false, change: (e) => handleValidationRuleChange(col.id, 'required', e.checked || undefined), htmlAttributes: { 'aria-label': l10n.getConstant('required') } }), l10n.getConstant('required')] }) }), col.type === 'string' && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "form-group", children: [_jsx("label", { htmlFor: `col-minLength-${col.id}`, children: l10n.getConstant('minLength') }), _jsx(NumericTextBoxComponent, { value: col.validationRules?.minLength ?? undefined, locale: locale, change: (e) => handleValidationRuleChange(col.id, 'minLength', e.value !== null && e.value !== undefined ? e.value : undefined), placeholder: l10n.getConstant('minimumCharacters'), cssClass: "e-textbox", htmlAttributes: { 'aria-label': l10n.getConstant('minLength') } })] }), _jsxs("div", { className: "form-group", children: [_jsx("label", { htmlFor: `col-maxLength-${col.id}`, children: l10n.getConstant('maxLength') }), _jsx(NumericTextBoxComponent, { value: col.validationRules?.maxLength ?? undefined, locale: locale, change: (e) => handleValidationRuleChange(col.id, 'maxLength', e.value !== null && e.value !== undefined ? e.value : undefined), placeholder: l10n.getConstant('maximumCharacters'), cssClass: "e-textbox", htmlAttributes: { 'aria-label': l10n.getConstant('maxLength') } })] })] })), col.type === 'number' && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "form-group", children: [_jsx("label", { htmlFor: `col-minValue-${col.id}`, children: l10n.getConstant('minValue') }), _jsx(NumericTextBoxComponent, { locale: locale, value: col.validationRules?.min ?? undefined, change: (e) => handleValidationRuleChange(col.id, 'min', e.value !== null && e.value !== undefined ? e.value : undefined), placeholder: l10n.getConstant('minimumValue'), cssClass: "e-textbox", htmlAttributes: { 'aria-label': l10n.getConstant('minValue') } })] }), _jsxs("div", { className: "form-group", children: [_jsx("label", { htmlFor: `col-maxValue-${col.id}`, children: l10n.getConstant('maxValue') }), _jsx(NumericTextBoxComponent, { value: col.validationRules?.max ?? undefined, locale: locale, change: (e) => handleValidationRuleChange(col.id, 'max', e.value !== null && e.value !== undefined ? e.value : undefined), placeholder: l10n.getConstant('maximumValue'), cssClass: "e-textbox", htmlAttributes: { 'aria-label': l10n.getConstant('maxValue') } })] })] })), col.type === 'date' && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "form-group", children: [_jsx("label", { htmlFor: `col-minDate-${col.id}`, children: l10n.getConstant('minDate') }), _jsx(DatePickerComponent, { locale: locale, value: col.validationRules?.min ? new Date(col.validationRules.min) : undefined, change: (e) => handleValidationRuleChange(col.id, 'min', e.value ? e.value.toISOString().slice(0, 10) : undefined), cssClass: "e-textbox", htmlAttributes: { 'aria-label': l10n.getConstant('minDate') } })] }), _jsxs("div", { className: "form-group", children: [_jsx("label", { htmlFor: `col-maxDate-${col.id}`, children: l10n.getConstant('maxDate') }), _jsx(DatePickerComponent, { locale: locale, value: col.validationRules?.max ? new Date(col.validationRules.max) : undefined, change: (e) => handleValidationRuleChange(col.id, 'max', e.value ? e.value.toISOString().slice(0, 10) : undefined), cssClass: "e-textbox", htmlAttributes: { 'aria-label': l10n.getConstant('maxDate') } })] })] }))] }) }), _jsx("div", { style: { height: '0.5px', borderBottom: '0.5px solid rgb(var(--sf-color-line-color))' } }), _jsxs("div", { className: "form-group", children: [_jsx("label", { style: { fontWeight: 600, marginBottom: '0.5rem', fontSize: '14px' }, htmlFor: `col-expression-${col.id}`, children: l10n.getConstant('expression') }), _jsxs("div", { className: "expression-editor-wrapper", children: [_jsx(ColumnExpressionEditor, { expression: col.expression || '', availableFields: columnsWithKeys, onExpressionChange: (expr) => handleColumnUpdate(col.id, { expression: expr }) }), col.expression && col.expression.trim() && (_jsx("small", { style: { color: '#666', marginTop: '8px', display: 'block' }, children: "Column is read-only (if calculated)." }))] })] }), _jsx("div", { style: { height: '0.5px', borderBottom: '0.5px solid rgb(var(--sf-color-line-color))' } }), _jsx("div", { className: "form-group", children: _jsx("div", { className: "custom-validation-section", children: _jsx(CustomValidationEditor, { componentFields: rows.map((r, _idx) => ({
148
- id: `${r.id}`,
149
- name: r.field,
150
- type: r.type || 'string',
151
- label: r.headerText
152
- })), excludeFieldId: col.field, existingRules: col.customValidation || [], values: { "label": "customValidation" }, onChange: (rules) => handleColumnUpdate(col.id, { customValidation: rules }) }) }) })] })] }))] }, col.id))) }), _jsx("div", { className: "", style: { width: '100%' }, children: _jsx(ButtonComponent, { style: { width: '100%' }, cssClass: "e-outline", onClick: handleAddColumn, type: "button", "aria-label": l10n.getConstant('addColumn'), children: l10n.getConstant('addColumn') }) })] }) }));
153
- };
@@ -1,8 +0,0 @@
1
- import React from 'react';
2
- interface ColumnSpanEditorProps {
3
- columnCount: number;
4
- initialWidths?: number[];
5
- onChange: (widths: (number | null)[]) => void;
6
- }
7
- export declare const ColumnSpanEditor: React.FC<ColumnSpanEditorProps>;
8
- export {};
@@ -1,98 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState, useEffect, useRef } from 'react';
3
- import { GridComponent, ColumnsDirective, ColumnDirective, Inject, Page } from '@syncfusion/ej2-react-grids';
4
- import { splitWidths } from '../../Common/utils';
5
- import { useLocalization } from '../context/LocalizationContext';
6
- import { floatFocus } from '../components/layout/LeftPane';
7
- export const ColumnSpanEditor = ({ columnCount, initialWidths, onChange, }) => {
8
- const [rows, setRows] = useState([]);
9
- const [maxValues, setMaxValues] = useState({});
10
- const gridRef = useRef(null);
11
- const { l10n } = useLocalization();
12
- const getInitialSplitWidths = (columnCount, initialWidths) => {
13
- const normalized = Array.from({ length: columnCount }, (_, i) => initialWidths && initialWidths[i] != null ? initialWidths[i] : null);
14
- return splitWidths(normalized);
15
- };
16
- // Initialize rows based on column count and initial widths
17
- useEffect(() => {
18
- if (columnCount < 1 || columnCount > 12) {
19
- console.warn(`Invalid column count: ${columnCount}`);
20
- return;
21
- }
22
- const resolvedWidths = getInitialSplitWidths(columnCount, initialWidths);
23
- // Create grid rows - no auto-fill, just use provided values or empty strings
24
- const newRows = Array(columnCount)
25
- .fill(null)
26
- .map((_, idx) => ({
27
- id: idx,
28
- columnName: `${idx + 1}`,
29
- width: ((initialWidths && !initialWidths.every((w) => w === null))) ? (initialWidths[idx] ? String(initialWidths[idx]) : '') : resolvedWidths[idx] ? String(resolvedWidths[idx]) : '',
30
- }));
31
- setRows(newRows);
32
- calculateMaxValues(newRows);
33
- }, [columnCount, initialWidths]);
34
- // Calculate max value for each field (100 - sum of all other fields)
35
- const calculateMaxValues = (rowsData) => {
36
- const filledValues = rowsData
37
- .map((row) => (row.width ? parseFloat(row.width) : 0))
38
- .filter((val) => val > 0);
39
- const totalFilled = filledValues.reduce((sum, val) => sum + val, 0);
40
- const newMaxValues = {};
41
- rowsData.forEach((row) => {
42
- const currentValue = row.width ? parseFloat(row.width) : 0;
43
- const othersFilled = totalFilled - currentValue;
44
- newMaxValues[row.id] = Math.max(0, 100 - othersFilled);
45
- });
46
- setMaxValues(newMaxValues);
47
- };
48
- // Handle width change in grid
49
- const handleWidthChange = (id, newValue) => {
50
- // Allow empty string or valid numeric values
51
- if (newValue === '' || newValue.trim() === '') {
52
- const updatedRows = rows.map((row) => row.id === id ? { ...row, width: '' } : row);
53
- setRows(updatedRows);
54
- calculateMaxValues(updatedRows);
55
- triggerChange(updatedRows);
56
- return;
57
- }
58
- let parsedWidth = parseFloat(newValue);
59
- // Validate: must be positive number
60
- if (isNaN(parsedWidth) || parsedWidth <= 0) {
61
- return;
62
- }
63
- // Auto-correct if exceeds max - set to max value
64
- const maxForField = (maxValues[id] != undefined) ? maxValues[id] : 100;
65
- if (parsedWidth > maxForField) {
66
- parsedWidth = maxForField;
67
- }
68
- const updatedRows = rows.map((row) => row.id === id ? { ...row, width: String(parsedWidth) } : row);
69
- setRows(updatedRows);
70
- calculateMaxValues(updatedRows);
71
- triggerChange(updatedRows);
72
- };
73
- // Trigger onChange callback with widths array (null for empty values)
74
- const triggerChange = (rowsData) => {
75
- const widths = rowsData.map((row) => row.width ? parseFloat(row.width) : null);
76
- onChange(widths);
77
- };
78
- // Width input template - text field with dynamic max value
79
- const widthTemplate = (props) => {
80
- const maxForField = maxValues[props.id] || 100;
81
- return (_jsx("div", { className: "e-input-group", children: _jsx("input", { type: 'number', defaultValue: props.width, max: maxForField, onFocus: floatFocus, "aria-label": "Span percentage", onKeyDown: (e) => {
82
- if (e.ctrlKey && e.key === 'a') {
83
- e.preventDefault();
84
- e.currentTarget.select();
85
- }
86
- }, onBlur: (e) => handleWidthChange(props.id, e.target.value), placeholder: "Width %", className: "e-input" }) }));
87
- };
88
- // Calculate grid height based on number of rows
89
- const calculateHeight = (rowCount) => {
90
- const baseHeight = 58; // Header + 1 row
91
- const rowHeight = 42;
92
- return baseHeight + rowCount * rowHeight;
93
- };
94
- if (columnCount < 1 || columnCount > 12) {
95
- return (_jsx("div", { className: "column-span-editor-error", children: _jsxs("span", { children: ["\u26A0\uFE0F Invalid column count: ", columnCount] }) }));
96
- }
97
- return (_jsx("div", { className: "options-editor", children: _jsxs(GridComponent, { ref: gridRef, dataSource: rows, allowPaging: false, height: calculateHeight(rows.length), allowSelection: false, cssClass: "options-grid table-col", children: [_jsxs(ColumnsDirective, { children: [_jsx(ColumnDirective, { field: "columnName", headerText: l10n.getConstant('columns') || "Columns", width: "80", textAlign: "Left", allowSorting: false, allowResizing: false }), _jsx(ColumnDirective, { field: "width", headerText: `${l10n.getConstant('width') || 'Width'} (%)`, width: "auto", template: widthTemplate, allowSorting: false, allowResizing: false })] }), _jsx(Inject, { services: [Page] })] }) }));
98
- };
@@ -1,29 +0,0 @@
1
- import React from 'react';
2
- import type { FormComponent, ConditionalDataRule } from '../types/FormSchema';
3
- export interface ConditionDataEditorProps {
4
- /** Array of available form field names/components for condition rules */
5
- componentFields: FormComponent[];
6
- /** The DataGrid component being edited */
7
- componentForEdit?: FormComponent;
8
- /** ID of the DataGrid being edited - exclude from columns */
9
- excludeFieldId?: string;
10
- /** Existing conditional data rules to pre-populate */
11
- existingRules?: ConditionalDataRule[];
12
- /** Callback when conditions change */
13
- onChange: (rules: ConditionalDataRule[]) => void;
14
- }
15
- /**
16
- * ConditionDataEditor Component
17
- *
18
- * Allows users to define conditional data sources for DataGrid components.
19
- * Users can create multiple conditions, each with its own QueryBuilder rule
20
- * and associated data source configuration.
21
- *
22
- * Features:
23
- * - Add/remove multiple conditional data rules
24
- * - QueryBuilder for condition definition
25
- * - DataSourceCollector for data source configuration
26
- * - Expandable UI (similar to ColumnBuilder pattern)
27
- * - Persists state to FormSchema
28
- */
29
- export declare const ConditionDataEditor: React.FC<ConditionDataEditorProps>;
@@ -1,177 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import React, { useRef, useEffect, useState } from 'react';
3
- import { QueryBuilderComponent } from '@syncfusion/ej2-react-querybuilder';
4
- import { ButtonComponent } from '@syncfusion/ej2-react-buttons';
5
- import { useLocalization } from '../context/LocalizationContext';
6
- import { DataSourceCollector } from './DataSourceCollector';
7
- import { ColumnBuilder } from './ColumnBuilder';
8
- import { createColumnTemplate } from './ConditionalEditorUtils';
9
- import { AccordionWrapper } from './CustomAccordion';
10
- /**
11
- * ConditionDataEditor Component
12
- *
13
- * Allows users to define conditional data sources for DataGrid components.
14
- * Users can create multiple conditions, each with its own QueryBuilder rule
15
- * and associated data source configuration.
16
- *
17
- * Features:
18
- * - Add/remove multiple conditional data rules
19
- * - QueryBuilder for condition definition
20
- * - DataSourceCollector for data source configuration
21
- * - Expandable UI (similar to ColumnBuilder pattern)
22
- * - Persists state to FormSchema
23
- */
24
- export const ConditionDataEditor = ({ componentFields, excludeFieldId, existingRules = [], onChange }) => {
25
- const { l10n } = useLocalization();
26
- const stateRef = useRef({
27
- rules: existingRules,
28
- expandedRuleId: null,
29
- nextRuleId: existingRules.length
30
- });
31
- const qbRefs = useRef({});
32
- const [, forceRender] = useState({});
33
- // Initialize QueryBuilder refs for each rule
34
- useEffect(() => {
35
- stateRef.current.rules.forEach((_, idx) => {
36
- if (!qbRefs.current[idx]) {
37
- qbRefs.current[idx] = React.createRef();
38
- }
39
- });
40
- }, [stateRef.current.rules.length]);
41
- /**
42
- * Build columns for QueryBuilder - excludes the current DataGrid field
43
- * to prevent self-referential conditions
44
- * Includes templates for different field types (dropdown, date, time, etc.)
45
- */
46
- const buildColumnsForQueryBuilder = (qbRef) => {
47
- return componentFields
48
- .filter(component => component.id !== excludeFieldId)
49
- .map(component => {
50
- const fieldType = inferFieldType(component);
51
- const operators = [
52
- { key: 'Is Empty', value: 'isempty' },
53
- { key: 'Is Not Empty', value: 'isnotempty' },
54
- { key: 'Equal', value: 'equal' },
55
- { key: 'Not Equal', value: 'notequal' },
56
- ...(fieldType === 'number' || fieldType === 'date'
57
- ? [
58
- { key: 'Greater than', value: 'greaterthan' },
59
- { key: 'Less than', value: 'lessthan' },
60
- { key: 'Greater than or equal', value: 'greaterthanorequal' },
61
- { key: 'Less than or equal', value: 'lessthanorequal' }
62
- ]
63
- : []),
64
- ...(fieldType === 'string'
65
- ? [
66
- { key: 'Starts With', value: 'startswith' },
67
- { key: 'Contains', value: 'contains' },
68
- { key: 'Not Contains', value: 'notcontains' }
69
- ]
70
- : [])
71
- ];
72
- return {
73
- field: component.id,
74
- label: component.label || component.id,
75
- type: fieldType,
76
- format: fieldType === 'date' ? 'MM/dd/yyyy' : undefined,
77
- operators,
78
- template: createColumnTemplate(component, qbRef)
79
- };
80
- });
81
- };
82
- /**
83
- * Infer field type from component type
84
- * Used for QueryBuilder column configuration
85
- */
86
- const inferFieldType = (component) => {
87
- const type = component.type;
88
- if (type === 'number' || type === 'rangeSlider' || type === 'rating')
89
- return 'number';
90
- if (type === 'date')
91
- return 'date';
92
- if (type === 'checkbox' || type === 'switch')
93
- return 'boolean';
94
- return 'string';
95
- };
96
- /**
97
- * Handle rule condition change from QueryBuilder
98
- */
99
- const handleRuleConditionChange = (ruleIndex) => {
100
- const qbRef = qbRefs.current[ruleIndex];
101
- if (qbRef?.current) {
102
- const rule = qbRef.current.getValidRules(qbRef.current.rule);
103
- stateRef.current.rules[ruleIndex] = {
104
- ...stateRef.current.rules[ruleIndex],
105
- condition: rule
106
- };
107
- onChange(stateRef.current.rules);
108
- }
109
- };
110
- /**
111
- * Handle data source change from DataSourceCollector
112
- */
113
- const handleDataSourceChange = (ruleIndex, dataSource) => {
114
- stateRef.current.rules[ruleIndex] = {
115
- ...stateRef.current.rules[ruleIndex],
116
- dataSource
117
- };
118
- onChange(stateRef.current.rules);
119
- forceRender({});
120
- };
121
- /**
122
- * Handle columns change from ColumnBuilder
123
- */
124
- const handleColumnsChange = (ruleIndex, columns) => {
125
- stateRef.current.rules[ruleIndex] = {
126
- ...stateRef.current.rules[ruleIndex],
127
- columns
128
- };
129
- onChange(stateRef.current.rules);
130
- forceRender({});
131
- };
132
- /**
133
- * Add new conditional data rule
134
- */
135
- const handleAddRule = () => {
136
- const newRuleId = stateRef.current.nextRuleId;
137
- const newRule = {
138
- condition: { condition: 'and', rules: [] },
139
- dataSource: []
140
- };
141
- stateRef.current.rules.push(newRule);
142
- stateRef.current.expandedRuleId = newRuleId;
143
- stateRef.current.nextRuleId = newRuleId + 1;
144
- qbRefs.current[newRuleId] = React.createRef();
145
- onChange(stateRef.current.rules);
146
- forceRender({});
147
- };
148
- /**
149
- * Delete conditional data rule
150
- */
151
- const handleDeleteRule = (ruleIndex) => {
152
- stateRef.current.rules.splice(ruleIndex, 1);
153
- // Clean up refs
154
- delete qbRefs.current[ruleIndex];
155
- stateRef.current.expandedRuleId = null;
156
- onChange(stateRef.current.rules);
157
- forceRender({});
158
- };
159
- return (_jsxs("div", { className: "condition-data-editor", children: [_jsx("div", { className: "rules-list", children: stateRef.current.rules.map((rule, idx) => {
160
- const ruleColumns = buildColumnsForQueryBuilder(qbRefs.current[idx]);
161
- return (_jsxs("div", { className: "rule-item", children: [_jsxs("div", { className: "rule-header", tabIndex: 0, id: `condition-header-${idx}`, "aria-expanded": stateRef.current.expandedRuleId === idx, "aria-labelledby": `condition-panel-${idx}`, "aria-label": `${l10n.getConstant('condition')} ${idx + 1}`, onClick: () => {
162
- stateRef.current.expandedRuleId =
163
- stateRef.current.expandedRuleId === idx ? null : idx;
164
- forceRender({});
165
- }, onKeyDown: (e) => {
166
- if (e.key === 'Enter' || e.key === ' ') {
167
- e.preventDefault();
168
- stateRef.current.expandedRuleId =
169
- stateRef.current.expandedRuleId === idx ? null : idx;
170
- forceRender({});
171
- }
172
- }, children: [_jsx("h4", { className: "rule-title", children: l10n.getConstant('condition') + ` ${idx + 1}` }), _jsx(ButtonComponent, { onClick: (e) => {
173
- e.stopPropagation();
174
- handleDeleteRule(idx);
175
- }, cssClass: "e-flat", iconCss: "e-icons e-trash", type: "button", title: l10n.getConstant('delete'), "aria-label": `${l10n.getConstant('delete')} ${l10n.getConstant('condition')} ${idx + 1}` })] }), stateRef.current.expandedRuleId === idx && (_jsxs("div", { className: "rule-details", id: `condition-panel-${idx}`, role: "region", "aria-labelledby": `condition-header-${idx}`, children: [_jsx(AccordionWrapper, { title: l10n.getConstant('condition'), children: _jsxs("div", { className: "form-group", role: "group", "aria-label": l10n.getConstant('condition'), children: [_jsx("p", { className: "property-description", id: `condition-description-${idx}`, children: l10n.getConstant('conditionalDataConditionDescription') }), _jsx("div", { onBlur: () => { }, tabIndex: -1, "aria-describedby": `condition-description-${idx}`, "aria-label": l10n.getConstant('condition'), children: _jsx(QueryBuilderComponent, { ref: qbRefs.current[idx], dataSource: [], columns: ruleColumns, rule: rule.condition, ruleChange: () => handleRuleConditionChange(idx) }) })] }) }), _jsx("div", { className: "form-group", role: "group", "aria-label": l10n.getConstant('dataSource'), children: _jsx(DataSourceCollector, { datakey: 'Conditionaldata', value: rule.dataSource, onChange: (dataSource) => handleDataSourceChange(idx, dataSource), type: "grid", isAccordion: true }) }), _jsx("div", { className: "form-group", role: "group", "aria-label": l10n.getConstant('columns'), children: _jsx(ColumnBuilder, { columns: rule.columns || [], onChange: (columns) => handleColumnsChange(idx, columns), isConditionData: true, label: 'columns' }) })] }, 'Condionaldata'))] }, idx));
176
- }) }), _jsx("div", { className: "rule-actions", role: "group", "aria-label": l10n.getConstant('conditionActions'), children: _jsx(ButtonComponent, { cssClass: "e-outline", onClick: handleAddRule, type: "button", "aria-label": l10n.getConstant('addCondition'), children: l10n.getConstant('addCondition') }) })] }));
177
- };
@@ -1,27 +0,0 @@
1
- /**
2
- * ConditionalEditorUtils - Shared utility functions for Conditional and ConditionalData editors
3
- * Exports field type inference, column templates, and helper functions
4
- */
5
- import type { QueryBuilderComponent } from '@syncfusion/ej2-react-querybuilder';
6
- import type { FormComponent } from '../types/FormSchema';
7
- /**
8
- * Infer field type from component type for QueryBuilder
9
- */
10
- export declare function inferFieldType(component: FormComponent): 'string' | 'number' | 'date' | 'boolean' | 'time';
11
- /**
12
- * Check if component is an array field type
13
- */
14
- export declare function isArrayField(component: FormComponent): boolean;
15
- /**
16
- * Check if component has selectable options
17
- */
18
- export declare function hasSelectableOptions(component: FormComponent): boolean;
19
- /**
20
- * Create column template for QueryBuilder with custom input widgets
21
- * Supports date, time, multiselect, dropdown, and other input types
22
- */
23
- export declare function createColumnTemplate(component: FormComponent, qbRef: React.RefObject<QueryBuilderComponent | null>, fetchedOptions?: Record<string, any[]>): {
24
- create: () => HTMLInputElement;
25
- destroy: (args: any) => void;
26
- write: (args: any) => void;
27
- };