feat: make sidebar editor awesome again
This commit is contained in:
parent
cc08ab9425
commit
0642d59d6a
6 changed files with 602 additions and 473 deletions
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
572
frappe/public/js/frappe/ui/sidebar/sidebar_editor.js
Normal file
572
frappe/public/js/frappe/ui/sidebar/sidebar_editor.js
Normal 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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue