feat: desktop screen wth new design
This commit is contained in:
parent
4f64cb1613
commit
7cdfb60797
17 changed files with 756 additions and 408 deletions
|
|
@ -530,7 +530,7 @@ def get_sidebar_items():
|
|||
for s in sidebars:
|
||||
w = frappe.get_doc("Workspace Sidebar", s)
|
||||
sidebar_items[s.lower()] = []
|
||||
|
||||
print(s)
|
||||
for si in w.items:
|
||||
workspace_sidebar = {
|
||||
"label": si.label,
|
||||
|
|
@ -539,8 +539,8 @@ def get_sidebar_items():
|
|||
"type": si.type,
|
||||
"icon": si.icon,
|
||||
"child": si.child,
|
||||
"collapsible": si.collapsible,
|
||||
"collapsed_by_default": si.collapsed_by_default,
|
||||
# "collapsible": si.collapsible,
|
||||
# "collapsed_by_default": si.collapsed_by_default,
|
||||
}
|
||||
if si.link_type == "Report":
|
||||
report_type, ref_doctype = frappe.db.get_value(
|
||||
|
|
|
|||
|
|
@ -4,34 +4,6 @@
|
|||
frappe.ui.form.on("Desktop Icon", {
|
||||
setup: function (frm) {
|
||||
load_installed_apps();
|
||||
frm.fields_dict.color.set_data(Object.keys(frappe.palette_map));
|
||||
},
|
||||
before_save: function (frm) {
|
||||
if (frm.doc.type == "workspace") {
|
||||
frappe.call({
|
||||
method: "frappe.client.get",
|
||||
args: {
|
||||
doctype: "Workspace", // e.g., "User"
|
||||
name: frm.doc.workspace,
|
||||
},
|
||||
callback: function (r) {
|
||||
if (r.message) {
|
||||
// Access attributes like r.message.another_field
|
||||
let doc = r.message;
|
||||
let url = `/app/${
|
||||
doc.public
|
||||
? frappe.router.slug(doc.title)
|
||||
: "private/" + frappe.router.slug(doc.title)
|
||||
}`;
|
||||
frm.doc.route = url;
|
||||
}
|
||||
},
|
||||
});
|
||||
} else if (frm.doc.type == "link") {
|
||||
frm.doc.route = frm.doc.link;
|
||||
} else if (frm.doc.type == "list") {
|
||||
frm.doc.route = `/app/${frappe.router.slug(frm.doc._doctype)}`;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -4,41 +4,26 @@
|
|||
"doctype": "DocType",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"module_name",
|
||||
"label",
|
||||
"standard",
|
||||
"custom",
|
||||
"icon_type",
|
||||
"type",
|
||||
"workspace",
|
||||
"route",
|
||||
"link_to",
|
||||
"parent_icon",
|
||||
"column_break_3",
|
||||
"app",
|
||||
"description",
|
||||
"category",
|
||||
"hidden",
|
||||
"blocked",
|
||||
"force_show",
|
||||
"section_break_7",
|
||||
"_doctype",
|
||||
"_report",
|
||||
"link",
|
||||
"column_break_10",
|
||||
"color",
|
||||
"icon",
|
||||
"logo_url",
|
||||
"reverse",
|
||||
"idx"
|
||||
"idx",
|
||||
"link"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "module_name",
|
||||
"fieldtype": "Data",
|
||||
"label": "Module Name"
|
||||
},
|
||||
{
|
||||
"fieldname": "label",
|
||||
"fieldtype": "Data",
|
||||
"label": "Label"
|
||||
"label": "Label",
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
|
|
@ -47,100 +32,28 @@
|
|||
"in_list_view": 1,
|
||||
"label": "Standard"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "custom",
|
||||
"fieldtype": "Check",
|
||||
"label": "Custom",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "app",
|
||||
"fieldtype": "Autocomplete",
|
||||
"label": "App"
|
||||
},
|
||||
{
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Description"
|
||||
},
|
||||
{
|
||||
"fieldname": "category",
|
||||
"fieldtype": "Data",
|
||||
"label": "Category"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "hidden",
|
||||
"fieldtype": "Check",
|
||||
"label": "Hidden"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "blocked",
|
||||
"fieldtype": "Check",
|
||||
"label": "Blocked"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "force_show",
|
||||
"fieldtype": "Check",
|
||||
"label": "Force Show",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_7",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "type",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Type",
|
||||
"options": "module\nlist\nlink\npage\nquery-report\nworkspace"
|
||||
},
|
||||
{
|
||||
"fieldname": "_doctype",
|
||||
"fieldtype": "Link",
|
||||
"label": "_doctype",
|
||||
"options": "DocType"
|
||||
},
|
||||
{
|
||||
"fieldname": "_report",
|
||||
"fieldtype": "Link",
|
||||
"label": "_report",
|
||||
"options": "Report"
|
||||
"label": "Link Type",
|
||||
"options": "DocType\nWorkspace\nExternal"
|
||||
},
|
||||
{
|
||||
"fieldname": "link",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Link"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_10",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "color",
|
||||
"fieldtype": "Autocomplete",
|
||||
"label": "Color"
|
||||
},
|
||||
{
|
||||
"fieldname": "icon",
|
||||
"fieldtype": "Icon",
|
||||
"label": "Icon"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "reverse",
|
||||
"fieldtype": "Check",
|
||||
"label": "Reverse Icon Color"
|
||||
},
|
||||
{
|
||||
"fieldname": "idx",
|
||||
"fieldtype": "Int",
|
||||
|
|
@ -152,20 +65,38 @@
|
|||
"label": "Workspace",
|
||||
"options": "Workspace"
|
||||
},
|
||||
{
|
||||
"fieldname": "route",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Route"
|
||||
},
|
||||
{
|
||||
"fieldname": "logo_url",
|
||||
"fieldtype": "Data",
|
||||
"label": "Logo URL"
|
||||
},
|
||||
{
|
||||
"fieldname": "icon_type",
|
||||
"fieldtype": "Select",
|
||||
"label": "Icon Type",
|
||||
"options": "Folder\nApp\nLink"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.standard == 1",
|
||||
"fieldname": "app",
|
||||
"fieldtype": "Autocomplete",
|
||||
"label": "App"
|
||||
},
|
||||
{
|
||||
"fieldname": "link_to",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"label": "Link To",
|
||||
"options": "type"
|
||||
},
|
||||
{
|
||||
"fieldname": "parent_icon",
|
||||
"fieldtype": "Link",
|
||||
"label": "Parent Icon",
|
||||
"options": "Desktop Icon"
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2025-09-29 01:47:25.718356",
|
||||
"modified": "2025-10-08 23:40:31.716144",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Desktop Icon",
|
||||
|
|
|
|||
|
|
@ -21,25 +21,16 @@ class DesktopIcon(Document):
|
|||
if TYPE_CHECKING:
|
||||
from frappe.types import DF
|
||||
|
||||
_doctype: DF.Link | None
|
||||
_report: DF.Link | None
|
||||
app: DF.Autocomplete | None
|
||||
blocked: DF.Check
|
||||
category: DF.Data | None
|
||||
color: DF.Autocomplete | None
|
||||
custom: DF.Check
|
||||
description: DF.SmallText | None
|
||||
force_show: DF.Check
|
||||
hidden: DF.Check
|
||||
icon_type: DF.Literal["Folder", "App", "Link"]
|
||||
idx: DF.Int
|
||||
label: DF.Data | None
|
||||
link: DF.SmallText | None
|
||||
link_to: DF.DynamicLink | None
|
||||
logo_url: DF.Data | None
|
||||
module_name: DF.Data | None
|
||||
reverse: DF.Check
|
||||
route: DF.Data | None
|
||||
parent_icon: DF.Link | None
|
||||
standard: DF.Check
|
||||
type: DF.Literal["module", "list", "link", "page", "query-report", "workspace"]
|
||||
type: DF.Literal["DocType", "Workspace", "External"]
|
||||
workspace: DF.Link | None
|
||||
# end: auto-generated types
|
||||
|
||||
|
|
@ -93,7 +84,10 @@ def get_desktop_icon_directory(app_name):
|
|||
|
||||
|
||||
def after_doctype_insert():
|
||||
frappe.db.add_unique("Desktop Icon", ("module_name", "owner", "standard"))
|
||||
pass
|
||||
|
||||
|
||||
# frappe.db.add_unique("Desktop Icon", ("owner", "standard"))
|
||||
|
||||
|
||||
def get_desktop_icons(user=None):
|
||||
|
|
@ -105,25 +99,16 @@ def get_desktop_icons(user=None):
|
|||
|
||||
if not user_icons:
|
||||
fields = [
|
||||
"module_name",
|
||||
"hidden",
|
||||
"label",
|
||||
"link",
|
||||
"type",
|
||||
"icon_type",
|
||||
"parent_icon",
|
||||
"icon",
|
||||
"color",
|
||||
"description",
|
||||
"category",
|
||||
"_doctype",
|
||||
"_report",
|
||||
"link_to",
|
||||
"idx",
|
||||
"force_show",
|
||||
"reverse",
|
||||
"custom",
|
||||
"standard",
|
||||
"blocked",
|
||||
"workspace",
|
||||
"route",
|
||||
"logo_url",
|
||||
]
|
||||
|
||||
|
|
@ -140,6 +125,7 @@ def get_desktop_icons(user=None):
|
|||
standard_icons = frappe.get_all("Desktop Icon", fields=fields, filters={"standard": 1})
|
||||
|
||||
standard_map = {}
|
||||
|
||||
for icon in standard_icons:
|
||||
if icon._doctype in blocked_doctypes:
|
||||
icon.blocked = 1
|
||||
|
|
@ -192,7 +178,11 @@ def get_desktop_icons(user=None):
|
|||
for d in user_icons:
|
||||
if d.label:
|
||||
d.label = _(d.label, context=d.parent)
|
||||
# includes
|
||||
|
||||
for s in user_icons:
|
||||
if s.parent_icon:
|
||||
s.parent_icon = frappe.db.get_value("Desktop Icon", s.parent_icon, "label")
|
||||
frappe.cache.hset("desktop_icons", user, user_icons)
|
||||
|
||||
return user_icons
|
||||
|
|
@ -635,39 +625,75 @@ def hide(name, user=None):
|
|||
|
||||
|
||||
def create_desktop_icons_from_workspace():
|
||||
all_workspaces = frappe.get_all("Workspace", filters={"public": 1}, pluck="name")
|
||||
from frappe.query_builder import DocType
|
||||
|
||||
workspace = DocType("Workspace")
|
||||
|
||||
all_workspaces = (
|
||||
frappe.qb.from_(workspace)
|
||||
.select(workspace.name)
|
||||
.where((workspace.public == 1) & (workspace.name != "Welcome Workspace"))
|
||||
).run(pluck=True)
|
||||
|
||||
for w in all_workspaces:
|
||||
icon = frappe.new_doc("Desktop Icon")
|
||||
icon.type = "workspace"
|
||||
icon.workspace = w
|
||||
icon.route = "/app/" + w.lower()
|
||||
icon.label = w
|
||||
icon.color = generate_color()
|
||||
icon.icon = frappe.db.get_value("Workspace", w, "icon")
|
||||
icon.insert(ignore_if_duplicate=True)
|
||||
if not frappe.db.exists("Desktop Icon", {"label": w}):
|
||||
icon = frappe.new_doc("Desktop Icon")
|
||||
icon.type = "Workspace"
|
||||
icon.workspace = w
|
||||
icon.route = "/app/" + w.lower()
|
||||
icon.label = w
|
||||
icon.icon_type = "Link"
|
||||
icon.standard = 1
|
||||
icon.color = generate_color()
|
||||
icon.icon = frappe.db.get_value("Workspace", w, "icon")
|
||||
icon.app = frappe.db.get_value("Workspace", w, "app")
|
||||
module = frappe.db.get_value("Workspace", w, "module")
|
||||
|
||||
icon_app = frappe.db.get_value("Module Def", module, "app_name")
|
||||
icon_app = frappe.get_hooks("app_title", app_name=icon_app)
|
||||
|
||||
parent_icon = frappe.db.exists("Desktop Icon", {"label": icon_app[0]})
|
||||
if parent_icon:
|
||||
icon.parent_icon = parent_icon
|
||||
|
||||
icon.insert(ignore_if_duplicate=True)
|
||||
|
||||
|
||||
def generate_color():
|
||||
import random
|
||||
|
||||
def hex():
|
||||
return random.randint(0, 255)
|
||||
|
||||
return "#%02X%02X%02X" % (hex(), hex(), hex())
|
||||
colors = ["orange", "pink", "blue", "green", "dark", "red", "yellow", "purple", "gray"]
|
||||
return random.choice(colors)
|
||||
|
||||
|
||||
def create_desktop_icons_from_installed_apps():
|
||||
from frappe.apps import is_desk_apps
|
||||
|
||||
apps = frappe.get_installed_apps()
|
||||
for a in apps:
|
||||
app_details = frappe.get_hooks("add_to_apps_screen", app_name=a)
|
||||
if len(app_details) != 0:
|
||||
if not is_desk_apps(app_details):
|
||||
icon = frappe.new_doc("Desktop Icon")
|
||||
icon.route = app_details[0]["route"]
|
||||
icon.label = app_details[0]["title"]
|
||||
icon.type = "link"
|
||||
icon.link = app_details[0]["route"]
|
||||
icon.logo_url = app_details[0]["logo"]
|
||||
icon.save()
|
||||
icon = frappe.new_doc("Desktop Icon")
|
||||
icon.route = app_details[0]["route"]
|
||||
icon.label = app_details[0]["title"]
|
||||
icon.type = "External"
|
||||
icon.standard = 1
|
||||
icon.icon_type = "App"
|
||||
icon.link = app_details[0]["route"]
|
||||
icon.logo_url = app_details[0]["logo"]
|
||||
icon.save()
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def set_sequence(desktop_icons):
|
||||
cnt = 1
|
||||
for item in json.loads(desktop_icons):
|
||||
frappe.db.set_value("Workspace", item.get("name"), "sequence_id", cnt)
|
||||
frappe.db.set_value("Workspace", item.get("name"), "parent_page", item.get("parent") or "")
|
||||
cnt += 1
|
||||
|
||||
frappe.clear_cache()
|
||||
frappe.toast(frappe._("Updated"))
|
||||
|
||||
|
||||
def create_desktop_icon():
|
||||
create_desktop_icons_from_installed_apps()
|
||||
frappe.db.commit()
|
||||
create_desktop_icons_from_workspace()
|
||||
frappe.db.commit()
|
||||
|
|
|
|||
|
|
@ -33,3 +33,13 @@ class WorkspaceSidebar(Document):
|
|||
def after_delete(self):
|
||||
if self.module and frappe.conf.developer_mode:
|
||||
delete_folder(self.module, "Workspace Sidebar", self.name)
|
||||
|
||||
|
||||
def create_workspace_sidebar_for_workspaces():
|
||||
all_workspaces = frappe.get_all("Workspace", pluck="name")
|
||||
existing_sidebars = frappe.get_all("Workspace Sidebar", pluck="title")
|
||||
for workspace in all_workspaces:
|
||||
if workspace not in existing_sidebars:
|
||||
sidebar = frappe.new_doc("Workspace Sidebar")
|
||||
sidebar.title = workspace
|
||||
sidebar.save()
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
:root{
|
||||
--desktop-blur: blur(10.2px);
|
||||
--desktop-modal-width: 508px;
|
||||
--desktop-modal-height: 448px;
|
||||
}
|
||||
.desktop-wrapper{
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
|
|
@ -5,6 +10,37 @@
|
|||
margin-left: auto;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.navbar-container{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: 10px 20px 10px 20px; /* Add padding if needed */
|
||||
box-sizing: border-box;
|
||||
height: 52px;
|
||||
}
|
||||
|
||||
.desktop-search-wrapper{
|
||||
flex: 1;
|
||||
max-width: 396px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#navbar-search{
|
||||
padding-left: 32px;
|
||||
}
|
||||
.desktop-search-icon{
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
.desktop-search-icon > .icon {
|
||||
stroke: var(--ink-gray-4);
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
.desktop-container{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -12,33 +48,7 @@
|
|||
flex-direction: column;
|
||||
margin-top: 20px;
|
||||
}
|
||||
#navbar-search{
|
||||
padding-left: 32px;
|
||||
}
|
||||
.desktop-search-icon{
|
||||
position: absolute;
|
||||
left: 6px;
|
||||
top: 2px;
|
||||
}
|
||||
.navbar-container{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 50%;
|
||||
justify-content: space-between;
|
||||
width: 100%; /* Allow it to span the full container */
|
||||
max-width: 600px; /* Match icon grid width if needed */
|
||||
padding: 0 15px; /* Add padding if needed */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.search-container{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.desktop-search-wrapper{
|
||||
max-width: 300px;
|
||||
margin: 0 var(--margin-md);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.icon-stroke{
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
|
|
@ -47,7 +57,8 @@
|
|||
display: grid;
|
||||
justify-items: center;
|
||||
margin-top: 50px;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
grid-template-rows: repeat(3, 1fr);
|
||||
}
|
||||
.desktop-icon{
|
||||
display: flex;
|
||||
|
|
@ -55,31 +66,49 @@
|
|||
width: 100px;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
gap: 12px;
|
||||
}
|
||||
.icon-container:has(img) {
|
||||
padding: 0;
|
||||
background-color: unset;
|
||||
}
|
||||
.icon-container img{
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
width: 54px;
|
||||
height: 54px;
|
||||
}
|
||||
.icon-container{
|
||||
padding: 10px;
|
||||
border: var(--gray-400) solid 1px;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 54px;
|
||||
height: 54px;
|
||||
background-color: var(--surface-gray-3);
|
||||
}
|
||||
.icon-container .icon{
|
||||
width: 27px;
|
||||
height: 27px;
|
||||
stroke: var(--gray-900);
|
||||
}
|
||||
.icon-container:hover{
|
||||
transform: scale(1.05);
|
||||
transition: transform 0.1s;
|
||||
}
|
||||
.icon-label{
|
||||
.icon-caption{
|
||||
text-align: center;
|
||||
text-wrap: nowrap;
|
||||
margin-top: 5px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.icon-title{
|
||||
font-weight: var(--weight-semibold);
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
.icon-subtitle{
|
||||
font-weight: var(--weight-regular);
|
||||
font-size: var(--text-xs);
|
||||
color: var(--ink-gray-5);
|
||||
}
|
||||
.timeless-style{
|
||||
width: 100vw;
|
||||
|
|
@ -95,4 +124,83 @@
|
|||
}
|
||||
.small-margin{
|
||||
margin-top: 30px;
|
||||
}
|
||||
.desktop-modal{
|
||||
backdrop-filter: var(--desktop-blur);
|
||||
display: flex !important;
|
||||
& .modal-dialog{
|
||||
& .modal-content {
|
||||
top: 120px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.desktop-modal-body {
|
||||
width: var(--desktop-modal-width);
|
||||
height: var(--desktop-modal-height);
|
||||
padding: 0px !important;
|
||||
padding-top: 23px !important;
|
||||
padding-bottom: 23px !important;
|
||||
& .icons{
|
||||
gap: 20px 0px;
|
||||
}
|
||||
}
|
||||
.modal-heading{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: var(--text-2xl);
|
||||
font-weight: var(--weight-semibold);
|
||||
color: var(--neutral-white);
|
||||
}
|
||||
.desktop-modal-heading {
|
||||
all: unset !important;
|
||||
position: absolute !important;
|
||||
top: -75px !important;
|
||||
width: 100% !important;
|
||||
& .title-section{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
& .modal-title{
|
||||
color: var(--neutral-white);
|
||||
font-size: var(--text-2xl);
|
||||
}
|
||||
}
|
||||
& .modal-actions {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
.modal-body .icons{
|
||||
margin-top: 0px;
|
||||
}
|
||||
.desktop-context-menu{
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.folder-icon{
|
||||
background-color: var(--gray-50);
|
||||
box-shadow: 0px 0px 1px 0px rgba(0, 0, 0, 0.14);
|
||||
padding: 7px;
|
||||
align-items: normal;
|
||||
|
||||
& .icons{
|
||||
gap: 2.1px;
|
||||
margin-top: 0px;
|
||||
& .desktop-icon {
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
& .icon-container{
|
||||
height: 9px;
|
||||
width: 9px;
|
||||
padding: 0px;
|
||||
border-radius: 2px;
|
||||
& .icon{
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,91 +1,41 @@
|
|||
<!-- jinja -->
|
||||
<div class="desktop-wrapper">
|
||||
{% if navbar_style == "Brand Logo" or navbar_style == "Brand Logo with Search" %}
|
||||
<header class="navbar navbar-expand" role="navigation" style="justify-content: center;">
|
||||
<div class="navbar-container">
|
||||
<a class="navbar-home" href="/app">
|
||||
<img
|
||||
class="brand-logo"
|
||||
src="{{ brand_logo }}"
|
||||
alt="{{ _("App Logo") }}"
|
||||
>
|
||||
</a>
|
||||
{% if navbar_style == "Brand Logo with Search" %}
|
||||
<div style="display: flex;">
|
||||
|
||||
<div class="search-container">
|
||||
<div class="desktop-search-wrapper text-muted">
|
||||
<input
|
||||
id="navbar-search"
|
||||
type="text"
|
||||
class="form-control"
|
||||
aria-haspopup="true"
|
||||
placeholder="Search Desktop Icons"
|
||||
>
|
||||
<span class="desktop-search-icon">
|
||||
<svg class="icon icon-sm"><use href="#icon-search"></use></svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span class="desktop-avatar" style="margin-left: -10px;">
|
||||
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if navbar_style == "Brand Logo" %}
|
||||
<span class="desktop-avatar">
|
||||
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</header>
|
||||
{% endif %}
|
||||
<div id="icon-style" style="display: none;" data-icon-style="{{ desktop_icon_style }}" data-navbar-style="{{ navbar_style }}"></div>
|
||||
<div class="desktop-container">
|
||||
{% if navbar_style == "macOS Launchpad" or navbar_style == "Timeless Launchpad" %}
|
||||
<div class="search-container">
|
||||
<div class="desktop-search-wrapper text-muted
|
||||
{% if navbar_style == 'Timeless Launchpad' %} timeless-style
|
||||
{% endif %}">
|
||||
<header class="navbar navbar-expand navbar-container" role="navigation">
|
||||
<a class="navbar-home" href="/app">
|
||||
<img
|
||||
class="brand-logo"
|
||||
src="{{ brand_logo }}"
|
||||
alt="{{ _("App Logo") }}"
|
||||
>
|
||||
</a>
|
||||
<div class="desktop-search-wrapper text-muted ">
|
||||
<input
|
||||
id="navbar-search"
|
||||
type="text"
|
||||
class="form-control"
|
||||
aria-haspopup="true"
|
||||
placeholder="Search Desktop Icons"
|
||||
placeholder="Search apps, workspaces"
|
||||
>
|
||||
<span class="desktop-search-icon">
|
||||
<svg class="icon icon-sm"><use href="#icon-search"></use></svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% elif navbar_style == "Apps with Search" %}
|
||||
<div class="search-container timeless-style apps-search">
|
||||
<h4 style="margin: 0px; font-size: 30px;">Apps</h4>
|
||||
<div class="desktop-search-wrapper text-muted">
|
||||
<input
|
||||
id="navbar-search"
|
||||
type="text"
|
||||
class="form-control"
|
||||
aria-haspopup="true"
|
||||
placeholder="Search Apps"
|
||||
>
|
||||
<span class="desktop-search-icon">
|
||||
<svg class="icon icon-sm"><use href="#icon-search"></use></svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="icons
|
||||
{% if navbar_style == 'Apps with Search' %} small-margin
|
||||
{% endif %}">
|
||||
<span class="desktop-avatar" style="margin-left: -10px;">
|
||||
|
||||
</span>
|
||||
</header>
|
||||
<div class="desktop-container">
|
||||
<!-- <div class="icons">
|
||||
{% for icon in icons %}
|
||||
<a class="desktop-icon" data-logo="{{ icon.logo_url }}" data-icon="{{ icon.icon }}" data-type="{{ icon.type }}" href="{{ icon.route }}" style="text-decoration:none">
|
||||
<div class="icon-container" data-color="{{ icon.color }}"> </div>
|
||||
<div class="icon-label">{{ icon.label }}</div>
|
||||
<a class="desktop-icon" data-logo="{{ icon.logo_url }}" data-icon="{{ icon.icon }}" data-type="{{ icon.type }}" style="text-decoration:none">
|
||||
<div class="icon-container"> </div>
|
||||
<div class="icon-caption">
|
||||
<div class="icon-title">{{ icon.label }}</div>
|
||||
<div class="icon-subtitle"></div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,18 @@
|
|||
frappe.desktop_utils = {};
|
||||
$.extend(frappe.desktop_utils, {
|
||||
modal: null,
|
||||
create_desktop_modal: function (icon, icon_title, icons_data, grid) {
|
||||
if (!this.modal) {
|
||||
this.modal = new DesktopModal(icon);
|
||||
}
|
||||
return this.modal;
|
||||
},
|
||||
close_desktop_modal: function () {
|
||||
if (this.modal) {
|
||||
this.modal.hide();
|
||||
}
|
||||
},
|
||||
});
|
||||
frappe.pages["desktop"].on_page_load = function (wrapper) {
|
||||
var page = frappe.ui.make_app_page({
|
||||
parent: wrapper,
|
||||
|
|
@ -5,125 +20,436 @@ frappe.pages["desktop"].on_page_load = function (wrapper) {
|
|||
single_column: true,
|
||||
hide_sidebar: true,
|
||||
});
|
||||
page.page_head.hide();
|
||||
$(frappe.render_template("desktop")).appendTo(page.body);
|
||||
setup();
|
||||
let desktop_page = new DesktopPage(page);
|
||||
frappe.pages["desktop"].desktop_page = desktop_page;
|
||||
// setup();
|
||||
};
|
||||
frappe.pages["desktop"].on_page_show = function (wrapper) {};
|
||||
function setup() {
|
||||
let desktop_icon_style = $("#icon-style").attr("data-icon-style");
|
||||
let navbar_style = $("#icon-style").attr("data-navbar-style");
|
||||
|
||||
$(".desktop-icon").each((i, el) => {
|
||||
let icon_name = $(el).attr("data-icon");
|
||||
let icon_container = $(el.children[0]);
|
||||
|
||||
if ($(el).attr("data-logo") != "None") {
|
||||
// create a img tag
|
||||
const logo_url = $(el).attr("data-logo");
|
||||
const $img = $("<img>").attr("src", logo_url);
|
||||
icon_container.append($img);
|
||||
icon_container.css("border", "none");
|
||||
} else {
|
||||
const svg = frappe.utils.icon(icon_name, "xl icon-stroke");
|
||||
|
||||
if (svg) {
|
||||
const $svg = $(svg);
|
||||
|
||||
// Apply stroke via CSS
|
||||
if (desktop_icon_style !== "Monochrome") {
|
||||
let bg_color, text_color;
|
||||
let color_scheme =
|
||||
frappe.palette[frappe.palette_map[icon_container.attr("data-color")]];
|
||||
if (desktop_icon_style === "Subtle") {
|
||||
bg_color = `var(${color_scheme[0]})`;
|
||||
text_color = color_scheme[1];
|
||||
} else if (desktop_icon_style === "Subtle Reverse") {
|
||||
bg_color = `var(${color_scheme[1]})`;
|
||||
text_color = color_scheme[0];
|
||||
} else if (desktop_icon_style === "Subtle Reverse w Opacity") {
|
||||
// #0289f7bd
|
||||
var style = window.getComputedStyle(document.body);
|
||||
console.log(style.getPropertyValue(color_scheme[1]));
|
||||
bg_color = style.getPropertyValue(color_scheme[1]) + "e6";
|
||||
text_color = color_scheme[0];
|
||||
}
|
||||
icon_container.css("background-color", `${bg_color}`);
|
||||
$svg.find("*").css("stroke", `var(${text_color})`);
|
||||
|
||||
// Apply to svg root
|
||||
$svg.css("stroke", `var(${bg_color})`);
|
||||
icon_container.css("border", "none");
|
||||
}
|
||||
|
||||
icon_container.append($svg);
|
||||
}
|
||||
}
|
||||
|
||||
// let color_name = icon_container.attr("data-color");
|
||||
// icon_container.css("background-color", color_name);
|
||||
function get_workspaces_from_app_name(app_name) {
|
||||
const app = frappe.boot.app_data.filter((a) => {
|
||||
return a.app_title === app_name;
|
||||
});
|
||||
setup_navbar(navbar_style);
|
||||
if (app.length > 0) return app[0].workspaces;
|
||||
}
|
||||
|
||||
function get_route(element) {
|
||||
function get_route(desktop_icon) {
|
||||
let route;
|
||||
if (element.attr("data-type") == "workspace") {
|
||||
route = window.location.origin + element.attr("data-route");
|
||||
if (!desktop_icon) return;
|
||||
let item = {};
|
||||
if (desktop_icon.type == "External" && desktop_icon.link) {
|
||||
route = window.location.origin + desktop_icon.link;
|
||||
} else {
|
||||
route = element.attr("data-route");
|
||||
if (desktop_icon.type == "Workspace") {
|
||||
item = {
|
||||
type: desktop_icon.type,
|
||||
link: frappe.router.slug(desktop_icon.workspace),
|
||||
};
|
||||
} else if (desktop_icon.type == "List") {
|
||||
item = {
|
||||
type: desktop_icon.type,
|
||||
link: desktop_icon.__doctype,
|
||||
};
|
||||
}
|
||||
route = frappe.utils.generate_route(item);
|
||||
}
|
||||
|
||||
return route;
|
||||
}
|
||||
|
||||
function setup_navbar(navbar_style) {
|
||||
if (navbar_style != "Awesomebar") {
|
||||
$(".sticky-top > .navbar").hide();
|
||||
} else {
|
||||
$(".navbar").show();
|
||||
}
|
||||
}
|
||||
|
||||
frappe.router.on("change", function () {
|
||||
let navbar_style = $("#icon-style").attr("data-navbar-style");
|
||||
if (frappe.get_route()[0] == "desktop") setup_navbar(navbar_style);
|
||||
else $(".navbar").show();
|
||||
});
|
||||
|
||||
frappe.pages["desktop"].on_page_show = function () {
|
||||
let desktop_icon_style = $("#icon-style").attr("data-icon-style");
|
||||
let navbar_style = $("#icon-style").attr("data-navbar-style");
|
||||
setup_avatar();
|
||||
if (navbar_style != "Awesomebar") {
|
||||
if (navbar_style == "macOS Launchpad")
|
||||
$(".desktop-container").css("align-items", "normal");
|
||||
setup_avatar();
|
||||
}
|
||||
setup_search();
|
||||
frappe.pages["desktop"].desktop_page.setup();
|
||||
};
|
||||
function setup_avatar() {
|
||||
$(".desktop-avatar").html(frappe.avatar(frappe.session.user, "avatar-medium"));
|
||||
}
|
||||
|
||||
function setup_search() {
|
||||
let all_icons = $(".icon-label");
|
||||
let icons_to_show = [];
|
||||
$(".desktop-search-wrapper > #navbar-search").on("input", function (e) {
|
||||
let search_query = $(e.target).val().toLowerCase();
|
||||
icons_to_show = [];
|
||||
all_icons.each(function (index, element) {
|
||||
$(element).parent().hide();
|
||||
let label = $(element).text().toLowerCase();
|
||||
if (label.includes(search_query)) {
|
||||
icons_to_show.push(element);
|
||||
}
|
||||
});
|
||||
toggle_icons(icons_to_show);
|
||||
});
|
||||
}
|
||||
|
||||
function toggle_icons(icons) {
|
||||
icons.forEach((i) => {
|
||||
$(i).parent().show();
|
||||
});
|
||||
}
|
||||
|
||||
class DesktopPage {
|
||||
constructor(page) {
|
||||
this.prepare();
|
||||
this.make(page);
|
||||
this.setup();
|
||||
}
|
||||
|
||||
prepare() {
|
||||
this.apps_icons = frappe.boot.desktop_icons.filter((c) => {
|
||||
return c.icon_type == "App" || c.icon_type == "Folder";
|
||||
});
|
||||
}
|
||||
|
||||
make(page) {
|
||||
page.page_head.hide();
|
||||
$(frappe.render_template("desktop")).appendTo(page.body);
|
||||
this.wrapper = page.body.find(".desktop-container");
|
||||
this.icon_grid = new DesktopIconGrid(this.wrapper, this.apps_icons);
|
||||
}
|
||||
|
||||
setup() {
|
||||
this.setup_avatar();
|
||||
this.setup_navbar();
|
||||
this.setup_icon_search();
|
||||
this.handke_route_change();
|
||||
}
|
||||
setup_avatar() {
|
||||
$(".desktop-avatar").html(frappe.avatar(frappe.session.user, "avatar-medium"));
|
||||
}
|
||||
setup_navbar() {
|
||||
$(".sticky-top > .navbar").hide();
|
||||
}
|
||||
|
||||
handke_route_change() {
|
||||
const me = this;
|
||||
frappe.router.on("change", function () {
|
||||
if (frappe.get_route()[0] == "desktop") me.setup_navbar();
|
||||
else {
|
||||
$(".navbar").show();
|
||||
frappe.desktop_utils.close_desktop_modal();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setup_icon_search() {
|
||||
let all_icons = $(".icon-title");
|
||||
let icons_to_show = [];
|
||||
$(".desktop-search-wrapper > #navbar-search").on("input", function (e) {
|
||||
let search_query = $(e.target).val().toLowerCase();
|
||||
console.log(search_query);
|
||||
icons_to_show = [];
|
||||
all_icons.each(function (index, element) {
|
||||
$(element).parent().hide();
|
||||
let label = $(element).text().toLowerCase();
|
||||
if (label.includes(search_query)) {
|
||||
icons_to_show.push(element);
|
||||
}
|
||||
});
|
||||
toggle_icons(icons_to_show);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class DesktopIconGrid {
|
||||
constructor(wrapper, icons_data, row_size, in_folder, in_modal, parent_icon, no_dragging) {
|
||||
this.wrapper = wrapper;
|
||||
this.icons_data = icons_data;
|
||||
this.row_size = row_size;
|
||||
this.icons = [];
|
||||
this.page_size = {
|
||||
col: 4,
|
||||
row: 3,
|
||||
total: function () {
|
||||
return this.col * this.row;
|
||||
},
|
||||
};
|
||||
this.in_folder = in_folder;
|
||||
this.in_modal = in_modal;
|
||||
this.parent_icon_obj = parent_icon;
|
||||
this.no_dragging = no_dragging;
|
||||
this.grids = [];
|
||||
this.prepare();
|
||||
this.make();
|
||||
}
|
||||
prepare() {
|
||||
this.total_pages = Math.ceil(this.icons_data.length / this.page_size.total());
|
||||
this.icons_data_by_page = this.split_data(this.icons_data, this.page_size.total());
|
||||
}
|
||||
make() {
|
||||
const me = this;
|
||||
for (let i = 0; i < this.total_pages; i++) {
|
||||
let template = `<div class="icons"></div>`;
|
||||
|
||||
if (this.row_size && this.in_modal) {
|
||||
template = `<div class="icons" style="display: none; grid-template-columns: repeat(${this.row_size}, 1fr)"></div>`;
|
||||
}
|
||||
this.grids.push($(template).appendTo(this.wrapper));
|
||||
this.make_icons(this.icons_data_by_page[i], this.grids[i]);
|
||||
if (!this.no_dragging) {
|
||||
this.setup_reordering(this.grids[i]);
|
||||
}
|
||||
this.grids[i].on("wheel", function (event) {
|
||||
if (event.originalEvent) {
|
||||
event = event.originalEvent; // for jQuery or wrapped events
|
||||
}
|
||||
|
||||
if (Math.abs(event.deltaX) > Math.abs(event.deltaY)) {
|
||||
event.preventDefault();
|
||||
if (event.deltaX > 0) {
|
||||
if (me.current_page != me.total_pages - 1) me.current_page++;
|
||||
me.change_to_page(me.current_page);
|
||||
} else {
|
||||
if (me.current_page != 0) me.current_page--;
|
||||
me.change_to_page(me.current_page);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
this.setup_pagination();
|
||||
}
|
||||
setup_pagination() {
|
||||
this.current_page = 0;
|
||||
this.change_to_page(this.current_page);
|
||||
}
|
||||
change_to_page(index) {
|
||||
this.grids.forEach((g) => $(g).css("display", "none"));
|
||||
this.grids[index].css("display", "grid");
|
||||
this.current_page = index;
|
||||
}
|
||||
split_data(icons, size) {
|
||||
const result = [];
|
||||
|
||||
for (let i = 0; i < icons.length; i += size) {
|
||||
result.push(icons.slice(i, i + size));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
make_icons(icons_data, grid) {
|
||||
icons_data.forEach((icon) => {
|
||||
let icon_html = new DesktopIcon(icon, this.in_folder).get_desktop_icon_html();
|
||||
this.icons.push(icon_html);
|
||||
grid.append(icon_html);
|
||||
});
|
||||
}
|
||||
|
||||
setup_reordering(grid) {
|
||||
const me = this;
|
||||
let sortable = new Sortable($(grid).get(0), {
|
||||
swapThreshold: 0.09,
|
||||
group: {
|
||||
name: "desktop",
|
||||
put: true,
|
||||
pull: true,
|
||||
},
|
||||
onEnd: function (evt) {
|
||||
let title = $(evt.item).find(".icon-title").text();
|
||||
if (me.parent_icon_obj) {
|
||||
let icon = me.parent_icon_obj.child_icons.findIndex((f) => f.label == title);
|
||||
me.parent_icon_obj.child_icons.splice(icon, 1);
|
||||
if (me.parent_icon_obj) me.parent_icon_obj.render_folder_thumbnail();
|
||||
}
|
||||
|
||||
// if (evt.to.parentElement.classList.contains("folder-icon")) {
|
||||
// // open the folder
|
||||
|
||||
// }
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
class DesktopIcon {
|
||||
constructor(icon, in_folder) {
|
||||
this.icon_data = icon;
|
||||
this.icon_title = this.icon_data.label;
|
||||
this.icon_subtitle = "";
|
||||
this.icon_type = this.icon_data.icon_type;
|
||||
this.in_folder = in_folder;
|
||||
this.type = this.icon_data.type;
|
||||
if (this.icon_type != "Folder") {
|
||||
this.icon_route = get_route(this.get_desktop_icon(this.icon_title));
|
||||
}
|
||||
this.icon = $(
|
||||
frappe.render_template("desktop_icon", { icon: this.icon_data, in_folder: in_folder })
|
||||
);
|
||||
this.icon_caption_area = $(this.icon.get(0).children[1]);
|
||||
this.child_icons = this.get_child_icons_data();
|
||||
// this.child_icons = this.get_desktop_icon(this.icon_title).child_icons;
|
||||
// this.child_icons_data = this.get_child_icons_data();
|
||||
this.parent_icon = this.icon_data.icon;
|
||||
this.setup_click();
|
||||
this.setup_context_menu();
|
||||
this.render_folder_thumbnail();
|
||||
this.setup_dragging();
|
||||
this.child_icons = this.get_child_icons_data();
|
||||
}
|
||||
get_child_icons_data() {
|
||||
return frappe.boot.desktop_icons.filter((f) => {
|
||||
return f.parent_icon == this.icon_title;
|
||||
});
|
||||
}
|
||||
get_desktop_icon_html() {
|
||||
return this.icon;
|
||||
}
|
||||
get_desktop_icon(icon_label) {
|
||||
return frappe.boot.desktop_icons.find((d) => {
|
||||
return d.label == icon_label;
|
||||
});
|
||||
}
|
||||
setup_click() {
|
||||
const me = this;
|
||||
if (this.child_icons.length && (this.icon_type == "App" || this.icon_type == "Folder")) {
|
||||
$(this.icon).on("click", () => {
|
||||
let modal = frappe.desktop_utils.create_desktop_modal(me);
|
||||
modal.setup(me.icon_title, me.child_icons, 4);
|
||||
modal.show();
|
||||
});
|
||||
$($(this.icon_caption_area).children()[1]).html(
|
||||
`${this.child_icons.length} Workspaces`
|
||||
);
|
||||
} else {
|
||||
this.icon.attr("href", this.icon_route);
|
||||
}
|
||||
}
|
||||
|
||||
setup_context_menu() {
|
||||
const me = this;
|
||||
this.context_menu_items = {
|
||||
icon: [
|
||||
{
|
||||
label: "Add Children Icon",
|
||||
icon: "add",
|
||||
onClick: function () {
|
||||
console.log("Open folder modal");
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
this.context_menu = new ContextMenu(this.context_menu_items["icon"]);
|
||||
$(this.icon).on("contextmenu", function (event) {
|
||||
event.preventDefault();
|
||||
me.context_menu.show(event);
|
||||
});
|
||||
}
|
||||
|
||||
render_folder_thumbnail() {
|
||||
if (this.icon_type == "Folder") {
|
||||
if (!this.folder_wrapper) this.folder_wrapper = this.icon.find(".icon-container");
|
||||
this.folder_wrapper.html("");
|
||||
this.folder_grid = new DesktopIconGrid(
|
||||
this.folder_wrapper,
|
||||
this.child_icons,
|
||||
4,
|
||||
true,
|
||||
true,
|
||||
null,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
setup_dragging() {
|
||||
this.icon.on("drag", (event) => {
|
||||
const mouse_x = event.clientX;
|
||||
const mouse_y = event.clientY;
|
||||
if (frappe.desktop_utils.modal) {
|
||||
let modal = frappe.desktop_utils.modal.modal
|
||||
.find(".modal-content")
|
||||
.get(0)
|
||||
.getBoundingClientRect();
|
||||
if (
|
||||
mouse_x > modal.right ||
|
||||
mouse_x < modal.left ||
|
||||
mouse_y > modal.bottom ||
|
||||
mouse_y < modal.top
|
||||
) {
|
||||
frappe.desktop_utils.close_desktop_modal();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class DesktopModal {
|
||||
constructor(icon) {
|
||||
this.parent_icon_obj = icon;
|
||||
}
|
||||
setup(icon_title, child_icons_data, grid_row_size) {
|
||||
const me = this;
|
||||
this.modal = new frappe.get_modal(icon_title, "");
|
||||
this.modal.find(".modal-header").addClass("desktop-modal-heading");
|
||||
this.modal.addClass("desktop-modal");
|
||||
this.modal.attr("draggable", true);
|
||||
this.modal.find(".modal-body").addClass("desktop-modal-body");
|
||||
this.$child_icons_wrapper = this.modal.find(".desktop-modal-body");
|
||||
|
||||
this.child_icon_grid = new DesktopIconGrid(
|
||||
this.$child_icons_wrapper,
|
||||
child_icons_data,
|
||||
grid_row_size,
|
||||
false,
|
||||
true,
|
||||
this.parent_icon_obj
|
||||
);
|
||||
|
||||
this.modal.on("hidden.bs.modal", function () {
|
||||
me.modal.remove();
|
||||
});
|
||||
}
|
||||
show() {
|
||||
this.modal.modal("show");
|
||||
}
|
||||
hide() {
|
||||
this.modal.modal("hide");
|
||||
}
|
||||
}
|
||||
class ContextMenu {
|
||||
constructor(menu_items) {
|
||||
this.template = $(`<div class="dropdown-menu desktop-context-menu" role="menu"></div>`);
|
||||
this.menu_items = menu_items;
|
||||
this.make();
|
||||
}
|
||||
make() {
|
||||
this.template.appendTo(document.body);
|
||||
this.menu_items.forEach((f) => {
|
||||
this.add_menu_item(f);
|
||||
});
|
||||
}
|
||||
add_menu_item(item) {
|
||||
$(`<div class="dropdown-menu-item">
|
||||
<a>
|
||||
<div class="sidebar-item-icon">
|
||||
${
|
||||
item.icon
|
||||
? frappe.utils.icon(item.icon)
|
||||
: `<img
|
||||
class="logo"
|
||||
src="${item.icon_url}"
|
||||
>`
|
||||
}
|
||||
</div>
|
||||
<span class="menu-item-title">${item.label}</span>
|
||||
</a>
|
||||
</div>`)
|
||||
.on("click", function () {
|
||||
item.onClick();
|
||||
this.template;
|
||||
})
|
||||
.appendTo(this.template);
|
||||
}
|
||||
show(event) {
|
||||
this.top = this.mouseY(event) + "px";
|
||||
this.left = this.mouseX(event) + "px";
|
||||
this.template.css("display", "block");
|
||||
this.template.css("top", this.top);
|
||||
this.template.css("left", this.left);
|
||||
}
|
||||
mouseX(evt) {
|
||||
if (evt.pageX) {
|
||||
return evt.pageX;
|
||||
} else if (evt.clientX) {
|
||||
return (
|
||||
evt.clientX +
|
||||
(document.documentElement.scrollLeft
|
||||
? document.documentElement.scrollLeft
|
||||
: document.body.scrollLeft)
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
mouseY(evt) {
|
||||
if (evt.pageY) {
|
||||
return evt.pageY;
|
||||
} else if (evt.clientY) {
|
||||
return (
|
||||
evt.clientY +
|
||||
(document.documentElement.scrollTop
|
||||
? document.documentElement.scrollTop
|
||||
: document.body.scrollTop)
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
"doctype": "Page",
|
||||
"icon": "",
|
||||
"idx": 0,
|
||||
"modified": "2025-08-18 16:17:19.559412",
|
||||
"modified": "2025-10-08 13:31:06.525425",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "desktop",
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
"page_name": "desktop",
|
||||
"roles": [
|
||||
{
|
||||
"role": "System Manager"
|
||||
"role": "All"
|
||||
}
|
||||
],
|
||||
"script": null,
|
||||
|
|
|
|||
|
|
@ -6,9 +6,6 @@ def get_context(context):
|
|||
if frappe.session.user == "Guest":
|
||||
frappe.local.flags.redirect_location = "/app"
|
||||
raise frappe.Redirect
|
||||
context.desktop_icon_style = frappe.get_single_value("Desktop Settings", "icon_style")
|
||||
context.navbar_style = frappe.get_single_value("Desktop Settings", "navbar_style")
|
||||
context.brand_logo = frappe.get_single_value("Navbar Settings", "app_logo")
|
||||
context.current_user = frappe.session.user
|
||||
context.icons = get_desktop_icons()
|
||||
return context
|
||||
|
|
|
|||
|
|
@ -416,6 +416,7 @@ ignore_links_on_delete = [
|
|||
"Route History",
|
||||
"Access Log",
|
||||
"Permission Log",
|
||||
"Desktop Icon",
|
||||
]
|
||||
|
||||
# Request Hooks
|
||||
|
|
@ -581,3 +582,13 @@ user_invitation = {
|
|||
"System Manager": [],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
add_to_apps_screen = [
|
||||
{
|
||||
"name": app_name,
|
||||
"logo": app_logo_url,
|
||||
"title": app_title,
|
||||
"route": app_home,
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ from semantic_version import Version
|
|||
|
||||
import frappe
|
||||
from frappe.defaults import _clear_cache
|
||||
from frappe.desk.doctype.desktop_icon.desktop_icon import sync_desktop_icons
|
||||
from frappe.desk.doctype.desktop_icon.desktop_icon import create_desktop_icon, sync_desktop_icons
|
||||
from frappe.utils import cint, is_git_url
|
||||
from frappe.utils.dashboard import sync_dashboards
|
||||
from frappe.utils.synchronization import filelock
|
||||
|
|
|
|||
|
|
@ -109,3 +109,4 @@ import "./frappe/ui/driver.js";
|
|||
import "./frappe/scanner";
|
||||
|
||||
import "./frappe/ui/address_autocomplete/autocomplete_dialog.js";
|
||||
import "./frappe/ui/desktop_icon.html";
|
||||
|
|
|
|||
19
frappe/public/js/frappe/ui/desktop_icon.html
Normal file
19
frappe/public/js/frappe/ui/desktop_icon.html
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<a class="desktop-icon" data-logo="{{ icon.logo_url }}" data-icon="{{ icon.icon }}" data-type="{{ icon.type }}" style="text-decoration:none">
|
||||
{% if (icon.logo_url) { %}
|
||||
<div class="icon-container"> <img src="{{ icon.logo_url }}" alt="{{ icon.label }}" /></div>
|
||||
{% } else if (icon.icon_type == "Folder") { %}
|
||||
<div class="icon-container folder-icon">
|
||||
|
||||
</div>
|
||||
{% } else { %}
|
||||
<div class="icon-container">
|
||||
{%= frappe.utils.icon(icon.icon || "list-alt" , "lg", "", "", "text-ink-gray-7 current-color", true)%}
|
||||
</div>
|
||||
{% } %}
|
||||
{% if (!in_folder) { %}
|
||||
<div class="icon-caption">
|
||||
<div class="icon-title">{{ icon.label }}</div>
|
||||
<div class="icon-subtitle"></div>
|
||||
</div>
|
||||
{% } %}
|
||||
</a>
|
||||
|
|
@ -35,7 +35,7 @@ frappe.ui.Sidebar = class Sidebar {
|
|||
}
|
||||
|
||||
setup(workspace_title) {
|
||||
this.workspace_title = frappe.utils.to_title_case(workspace_title);
|
||||
this.workspace_title = workspace_title;
|
||||
this.sidebar_header = new frappe.ui.SidebarHeader(this);
|
||||
this.make_sidebar();
|
||||
this.setup_complete = true;
|
||||
|
|
@ -545,8 +545,6 @@ frappe.ui.Sidebar = class Sidebar {
|
|||
} else {
|
||||
frappe.app.sidebar.setup(sidebars[0] || "Build");
|
||||
}
|
||||
} else {
|
||||
this.set_sidebar_for_page();
|
||||
}
|
||||
|
||||
this.set_active_workspace_item();
|
||||
|
|
|
|||
|
|
@ -74,10 +74,6 @@ frappe.ui.SidebarHeader = class SidebarHeader {
|
|||
);
|
||||
if (icon.length > 0) {
|
||||
this.header_icon = icon[0].icon;
|
||||
this.header_logo_color = icon[0].color;
|
||||
this.header_bg_color = frappe.palette[frappe.palette_map[this.header_logo_color]][0];
|
||||
this.header_stroke_color =
|
||||
frappe.palette[frappe.palette_map[this.header_logo_color]][1];
|
||||
}
|
||||
}
|
||||
setup_app_switcher() {
|
||||
|
|
|
|||
|
|
@ -202,4 +202,7 @@ $light-yellow: #fef4e2;
|
|||
rgba(205, 41, 41, 0.804) 99.87%
|
||||
);
|
||||
--angular-blue: conic-gradient(rgba(0, 110, 219, 0) 72.38%, rgba(0, 110, 219, 0) 99.87%);
|
||||
|
||||
// Surfaces
|
||||
--surface-gray-3: #ededed;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue