@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,300 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { TextBoxComponent, NumericTextBoxComponent, TextAreaComponent, MaskedTextBoxComponent } from '@syncfusion/ej2-react-inputs';
4
+ import { CheckBoxComponent, ButtonComponent, RadioButtonComponent, SwitchComponent } from '@syncfusion/ej2-react-buttons';
5
+ import { SplitButtonComponent } from '@syncfusion/ej2-react-splitbuttons';
6
+ import { DatePickerComponent, DateRangePickerComponent, DateTimePickerComponent, Inject as DRPInject, Islamic, TimePickerComponent } from '@syncfusion/ej2-react-calendars';
7
+ import { CheckBoxSelection, DropDownListComponent, MultiSelectComponent, VirtualScroll, Inject } from '@syncfusion/ej2-react-dropdowns';
8
+ import { SignatureComponent } from '@syncfusion/ej2-react-inputs';
9
+ import { RatingComponent } from '@syncfusion/ej2-react-inputs';
10
+ import { UploaderComponent } from '@syncfusion/ej2-react-inputs';
11
+ import { SliderComponent } from '@syncfusion/ej2-react-inputs';
12
+ import { ColorPickerComponent } from '@syncfusion/ej2-react-inputs';
13
+ import { MessageComponent } from '@syncfusion/ej2-react-notifications';
14
+ import { ImageEditorComponent } from '@syncfusion/ej2-react-image-editor';
15
+ import { RichTextEditorComponent, Toolbar, HtmlEditor, Inject as RTEInject, Link, QuickToolbar, Image as RTEImage, Count, Table, EmojiPicker, PasteCleanup, SlashMenu, CodeBlock, ClipBoardCleanup, AutoFormat } from '@syncfusion/ej2-react-richtexteditor';
16
+ import { DataGridComponent } from '../../Common/DataGridComponent';
17
+ import { mapButtonStyleToClass, mapPositionToJustifyContent, resolveButtonType, resolveOptions, useFetchLookupOptions, useFetchOptions } from '../../Common/utils';
18
+ import CheckboxGroup from '../../Common/CheckboxGroup';
19
+ /**
20
+ * Converts htmlAttributes from array format to object format
21
+ * Array format: [{key: "data-test", value: "ms-1"}, ...]
22
+ * Object format: {"data-test": "ms-1", ...}
23
+ */
24
+ const showAttributes = (attributes) => {
25
+ if (!attributes)
26
+ return {};
27
+ if (Array.isArray(attributes)) {
28
+ return attributes.reduce((acc, item) => {
29
+ if (item.key && item.value !== undefined) {
30
+ acc[item.key] = item.value;
31
+ }
32
+ return acc;
33
+ }, {});
34
+ }
35
+ return attributes;
36
+ };
37
+ /**
38
+ * Shared component rendering utility used by both MainSection and ComponentPreview
39
+ * Renders a FormComponent with all properties applied consistently
40
+ */
41
+ export const RenderComponent = ({ component, formState, gridRefs, l10n, locale, formSettings }) => {
42
+ const fetchedOptions = useFetchOptions(component.options);
43
+ const fetchedDataSource = useFetchOptions(component.dataSource);
44
+ const commonProps = {
45
+ floatLabelType: 'Never',
46
+ placeholder: component.placeholder || '',
47
+ enabled: !component.disabled,
48
+ };
49
+ const fetchedLookupDataSource = useFetchLookupOptions(component.dataSource?.type === 'lookup' ? component.dataSource?.formName : undefined);
50
+ const lookupComponent = component.type === 'lookup'
51
+ ? component
52
+ : undefined;
53
+ const lookupOptions = useFetchLookupOptions(lookupComponent?.lookUpConfig?.formName, lookupComponent?.lookUpConfig?.field);
54
+ let value = formState?.values[component.id];
55
+ const currentValue = formState?.values[component.id];
56
+ if (formState && component.defaultValue !== undefined && component.defaultValue !== '' && component.defaultValue !== null) {
57
+ const isEmpty = currentValue === '' || currentValue === undefined || currentValue === null;
58
+ if (currentValue !== component.defaultValue && !isEmpty) {
59
+ formState.onChange(component.id, { value: component.defaultValue });
60
+ formState.values[component.id] = component.defaultValue;
61
+ }
62
+ }
63
+ const resolvedOptions = resolveOptions(component.options, fetchedOptions);
64
+ const resolvedDataSource = resolveOptions(component.dataSource, fetchedDataSource, fetchedLookupDataSource);
65
+ const isDefaultOptions = component.options?.length === 1 && component.options[0] === 'defaultOptionValue';
66
+ const sizeCSSClass = formSettings?.size === 'Small' || component.size === 'Small' ? ' e-small' : component.size === 'Bigger' ? ' e-bigger' : '';
67
+ const createValidationHandlers = (getValue) => {
68
+ const validateOn = component?.validateOn || 'Blur';
69
+ const handlers = {};
70
+ // If validateOn is 'Change' or undefined, add change and blur handlers
71
+ if (!validateOn || validateOn === 'Change') {
72
+ handlers.onChange = (args) => formState?.onChange(component.id, { value: getValue(args) });
73
+ handlers.onBlur = () => formState?.onBlur(component.id);
74
+ }
75
+ // If validateOn is 'Blur', add change and blur handlers
76
+ if (validateOn === 'Blur') {
77
+ handlers.change = (args) => formState?.onChange(component.id, { value: getValue(args) });
78
+ handlers.blur = () => formState?.onBlur(component.id);
79
+ }
80
+ return handlers;
81
+ };
82
+ switch (component.type) {
83
+ case 'textbox':
84
+ const textboxErrorClass = formState?.errors[component.id] ? 'e-error' : '';
85
+ const textboxCssClass = [component.cssClass || '', textboxErrorClass].filter(Boolean).join(' ') || undefined;
86
+ const textboxAttrs = formState?.values.htmlAttributes ? showAttributes(formState.values.htmlAttributes) : showAttributes(component.htmlAttributes);
87
+ return (_jsx(TextBoxComponent, { id: component.id, name: component.id, locale: locale, type: component.multiline ? 'hidden' : component.textboxType ? component.textboxType : 'text', ...commonProps, value: value !== undefined ? value : (component.defaultValue || ''), floatLabelType: component.floatLabelType || 'Never', multiline: component.multiline, appendTemplate: component.suffix, prependTemplate: component.prefix, showClearButton: component.showClearButton, autocomplete: component.autocomplete === false ? 'off' : 'on', readOnly: component.readOnly || false, cssClass: `${textboxCssClass}${sizeCSSClass}`, htmlAttributes: textboxAttrs, ...createValidationHandlers((args) => args.value || '') }, `form-${component.type}-${component.multiline}-${component.suffix}-${component.prefix}-${component.showClearButton}-${component.mask}-${JSON.stringify(component.defaultValue)}`));
88
+ case 'textarea':
89
+ const textareaErrorClass = formState?.errors[component.id] ? 'e-error' : '';
90
+ const textareaCssClass = [component.cssClass || '', textareaErrorClass].filter(Boolean).join(' ') || undefined;
91
+ const textareaAttrs = formState?.values.htmlAttributes ? showAttributes(formState.values.htmlAttributes) : showAttributes(component.htmlAttributes);
92
+ return (_jsx(TextAreaComponent, { id: component.id, locale: locale, name: component.id, ...commonProps, value: value !== undefined ? value : (component.defaultValue || ''), floatLabelType: component.floatLabelType || 'Never', showClearButton: component.showClearButton || false, readOnly: component.readOnly || false, cols: component.cols || 200, rows: component.textareaRows || 4, maxLength: component?.maxLength || 255, cssClass: `${textareaCssClass}${sizeCSSClass}`, htmlAttributes: textareaAttrs, appendTemplate: component.suffix, prependTemplate: component.prefix, resizeMode: 'Vertical', ...createValidationHandlers((args) => args.value || '') }, `form-${component.type}-${component.multiline}-${component.showClearButton}-${component.mask}-${JSON.stringify(component.defaultValue)}`));
93
+ case 'number':
94
+ const numericErrorClass = formState?.errors[component.id] ? 'e-error' : '';
95
+ const numericCssClass = [component.cssClass || '', numericErrorClass].filter(Boolean).join(' ') || undefined;
96
+ const numericAttrs = formState?.values.htmlAttributes ? showAttributes(formState.values.htmlAttributes) : showAttributes(component.htmlAttributes);
97
+ value = value === "" ? undefined : value;
98
+ return (_jsx(NumericTextBoxComponent, { id: component.id, name: component.id, ...commonProps, locale: locale, value: value !== undefined || value !== "" ? value : (component.defaultValue || 0), decimals: component.decimals || 2, currency: component.currency, format: component.numberFormat || 'n2', step: component.step || 1, min: component?.minValue, max: component?.maxValue, readOnly: component.readOnly || false, showClearButton: component.showClearButton || false, floatLabelType: component.floatLabelType || 'Never', cssClass: `${numericCssClass}${sizeCSSClass}`, htmlAttributes: numericAttrs, appendTemplate: component.suffix, prependTemplate: component.prefix, ...createValidationHandlers((args) => args.value || '') }, `form-${component.type}-${component.showClearButton}-${component.decimals}-${JSON.stringify(component.defaultValue)}`));
99
+ case 'checkbox':
100
+ const checkboxErrorClass = formState?.errors[component.id] ? 'e-error' : '';
101
+ const checkboxCssClass = [component.cssClass || '', checkboxErrorClass].filter(Boolean).join(' ') || undefined;
102
+ const checkboxAttrs = formState?.values.htmlAttributes ? showAttributes(formState.values.htmlAttributes) : showAttributes(component.htmlAttributes);
103
+ const componentLabel = component.label && component?.required ? `${component.label} *` : component.label;
104
+ return (_jsx(CheckBoxComponent, { id: component.id, name: component.id, label: componentLabel, locale: locale, labelPosition: component.checkboxLabelPosition || 'After', checked: (formState?.values[component.id] ?? component.checked ?? component.defaultValue), indeterminate: component.indeterminate || false, disabled: component.disabled, cssClass: `${checkboxCssClass}${sizeCSSClass}`, htmlAttributes: checkboxAttrs, title: component.tooltip, change: (args) => formState?.onChange(component.id, { value: args.checked }) }, `form-${component.type}-${component.showClearButton}-${JSON.stringify(component.defaultValue)}-${component.checkboxLabelPosition}`));
105
+ case 'checkboxGroup':
106
+ component.options = isDefaultOptions ? [l10n.getConstant('defaultOptionValue')] : component.options || ['Option 1'];
107
+ const checkboxGroupOptions = isDefaultOptions ? [l10n.getConstant('defaultOptionValue')] : resolvedOptions || ['Option 1'];
108
+ return (_jsx(CheckboxGroup, { component: { ...component, options: checkboxGroupOptions }, formState: formState }, `form-${component.type}-${component.id}-${component.checkboxLabelPosition}`));
109
+ case 'radio':
110
+ const radioErrorClass = formState?.errors[component.id] ? 'e-error' : '';
111
+ const radioCssClass = [component.cssClass || '', radioErrorClass].filter(Boolean).join(' ') || undefined;
112
+ const radioAttrs = formState?.values.htmlAttributes ? showAttributes(formState.values.htmlAttributes) : showAttributes(component.htmlAttributes);
113
+ component.options = isDefaultOptions ? [l10n.getConstant('defaultOptionValue')] : Array.isArray(component.options) ? component.options : ['Option 1'];
114
+ return (_jsx("div", { id: "radiogroup", className: radioCssClass, title: component.tooltip, children: component.options?.map((option, index) => (_jsx(RadioButtonComponent, { name: component.id, label: option, locale: locale, value: option, checked: component.defaultValue === option, disabled: component.disabled, cssClass: `${radioCssClass}${sizeCSSClass}`, htmlAttributes: radioAttrs, labelPosition: component.fieldLabelPosition, change: (args) => formState?.onChange(component.id, { value: args.value }) }, `${component.id}_${index}`))) }, component.id));
115
+ case 'dropdown':
116
+ const dropdownErrorClass = formState?.errors[component.id] ? 'e-error' : '';
117
+ const dropdownCssClass = [component.cssClass || '', dropdownErrorClass].filter(Boolean).join(' ') || undefined;
118
+ const dropdownAttrs = formState?.values.htmlAttributes ? showAttributes(formState.values.htmlAttributes) : showAttributes(component.htmlAttributes);
119
+ component.options = isDefaultOptions ? [l10n.getConstant('defaultOptionValue')] : component.options || ['Option 1'];
120
+ const dropdownOptions = isDefaultOptions ? [l10n.getConstant('defaultOptionValue')] : resolvedOptions || ['Option 1'];
121
+ return (_jsx(DropDownListComponent, { id: component.id, name: component.id, locale: locale, ...commonProps, cssClass: `${dropdownCssClass}${sizeCSSClass}`, dataSource: dropdownOptions, value: value !== undefined ? value : (component.defaultValue ?? null), floatLabelType: component.floatLabelType || 'Never', showClearButton: component.showClearButton || false, readonly: component.readOnly || false, enableVirtualization: component.enableVirtualization || false, popupHeight: component.popupHeight || '300px', popupWidth: component.popupWidth || '100%', htmlAttributes: dropdownAttrs, ...createValidationHandlers((args) => args.value || ''), children: _jsx(DRPInject, { services: [VirtualScroll] }) }, `form-${component.type}-${component.showClearButton}-${component.options}`));
122
+ case 'multiselect':
123
+ const multiSelectErrorClass = formState?.errors[component.id] ? 'e-error' : '';
124
+ const multiSelectCssClass = [component.cssClass || '', multiSelectErrorClass].filter(Boolean).join(' ') || undefined;
125
+ const multiSelectAttrs = formState?.values.htmlAttributes ? showAttributes(formState.values.htmlAttributes) : showAttributes(component.htmlAttributes);
126
+ component.options = isDefaultOptions ? [l10n.getConstant('defaultOptionValue')] : component.options || ['Option 1'];
127
+ const multiSelectOptions = isDefaultOptions ? [l10n.getConstant('defaultOptionValue')] : resolvedOptions || ['Option 1'];
128
+ return (_jsx(MultiSelectComponent, { id: component.id, locale: locale, name: component.id, value: value !== undefined ? value : (component.defaultValue ?? null), floatLabelType: component.floatLabelType || 'Never', placeholder: component.placeholder || '', enabled: !component.disabled, dataSource: multiSelectOptions, fields: { text: 'text', value: 'value' }, showClearButton: component.showClearButton || false, readonly: component.readOnly || false, allowCustomValue: component.allowCustomValue || false, addTagOnBlur: component.addTagOnBlur || false, delimiterChar: component.delimiterChar || ',', showSelectAll: component.showSelectAll || false, mode: component.showSelectAll ? 'CheckBox' : component.visualMode ? component.visualMode : undefined, selectAllText: component.selectAllText, unSelectAllText: component.unSelectAllText, showDropDownIcon: component.showDropDownIcon !== false, enableVirtualization: component.enableVirtualization || false, popupHeight: component.popupHeight || '300px', popupWidth: component.popupWidth || '100%', cssClass: `${multiSelectCssClass}${sizeCSSClass}`, htmlAttributes: multiSelectAttrs, ...createValidationHandlers((args) => args.value || ''), children: _jsx(DRPInject, { services: [CheckBoxSelection, VirtualScroll] }) }, `form-${component.type}-${component.showClearButton}-${component.id}-${JSON.stringify(component.defaultValue)}-${component.options}`));
129
+ case 'date':
130
+ const datePickerErrorClass = formState?.errors[component.id] ? 'e-error' : '';
131
+ const datePickerCssClass = [component.cssClass || '', datePickerErrorClass].filter(Boolean).join(' ') || undefined;
132
+ const datePickerAttrs = formState?.values.htmlAttributes ? showAttributes(formState.values.htmlAttributes) : showAttributes(component.htmlAttributes);
133
+ return (_jsx(DatePickerComponent, { ...commonProps, id: component.id, locale: locale, name: component.id, cssClass: `${datePickerCssClass}${sizeCSSClass}`, value: value !== undefined ? value : (component.defaultValue ?? null), floatLabelType: component.floatLabelType || 'Never', format: component.format || 'MM/dd/yyyy', placeholder: component.placeholder || '', readOnly: component.readOnly || false, showClearButton: component.showClearButton === undefined ? false : component.showClearButton, showTodayButton: component.showTodayButton !== false ? true : false, calendarMode: component.calendarMode || 'Gregorian', dayHeaderFormat: component.dayHeaderFormat || 'Short', depth: component.depth || 'Month', start: component.start || 'Month', firstDayOfWeek: component.firstDayOfWeek || 0, min: component?.minDate ? new Date(component.minDate) : undefined, max: component?.maxDate ? new Date(component.maxDate) : undefined, htmlAttributes: datePickerAttrs, ...createValidationHandlers((args) => args.value || ''), children: _jsx(Inject, { services: [Islamic] }) }, `form-${component.type}-${component.showClearButton}-${component.id}`));
134
+ case 'dateTime':
135
+ const dateTimePickerErrorClass = formState?.errors[component.id] ? 'e-error' : '';
136
+ const dateTimePickerCssClass = [component.cssClass || '', dateTimePickerErrorClass].filter(Boolean).join(' ') || undefined;
137
+ const dateTimePickerAttrs = formState?.values.htmlAttributes ? showAttributes(formState.values.htmlAttributes) : showAttributes(component.htmlAttributes);
138
+ return (_jsx(DateTimePickerComponent, { ...commonProps, id: component.id, name: component.id, floatLabelType: component.floatLabelType || 'Never', dayHeaderFormat: component.dayHeaderFormat || 'Short', value: value !== undefined ? value : (component.defaultValue ?? null), format: component.format || 'MM/dd/yyyy hh:mm a', showClearButton: component.showClearButton === undefined ? false : component.showClearButton, showTodayButton: component.showTodayButton !== false ? true : false, depth: component.depth || 'Month', start: component.start || 'Month', step: component.step || 1, locale: locale, readonly: component.readOnly || false, cssClass: `${dateTimePickerCssClass}${sizeCSSClass}`, htmlAttributes: dateTimePickerAttrs, min: component?.minDate ? new Date(component.minDate) : undefined, max: component?.maxDate ? new Date(component.maxDate) : undefined, minTime: component?.minTime ? new Date(`2000-01-01 ${component.minTime}`) : undefined, maxTime: component?.maxTime ? new Date(`2000-01-01 ${component.maxTime}`) : undefined, enableMask: false, ...createValidationHandlers((args) => args.value || ''), children: _jsx(Inject, { services: [Islamic] }) }, `form-${component.type}-${component.showClearButton}-${component.id}`));
139
+ case 'time':
140
+ const timePickerErrorClass = formState?.errors[component.id] ? 'e-error' : '';
141
+ const timePickerCssClass = [component.cssClass || '', timePickerErrorClass].filter(Boolean).join(' ') || undefined;
142
+ const timePickerAttrs = formState?.values.htmlAttributes ? showAttributes(formState.values.htmlAttributes) : showAttributes(component.htmlAttributes);
143
+ return (_jsx(TimePickerComponent, { id: component.id, name: component.id, floatLabelType: component.floatLabelType || 'Never', placeholder: component.placeholder || '', enabled: !component.disabled, locale: locale, value: value !== undefined ? value : (component.defaultValue ?? null), format: component.format || 'hh:mm a', showClearButton: component.showClearButton === undefined ? false : component.showClearButton, step: component.step || 30, readonly: component.readOnly || false, cssClass: `${timePickerCssClass}${sizeCSSClass}`, htmlAttributes: timePickerAttrs, min: component?.minTime ? new Date(`2000-01-01 ${component.minTime}`) : undefined, max: component?.maxTime ? new Date(`2000-01-01 ${component.maxTime}`) : undefined, enableMask: false, ...createValidationHandlers((args) => args.value || '') }, `form-${component.type}-${component.showClearButton}-${component.id}`));
144
+ case 'dateRange':
145
+ const dateRangeErrorClass = formState?.errors[component.id] ? 'e-error' : '';
146
+ const dateRangeCssClass = [component.cssClass || '', dateRangeErrorClass].filter(Boolean).join(' ') || undefined;
147
+ const dateRangeAttrs = formState?.values.htmlAttributes ? showAttributes(formState.values.htmlAttributes) : showAttributes(component.htmlAttributes);
148
+ return (_jsx(DateRangePickerComponent, { id: component.id, name: component.id, floatLabelType: component.floatLabelType || 'Never', dayHeaderFormat: component.dayHeaderFormat || 'Short', placeholder: component.placeholder || '', locale: locale, enabled: !component.disabled, value: value !== undefined ? value : (component.defaultValue ?? null), depth: component.depth || 'Month', start: component.start || 'Month', min: component?.minDate ? new Date(component.minDate) : undefined, max: component?.maxDate ? new Date(component.maxDate) : undefined, format: component.format || 'MM/dd/yyyy', separator: component.separator || ' - ', showClearButton: component.showClearButton === undefined ? false : component.showClearButton, readonly: component.readOnly || false, cssClass: `${dateRangeCssClass}${sizeCSSClass}`, htmlAttributes: dateRangeAttrs, ...createValidationHandlers((args) => args.value || '') }, `form-${component.type}-${component.showClearButton}-${component.id}`));
149
+ case 'switch':
150
+ const switchErrorClass = formState?.errors[component.id] ? 'e-error' : '';
151
+ const switchCssClass = [component.cssClass || '', switchErrorClass].filter(Boolean).join(' ') || undefined;
152
+ const switchAttrs = formState?.values.htmlAttributes ? showAttributes(formState.values.htmlAttributes) : showAttributes(component.htmlAttributes);
153
+ return (_jsx(SwitchComponent, { id: component.id, name: component.id, value: component.defaultValue, cssClass: `${switchCssClass}${sizeCSSClass}`, locale: locale, checked: component.checked, disabled: component.disabled, offLabel: component.offLabel || '', onLabel: component.onLabel || '', htmlAttributes: switchAttrs, change: (args) => formState?.onChange(component.id, { value: args.checked }) }, `form-${component.type}-${component.showClearButton}-${JSON.stringify(component.defaultValue)}`));
154
+ case 'button':
155
+ // If buttonsGroups is provided, render a button group in-place of a single button
156
+ const groupButtons = (component.buttonsGroups || component.buttons || []);
157
+ if (groupButtons && groupButtons.length > 0) {
158
+ const styleObj = {
159
+ display: 'flex',
160
+ flexWrap: 'wrap',
161
+ alignItems: 'center',
162
+ gap: component.gap || '8px',
163
+ justifyContent: mapPositionToJustifyContent(component.position),
164
+ width: component.width || undefined
165
+ };
166
+ return (_jsx("div", { id: component.id, role: "group", "aria-label": 'Button Group', className: component.cssClass, style: styleObj, children: groupButtons.map((btn, idx) => {
167
+ const btnId = `${component.id}_${idx}`;
168
+ const btnCssClass = [
169
+ mapButtonStyleToClass(btn.style)
170
+ ].filter(Boolean).join(' ');
171
+ return (_jsx(ButtonComponent, { id: String(btnId), locale: locale, name: String(btnId), type: resolveButtonType(btn.type), cssClass: `${btnCssClass}${sizeCSSClass}`, disabled: btn.disabled || false, content: btn.label || `Button ${idx + 1}`, iconCss: btn.iconCss, iconPosition: btn.iconPosition }, String(btnId)));
172
+ }) }, `form-${component.type}-${component.id}`));
173
+ }
174
+ // Fallback: render a single ButtonComponent when no group is configured
175
+ const buttonErrorClass = formState?.errors[component.id] ? 'e-error' : '';
176
+ const buttonCssClass = [
177
+ mapButtonStyleToClass(component.style),
178
+ component.buttonType === 'submit' ? 'submit-btn' : '',
179
+ component.cssClass || '',
180
+ buttonErrorClass
181
+ ].filter(Boolean).join(' ') || 'e-primary';
182
+ const buttonContainerStyle = {
183
+ display: 'flex',
184
+ justifyContent: mapPositionToJustifyContent(component.position)
185
+ };
186
+ return (_jsx("div", { id: `${component.id}-container`, "aria-label": 'Button', style: buttonContainerStyle, children: _jsx(ButtonComponent, { id: component.id, name: component.id, content: component.label, cssClass: `${buttonCssClass}${sizeCSSClass}`, locale: locale, disabled: component.disabled || false, iconCss: component.iconCss, iconPosition: component.iconPosition, type: resolveButtonType(component.buttonType) }) }, `form-${component.type}-${component.id}`));
187
+ case 'splitButton':
188
+ const splitButtonErrorClass = formState?.errors[component.id] ? 'e-error' : '';
189
+ const splitButtonCssClassList = [
190
+ component.style === 'primary' ? 'e-primary' : '',
191
+ component.style === 'flat' ? '' : '',
192
+ component.style === 'information' ? 'e-info' : '',
193
+ component.style === 'error' ? 'e-danger' : '',
194
+ component.style === 'success' ? 'e-success' : '',
195
+ component.style === 'warning' ? 'e-warning' : '',
196
+ component.cssClass || '',
197
+ splitButtonErrorClass
198
+ ].filter(Boolean).join(' ') || undefined;
199
+ const splitButtonContainerStyle = {
200
+ display: 'flex',
201
+ justifyContent: mapPositionToJustifyContent(component.position)
202
+ };
203
+ component.options = isDefaultOptions ? [l10n.getConstant('defaultOptionValue')] : component.options || ['Option 1'];
204
+ return (_jsx("div", { id: `${component.id}-container`, "aria-label": 'Splitbutton', style: splitButtonContainerStyle, children: _jsx(SplitButtonComponent, { id: component.id, name: component.id, locale: locale, content: component.label || 'Split Button', items: component.options?.map((option, index) => ({
205
+ text: option,
206
+ id: `${component.id}_option_${index}`
207
+ })) || [{ text: 'Option 1', id: `${component.id}_option_0` }], cssClass: `${splitButtonCssClassList}${sizeCSSClass}`, disabled: component.disabled || false, iconCss: component.iconCss, iconPosition: component.iconPosition || 'Left' }, component.id) }, `form-${component.type}-${component.id}`));
208
+ case 'signature':
209
+ return (_jsx(SignatureComponent, { id: component.id, locale: locale, name: component.id, className: `${component.cssClass}${sizeCSSClass}`, backgroundColor: component.backgroundColor || '', strokeColor: component.strokeColor || '', maxStrokeWidth: component?.maxValue, minStrokeWidth: component?.minValue, velocity: component.velocity, disabled: component.disabled }, `form-${component.type}-${component.showRatingLabel}`));
210
+ case 'rating':
211
+ return (_jsx(RatingComponent, { id: component.id, locale: locale, name: component.id, value: value !== undefined ? value : (component.defaultValue || 0), disabled: component.disabled, cssClass: `${component.cssClass}${sizeCSSClass}`, allowReset: component.allowReset, itemsCount: component.itemsCount, min: component?.minValue, precision: component.precision, readOnly: component.readOnly, showTooltip: component.showRatingTooltip || false, showLabel: component.showRatingLabel, labelPosition: component.showRatingLabel ? component.ratingLabelPosition : undefined }, `form-${component.type}-${component.showRatingLabel}-${component.showRatingTooltip}-${component.allowReset}`));
212
+ case 'imageEditor':
213
+ return (_jsx(ImageEditorComponent, { locale: locale, id: component.id, name: component.id, disabled: component.disabled, cssClass: `${component.cssClass}${sizeCSSClass}`, height: (component.height || '350px').toString(), width: (component.width || '100%').toString() }, `form-${component.type}`));
214
+ case 'fileUpload':
215
+ const uploaderAttrs = formState?.values.htmlAttributes ? showAttributes(formState.values.htmlAttributes) : showAttributes(component.htmlAttributes);
216
+ return (_jsx(UploaderComponent, { id: component.id, name: component.id, type: 'file', locale: locale, cssClass: `${component.cssClass}${sizeCSSClass}`, enabled: !component.disabled, allowedExtensions: component.allowExtensions, asyncSettings: { saveUrl: component.saveUrl, removeUrl: component.removeUrl }, buttons: component.buttons || {
217
+ "browse": "Choose File",
218
+ "clear": "Clear All",
219
+ "upload": "Upload All"
220
+ }, maxFileSize: component?.maxValue, minFileSize: component?.minValue, multiple: !!component.allowMultiple, showFileList: component.showFileList, htmlAttributes: uploaderAttrs }));
221
+ case 'rangeSlider':
222
+ return (_jsx(SliderComponent, { id: component.id, locale: locale, name: component.id, cssClass: `${component.cssClass}${sizeCSSClass}`, min: component.minValue || 0, max: component.maxValue || 100, readonly: component.readOnly, showButtons: component.showButtons, step: component.step || 1, value: value !== undefined ? value : (component.defaultValue ?? 0), enabled: !component.disabled }, `form-${component.type}-${component.showButtons}`));
223
+ case 'colorPicker':
224
+ return (_jsx(ColorPickerComponent, { cssClass: `${component.cssClass}${sizeCSSClass}`, id: component.id, locale: locale, name: component.id, enableOpacity: component.enableOpacity, showButtons: component.showButtons, showRecentColors: component.showRecentColors, noColor: component.showNoColor, value: value !== undefined ? value : (component.defaultValue || ''), disabled: component.disabled }, `form-${component.type}-${component.showButtons}-${component.showRecentColors}-${component.showNoColor}`));
225
+ case 'inputMask':
226
+ const maskedTextBoxErrorClass = formState?.errors[component.id] ? 'e-error' : '';
227
+ const maskedTextBoxCssClass = [component.cssClass || '', maskedTextBoxErrorClass].filter(Boolean).join(' ') || undefined;
228
+ const maskedTextBoxAttrs = formState?.values.htmlAttributes ? showAttributes(formState.values.htmlAttributes) : showAttributes(component.htmlAttributes);
229
+ return (_jsx(MaskedTextBoxComponent, { id: component.id, name: component.id, locale: locale, value: value !== undefined ? value : (component.defaultValue || ''), floatLabelType: component.floatLabelType || 'Never', placeholder: component.placeholder || '', enabled: !component.disabled, mask: component.mask || '000-000-0000', promptChar: component.promptChar || '_', customCharacters: component.customCharacters, readOnly: component.readOnly || false, showClearButton: component.showClearButton, cssClass: `${maskedTextBoxCssClass}${sizeCSSClass}`, htmlAttributes: maskedTextBoxAttrs, appendTemplate: component.suffix, prependTemplate: component.prefix, ...createValidationHandlers((args) => args.value || '') }, `form-${component.type}-${component.showClearButton}-${component.id}`));
230
+ case 'message':
231
+ return (_jsx(MessageComponent, { id: component.id, locale: locale, name: component.id, severity: component.severity || 'info', content: l10n.getConstant(component.content || component.label) || component.content || l10n.getConstant('message'), disabled: component.disabled, cssClass: `${component.cssClass}${sizeCSSClass}`, showCloseIcon: component.showCloseIcon, showIcon: (component.showIcon === undefined ? false : component.showIcon) || false, variant: component.variant }, `form-${component.type}-${component.showCloseIcon}-${component.showIcon}`));
232
+ case 'dataGrid':
233
+ // Ensure gridRefs map exists and has a ref for this component
234
+ if (gridRefs && !gridRefs[component.id]) {
235
+ gridRefs[component.id] = React.createRef();
236
+ }
237
+ return (_jsx(DataGridComponent, { ref: gridRefs?.[component.id] || undefined, locale: locale, cssClass: `${component.cssClass}${sizeCSSClass}`, ...component, dataSource: resolvedDataSource }, `form-${component.type}-${component.id}-${resolvedDataSource}`));
238
+ case 'lookup': {
239
+ const lookupComponent = component;
240
+ if (!lookupComponent.lookUpConfig?.formName || !lookupComponent.lookUpConfig?.field) {
241
+ return (_jsxs("div", { className: "e-input-group", children: [_jsx("input", { className: "e-input", type: "text", disabled: true }), _jsx("span", { className: "e-input-group-icon e-icons e-chevron-down" })] }));
242
+ }
243
+ const lookupErrorClass = formState?.errors[component.id] ? 'e-error' : '';
244
+ const lookupCssClass = [component.cssClass || '', lookupErrorClass].filter(Boolean).join(' ') || undefined;
245
+ const displayFormat = lookupComponent.lookUpConfig?.displayFormat || 'Dropdown';
246
+ // Radio Button Group - for Single selection
247
+ if (displayFormat === 'Radio') {
248
+ component.options = Array.isArray(lookupOptions) ? lookupOptions : ['Option 1'];
249
+ return (_jsx("div", { id: "radiogroup", className: lookupCssClass, title: component.tooltip, children: component.options?.map((option, index) => (_jsx(RadioButtonComponent, { name: component.id, label: option, locale: locale, value: option, checked: component.defaultValue === option, disabled: component.disabled, cssClass: `${lookupCssClass}${sizeCSSClass}`, htmlAttributes: component.htmlAttributes, change: (args) => formState?.onChange(component.id, { value: args.value }) }, `${component.id}_${index}`))) }, `form-${component.type}-${component.id}`));
250
+ }
251
+ // CheckboxGroup - for Multiple selection
252
+ if (displayFormat === 'Checkbox' || displayFormat === 'CheckboxGroup') {
253
+ component.options = Array.isArray(lookupOptions) ? lookupOptions : ['Option 1'];
254
+ return (_jsx(CheckboxGroup, { component: {
255
+ ...component,
256
+ options: component.options,
257
+ id: component.id,
258
+ disabled: component.disabled,
259
+ cssClass: lookupCssClass
260
+ }, formState: formState }, `form-${component.type}-${component.id}-${lookupOptions}`));
261
+ }
262
+ if (displayFormat === 'MultiSelect') {
263
+ value = [];
264
+ return (_jsx(MultiSelectComponent, { id: component.id, name: component.id, locale: locale, ...commonProps, cssClass: `${lookupCssClass}${sizeCSSClass}`, dataSource: lookupOptions, value: value !== undefined ? value : (component.defaultValue ?? null), floatLabelType: component.floatLabelType || 'Never', showDropDownIcon: true, showClearButton: component.showClearButton || false, readonly: component.readOnly || false, popupHeight: component.popupHeight || '300px', popupWidth: component.popupWidth || '100%', htmlAttributes: component.htmlAttributes, ...createValidationHandlers((args) => args.value || '') }, `form-${component.type}-${component.id}-lookup-${lookupComponent.lookUpConfig.field}`));
265
+ }
266
+ // Default: Dropdown - for Single selection
267
+ return (_jsx(DropDownListComponent, { id: component.id, name: component.id, locale: locale, ...commonProps, cssClass: `${lookupCssClass}${sizeCSSClass}`, dataSource: lookupOptions, value: value !== undefined ? value : (component.defaultValue ?? null), floatLabelType: component.floatLabelType || 'Never', showClearButton: component.showClearButton || false, readonly: component.readOnly || false, popupHeight: component.popupHeight || '300px', popupWidth: component.popupWidth || '100%', htmlAttributes: component.htmlAttributes, ...createValidationHandlers((args) => args.value || '') }, `form-${component.type}-${component.id}-lookup-${lookupComponent.lookUpConfig.field}`));
268
+ }
269
+ case 'richTextEditor':
270
+ const richTextErrorClass = formState?.errors[component.id] ? 'e-error' : '';
271
+ const richTextCssClass = [component.cssClass || '', richTextErrorClass].filter(Boolean).join(' ') || undefined;
272
+ const richTextAttrs = formState?.values.htmlAttributes ? showAttributes(formState.values.htmlAttributes) : showAttributes(component.htmlAttributes);
273
+ const toolbarSettings = {
274
+ items: ['Undo', 'Redo', '|', 'Bold', 'Italic', 'Underline', 'StrikeThrough', 'InlineCode', '|', 'CreateLink', 'Image', 'CreateTable', 'CodeBlock',
275
+ 'HorizontalLine', 'Blockquote', 'EmojiPicker', '|', 'LineHeight', 'Formats', 'Alignments', '|', 'BulletFormatList', 'NumberFormatList', 'Checklist', '|', 'Outdent', 'Indent', '|',
276
+ 'FontColor', 'BackgroundColor', 'FontName', 'FontSize', '|', 'LowerCase', 'UpperCase', 'SuperScript', 'SubScript', '|', 'SourceCode', '|']
277
+ };
278
+ if (component?.enableImportFromWord?.enabled) {
279
+ toolbarSettings.items.push('ImportWord');
280
+ }
281
+ if (component?.enableExportToWord?.enabled) {
282
+ toolbarSettings.items.push('ExportWord');
283
+ }
284
+ if (component?.enableExportToPdf?.enabled) {
285
+ toolbarSettings.items.push('ExportPdf');
286
+ }
287
+ toolbarSettings.items.push('Print');
288
+ return (_jsx(RichTextEditorComponent, { id: component.id, locale: locale, name: component.id, toolbarSettings: toolbarSettings, value: value !== undefined ? value : (component.defaultValue || ''), placeholder: component.placeholder || '', enabled: !component.disabled, readonly: component.readOnly || false, maxLength: component.maxLength || 1000, cssClass: `${richTextCssClass}${sizeCSSClass}`, height: component.height || '300px', width: component.width || '100%', htmlAttributes: richTextAttrs, ...createValidationHandlers((args) => args.value || ''), children: _jsx(RTEInject, { services: [Toolbar, Link, HtmlEditor, QuickToolbar, RTEImage, Count, Table, EmojiPicker, PasteCleanup, SlashMenu, CodeBlock, ClipBoardCleanup, AutoFormat] }) }, `form-${component.type}-${component.id}-${component.width}`));
289
+ case 'autoNumber':
290
+ // Calculate formatted auto number value
291
+ const getAutoNumberValue = () => {
292
+ const currentIndex = formState?.values[component.id] || component.startIndex || 0;
293
+ const formatted = `${component.prefix || ''}${currentIndex}${component.suffix || ''}`;
294
+ return formatted;
295
+ };
296
+ return (_jsx(TextBoxComponent, { id: component.id, name: component.id, locale: locale, type: "text", ...commonProps, value: getAutoNumberValue(), floatLabelType: "Never", readOnly: true, disabled: true, cssClass: `${sizeCSSClass}` }, `form-${component.type}-${component.startIndex}-${component.prefix}-${component.suffix}`));
297
+ default:
298
+ return (_jsx("div", { className: "unsupported-component", children: _jsxs("p", { children: ["Unsupported component type: ", component.type] }) }, component.id));
299
+ }
300
+ };
@@ -0,0 +1,67 @@
1
+ /* Syncfusion Essential JS 2 - Default Theme (Tailwind 3) */
2
+ /* This provides a fallback theme. The ThemeContext will override this dynamically. */
3
+
4
+ /* Base Components */
5
+ @import "./themes/index.css";
6
+ @import '@syncfusion/ej2-base/styles/tailwind3.css';
7
+
8
+ /* Form Components */
9
+ @import '@syncfusion/ej2-buttons/styles/tailwind3.css';
10
+ @import '@syncfusion/ej2-inputs/styles/tailwind3.css';
11
+
12
+ /* Dropdown & Selection Components */
13
+ @import '@syncfusion/ej2-dropdowns/styles/tailwind3.css';
14
+
15
+ /* Calendar & Date/Time Components */
16
+ @import '@syncfusion/ej2-calendars/styles/tailwind3.css';
17
+ @import '@syncfusion/ej2-react-calendars/styles/tailwind3.css';
18
+
19
+ /* Layout & Container Components */
20
+ @import '@syncfusion/ej2-navigations/styles/tailwind3.css';
21
+ @import '@syncfusion/ej2-notifications/styles/tailwind3.css';
22
+
23
+ /* Supporting Components */
24
+ @import '@syncfusion/ej2-popups/styles/tailwind3.css';
25
+ @import '@syncfusion/ej2-lists/styles/tailwind3.css';
26
+ @import '@syncfusion/ej2-splitbuttons/styles/tailwind3.css';
27
+ @import "@syncfusion/ej2-react-popups/styles/tailwind3.css";
28
+ @import "@syncfusion/ej2-react-grids/styles/tailwind3.css";
29
+ @import "@syncfusion/ej2-react-inplace-editor/styles/tailwind3.css";
30
+ @import "@syncfusion/ej2-splitbuttons/styles/tailwind3.css";
31
+ @import "@syncfusion/ej2-image-editor/styles/tailwind3.css";
32
+ @import "@syncfusion/ej2-layouts/styles/tailwind3.css";
33
+ @import '@syncfusion/ej2-richtexteditor/styles/tailwind3.css';
34
+ @import "@syncfusion/ej2-querybuilder/styles/tailwind3.css";
35
+
36
+
37
+ .e-menu-icon.e-icons.e-repeat {
38
+ margin-top: -3px !important;
39
+ }
40
+
41
+ .e-menu-item:has(.e-menu-icon.e-icons.e-repeat) {
42
+ padding-left: 0;
43
+ height: 28px !important;
44
+ }
45
+
46
+ .e-menu-item.e-focused:has(.e-menu-icon.e-icons.e-repeat) {
47
+ background: var(--color-sf-secondary-bg-color-hover);
48
+ border-color: var(--color-sf-secondary-border-color-hover);
49
+ }
50
+
51
+ .e-menu-item:has(.e-menu-icon.e-icons.e-repeat) .e-icons.e-caret {
52
+ display: none;
53
+ }
54
+
55
+ .e-menu-item:has(.e-menu-icon.e-icons.e-repeat) .e-icons.e-repeat {
56
+ font-size: 14px;
57
+ margin-left: 7px;
58
+ }
59
+
60
+ .e-menu-wrapper ul {
61
+ width: 28px;
62
+ height: 100%;
63
+ }
64
+
65
+ .e-menu-parent.e-ul {
66
+ min-width: 130px;
67
+ }
@@ -0,0 +1,110 @@
1
+ /* Form Field Wrapper - Container for fields with action buttons */
2
+ .form-field-wrapper {
3
+ position: relative;
4
+ transition: box-shadow 0.15s ease;
5
+ z-index: 1; /* Base layer for form fields per FR-026 */
6
+ }
7
+
8
+ /* Prevent nested wrappers from showing parent buttons */
9
+ .form-field-wrapper .form-field-wrapper {
10
+ z-index: 2; /* Nested wrappers appear above parent */
11
+ }
12
+
13
+ /* Action Button Wrapper */
14
+ .action-button-wrapper {
15
+ position: absolute;
16
+ top: -2.9rem;
17
+ right: 0.5rem;
18
+ height: 48px;
19
+ display: flex;
20
+ align-items: center;
21
+ visibility: hidden;
22
+ pointer-events: none;
23
+ transition: opacity 0.15s ease;
24
+ z-index: 1000; /* Buttons layer above fields per FR-026 */
25
+ }
26
+
27
+ .draggable-fields-container > .form-field-wrapper:first-child > .action-button-wrapper {
28
+ top: -1.5rem;
29
+ }
30
+ .panel-children > .form-field-wrapper:first-child > .action-button-wrapper {
31
+ top: -1.5rem;
32
+ }
33
+ .nested-components > .form-field-wrapper:first-child > .action-button-wrapper {
34
+ top: -1.5rem;
35
+ }
36
+ .tab-children > .form-field-wrapper:first-child > .action-button-wrapper {
37
+ top: -1.5rem;
38
+ }
39
+
40
+ /* Action Buttons Container */
41
+ .action-buttons {
42
+ display: flex;
43
+ gap: 0.25rem;
44
+ opacity: 1;
45
+ pointer-events: auto;
46
+ background: var(--color-sf-content-bg-color-alt1);
47
+ padding: 0.25rem;
48
+ border-radius: 4px;
49
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
50
+ }
51
+
52
+ /* Button Visibility - Show on hover or focus (only direct child, not nested) */
53
+ .form-preview-area:not(.dragging-over):not(.is-reordering):not(.is-nesteditemreordering):not(:has(.dragging-over)) .form-field-wrapper:hover > .action-button-wrapper {
54
+ visibility: visible;
55
+ pointer-events: auto;
56
+ }
57
+
58
+ .small-screen .form-preview-area:not(.dragging-over):not(.is-reordering):not(.is-nesteditemreordering):not(:has(.dragging-over)) .form-field-wrapper.is-active > .action-button-wrapper {
59
+ visibility: visible;;
60
+ pointer-events: auto;
61
+ }
62
+
63
+ /* Hide parent action buttons when hovering over nested children */
64
+ .form-preview-area:not(.dragging-over):not(.is-reordering):not(.is-nesteditemreordering):not(:has(.dragging-over)) .form-field-wrapper:has(.form-field-wrapper:hover) > .action-button-wrapper {
65
+ visibility: hidden;
66
+ pointer-events: none;
67
+ }
68
+
69
+ .form-field-wrapper.is-active .change-component {
70
+ display: block !important;
71
+ }
72
+
73
+ /* Individual Action Button Sizing */
74
+ .action-btn {
75
+ min-width: 28px !important;
76
+ height: 28px !important;
77
+ padding: 4px !important;
78
+ }
79
+
80
+ .mode-button {
81
+ height: 28px;
82
+ padding-top: 4px !important;
83
+ padding-bottom: 4px !important;
84
+ }
85
+
86
+ .action-btn .e-icons {
87
+ font-size: 16px;
88
+ }
89
+
90
+ /* Visual Feedback for Pasted Fields */
91
+ @keyframes field-inserted {
92
+ 0% {
93
+ border: 2px solid var(--primary-color, #007bff);
94
+ background-color: var(--primary-color-light, rgba(0, 123, 255, 0.1));
95
+ }
96
+ 100% {
97
+ border: 1px solid transparent;
98
+ background-color: transparent;
99
+ }
100
+ }
101
+
102
+ .field-inserted {
103
+ animation: field-inserted 2s ease-out forwards;
104
+ }
105
+
106
+ /* Dark Theme Adjustments */
107
+ [data-theme="dark"] .action-buttons {
108
+ background: var(--surface-color, #2d2d2d);
109
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
110
+ }