feat: make sidebar editor awesome again

This commit is contained in:
sokumon 2025-12-10 15:54:05 +05:30
parent cc08ab9425
commit 0642d59d6a
6 changed files with 602 additions and 473 deletions

View file

@ -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;
// $(`<span class="text-muted">${__("Loading Filters...")}</span>`).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;
}
};

View file

@ -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;
// $(`<span class="text-muted">${__("Loading Filters...")}</span>`).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();
}
}

View file

@ -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();
},
},
{

View file

@ -45,7 +45,7 @@
{% } %}
</a>
{% } %}
{% if(!item.standard) { %}
<div class="sidebar-item-edit-controls edit-mode {%= edit_mode ? '' : 'hidden' %}">
<button class="btn btn-secondary btn-sm drag-handle">
<svg class="es-icon es-line icon-xs" aria-hidden="true">
@ -56,6 +56,7 @@
{%= frappe.utils.icon("dot-horizontal", "xs") %}
</button>
</div>
{% } %}
</div>
<div class="sidebar-child-item nested-container"></div>
</div>

View file

@ -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);
},
},
];

View file

@ -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 {