+
@@ -182,6 +45,30 @@ function move_columns_to_section() {
+
+
diff --git a/frappe/public/js/form_builder/components/Field.vue b/frappe/public/js/form_builder/components/Field.vue
index 6bae5bd35f..d44674964a 100644
--- a/frappe/public/js/form_builder/components/Field.vue
+++ b/frappe/public/js/form_builder/components/Field.vue
@@ -166,7 +166,7 @@ onMounted(() => selected.value && label_input.value.focus_on_label());
&.hovered,
&.selected {
- border-color: var(--primary);
+ border-color: var(--border-primary);
.btn.btn-icon {
opacity: 1 !important;
}
diff --git a/frappe/public/js/form_builder/components/SearchBox.vue b/frappe/public/js/form_builder/components/SearchBox.vue
index bae326058d..050037a6b0 100644
--- a/frappe/public/js/form_builder/components/SearchBox.vue
+++ b/frappe/public/js/form_builder/components/SearchBox.vue
@@ -6,7 +6,7 @@
class="search-input form-control"
type="text"
:placeholder="__('Search properties...')"
- @input="event => $emit('update:modelValue', event.target.value)"
+ @input="(event) => $emit('update:modelValue', event.target.value)"
/>
diff --git a/frappe/public/js/form_builder/components/Section.vue b/frappe/public/js/form_builder/components/Section.vue
index 9266d5c8d3..128227de98 100644
--- a/frappe/public/js/form_builder/components/Section.vue
+++ b/frappe/public/js/form_builder/components/Section.vue
@@ -1,10 +1,87 @@
+
+
+
+
-
-
-
+// column
+function add_column() {
+ props.section.columns.push({
+ fields: [],
+ df: store.get_df("Column Break"),
+ });
+}
+
+function remove_column() {
+ if (store.is_customize_form && column.value.df.is_custom_field == 0) {
+ frappe.msgprint(__("Cannot delete standard field. You can hide it if you want"));
+ throw "cannot delete standard field";
+ } else if (column.value.fields.length == 0 || store.has_standard_field(column.value)) {
+ delete_column();
+ } else {
+ confirm_dialog(
+ __("Delete Column", null, "Title of confirmation dialog"),
+ __(
+ "Are you sure you want to delete the column? All the fields in the column will be moved to the previous column.",
+ null,
+ "Confirmation dialog message"
+ ),
+ () => delete_column(),
+ __("Delete column", null, "Button text"),
+ () => delete_column(true),
+ __("Delete entire column with fields", null, "Button text")
+ );
+ }
+}
+
+function delete_column(with_children) {
+ // move all fields to previous column
+ let columns = props.section.columns;
+ let index = columns.length - 1;
+
+ if (with_children && index == 0 && columns.length == 1) {
+ if (column.value.fields.length == 0) {
+ frappe.msgprint(__("Section must have at least one column"));
+ throw "section must have at least one column";
+ }
+
+ columns.unshift({
+ df: store.get_df("Column Break"),
+ fields: [],
+ is_first: true,
+ });
+ index++;
+ }
+
+ if (!with_children) {
+ if (index > 0) {
+ let prev_column = columns[index - 1];
+ prev_column.fields = [...prev_column.fields, ...column.value.fields];
+ } else {
+ if (column.value.fields.length == 0) {
+ // set next column as first column
+ let next_column = columns[index + 1];
+ if (next_column) {
+ next_column.is_first = true;
+ } else {
+ frappe.msgprint(__("Section must have at least one column"));
+ throw "section must have at least one column";
+ }
+ } else {
+ // create a new column if current column has fields and push fields to it
+ columns.unshift({
+ df: store.get_df("Column Break"),
+ fields: column.value.fields,
+ is_first: true,
+ });
+ index++;
+ }
+ }
+ }
+
+ // remove column
+ columns.splice(index, 1);
+ store.form.selected_field = null;
+}
+
+const options = computed(() => {
+ let groups = [
+ {
+ group: "Section",
+ items: [
+ { label: "Add section above", onClick: add_section_above },
+ { label: "Remove section", onClick: remove_section },
+ ],
+ },
+ {
+ group: "Column",
+ items: [{ label: "Add column", onClick: add_column }],
+ },
+ ];
+
+ // add remove column option if there are more than one columns
+ if (props.section.columns.length > 1) {
+ groups[1].items.push({
+ label: "Remove column",
+ tooltip: "Remove last column",
+ onClick: remove_column,
+ });
+ } else if (props.section.columns[0].fields.length) {
+ // add remove all fields option if there is only one column and it has fields
+ groups[1].items.push({
+ label: "Empty column",
+ tooltip: "Remove all fields in the column",
+ onClick: () => delete_column(true),
+ });
+ }
+
+ // add move to tab option if the current section is not the first section
+ if (props.tab.sections.indexOf(props.section) > 0) {
+ groups[0].items.push({
+ label: "Move sections to new tab",
+ tooltip: "Move current and all subsequent sections to a new tab",
+ onClick: move_sections_to_tab,
+ });
+ }
+
+ return groups;
+});
+
diff --git a/frappe/public/js/form_builder/components/controls/CheckControl.vue b/frappe/public/js/form_builder/components/controls/CheckControl.vue
index fbdb76396d..e8ec928763 100644
--- a/frappe/public/js/form_builder/components/controls/CheckControl.vue
+++ b/frappe/public/js/form_builder/components/controls/CheckControl.vue
@@ -20,7 +20,7 @@ let slots = useSlots();
type="checkbox"
:checked="value"
:disabled="read_only"
- @change="event => $emit('update:modelValue', event.target.checked)"
+ @change="(event) => $emit('update:modelValue', event.target.checked)"
/>
{{ df.label }}
@@ -31,7 +31,8 @@ let slots = useSlots();