From 0642d59d6a7fd801005ff51a015ffce1454bfc3a Mon Sep 17 00:00:00 2001 From: sokumon Date: Wed, 10 Dec 2025 15:54:05 +0530 Subject: [PATCH] feat: make sidebar editor awesome again --- frappe/public/js/frappe/ui/sidebar/sidebar.js | 456 +------------- .../js/frappe/ui/sidebar/sidebar_editor.js | 572 ++++++++++++++++++ .../js/frappe/ui/sidebar/sidebar_header.js | 3 +- .../js/frappe/ui/sidebar/sidebar_item.html | 3 +- .../js/frappe/ui/sidebar/sidebar_item.js | 27 +- frappe/public/scss/desk/sidebar.scss | 14 +- 6 files changed, 602 insertions(+), 473 deletions(-) create mode 100644 frappe/public/js/frappe/ui/sidebar/sidebar_editor.js diff --git a/frappe/public/js/frappe/ui/sidebar/sidebar.js b/frappe/public/js/frappe/ui/sidebar/sidebar.js index 7bcd716bec..2eab820268 100644 --- a/frappe/public/js/frappe/ui/sidebar/sidebar.js +++ b/frappe/public/js/frappe/ui/sidebar/sidebar.js @@ -1,4 +1,5 @@ import "./sidebar_item"; +import { SidebarEditor } from "./sidebar_editor"; frappe.ui.Sidebar = class Sidebar { constructor() { if (!frappe.boot.setup_complete) { @@ -7,13 +8,13 @@ frappe.ui.Sidebar = class Sidebar { } this.make_dom(); // states - this.edit_mode = false; + this.editor = new SidebarEditor(this); + this.edit_mode = this.editor.edit_mode; this.sidebar_expanded = false; this.all_sidebar_items = frappe.boot.workspace_sidebar_item; this.$items = []; this.fields_for_dialog = []; this.workspace_sidebar_items = []; - this.new_sidebar_items = []; this.$items_container = this.wrapper.find(".sidebar-items"); this.$standard_items_sections = this.wrapper.find(".standard-items-sections"); this.$sidebar = this.wrapper.find(".body-sidebar"); @@ -30,7 +31,7 @@ frappe.ui.Sidebar = class Sidebar { this.sidebar_data = frappe.boot.workspace_sidebar_item[this.workspace_title]; this.workspace_sidebar_items = this.sidebar_data.items; if (this.edit_mode) { - this.workspace_sidebar_items = this.new_sidebar_items; + this.workspace_sidebar_items = this.editor.new_sidebar_items; } this.choose_app_name(); this.find_nested_items(); @@ -102,7 +103,6 @@ frappe.ui.Sidebar = class Sidebar { this.$sidebar.attr("data-title", this.sidebar_title); this.sidebar_header = new frappe.ui.SidebarHeader(this); this.make_sidebar(); - this.setup_complete = true; } check_for_private_workspace(workspace_title) { if (workspace_title == "private" || workspace_title == "Personal") { @@ -203,7 +203,12 @@ frappe.ui.Sidebar = class Sidebar { } make_sidebar() { this.empty(); - this.create_sidebar(this.workspace_sidebar_items); + this.wrapper.find(".collapse-sidebar-link").removeClass("hidden"); + if (this.editor.edit_mode) { + this.create_sidebar(this.editor.new_sidebar_items); + } else { + this.create_sidebar(this.workspace_sidebar_items); + } // Scroll sidebar to selected page if it is not in viewport. this.wrapper.find(".selected").length && @@ -241,6 +246,7 @@ frappe.ui.Sidebar = class Sidebar { this.standard_items.push({ label: __("Search"), icon: "search", + standard: true, type: "Button", id: "navbar-modal-search", suffix: { @@ -252,6 +258,7 @@ frappe.ui.Sidebar = class Sidebar { this.standard_items.push({ label: __("Notification"), icon: "bell", + standard: true, type: "Button", class: "sidebar-notification hidden", onClick: () => { @@ -464,443 +471,4 @@ frappe.ui.Sidebar = class Sidebar { }); return sidebars; } - - toggle_editing_mode() { - const me = this; - if (this.edit_mode) { - this.open(); - this.wrapper.attr("data-mode", "edit"); - this.new_sidebar_items = Array.from(me.workspace_sidebar_items); - $(this.active_item).removeClass("active-sidebar"); - $(".collapse-sidebar-link").addClass("hidden"); - this.wrapper.find(".edit-mode").removeClass("hidden"); - this.add_new_item_button = this.wrapper.find("[data-name='add-sidebar-item']"); - this.setup_sorting(); - - this.setup_editing_controls(); - this.add_new_item_button.on("click", function () { - me.show_new_dialog(); - }); - } else { - this.wrapper.removeAttr("data-mode"); - $(this.active_item).addClass("active-sidebar"); - $(".collapse-sidebar-link").removeClass("hidden"); - this.wrapper.find(".edit-mode").addClass("hidden"); - this.add_new_item_button = this.wrapper.find("[data-name='add-sidebar-item']"); - } - } - setup_sorting() { - const me = this; - this.sortable = Sortable.create($(".sidebar-items").get(0), { - handler: ".drag-handle", - onEnd: function (event) { - if (me.new_sidebar_items.length == 0) { - me.new_sidebar_items = Array.from(me.workspace_sidebar_items); - } - let old_index = event.oldIndex; - let new_index = event.newIndex; - me.new_sidebar_items[old_index]; - let b = me.new_sidebar_items[old_index]; - me.new_sidebar_items[old_index] = me.new_sidebar_items[new_index]; - me.new_sidebar_items[new_index] = b; - }, - }); - this.setup_sorting_for_nested_container(); - } - setup_sorting_for_nested_container() { - const me = this; - $(".nested-container").each(function (index, el) { - Sortable.create(el, { - handle: ".drag-handle", - onEnd: function (event) { - let new_index = event.newIndex; - let old_index = event.oldIndex; - let item_label = $(event.item).data("id"); - me.new_sidebar_items.forEach((item) => { - if (item.nested_items.length) { - let child = item.nested_items.find( - (child) => child.label === item_label - ); - if (child) { - let b = item.nested_items[old_index]; - item.nested_items[old_index] = item.nested_items[new_index]; - item.nested_items[new_index] = b; - } - } - }); - }, - }); - }); - } - make_dialog(opts) { - let title = "New Sidebar Item"; - - const me = this; - this.dialog_opts = opts; - - // Create the dialog - let dialog_fields = [ - { - fieldname: "label", - fieldtype: "Data", - in_list_view: 1, - label: "Label", - onchange: function (opts) { - let label = this.get_value(); - switch (label) { - case "Home": - d.set_value("icon", "home"); - d.set_value("link_type", "Workspace"); - d.set_value("link_to", me.workspace_title); - break; - - case "Reports": - d.set_value("type", "Section Break"); - d.set_value("link_to", null); - break; - - case "Dashboard": - d.set_value("link_type", "Dashboard"); - d.set_value("link_to", me.workspace_title); - d.set_value("icon", "layout-dashboard"); - break; - - case "Learn": - d.set_value("icon", "graduation-cap"); - d.set_value("link_type", "URL"); - break; - - case "Settings": - d.set_value("icon", "settings"); - break; - } - - if (d.get_value("type") == "Link" && d.get_value("link_type") !== "URL") { - d.set_value("link_to", label); - } - - if ( - me.dialog_opts && - me.dialog_opts.parent_item && - me.dialog_opts.parent_item.label == "Reports" - ) { - d.set_value("icon", "table"); - d.set_value("link_type", "Report"); - } - }, - }, - { - default: "Link", - fieldname: "type", - fieldtype: "Select", - in_list_view: 1, - label: "Type", - options: "Link\nSection Break\nSpacer\nSidebar Item Group", - onchange: function () { - let type = this.get_value(); - if (type == "Section Break") { - d.set_value("link_to", null); - } - }, - }, - { - default: "DocType", - depends_on: "eval: doc.type == 'Link'", - fieldname: "link_type", - fieldtype: "Select", - in_list_view: 1, - label: "Link Type", - options: "DocType\nPage\nReport\nWorkspace\nDashboard\nURL", - onchange: function () { - d.set_value("link_to", null); - }, - }, - { - depends_on: "eval: doc.link_type != \"URL\" && doc.type == 'Link'", - fieldname: "link_to", - fieldtype: "Dynamic Link", - in_list_view: 1, - label: "Link To", - options: "link_type", - onchange: function () { - if (d.get_value("link_type") == "DocType") { - let doctype = this.get_value(); - if (doctype) { - me.setup_filter(d, doctype); - } - } - }, - }, - { - depends_on: 'eval: doc.link_type == "URL"', - fieldname: "url", - fieldtype: "Data", - label: "URL", - }, - { - depends_on: - 'eval: doc.type == "Link" || (doc.indent == 1 && doc.type == "Section Break")', - fieldname: "icon", - fieldtype: "Icon", - options: "Emojis", - in_list_view: 1, - label: "Icon", - }, - { - fieldtype: "HTML", - fieldname: "filter_area", - }, - { - depends_on: 'eval: doc.type == "Section Break"', - fieldname: "display_section", - fieldtype: "Section Break", - label: "Options", - }, - { - default: "0", - depends_on: 'eval: doc.type == "Section Break"', - fieldname: "indent", - fieldtype: "Check", - label: "Indent", - }, - { - depends_on: "eval: doc.indent == 1", - fieldname: "show_arrow", - fieldtype: "Check", - label: "Show Arrow", - }, - { - default: "1", - depends_on: 'eval: doc.type == "Section Break"', - fieldname: "collapsible", - fieldtype: "Check", - label: "Collapsible", - }, - { - fieldname: "column_break_krzu", - fieldtype: "Column Break", - }, - { - default: "0", - depends_on: 'eval: doc.type == "Section Break"', - fieldname: "keep_closed", - fieldtype: "Check", - label: "Keep Closed", - }, - { - fieldname: "details_section", - fieldtype: "Section Break", - label: "Details", - }, - - { - fieldtype: "Section Break", - }, - { - fieldname: "display_depends_on", - fieldtype: "Code", - label: "Display Depends On (JS)", - options: "JS", - max_height: "10px", - }, - { - fieldtype: "Section Break", - }, - { - fieldname: "route_options", - fieldtype: "Code", - display_depends_on: "eval: doc.link_type == 'Page'", - label: "Route Options", - options: "JSON", - max_height: "50px", - }, - ]; - if (opts && opts.item) { - dialog_fields.forEach((f) => { - if ( - opts.item[f.fieldname] !== undefined && - f.fieldtype !== "Section Break" && - f.fieldtype !== "Column Break" - ) { - f.default = opts.item[f.fieldname]; - } - }); - title = "Edit Sidebar Item"; - } - let d; - this.dialog = d = new frappe.ui.Dialog({ - title: title, - fields: dialog_fields, - primary_action_label: "Save", - size: "small", - primary_action(values) { - if (me.filter_group) { - me.filter_group.get_filters(); - } - - if (me.new_sidebar_items.length === 0) { - me.new_sidebar_items = Array.from(me.workspace_sidebar_items); - } - if (opts && opts.nested) { - values.child = 1; - console.log("Add it as a nested item"); - console.log(opts.parent_item); - let index = me.new_sidebar_items.findIndex((f) => { - return f.label == opts.parent_item.label; - }); - - if (!me.new_sidebar_items[index].nested_items) { - me.new_sidebar_items[index].nested_items = []; - } - me.new_sidebar_items[index].nested_items.push(values); - } else if (opts && opts.item) { - if (opts.item.child) { - let parent_icon = me.find_parent(me.new_sidebar_items, opts.item); - if (parent_icon) { - let index = parent_icon.nested_items.indexOf(opts.item); - let parent_icon_index = me.new_sidebar_items.indexOf(parent_icon); - me.new_sidebar_items[parent_icon_index].nested_items[index] = values; - } - } else { - let index = me.new_sidebar_items.indexOf(opts.item); - - me.new_sidebar_items[index] = { - ...me.new_sidebar_items[index], - ...values, - }; - } - } else { - me.new_sidebar_items.push(values); - } - me.create_sidebar(me.new_sidebar_items); - me.setup_sorting_for_nested_container(); - d.hide(); - }, - }); - - return d; - } - setup_filter(d, doctype) { - if (this.filter_group) { - this.filter_group.wrapper.empty(); - delete this.filter_group; - } - - // let $loading = this.dialog.get_field("filter_area_loading").$wrapper; - // $(`${__("Loading Filters...")}`).appendTo($loading); - - this.filters = []; - - this.generate_filter_from_json && this.generate_filter_from_json(); - - this.filter_group = new frappe.ui.FilterGroup({ - parent: d.get_field("filter_area").$wrapper, - doctype: doctype, - on_change: () => {}, - }); - - frappe.model.with_doctype(doctype, () => { - this.filter_group.add_filters_to_filter_group(this.filters); - }); - } - hide_field(fieldname) { - this.dialog.set_df_property(fieldname, "hidden", true); - } - - show_field(fieldname) { - this.dialog.set_df_property(fieldname, "hidden", false); - } - setup_editing_controls() { - const me = this; - this.save_sidebar_button = this.wrapper.find(".save-sidebar"); - this.discard_button = this.wrapper.find(".discard-button"); - this.save_sidebar_button.off("click").on("click", async function (event) { - frappe.show_alert({ - message: __("Saving Sidebar"), - indicator: "success", - }); - - await frappe.call({ - type: "POST", - method: "frappe.desk.doctype.workspace_sidebar.workspace_sidebar.add_sidebar_items", - args: { - sidebar_title: - me.workspace_title || frappe.app.sidebar.sidebar_header.workspace_title, - sidebar_items: me.new_sidebar_items, - }, - callback: function (r) { - frappe.boot.workspace_sidebar_item[me.workspace_title.toLowerCase()] = [ - ...me.new_sidebar_items, - ]; - frappe.ui.toolbar.clear_cache(); - me.edit_mode = false; - me.toggle_editing_mode(); - me.make_sidebar(me); - }, - }); - }); - - this.discard_button.on("click", function () { - me.edit_mode = false; - me.toggle_editing_mode(); - me.make_sidebar(me); - }); - } - - find_parent(sidebar_items, item) { - for (const f of sidebar_items) { - if (f.nested_items && f.nested_items.includes(item)) { - return f; - } - } - } - - delete_item(item) { - let index; - if (item.child) { - let parent_icon = this.find_parent(this.new_sidebar_items, item); - index = parent_icon.nested_items.indexOf(item); - parent_icon.nested_items.splice(index, 1); - } else { - index = this.new_sidebar_items.indexOf(item); - this.new_sidebar_items.splice(index, 1); - } - this.create_sidebar(this.new_sidebar_items); - } - - add_below(item) { - let index = this.workspace_sidebar_items.indexOf(item); - this.show_new_dialog(index); - this.create_sidebar(this.new_sidebar_items); - } - - duplicate_item(item) { - let index = this.workspace_sidebar_items.indexOf(item); - this.new_sidebar_items.splice(index, 0, item); - this.create_sidebar(this.new_sidebar_items); - } - - edit_item(item) { - let d = this.make_dialog({ - item: item, - }); - d.show(); - } - - show_new_dialog(opts) { - let d = this.make_dialog(opts); - d.show(); - } - make_fields_for_grids(fields) { - let doc_fields = Array.from(fields); - doc_fields = doc_fields - .filter((f) => f.fieldtype !== "Section Break" && f.fieldtype !== "Column Break") - .map((f, i) => ({ - ...f, - in_list_view: i < 5 ? 1 : 0, - })); - let link_to_field = doc_fields.find((f) => f.label == "Link To"); - link_to_field.field_in_dialog = true; - return doc_fields; - } }; diff --git a/frappe/public/js/frappe/ui/sidebar/sidebar_editor.js b/frappe/public/js/frappe/ui/sidebar/sidebar_editor.js new file mode 100644 index 0000000000..5a7f32023a --- /dev/null +++ b/frappe/public/js/frappe/ui/sidebar/sidebar_editor.js @@ -0,0 +1,572 @@ +export class SidebarEditor { + constructor(sidebar) { + this.sidebar = sidebar; + this.edit_mode = false; + this.setup(); + } + setup() { + console.log("Setting Up Editor"); + this.setup_editing_off(); + } + setup_editing_off() { + const me = this; + frappe.router.on("change", function () { + if (frappe.get_prev_route().length == 0) return; + if (frappe.get_prev_route().length !== frappe.get_route().length && me.edit_mode) { + me.stop(); + } + }); + } + toggle() { + if (this.edit_mode) { + this.stop(); + } else { + this.start(); + } + } + + start() { + const me = this; + this.edit_mode = true; + console.log("Start Editing"); + this.sidebar.open(); + + this.sidebar.wrapper.attr("data-mode", "edit"); + this.new_sidebar_items = Array.from(me.sidebar.workspace_sidebar_items); + $(this.active_item).removeClass("active-sidebar"); + this.sidebar.wrapper.find(".edit-mode").toggleClass("hidden"); + this.add_new_item_button = this.sidebar.wrapper.find("[data-name='add-sidebar-item']"); + this.setup_sorting(); + + this.setup_editing_controls(); + this.add_new_item_button.on("click", function () { + me.show_new_dialog(); + }); + } + stop() { + this.edit_mode = false; + $(this.active_item).addClass("active-sidebar"); + this.sidebar.wrapper.find(".edit-mode").toggleClass("hidden"); + this.sidebar.wrapper.removeAttr("data-mode"); + this.add_new_item_button = this.sidebar.wrapper.find("[data-name='add-sidebar-item']"); + } + + setup_editing_controls() { + const me = this; + this.save_sidebar_button = this.sidebar.wrapper.find(".save-sidebar"); + this.discard_button = this.sidebar.wrapper.find(".discard-button"); + this.save_sidebar_button.on("click", async function (event) { + frappe.show_alert({ + message: __("Saving Sidebar"), + indicator: "success", + }); + me.prepare_data(); + await frappe.call({ + type: "POST", + method: "frappe.desk.doctype.workspace_sidebar.workspace_sidebar.add_sidebar_items", + args: { + sidebar_title: me.workspace_title || me.sidebar.sidebar_title, + sidebar_items: me.new_sidebar_items, + }, + callback: function (r) { + frappe.boot.workspace_sidebar_item[r.message.name.toLowerCase()] = [ + ...me.new_sidebar_items, + ]; + frappe.ui.toolbar.clear_cache(); + me.stop(); + me.sidebar.make_sidebar(); + }, + }); + }); + + this.discard_button.on("click", function () { + me.toggle(); + me.sidebar.make_sidebar(); + }); + } + prepare_data() { + this.new_sidebar_items.forEach((item) => { + item.nested_items.forEach((nested_item) => { + if (nested_item.parent) { + delete nested_item.parent; + } + }); + }); + } + setup_sorting() { + const me = this; + this.fetch; + this.sortable = Sortable.create($(".sidebar-items").get(0), { + handler: ".drag-handle", + group: "sidebar-item", + onMove: function (evt, originalEvent) { + me.close_section = false; + let item_name = $(evt.related).attr("item-name"); + let item_data = me.get_item_data(item_name); + if (item_data && item_data.type == "Section Break") { + let item_obj = me.get_item_obj(item_data); + if (me.current_section_break) me.current_section_break.close(); + me.current_section_break = item_obj; + if (item_obj && item_obj.collapsed) { + item_obj.open(); + return 1; + } + if (me.current_section_break) { + let nested_container = me.current_section_break.wrapper + .find(".nested-container") + .first() + .get(0) + .getBoundingClientRect(); + console.log(nested_container.top > originalEvent.clientY); + if ( + nested_container.top > originalEvent.clientY || + originalEvent.clientY < nested_container.bottom + ) { + me.current_section_break.close(); + me.current_section_break = null; + } + } + } + }, + onStart: function () { + me.sorting = true; + }, + onEnd: function (event) { + if (me.new_sidebar_items.length == 0) { + me.new_sidebar_items = Array.from(me.workspace_sidebar_items); + } + let old_index = event.oldIndex; + let new_index = event.newIndex; + me.new_sidebar_items[old_index]; + let b = me.new_sidebar_items[old_index]; + me.new_sidebar_items[old_index] = me.new_sidebar_items[new_index]; + me.new_sidebar_items[new_index] = b; + }, + }); + + this.setup_sorting_for_nested_container(); + } + get_item_data(item_name) { + let item_data; + if (item_name) { + this.new_sidebar_items.forEach((item) => { + if (item.label == item_name) { + item_data = item; + } + if (item.nested_items && item.nested_items.length > 0) { + item.nested_items.forEach((nested_item) => { + if (nested_item.label == item_name) { + item_data = nested_item; + } + }); + } + }); + + return item_data; + } + } + get_item_obj(item_data) { + return frappe.app.sidebar.items.find((item) => { + return item.item == item_data; + }); + } + setup_sorting_for_nested_container() { + const me = this; + $(".nested-container").each(function (index, el) { + Sortable.create(el, { + handle: ".drag-handle", + group: "sidebar-item", + onAdd: function (event) { + let old_index = event.oldIndex; + let item_data = me.new_sidebar_items[old_index]; + me.new_sidebar_items.splice(old_index, 1); + item_data.child = 1; + let section_name = $(event.to).parent().attr("item-name"); + me.get_item_data(section_name).nested_items.splice( + event.newIndex, + 0, + item_data + ); + }, + onEnd: function (event) { + let new_index = event.newIndex; + let old_index = event.oldIndex; + let item_label = $(event.item).data("id"); + me.new_sidebar_items.forEach((item) => { + if (item.nested_items.length) { + let child = item.nested_items.find( + (child) => child.label === item_label + ); + if (child) { + let b = item.nested_items[old_index]; + item.nested_items[old_index] = item.nested_items[new_index]; + item.nested_items[new_index] = b; + } + } + }); + }, + }); + }); + } + + make_fields_for_grids(fields) { + let doc_fields = Array.from(fields); + doc_fields = doc_fields + .filter((f) => f.fieldtype !== "Section Break" && f.fieldtype !== "Column Break") + .map((f, i) => ({ + ...f, + in_list_view: i < 5 ? 1 : 0, + })); + let link_to_field = doc_fields.find((f) => f.label == "Link To"); + link_to_field.field_in_dialog = true; + return doc_fields; + } + + make_dialog(opts) { + let title = "New Sidebar Item"; + + const me = this; + this.dialog_opts = opts; + + // Create the dialog + let dialog_fields = [ + { + fieldname: "label", + fieldtype: "Data", + in_list_view: 1, + label: "Label", + onchange: function (opts) { + let label = this.get_value(); + switch (label) { + case "Home": + d.set_value("icon", "home"); + d.set_value("link_type", "Workspace"); + d.set_value("link_to", me.workspace_title); + break; + + case "Reports": + d.set_value("type", "Section Break"); + d.set_value("link_to", null); + break; + + case "Dashboard": + d.set_value("link_type", "Dashboard"); + d.set_value("link_to", me.workspace_title); + d.set_value("icon", "layout-dashboard"); + break; + + case "Learn": + d.set_value("icon", "graduation-cap"); + d.set_value("link_type", "URL"); + break; + + case "Settings": + d.set_value("icon", "settings"); + break; + } + + if (d.get_value("type") == "Link" && d.get_value("link_type") !== "URL") { + d.set_value("link_to", label); + } + + if ( + me.dialog_opts && + me.dialog_opts.parent_item && + me.dialog_opts.parent_item.label == "Reports" + ) { + d.set_value("icon", "table"); + d.set_value("link_type", "Report"); + } + }, + }, + { + default: "Link", + fieldname: "type", + fieldtype: "Select", + in_list_view: 1, + label: "Type", + options: "Link\nSection Break\nSpacer\nSidebar Item Group", + onchange: function () { + let type = this.get_value(); + if (type == "Section Break") { + d.set_value("link_to", null); + } + }, + }, + { + default: "DocType", + depends_on: "eval: doc.type == 'Link'", + fieldname: "link_type", + fieldtype: "Select", + in_list_view: 1, + label: "Link Type", + options: "DocType\nPage\nReport\nWorkspace\nDashboard\nURL", + onchange: function () { + d.set_value("link_to", null); + }, + }, + { + depends_on: "eval: doc.link_type != \"URL\" && doc.type == 'Link'", + fieldname: "link_to", + fieldtype: "Dynamic Link", + in_list_view: 1, + label: "Link To", + options: "link_type", + onchange: function () { + if (d.get_value("link_type") == "DocType") { + let doctype = this.get_value(); + if (doctype) { + me.setup_filter(d, doctype); + } + } + }, + }, + { + depends_on: 'eval: doc.link_type == "URL"', + fieldname: "url", + fieldtype: "Data", + label: "URL", + }, + { + depends_on: + 'eval: doc.type == "Link" || (doc.indent == 1 && doc.type == "Section Break")', + fieldname: "icon", + fieldtype: "Icon", + options: "Emojis", + in_list_view: 1, + label: "Icon", + }, + { + fieldtype: "HTML", + fieldname: "filter_area", + }, + { + depends_on: 'eval: doc.type == "Section Break"', + fieldname: "display_section", + fieldtype: "Section Break", + label: "Options", + }, + { + default: "0", + depends_on: 'eval: doc.type == "Section Break"', + fieldname: "indent", + fieldtype: "Check", + label: "Indent", + }, + { + depends_on: "eval: doc.indent == 1", + fieldname: "show_arrow", + fieldtype: "Check", + label: "Show Arrow", + }, + { + default: "1", + depends_on: 'eval: doc.type == "Section Break"', + fieldname: "collapsible", + fieldtype: "Check", + label: "Collapsible", + }, + { + fieldname: "column_break_krzu", + fieldtype: "Column Break", + }, + { + default: "0", + depends_on: 'eval: doc.type == "Section Break"', + fieldname: "keep_closed", + fieldtype: "Check", + label: "Keep Closed", + }, + { + fieldname: "details_section", + fieldtype: "Section Break", + label: "Details", + }, + + { + fieldtype: "Section Break", + }, + { + fieldname: "display_depends_on", + fieldtype: "Code", + label: "Display Depends On (JS)", + options: "JS", + max_height: "10px", + }, + { + fieldtype: "Section Break", + }, + { + fieldname: "route_options", + fieldtype: "Code", + display_depends_on: "eval: doc.link_type == 'Page'", + label: "Route Options", + options: "JSON", + max_height: "50px", + }, + ]; + if (opts && opts.item) { + dialog_fields.forEach((f) => { + if ( + opts.item[f.fieldname] !== undefined && + f.fieldtype !== "Section Break" && + f.fieldtype !== "Column Break" + ) { + f.default = opts.item[f.fieldname]; + } + }); + title = "Edit Sidebar Item"; + } + let d; + this.dialog = d = new frappe.ui.Dialog({ + title: title, + fields: dialog_fields, + primary_action_label: "Save", + size: "small", + primary_action(values) { + if (me.filter_group) { + me.filter_group.get_filters(); + } + + if (me.new_sidebar_items.length === 0) { + me.new_sidebar_items = Array.from(me.workspace_sidebar_items); + } + if (opts && opts.nested) { + values.child = 1; + console.log("Add it as a nested item"); + console.log(opts.parent_item); + let index = me.new_sidebar_items.findIndex((f) => { + return f.label == opts.parent_item.label; + }); + + if (!me.new_sidebar_items[index].nested_items) { + me.new_sidebar_items[index].nested_items = []; + } + me.new_sidebar_items[index].nested_items.push(values); + } else if (opts && opts.item) { + if (opts.item.child) { + let parent_icon = me.find_parent(me.new_sidebar_items, opts.item); + if (parent_icon) { + let index = parent_icon.nested_items.indexOf(opts.item); + let parent_icon_index = me.new_sidebar_items.indexOf(parent_icon); + me.new_sidebar_items[parent_icon_index].nested_items[index] = values; + } + } else { + let index = me.new_sidebar_items.indexOf(opts.item); + + me.new_sidebar_items[index] = { + ...me.new_sidebar_items[index], + ...values, + }; + } + } else { + me.new_sidebar_items.push(values); + } + me.sidebar.create_sidebar(me.new_sidebar_items); + me.setup_sorting_for_nested_container(); + d.hide(); + }, + }); + + return d; + } + setup_filter(d, doctype) { + if (this.filter_group) { + this.filter_group.wrapper.empty(); + delete this.filter_group; + } + + // let $loading = this.dialog.get_field("filter_area_loading").$wrapper; + // $(`${__("Loading Filters...")}`).appendTo($loading); + + this.filters = []; + + this.generate_filter_from_json && this.generate_filter_from_json(); + + this.filter_group = new frappe.ui.FilterGroup({ + parent: d.get_field("filter_area").$wrapper, + doctype: doctype, + on_change: () => {}, + }); + + frappe.model.with_doctype(doctype, () => { + this.filter_group.add_filters_to_filter_group(this.filters); + }); + } + + show_new_dialog(opts) { + let d = this.make_dialog(opts); + d.show(); + } + + hide_field(fieldname) { + this.dialog.set_df_property(fieldname, "hidden", true); + } + + show_field(fieldname) { + this.dialog.set_df_property(fieldname, "hidden", false); + } + + find_parent(sidebar_items, item) { + for (const f of sidebar_items) { + if (f.nested_items && f.nested_items.includes(item)) { + return f; + } + } + } + + delete_item(item) { + let index; + if (item.child) { + let parent_icon = this.find_parent(this.new_sidebar_items, item); + index = parent_icon.nested_items.indexOf(item); + parent_icon.nested_items.splice(index, 1); + } else { + index = this.new_sidebar_items.indexOf(item); + this.new_sidebar_items.splice(index, 1); + } + this.sidebar.make_sidebar(); + } + + add_below(item) { + let index = this.new_sidebar_items.indexOf(item); + this.show_new_dialog(index); + this.sidebar.make_sidebar(); + } + + duplicate_item(item) { + let index = this.new_sidebar_items.indexOf(item); + this.new_sidebar_items.splice(index, 0, item); + this.sidebar.make_sidebar(); + } + + edit_item(item) { + let d = this.make_dialog({ + item: item, + }); + d.show(); + this.sidebar.make_sidebar(); + } + + perform_action(action_name, item_data) { + let index = this.new_sidebar_items.indexOf(item_data); + let parent_item = this.find_parent(this.new_sidebar_items, item_data); + switch (action_name) { + case "edit": + this.edit_item(item_data); + break; + case "delete": + this.delete_item(item_data); + break; + case "add_item_below": + this.edit_item(item_data); + break; + case "duplicate": + this.duplicate_item(item_data); + break; + default: + break; + } + this.sidebar.make_sidebar(); + } +} diff --git a/frappe/public/js/frappe/ui/sidebar/sidebar_header.js b/frappe/public/js/frappe/ui/sidebar/sidebar_header.js index 425fba626b..eb579b8583 100644 --- a/frappe/public/js/frappe/ui/sidebar/sidebar_header.js +++ b/frappe/public/js/frappe/ui/sidebar/sidebar_header.js @@ -29,8 +29,7 @@ frappe.ui.SidebarHeader = class SidebarHeader { label: __("Edit Sidebar"), icon: "edit", onClick: function () { - me.sidebar.edit_mode = true; - me.sidebar.toggle_editing_mode(); + me.sidebar.editor.toggle(); }, }, { diff --git a/frappe/public/js/frappe/ui/sidebar/sidebar_item.html b/frappe/public/js/frappe/ui/sidebar/sidebar_item.html index 2457ab0f76..9106234bd7 100644 --- a/frappe/public/js/frappe/ui/sidebar/sidebar_item.html +++ b/frappe/public/js/frappe/ui/sidebar/sidebar_item.html @@ -45,7 +45,7 @@ {% } %} {% } %} - + {% if(!item.standard) { %} + {% } %} \ No newline at end of file diff --git a/frappe/public/js/frappe/ui/sidebar/sidebar_item.js b/frappe/public/js/frappe/ui/sidebar/sidebar_item.js index 67d76c4689..3b8d7fb038 100644 --- a/frappe/public/js/frappe/ui/sidebar/sidebar_item.js +++ b/frappe/public/js/frappe/ui/sidebar/sidebar_item.js @@ -20,7 +20,7 @@ frappe.ui.sidebar_item.TypeLink = class SidebarItem { name: this.item.link_to, }; - if (this.item.report || !frappe.app.sidebar.edit_mode) { + if (this.item.report || !frappe.app.sidebar.editor.edit_mode) { args.is_query_report = this.item.report.report_type === "Query Report" || this.item.report.report_type == "Script Report"; @@ -69,7 +69,7 @@ frappe.ui.sidebar_item.TypeLink = class SidebarItem { frappe.render_template("sidebar_item", { item: this.item, path: this.path, - edit_mode: frappe.app.sidebar.edit_mode, + edit_mode: frappe.app.sidebar.editor.edit_mode, }) ); $(this.container).append(this.wrapper); @@ -104,22 +104,21 @@ frappe.ui.sidebar_item.TypeLink = class SidebarItem { label: "Edit Item", icon: "pen", onClick: () => { - frappe.app.sidebar.edit_item(me.item); + frappe.app.sidebar.editor.perform_action("edit", me.item); }, }, { label: "Add Item Below", icon: "add", onClick: () => { - frappe.app.sidebar.add_below(me.item); + frappe.app.sidebar.editor.perform_action("add_below", me.item); }, }, { label: "Duplicate", icon: "copy", onClick: () => { - console.log("Start Deleting"); - frappe.app.sidebar.duplicate_item(me.item); + frappe.app.sidebar.editor.perform_action("duplicate", me.item); }, }, { @@ -127,8 +126,7 @@ frappe.ui.sidebar_item.TypeLink = class SidebarItem { icon: "trash-2", onClick: () => { console.log(me.item); - frappe.app.sidebar.delete_item(me.item); - console.log("Start Deleting"); + frappe.app.sidebar.editor.perform_action("delete", me.item); }, }, ]; @@ -276,7 +274,7 @@ frappe.ui.sidebar_item.TypeSectionBreak = class SectionBreakSidebarItem extends this.section_breaks_state[this.workspace_title] = {}; } - const title = this.$drop_icon.parent().parent().attr("title"); + const title = this.wrapper.attr("title"); this.section_breaks_state[this.workspace_title][title] = this.collapsed; localStorage.setItem("section-breaks-state", JSON.stringify(this.section_breaks_state)); @@ -290,14 +288,14 @@ frappe.ui.sidebar_item.TypeSectionBreak = class SectionBreakSidebarItem extends icon: "pen", onClick: () => { console.log("Start ediitng"); - frappe.app.sidebar.edit_item(me.item); + frappe.app.sidebar.editor.perform_action("edit", me.item); }, }, { label: "Add Nested Items", icon: "add", onClick: () => { - frappe.app.sidebar.show_new_dialog({ + frappe.app.sidebar.editor.show_new_dialog({ nested: true, parent_item: me.item, }); @@ -307,17 +305,14 @@ frappe.ui.sidebar_item.TypeSectionBreak = class SectionBreakSidebarItem extends label: "Duplicate", icon: "copy", onClick: () => { - console.log("Start Deleting"); - frappe.app.sidebar.duplicate_item(me.item); + frappe.app.sidebar.editor.perform_action("duplicate", me.item); }, }, { label: "Delete", icon: "trash-2", onClick: () => { - console.log(me.item); - frappe.app.sidebar.delete_item(me.item); - console.log("Start Deleting"); + frappe.app.sidebar.editor.perform_action("delete", me.item); }, }, ]; diff --git a/frappe/public/scss/desk/sidebar.scss b/frappe/public/scss/desk/sidebar.scss index ce53216a52..dbb793ed83 100644 --- a/frappe/public/scss/desk/sidebar.scss +++ b/frappe/public/scss/desk/sidebar.scss @@ -144,15 +144,6 @@ @include transition(all, 0.3s, cubic-bezier(0.4, 0, 0.2, 1)); } - .sidebar-item-container { - /* nested container */ - .sidebar-item-container { - .standard-sidebar-item { - justify-content: start; - } - } - } - .standard-items-sections { margin-top: 14px; .dropdown-notifications { @@ -273,7 +264,10 @@ } } .collapse-sidebar-link { - display: flex; + display: none; + } + .dropdown-navbar-user { + display: none; } } .sidebar-item-edit-controls {