feat: App switcher
This commit is contained in:
parent
5c820a1e79
commit
8dbc2832c5
8 changed files with 176 additions and 37 deletions
7
.github/frappe-framework-logo.svg
vendored
7
.github/frappe-framework-logo.svg
vendored
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 626 B |
|
|
@ -146,6 +146,32 @@ def load_desktop_data(bootinfo):
|
|||
bootinfo.sidebar_pages = get_workspace_sidebar_items()
|
||||
bootinfo.module_wise_workspaces = get_controller("Workspace").get_module_wise_workspaces()
|
||||
bootinfo.dashboards = frappe.get_all("Dashboard")
|
||||
bootinfo.app_data = []
|
||||
|
||||
Workspace = frappe.qb.DocType("Workspace")
|
||||
Module = frappe.qb.DocType("Module Def")
|
||||
|
||||
for app_name in frappe.get_installed_apps():
|
||||
bootinfo.app_data.append(
|
||||
dict(
|
||||
app_name=app_name,
|
||||
app_title=frappe.get_hooks("app_title", app_name=app_name),
|
||||
app_home=frappe.get_hooks("app_home", app_name=app_name),
|
||||
app_logo_url=frappe.get_hooks("app_logo_url", app_name=app_name),
|
||||
modules=[m.name for m in frappe.get_all("Module Def", dict(app_name=app_name))],
|
||||
workspaces=[
|
||||
r[0]
|
||||
for r in (
|
||||
frappe.qb.from_(Workspace)
|
||||
.inner_join(Module)
|
||||
.on(Workspace.module == Module.name)
|
||||
.select(Workspace.name)
|
||||
.where(Module.app_name == app_name)
|
||||
.run()
|
||||
)
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def get_allowed_pages(cache=False):
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ frappe.ui.form.on("Workspace Settings", {
|
|||
frm.docfields.push({
|
||||
fieldtype: "Check",
|
||||
fieldname: page.name,
|
||||
hidden: !frappe.boot.app_data_map[frappe.current_app].workspaces.includes(
|
||||
page.title
|
||||
),
|
||||
label: page.title + (page.parent_page ? ` (${page.parent_page})` : ""),
|
||||
initial_value: workspace_visibilty[page.name] !== 0, // not set is also visible
|
||||
});
|
||||
|
|
@ -38,7 +41,7 @@ frappe.ui.form.on("Workspace Settings", {
|
|||
},
|
||||
after_save(frm) {
|
||||
// reload page to show latest sidebar
|
||||
window.location.reload();
|
||||
frappe.app.sidebar.reload();
|
||||
},
|
||||
refresh(frm) {
|
||||
let get_page = (e) => frm.workspace_map[e.fieldobj.df.fieldname];
|
||||
|
|
|
|||
|
|
@ -3,12 +3,13 @@ import os
|
|||
from . import __version__ as app_version
|
||||
|
||||
app_name = "frappe"
|
||||
app_title = "Frappe Framework"
|
||||
app_title = "Framework"
|
||||
app_publisher = "Frappe Technologies"
|
||||
app_description = "Full stack web framework with Python, Javascript, MariaDB, Redis, Node"
|
||||
app_license = "MIT"
|
||||
app_logo_url = "/assets/frappe/images/frappe-framework-logo.svg"
|
||||
develop_version = "15.x.x-develop"
|
||||
app_home = "/app/build"
|
||||
|
||||
app_email = "developers@frappe.io"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 12C0 5.37258 5.37258 0 12 0H88C94.6274 0 100 5.37258 100 12V88C100 94.6274 94.6274 100 88 100H12C5.37258 100 0 94.6274 0 88V12Z" fill="#171717"/>
|
||||
<path d="M68.4319 25H35V33.794H68.4319V25Z" fill="white"/>
|
||||
<path d="M35 47.4357V75H45.671V56.2445H66.2149V47.4357H35Z" fill="white"/>
|
||||
<svg fill="none" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M35.7143 0H14.2857C6.39593 0 0 6.39593 0 14.2857V35.7143C0 43.6041 6.39593 50 14.2857 50H35.7143C43.6041 50 50 43.6041 50 35.7143V14.2857C50 6.39593 43.6041 0 35.7143 0Z" fill="#7B808A"></path>
|
||||
<path d="M25 10.7141L12.625 17.8569V19.9284L16.1964 21.9819L23.2143 26.0176V34.1249L16.1964 30.0712V26.6248L12.625 24.5177V32.1249L25 39.2677L37.375 32.1249V17.8391L25 10.6963V10.7141ZM17.9821 18.8927L25 14.8391L32.0178 18.8927L25 22.9463L17.9821 18.8927ZM33.8035 30.0891L26.7857 34.1427V26.0355L33.8035 21.9819V30.0891Z" fill="white"></path>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 399 B After Width: | Height: | Size: 626 B |
|
|
@ -33,29 +33,46 @@ frappe.ui.Sidebar = class Sidebar {
|
|||
}
|
||||
|
||||
make_dom() {
|
||||
this.set_default_app();
|
||||
this.wrapper = $(`
|
||||
<div class="body-sidebar-container">
|
||||
<div class="body-sidebar-placeholder"></div>
|
||||
<div class="body-sidebar">
|
||||
<a href="/app" style="text-decoration: none">
|
||||
<div class="standard-sidebar-item">
|
||||
<div class="sidebar-item-icon">
|
||||
<img
|
||||
class="app-logo"
|
||||
src="${frappe.boot.app_logo_url}"
|
||||
alt="${__("App Logo")}"
|
||||
>
|
||||
</div>
|
||||
<div class="sidebar-item-label" style="margin-left: 5px; margin-top: 1px">
|
||||
${__(frappe.boot.sysdefaults.app_name)}
|
||||
<div class="body-sidebar">
|
||||
<a class="app-switcher-dropdown"
|
||||
style="text-decoration: none; width: 100%; position: relative;">
|
||||
|
||||
<div class="standard-sidebar-item">
|
||||
<div class="d-flex">
|
||||
<div class="sidebar-item-icon">
|
||||
<img
|
||||
class="app-logo"
|
||||
src="${frappe.boot.app_data[0].app_logo_url}"
|
||||
alt="${__("App Logo")}"
|
||||
>
|
||||
</div>
|
||||
<div class="sidebar-item-label" style="margin-left: 5px; margin-top: 1px">
|
||||
${__(frappe.boot.app_data[0].app_title)}
|
||||
</div>
|
||||
</div>
|
||||
<div class="sidebar-item-control">
|
||||
<button class="btn-reset drop-icon show-in-edit-mode">
|
||||
<svg class="es-icon es-line icon-sm" style="" aria-hidden="true">
|
||||
<use class="" href="#es-line-down"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div class="app-switcher-menu hidden" role="menu">
|
||||
</div>
|
||||
<div class="sidebar-items">
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<a class="edit-sidebar-link text-extra-muted">Edit sidebar</a>
|
||||
</div>
|
||||
</a>
|
||||
<div class="sidebar-items">
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<a class="edit-sidebar-link text-extra-muted">Edit sidebar</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
`).prependTo("body");
|
||||
|
|
@ -65,6 +82,70 @@ frappe.ui.Sidebar = class Sidebar {
|
|||
this.wrapper.find(".body-sidebar .edit-sidebar-link").on("click", () => {
|
||||
frappe.quick_edit("Workspace Settings");
|
||||
});
|
||||
|
||||
this.setup_app_switcher();
|
||||
}
|
||||
|
||||
set_default_app() {
|
||||
// sort apps based on # of workspaces
|
||||
frappe.boot.app_data.sort((a, b) => (a.workspaces.length < b.workspaces.length ? 1 : -1));
|
||||
frappe.current_app = frappe.boot.app_data[0].app_name;
|
||||
}
|
||||
|
||||
setup_app_switcher() {
|
||||
let app_switcher_menu = $(".app-switcher-menu");
|
||||
|
||||
$(".app-switcher-dropdown").on("click", () => {
|
||||
app_switcher_menu.toggleClass("hidden");
|
||||
});
|
||||
|
||||
// hover out of the sidebar
|
||||
this.wrapper.find(".body-sidebar").on("mouseleave", () => {
|
||||
app_switcher_menu.addClass("hidden");
|
||||
|
||||
// hide any expanded menus as they leave a blank space in the sidebar
|
||||
this.wrapper.find(".drop-icon[data-state='opened'").click();
|
||||
});
|
||||
|
||||
frappe.boot.app_data_map = {};
|
||||
for (var app of frappe.boot.app_data) {
|
||||
frappe.boot.app_data_map[app.app_name] = app;
|
||||
if (app.workspaces?.length) {
|
||||
$(`<div class="app-item" data-app-name="${app.app_name}"
|
||||
data-app-home="${app.app_home}">
|
||||
<a>
|
||||
<div class="sidebar-item-icon">
|
||||
<img
|
||||
style="margin-right: var(--margin-sm);"
|
||||
class="app-logo"
|
||||
src="${app.app_logo_url}"
|
||||
alt="${__("App Logo")}"
|
||||
>
|
||||
</div>
|
||||
<span>${app.app_title}</span>
|
||||
</a>
|
||||
</div>`).appendTo(app_switcher_menu);
|
||||
}
|
||||
}
|
||||
|
||||
app_switcher_menu.find(".app-item").on("click", (e) => {
|
||||
let item = $(e.delegateTarget);
|
||||
frappe.current_app = item.attr("data-app-name");
|
||||
frappe.set_route(item.attr("data-app-home"));
|
||||
|
||||
this.wrapper
|
||||
.find(".app-switcher-dropdown .sidebar-item-icon img")
|
||||
.attr("src", frappe.boot.app_data_map[frappe.current_app].app_logo_url);
|
||||
this.wrapper
|
||||
.find(".app-switcher-dropdown .sidebar-item-label")
|
||||
.html(frappe.boot.app_data_map[frappe.current_app].app_title);
|
||||
|
||||
// hide menu
|
||||
app_switcher_menu.toggleClass("hidden");
|
||||
|
||||
// re-render the sidebar
|
||||
this.make_sidebar();
|
||||
});
|
||||
}
|
||||
|
||||
setup_pages() {
|
||||
|
|
@ -97,10 +178,12 @@ frappe.ui.Sidebar = class Sidebar {
|
|||
this.wrapper.find(".standard-sidebar-section").remove();
|
||||
}
|
||||
|
||||
let app_workspaces = frappe.boot.app_data_map[frappe.current_app || "frappe"].workspaces;
|
||||
|
||||
let parent_pages = this.all_pages.filter((p) => !p.parent_page).uniqBy((p) => p.title);
|
||||
parent_pages = [
|
||||
...parent_pages.filter((p) => !p.public),
|
||||
...parent_pages.filter((p) => p.public),
|
||||
...parent_pages.filter((p) => !p.public && app_workspaces.includes(p.title)),
|
||||
...parent_pages.filter((p) => p.public && app_workspaces.includes(p.title)),
|
||||
];
|
||||
|
||||
this.build_sidebar_section("All", parent_pages);
|
||||
|
|
@ -238,12 +321,22 @@ frappe.ui.Sidebar = class Sidebar {
|
|||
$drop_icon.removeClass("hidden");
|
||||
}
|
||||
$drop_icon.on("click", () => {
|
||||
let icon =
|
||||
$drop_icon.find("use").attr("href") === "#es-line-down"
|
||||
? "#es-line-up"
|
||||
: "#es-line-down";
|
||||
$drop_icon.find("use").attr("href", icon);
|
||||
let opened = $drop_icon.find("use").attr("href") === "#es-line-down";
|
||||
|
||||
if (!opened) {
|
||||
$drop_icon.attr("data-state", "closed").find("use").attr("href", "#es-line-down");
|
||||
} else {
|
||||
$drop_icon.attr("data-state", "opened").find("use").attr("href", "#es-line-up");
|
||||
}
|
||||
``;
|
||||
$child_item_section.toggleClass("hidden");
|
||||
});
|
||||
}
|
||||
|
||||
reload() {
|
||||
return frappe.workspace.get_pages().then((r) => {
|
||||
frappe.boot.sidebar_pages = r;
|
||||
this.setup_pages();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -43,9 +43,6 @@ frappe.ui.toolbar.Toolbar = class {
|
|||
search_modal.find("#modal-search").focus();
|
||||
}, 300);
|
||||
});
|
||||
$(".navbar-toggle-full-width").click(() => {
|
||||
frappe.ui.toolbar.toggle_full_width();
|
||||
});
|
||||
}
|
||||
|
||||
setup_read_only_mode() {
|
||||
|
|
|
|||
|
|
@ -192,6 +192,27 @@ body {
|
|||
}
|
||||
}
|
||||
|
||||
.app-switcher-menu {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
padding: var(--padding-xs);
|
||||
box-shadow: var(--shadow-base);
|
||||
background-color: var(--neutral);
|
||||
min-width: 180px;
|
||||
z-index: 1;
|
||||
border-radius: var(--border-radius-tiny);
|
||||
}
|
||||
|
||||
.app-item {
|
||||
padding: var(--padding-xs);
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
// form sidebar
|
||||
.form-sidebar {
|
||||
.sidebar-section {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue