From e093d78f487c77562dc86eaacbcca5106bf6b07a Mon Sep 17 00:00:00 2001 From: Harsh Tandiya Date: Tue, 13 Jan 2026 20:09:39 +0530 Subject: [PATCH 01/11] feat: add 'display_depends_on' field to form field configuration * Added 'display_depends_on' field to the form field JSON and Python definitions. * Updated field order to include 'reqd' and 'fieldtype' for better organization. --- .../forms_pro/doctype/form_field/form_field.json | 14 ++++++++++---- .../forms_pro/doctype/form_field/form_field.py | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/forms_pro/forms_pro/doctype/form_field/form_field.json b/forms_pro/forms_pro/doctype/form_field/form_field.json index 236861a..07209d6 100644 --- a/forms_pro/forms_pro/doctype/form_field/form_field.json +++ b/forms_pro/forms_pro/doctype/form_field/form_field.json @@ -6,14 +6,15 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ + "reqd", "label", + "fieldtype", "fieldname", "description", "column_break_yznn", - "fieldtype", - "reqd", "options", - "default" + "default", + "display_depends_on" ], "fields": [ { @@ -62,13 +63,18 @@ "fieldname": "default", "fieldtype": "Small Text", "label": "Default" + }, + { + "fieldname": "display_depends_on", + "fieldtype": "Code", + "label": "Display Depends On" } ], "grid_page_length": 50, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2026-01-09 14:57:39.192268", + "modified": "2026-01-13 19:30:52.799413", "modified_by": "Administrator", "module": "Forms Pro", "name": "Form Field", diff --git a/forms_pro/forms_pro/doctype/form_field/form_field.py b/forms_pro/forms_pro/doctype/form_field/form_field.py index 12f3caa..87b014b 100644 --- a/forms_pro/forms_pro/doctype/form_field/form_field.py +++ b/forms_pro/forms_pro/doctype/form_field/form_field.py @@ -16,6 +16,7 @@ class FormField(Document): default: DF.SmallText | None description: DF.SmallText | None + display_depends_on: DF.Code | None fieldname: DF.Data fieldtype: DF.Literal[ "Attach", From 66a8e0e819a85a968f2b710779f1bfc2c4bcb15b Mon Sep 17 00:00:00 2001 From: Harsh Tandiya Date: Tue, 13 Jan 2026 20:29:45 +0530 Subject: [PATCH 02/11] chore introduce FormFieldTypes enum and update fieldtype to use it --- frontend/src/types/formfield.ts | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/frontend/src/types/formfield.ts b/frontend/src/types/formfield.ts index ae8dc36..8684cfe 100644 --- a/frontend/src/types/formfield.ts +++ b/frontend/src/types/formfield.ts @@ -1,10 +1,30 @@ -export interface FormField { +export enum FormFieldTypes { + Attach = "Attach", + Data = "Data", + Number = "Number", + Email = "Email", + Date = "Date", + DateTime = "Date Time", + DateRange = "Date Range", + TimePicker = "Time Picker", + Password = "Password", + Select = "Select", + Switch = "Switch", + Textarea = "Textarea", + TextEditor = "Text Editor", + Link = "Link", + Checkbox = "Checkbox", + Rating = "Rating", +} + +export type FormField = { label: string; fieldname: string; - fieldtype: string; + fieldtype: FormFieldTypes; description?: string; reqd?: boolean; options?: string; default?: string; idx?: number; -} + display_depends_on?: string; +}; From e6f8a1ba9a6047e2191ce5367ef55fd21e53b121 Mon Sep 17 00:00:00 2001 From: Harsh Tandiya Date: Wed, 14 Jan 2026 13:49:08 +0530 Subject: [PATCH 03/11] refactor: move FieldPropertiesForm to a new directory and update imports * Moved FieldPropertiesForm.vue to the field-editor directory for better organization. * Updated import paths in FieldEditorSidebar.vue to reflect the new location. --- .../src/components/FieldEditorSidebar.vue | 2 +- .../builder/FieldPropertiesForm.vue | 66 ----------- .../field-editor/FieldPropertiesForm.vue | 109 ++++++++++++++++++ 3 files changed, 110 insertions(+), 67 deletions(-) delete mode 100644 frontend/src/components/builder/FieldPropertiesForm.vue create mode 100644 frontend/src/components/builder/field-editor/FieldPropertiesForm.vue diff --git a/frontend/src/components/FieldEditorSidebar.vue b/frontend/src/components/FieldEditorSidebar.vue index 9b34272..0192aa2 100644 --- a/frontend/src/components/FieldEditorSidebar.vue +++ b/frontend/src/components/FieldEditorSidebar.vue @@ -1,6 +1,6 @@ diff --git a/frontend/src/components/builder/FieldPropertiesForm.vue b/frontend/src/components/builder/FieldPropertiesForm.vue deleted file mode 100644 index 465dd05..0000000 --- a/frontend/src/components/builder/FieldPropertiesForm.vue +++ /dev/null @@ -1,66 +0,0 @@ - - diff --git a/frontend/src/components/builder/field-editor/FieldPropertiesForm.vue b/frontend/src/components/builder/field-editor/FieldPropertiesForm.vue new file mode 100644 index 0000000..b5a97f9 --- /dev/null +++ b/frontend/src/components/builder/field-editor/FieldPropertiesForm.vue @@ -0,0 +1,109 @@ + + From b32f21a71c456d9333afe102cafda4dec9c9f460 Mon Sep 17 00:00:00 2001 From: Harsh Tandiya Date: Wed, 14 Jan 2026 14:47:57 +0530 Subject: [PATCH 04/11] feat: enhance outside click detection for FormBuilderContent --- .../src/components/FormBuilderContent.vue | 84 ++++++++++++++++++- 1 file changed, 81 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/FormBuilderContent.vue b/frontend/src/components/FormBuilderContent.vue index d67a833..9ccf909 100644 --- a/frontend/src/components/FormBuilderContent.vue +++ b/frontend/src/components/FormBuilderContent.vue @@ -14,6 +14,54 @@ const editFormStore = useEditForm(); // Ref for the entire FormBuilderContent component const fieldContentRef = ref(null); +// Function to check if an element is a dropdown/popover (including portals) +const isDropdownOrPopover = (element: Element | null): boolean => { + if (!element) return false; + + // Walk up the DOM tree to check for dropdown indicators + let current: Element | null = element; + while (current && current !== document.body) { + // Check for Headless UI patterns + if ( + current.hasAttribute("role") && + (current.getAttribute("role") === "listbox" || + current.getAttribute("role") === "option" || + current.getAttribute("role") === "combobox") + ) { + return true; + } + + // Check for Headless UI data attributes + if (current.hasAttribute("data-headlessui-state") || current.id?.includes("headlessui")) { + return true; + } + + // Check for Radix UI patterns + if ( + current.hasAttribute("data-radix-popper-content-wrapper") || + current.id?.startsWith("radix") || + current.hasAttribute("data-radix") + ) { + return true; + } + + // Check for common dropdown classes + const classList = current.classList; + if ( + classList.contains("dropdown-menu") || + classList.contains("combobox-options") || + classList.contains("popover-content") || + current.hasAttribute("data-popover") + ) { + return true; + } + + current = current.parentElement; + } + + return false; +}; + // Set up outside click detection for the entire FormBuilderContent component onClickOutside(fieldContentRef, (event) => { // Check if the click is on any other form builder components @@ -24,8 +72,38 @@ onClickOutside(fieldContentRef, (event) => { target.closest(".form-builder-sidebar") || target.closest(".form-builder-header"); - // Only deselect if NOT clicking on other form builder components - if (!isFormBuilderComponent) { + // Check if the click is on a dropdown menu (which may be rendered in a portal) + // This handles Headless UI, Radix UI, and other common dropdown patterns + const isDropdownElement = isDropdownOrPopover(target); + + // Also check if there are any visible/open dropdowns in the DOM + // This catches dropdowns that might be open but the click target isn't directly on them + const hasOpenDropdown = !!( + document.querySelector('[role="listbox"]:not([hidden]):not([style*="display: none"])') || + document.querySelector('[role="combobox"][aria-expanded="true"]') || + document.querySelector('[data-headlessui-state="open"]') || + document.querySelector('[aria-expanded="true"][role="combobox"]') + ); + + // Check if the active element (focused element) is within the sidebar + // This helps catch cases where a dropdown is open and the user is interacting with it + const activeElement = document.activeElement; + const isActiveElementInSidebar = activeElement + ? !!( + activeElement.closest(".field-editor-sidebar") || + activeElement.closest('[data-form-builder-component="field-editor-sidebar"]') || + activeElement.closest('[data-form-builder-component="field-properties-form"]') + ) + : false; + + // Only deselect if NOT clicking on other form builder components or dropdowns + // Also don't deselect if there's an open dropdown or if the active element is in the sidebar + if ( + !isFormBuilderComponent && + !isDropdownElement && + !hasOpenDropdown && + !isActiveElementInSidebar + ) { editFormStore.selectField(null); } }); @@ -36,6 +114,7 @@ onClickOutside(fieldContentRef, (event) => {
@@ -69,7 +148,6 @@ onClickOutside(fieldContentRef, (event) => {