feat(design): espresso style sidebar

This commit is contained in:
Rushabh Mehta 2024-08-22 17:16:51 +05:30
parent a162e61e26
commit 087ee41ea1
38 changed files with 832 additions and 1453 deletions

View file

@ -143,7 +143,7 @@ def load_conf_settings(bootinfo):
def load_desktop_data(bootinfo):
from frappe.desk.desktop import get_workspace_sidebar_items
bootinfo.allowed_workspaces = get_workspace_sidebar_items().get("pages")
bootinfo.sidebar_pages = get_workspace_sidebar_items()
bootinfo.module_wise_workspaces = get_controller("Workspace").get_module_wise_workspaces()
bootinfo.dashboards = frappe.get_all("Dashboard")

View file

@ -341,32 +341,6 @@ def update_page(name, title, icon, indicator_color, parent, public):
return {"name": title, "public": public, "label": new_name}
def hide_unhide_page(page_name: str, is_hidden: bool):
page = frappe.get_doc("Workspace", page_name)
if page.get("public") and not is_workspace_manager():
frappe.throw(
_("Need Workspace Manager role to hide/unhide public workspaces"), frappe.PermissionError
)
if not page.get("public") and page.get("for_user") != frappe.session.user and not is_workspace_manager():
frappe.throw(_("Cannot update private workspace of other users"), frappe.PermissionError)
page.is_hidden = int(is_hidden)
page.save(ignore_permissions=True)
return True
@frappe.whitelist()
def hide_page(page_name: str):
return hide_unhide_page(page_name, 1)
@frappe.whitelist()
def unhide_page(page_name: str):
return hide_unhide_page(page_name, 0)
@frappe.whitelist()
def duplicate_page(page_name, new_page):
if not loads(new_page):
@ -426,40 +400,6 @@ def delete_page(page):
return {"name": page.get("name"), "public": page.get("public"), "title": page.get("title")}
@frappe.whitelist()
def sort_pages(sb_public_items, sb_private_items):
if not loads(sb_public_items) and not loads(sb_private_items):
return
sb_public_items = loads(sb_public_items)
sb_private_items = loads(sb_private_items)
workspace_public_pages = get_page_list(["name", "title"], {"public": 1})
workspace_private_pages = get_page_list(["name", "title"], {"for_user": frappe.session.user})
if sb_private_items:
return sort_page(workspace_private_pages, sb_private_items)
if sb_public_items and is_workspace_manager():
return sort_page(workspace_public_pages, sb_public_items)
return False
def sort_page(workspace_pages, pages):
for seq, d in enumerate(pages):
for page in workspace_pages:
if page.title == d.get("title"):
doc = frappe.get_doc("Workspace", page.name)
doc.sequence_id = seq + 1
doc.parent_page = d.get("parent_page") or ""
doc.flags.ignore_links = True
doc.save(ignore_permissions=True)
break
return True
def last_sequence_id(doc):
doc_exists = frappe.db.exists({"doctype": "Workspace", "public": doc.public, "for_user": doc.for_user})

View file

@ -5,33 +5,35 @@ frappe.ui.form.on("Workspace Settings", {
setup(frm) {
frm.hide_full_form_button = true;
frm.docfields = [];
frm.workspace_map = {};
let workspace_visibilty = JSON.parse(frm.doc.workspace_visibility_json || "{}");
// build fields from workspaces
let cnt = 0,
column_added = false;
for (let w of frappe.boot.allowed_workspaces) {
if (w.public) {
for (let page of frappe.boot.allowed_workspaces) {
if (page.public) {
frm.workspace_map[page.name] = page;
cnt++;
frm.docfields.push({
fieldtype: "Check",
fieldname: w.name,
label: w.title,
initial_value: workspace_visibilty[w.name] !== 0, // not set is also visible
fieldname: page.name,
label: page.title + (page.parent_page ? ` (${page.parent_page})` : ""),
initial_value: workspace_visibilty[page.name] !== 0, // not set is also visible
});
}
if (cnt >= frappe.boot.allowed_workspaces.length / 2 && !column_added) {
// add column break to split into 2 columns
frm.docfields.push({ fieldtype: "Column Break" });
column_added = true;
}
}
frappe.temp = frm;
},
validate(frm) {
frm.doc.workspace_visibility_json = JSON.stringify(frm.dialog.get_values());
frm.doc.workspace_sequence = JSON.stringify(
frm.wrapper
.find(".frappe-control")
.get()
.map((e) => e.fieldobj.df.fieldname)
);
frm.doc.workspace_setup_completed = 1;
},
after_save(frm) {
@ -39,6 +41,50 @@ frappe.ui.form.on("Workspace Settings", {
window.location.reload();
},
refresh(frm) {
frm.dialog.set_alert(__("Select modules you want to see in the sidebar"));
let get_page = (e) => frm.workspace_map[e.fieldobj.df.fieldname];
frm.dialog.set_alert(__("Select, sort modules you want to see in the sidebar"));
if (!frm.workspace_sortable) {
frm.wrapper.find(".frappe-control").css({ "margin-bottom": "0.5rem" });
let forms = frm.wrapper.find("form");
frm.workspace_sortable = Sortable.create(forms.get(0), {
group: "workspace_settings",
animation: 150,
onEnd: (o) => {
// re-order so that child items are below parent items
for (let e of frm.wrapper.find(".frappe-control").get()) {
let page = get_page(e);
if (page.parent_page) {
// insert as the last child of the parent element
let parent_element = frm.wrapper
.find(`[data-fieldname="${page.parent_page}"`)
.closest(".frappe-control");
let parent_page = page.parent_page;
// find the last child
while (parent_element) {
let next_element = parent_element.next(".frappe-control");
if (!next_element.length) {
// end of list
$(e).insertAfter(parent_element);
break;
} else {
let page = get_page(next_element.get(0));
if (page.parent_page != parent_page) {
// different parent, last child found
$(e).insertAfter(parent_element);
break;
}
}
parent_element = next_element;
}
}
}
},
});
}
},
});

View file

@ -1,7 +1,9 @@
# Copyright (c) 2024, Frappe Technologies and contributors
# For license information, please see license.txt
# import frappe
import json
import frappe
from frappe.model.document import Document
@ -19,3 +21,12 @@ class WorkspaceSettings(Document):
# end: auto-generated types
pass
def validate(self):
cnt = 1
for page_name in json.loads(self.workspace_sequence):
frappe.db.set_value("Workspace", page_name, "sequence_id", cnt)
cnt += 1
def on_update(self):
frappe.clear_cache()

View file

@ -37,6 +37,7 @@ frappe.Application = class Application {
this.load_bootinfo();
this.load_user_permissions();
this.make_nav_bar();
this.make_sidebar();
this.set_favicon();
this.set_fullwidth_if_enabled();
this.add_browser_class();
@ -46,6 +47,51 @@ frappe.Application = class Application {
frappe.ui.keys.setup();
this.setup_theme();
// page container
this.make_page_container();
this.setup_tours();
this.set_route();
// trigger app startup
$(document).trigger("startup");
$(document).trigger("app_ready");
this.show_notices();
this.show_notes();
if (frappe.ui.startup_setup_dialog && !frappe.boot.setup_complete) {
frappe.ui.startup_setup_dialog.pre_show();
frappe.ui.startup_setup_dialog.show();
}
// listen to build errors
this.setup_build_events();
if (frappe.sys_defaults.email_user_password) {
var email_list = frappe.sys_defaults.email_user_password.split(",");
for (var u in email_list) {
if (email_list[u] === frappe.user.name) {
this.set_password(email_list[u]);
}
}
}
// REDESIGN-TODO: Fix preview popovers
this.link_preview = new frappe.ui.LinkPreview();
frappe.broadcast.emit("boot", {
csrf_token: frappe.csrf_token,
user: frappe.session.user,
});
}
make_sidebar() {
this.sidebar = new frappe.ui.Sidebar({});
}
setup_theme() {
frappe.ui.keys.add_shortcut({
shortcut: "shift+ctrl+g",
description: __("Switch Theme"),
@ -71,9 +117,9 @@ frappe.Application = class Application {
});
frappe.ui.set_theme();
}
// page container
this.make_page_container();
setup_tours() {
if (
!window.Cypress &&
frappe.boot.onboarding_tours &&
@ -90,13 +136,9 @@ frappe.Application = class Application {
});
}
}
this.set_route();
// trigger app startup
$(document).trigger("startup");
$(document).trigger("app_ready");
}
show_notices() {
if (frappe.boot.messages) {
frappe.msgprint(frappe.boot.messages);
}
@ -116,13 +158,6 @@ frappe.Application = class Application {
console.log(`%c${console_security_message}`, "font-size: large");
}
this.show_notes();
if (frappe.ui.startup_setup_dialog && !frappe.boot.setup_complete) {
frappe.ui.startup_setup_dialog.pre_show();
frappe.ui.startup_setup_dialog.show();
}
frappe.realtime.on("version-update", function () {
var dialog = frappe.msgprint({
message: __(
@ -136,26 +171,6 @@ frappe.Application = class Application {
});
dialog.get_close_btn().toggle(false);
});
// listen to build errors
this.setup_build_events();
if (frappe.sys_defaults.email_user_password) {
var email_list = frappe.sys_defaults.email_user_password.split(",");
for (var u in email_list) {
if (email_list[u] === frappe.user.name) {
this.set_password(email_list[u]);
}
}
}
// REDESIGN-TODO: Fix preview popovers
this.link_preview = new frappe.ui.LinkPreview();
frappe.broadcast.emit("boot", {
csrf_token: frappe.csrf_token,
user: frappe.session.user,
});
}
set_route() {
@ -275,6 +290,8 @@ frappe.Application = class Application {
setup_workspaces() {
frappe.modules = {};
frappe.workspaces = {};
frappe.boot.allowed_workspaces = frappe.boot.sidebar_pages.pages;
for (let page of frappe.boot.allowed_workspaces || []) {
frappe.modules[page.module] = page;
frappe.workspaces[frappe.router.slug(page.name)] = page;

View file

@ -503,7 +503,7 @@ frappe.ui.form.Layout = class Layout {
let tabs_content = this.tabs_content[0];
if (!tabs_list.length) return;
$(window).scroll(
$(".main-section").scroll(
frappe.utils.throttle(() => {
let current_scroll = document.documentElement.scrollTop;
if (current_scroll > 0 && last_scroll <= current_scroll) {

View file

@ -124,7 +124,7 @@ frappe.ui.form.Attachments = class Attachments {
let file_label = `
<a href="${file_url}" target="_blank" title="${frappe.utils.escape_html(file_name)}"
class="ellipsis" style="max-width: calc(100% - 43px);"
class="ellipsis attachment-file-label"
>
<span>${file_name}</span>
</a>`;
@ -151,7 +151,7 @@ frappe.ui.form.Attachments = class Attachments {
${frappe.utils.icon(attachment.is_private ? "es-line-lock" : "es-line-unlock", "sm ml-0")}
</a>`;
$(`<li class="attachment-row">`)
$(`<div class="attachment-row"></div>`)
.append(frappe.get_data_pill(file_label, fileid, remove_action, icon))
.insertAfter(this.add_attachment_wrapper);
}

View file

@ -145,6 +145,7 @@ frappe.ui.form.Sidebar = class {
callback: function (res) {
me.sidebar
.find(".auto-repeat-status")
.removeClass("hidden")
.html(__("Repeats {0}", [__(res.message.frequency)]));
me.sidebar.find(".auto-repeat-status").on("click", function () {
frappe.set_route("Form", "Auto Repeat", me.frm.doc.auto_repeat);

View file

@ -1,6 +1,6 @@
<ul class="list-unstyled sidebar-menu user-actions hidden"></ul>
<ul class="list-unstyled sidebar-menu sidebar-image-section hide">
<li class="sidebar-image-wrapper">
<div class="sidebar-section user-actions hidden"></div>
<div class="sidebar-section sidebar-image-section hide">
<div class="sidebar-image-wrapper">
<img class="sidebar-image">
<div class="sidebar-standard-image">
<div class="standard-image"></div>
@ -16,8 +16,8 @@
</div>
</div>
{% endif %}
</li>
</ul>
</div>
</div>
{% if frm.meta.beta %}
<div class="sidebar-menu">
<p><label class="indicator-pill yellow" title="{{ __("This feature is brand new and still experimental") }}">{{ __("Experimental") }}</label></p>
@ -27,21 +27,23 @@
</div>
{% endif %}
<ul class="list-unstyled sidebar-menu sidebar-rating hide">
<li style="position: relative;">
<div class="sidebar-section sidebar-rating hide">
<div style="position: relative;">
<a class="strong badge-hover">
<span>{%= __("Feedback") %}</span>
</a>
</li>
<li class="rating-icons"></li>
</ul>
<ul class="list-unstyled sidebar-menu hidden">
</div>
<div class="rating-icons"></div>
</div>
<div class="sidebar-section hidden">
{% if (frappe.help.has_help(doctype)) { %}
<li><a class="help-link list-link" data-doctype="{{ doctype }}">{{ __("Help") }}</a></li>
<div>
<a class="help-link list-link" data-doctype="{{ doctype }}">{{ __("Help") }}</a>
</div>
{% } %}
</ul>
<ul class="list-unstyled sidebar-menu form-assignments">
<li>
</div>
<div class="sidebar-section form-assignments">
<div>
<span class="form-sidebar-items">
<span class="add-assignment-label">
<svg class="es-icon ml-0 icon-sm"><use href="#es-line-add-people"></use></svg>
@ -52,10 +54,10 @@
</button>
</span>
<div class="assignments"></div>
</li>
</ul>
<ul class="list-unstyled sidebar-menu form-attachments">
<li class="attachments-actions">
</div>
</div>
<div class="sidebar-section form-attachments">
<div class="attachments-actions">
<span class="form-sidebar-items">
<span>
<svg class="es-icon ml-0 icon-sm">
@ -69,7 +71,7 @@
<svg class="es-icon icon-sm"><use href="#es-line-add"></use></svg>
</button>
</span>
</li>
</div>
<a class="show-all-btn hidden" href="">
<svg class="es-icon icon-sm">
<use href="#es-line-preview"></use>
@ -78,32 +80,32 @@
{%= __("Show All") %}
</span>
</a>
</ul>
<ul class="list-unstyled sidebar-menu form-reviews">
<li class="reviews">
<span class="form-sidebar-items">
<span>
<svg class="es-icon ml-0 icon-sm"><use href="#es-line-star"></use></svg>
<span class="ellipsis">{%= __("Reviews") %}</span>
</span>
<button class="add-review-btn btn btn-link icon-btn">
<svg class="es-icon icon-sm"><use href="#es-line-add"></use></svg>
</button>
</div>
<div class="sidebar-section form-reviews">
<div class="reviews">
<span class="form-sidebar-items">
<span>
<svg class="es-icon ml-0 icon-sm"><use href="#es-line-star"></use></svg>
<span class="ellipsis">{%= __("Reviews") %}</span>
</span>
</li>
</ul>
<ul class="list-unstyled sidebar-menu form-tags">
<li>
<span class="form-sidebar-items">
<span>
<svg class="es-icon ml-0 icon-sm"><use href="#es-line-tag"></use></svg>
<span class="tags-label ellipsis">{%= __("Tags") %}</span>
</span>
</span>
</li>
</ul>
<ul class="list-unstyled sidebar-menu form-shared">
<li>
<button class="add-review-btn btn btn-link icon-btn">
<svg class="es-icon icon-sm"><use href="#es-line-add"></use></svg>
</button>
</span>
</div>
</div>
<div class="sidebar-section form-tags">
<div>
<span class="form-sidebar-items">
<div>
<svg class="es-icon ml-0 icon-sm"><use href="#es-line-tag"></use></svg>
<span class="tags-label ellipsis">{%= __("Tags") %}</span>
</div>
</span>
</div>
</div>
<div class="sidebar-section form-shared">
<div>
<span class="form-sidebar-items">
<span class="share-label">
<svg class="es-icon ml-0 icon-sm"><use href="#es-line-add-people"></use></svg>
@ -114,34 +116,33 @@
</button>
</span>
<div class="shares"></div>
</li>
</ul>
<ul class="list-unstyled sidebar-menu followed-by-section hidden">
<li class="sidebar-label followed-by-label">
</div>
</div>
<div class="sidebar-section followed-by-section hidden">
<div class="sidebar-label followed-by-label">
<svg class="icon icon-sm">
<use href="#icon-link-url" class="like-icon"></use>
</svg>
{%= __("Followed by") %}
</li>
<li class="followed-by"></li>
<li class="document-follow">
</div>
<div class="followed-by"></div>
<div class="document-follow">
<a class="badge-hover follow-document-link hidden">
{%= __("Follow") %}
</a>
<a class="badge-hover unfollow-document-link hidden">
{%= __("Unfollow") %}
</a>
</li>
</ul>
<ul class="list-unstyled sidebar-menu">
<a><li class="auto-repeat-status"><li></a>
</ul>
<ul class="list-unstyled sidebar-menu text-muted">
<li class="pageview-count"></li>
<hr>
<li class="created-modified-section">
</li>
</ul>
</div>
</div>
<div class="sidebar-section hidden">
<a><span class="auto-repeat-status"><span></a>
</div>
<div class="sidebar-section text-muted">
<div class="pageview-count"></div>
<div class="created-modified-section">
</div>
</div>
{% if(frappe.get_form_sidebar_extension) { %}
{{ frappe.get_form_sidebar_extension() }}
{% } %}

View file

@ -169,7 +169,7 @@ frappe.views.BaseList = class BaseList {
setup_page() {
this.page = this.parent.page;
this.$page = $(this.parent);
!this.hide_card_layout && this.page.main.addClass("frappe-card");
this.page.main.addClass("layout-main-list");
this.page.page_form.removeClass("row").addClass("flex");
this.hide_page_form && this.page.page_form.hide();
this.hide_sidebar && this.$page.addClass("no-list-sidebar");

View file

@ -13,10 +13,10 @@ export default class ListFilter {
make() {
// init dom
this.wrapper.html(`
<li class="input-area"></li>
<li class="sidebar-action">
<div class="input-area"></div>
<div class="sidebar-action">
<a class="saved-filters-preview">${__("Show Saved")}</a>
</li>
</div>
<div class="saved-filters"></div>
`);

View file

@ -1,91 +1,91 @@
<ul class="list-unstyled sidebar-menu user-actions hide">
<li class="divider"></li>
</ul>
<ul class="list-unstyled sidebar-menu">
<div class="sidebar-section views-section hide">
<li class="sidebar-label">
</li>
<div class="current-view">
<li class="list-link">
<a class="btn btn-default btn-sm list-sidebar-button"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
href="#"
>
<span class="selected-view ellipsis">
</span>
<span>
<svg class="icon icon-xs">
<use href="#icon-select"></use>
</svg>
</span>
</a>
<ul class="dropdown-menu views-dropdown" role="menu">
</ul>
</li>
<li class="sidebar-action">
<a class="view-action"></a>
</li>
<div class="sidebar-section user-actions hide">
</div>
<div class="sidebar-section views-section hide">
<div class="sidebar-label">
</div>
<div class="current-view">
<div class="list-link">
<a class="btn btn-default btn-sm list-sidebar-button"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
href="#"
>
<span class="selected-view ellipsis">
</span>
<span>
<svg class="icon icon-xs">
<use href="#icon-select"></use>
</svg>
</span>
</a>
<ul class="dropdown-menu views-dropdown" role="menu">
</ul>
</div>
<div class="sidebar-action">
<a class="view-action"></a>
</div>
</div>
</div>
<div class="sidebar-section filter-section">
<li class="sidebar-label">
{{ __("Filter By") }}
</li>
<div class="list-group-by">
</div>
<div class="sidebar-section filter-section">
<div class="sidebar-label">
<svg class="es-icon es-line icon-xs" aria-hidden="true">
<use class="" href="#es-line-down"></use>
</svg>
{{ __("Filter By") }}
</div>
<div class="sidebar-section tags-section">
<li class="sidebar-label">
<svg class="es-icon es-line icon-xs" aria-hidden="true">
<use class="" href="#es-line-right-chevron"></use>
</svg>
<span>{{ __("Tags") }}</span>
</li>
<div class="list-group-by">
</div>
</div>
<div class="list-tags hide">
<li class="list-stats list-link">
<a
class="btn btn-default btn-sm list-sidebar-button"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
href="#"
>
<span>{{ __("Tags") }}</span>
<span>
<svg class="icon icon-xs">
<use href="#icon-select"></use>
</svg>
</span>
</a>
<ul class="dropdown-menu list-stats-dropdown" role="menu">
<div class="dropdown-search">
<input type="text" placeholder={{__("Search") }} data-element="search" class="form-control input-xs">
</div>
<div class="stat-result">
</div>
</ul>
</li>
<li class="sidebar-label">
<li class="sidebar-action show-tags">
<a class="list-tag-preview">{{ __("Show Tags") }}</a>
</li>
</li>
</div>
<div class="sidebar-section tags-section">
<div class="sidebar-label">
<svg class="es-icon es-line icon-xs" aria-hidden="true">
<use class="" href="#es-line-right-chevron"></use>
</svg>
<span>{{ __("Tags") }}</span>
</div>
<div class="sidebar-section save-filter-section">
<li class="sidebar-label">
<svg class="es-icon es-line icon-xs" aria-hidden="true">
<use class="" href="#es-line-right-chevron"></use>
</svg>
<span>{{ __("Saved Filters") }}</span>
</li>
<li class="list-filters list-link hide"></li>
</ul>
<div class="list-tags hide">
<div class="list-stats list-link">
<a
class="btn btn-default btn-sm list-sidebar-button"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
href="#"
>
<span>{{ __("Tags") }}</span>
<span>
<svg class="icon icon-xs">
<use href="#icon-select"></use>
</svg>
</span>
</a>
<ul class="dropdown-menu list-stats-dropdown" role="menu">
<div class="dropdown-search">
<input type="text" placeholder={{__("Search") }} data-element="search" class="form-control input-xs">
</div>
<div class="stat-result">
</div>
</ul>
</div>
<div class="sidebar-label">
<div class="sidebar-action show-tags">
<a class="list-tag-preview">{{ __("Show Tags") }}</a>
</div>
</div>
</div>
</div>
<div class="sidebar-section save-filter-section">
<div class="sidebar-label">
<svg class="es-icon es-line icon-xs" aria-hidden="true">
<use class="" href="#es-line-right-chevron"></use>
</svg>
<span>{{ __("Saved Filters") }}</span>
</div>
<div class="list-filters list-link hide"></div>
</div>

View file

@ -174,6 +174,7 @@ frappe.views.ListSidebar = class ListSidebar {
let sections = [
["tags-section", "list-tags"],
["save-filter-section", "list-filters"],
["filter-section", "list-group-by"],
];
for (let s of sections) {
@ -237,11 +238,11 @@ frappe.views.ListSidebar = class ListSidebar {
}
set_loading_state(dropdown) {
dropdown.html(`<li>
dropdown.html(`<div>
<div class="empty-state">
${__("Loading...")}
</div>
</li>`);
</div>`);
}
render_stat(stats) {

View file

@ -55,11 +55,11 @@ frappe.views.ListGroupBy = class ListGroupBy {
let html = `
<div class="list-group-by-fields">
</div>
<li class="add-list-group-by sidebar-action">
<div class="add-list-group-by sidebar-action">
<a class="add-group-by">
${__("Edit Filters")}
</a>
</li>
</div>
`;
this.$wrapper.html(html);
}
@ -80,17 +80,17 @@ frappe.views.ListGroupBy = class ListGroupBy {
fieldtype = docfield.fieldtype;
}
return `<li class="group-by-field list-link">
<a class="btn btn-default btn-sm list-sidebar-button" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false"
data-label="${label}" data-fieldname="${fieldname}" data-fieldtype="${fieldtype}"
href="#" onclick="return false;">
<span class="ellipsis">${__(label)}</span>
<span>${frappe.utils.icon("select", "xs")}</span>
</a>
return `<div class="group-by-field list-link">
<a class="btn btn-default btn-sm list-sidebar-button" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false"
data-label="${label}" data-fieldname="${fieldname}" data-fieldtype="${fieldtype}"
href="#" onclick="return false;">
<span class="ellipsis">${__(label)}</span>
<span>${frappe.utils.icon("select", "xs")}</span>
</a>
<ul class="dropdown-menu group-by-dropdown" role="menu">
</ul>
</li>`;
</div>`;
};
let html = this.group_by_fields.map(get_item_html).join("");
this.$wrapper.find(".list-group-by-fields").html(html);
@ -230,13 +230,13 @@ frappe.views.ListGroupBy = class ListGroupBy {
let applied_html = applied
? `<span class="applied"> ${frappe.utils.icon("tick", "xs")} </span>`
: "";
return `<li class="group-by-item ${applied ? "selected" : ""}" data-value="${value}">
return `<div class="group-by-item ${applied ? "selected" : ""}" data-value="${value}">
<a class="dropdown-item" href="#" onclick="return false;">
${applied_html}
<span class="group-by-value ellipsis" data-name="${field.name}">${label}</span>
<span class="group-by-count">${field.count}</span>
</a>
</li>`;
</div>`;
}
setup_filter_by() {

View file

@ -11,7 +11,7 @@ frappe.utils.logtypes.show_log_retention_message = (doctype) => {
}
const add_sidebar_message = (message) => {
let sidebar_entry = $('<ul class="list-unstyled sidebar-menu"></ul>').appendTo(
let sidebar_entry = $('<div class="sidebar-section></div>').appendTo(
cur_list.page.sidebar
);
$(`<div>${message}</div>`).appendTo(sidebar_entry);

View file

@ -48,7 +48,7 @@ frappe.ui.Page = class Page {
setup_scroll_handler() {
let last_scroll = 0;
$(window).scroll(
$(".main-section").scroll(
frappe.utils.throttle(() => {
$(".page-head").toggleClass("drop-shadow", !!document.documentElement.scrollTop);
let current_scroll = document.documentElement.scrollTop;
@ -87,8 +87,8 @@ frappe.ui.Page = class Page {
// nesting under col-sm-12 for consistency
this.add_view(
"main",
'<div class="row layout-main">\
<div class="col-md-12 layout-main-section-wrapper">\
'<div class="layout-main">\
<div class="layout-main-section-wrapper">\
<div class="layout-main-section"></div>\
<div class="layout-footer hide"></div>\
</div>\
@ -98,9 +98,9 @@ frappe.ui.Page = class Page {
this.add_view(
"main",
`
<div class="row layout-main">
<div class="col-lg-2 layout-side-section"></div>
<div class="col layout-main-section-wrapper">
<div class="layout-main">
<div class="layout-side-section"></div>
<div class="layout-main-section-wrapper">
<div class="layout-main-section"></div>
<div class="layout-footer hide"></div>
</div>
@ -156,8 +156,6 @@ frappe.ui.Page = class Page {
this.make_page();
}
this.card_layout && this.main.addClass("frappe-card");
// keyboard shortcuts
let menu_btn = this.menu_btn_group.find("button");
menu_btn.attr("title", __("Menu")).tooltip({ delay: { show: 600, hide: 100 } });

View file

@ -1,70 +1,238 @@
frappe.provide("frappe.ui");
frappe.ui.Sidebar = class Sidebar {
constructor({ wrapper, css_class }) {
this.wrapper = wrapper;
this.css_class = css_class;
constructor() {
this.items = {};
this.make_dom();
this.sidebar_items = {
public: {},
private: {},
};
this.indicator_colors = [
"green",
"cyan",
"blue",
"orange",
"yellow",
"gray",
"grey",
"red",
"pink",
"darkgrey",
"purple",
"light-blue",
];
this.setup_pages();
}
make_dom() {
this.wrapper.html(`
<div class="${this.css_class} overlay-sidebar hidden-xs hidden-sm">
this.wrapper = $(`
<div class="body-sidebar-container">
<div class="body-sidebar hidden-xs hidden-sm">
<a href="/app">
<img
class="app-logo"
src="${frappe.boot.app_logo_url}"
alt="${__("App Logo")}"
>
</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");
this.$sidebar = this.wrapper.find(".sidebar-items");
this.wrapper.find(".body-sidebar .edit-sidebar-link").on("click", () => {
frappe.quick_edit("Workspace Settings");
});
}
setup_pages() {
this.sidebar_pages = frappe.boot.sidebar_pages;
this.all_pages = this.sidebar_pages.pages;
this.has_access = this.sidebar_pages.has_access;
this.has_create_access = this.sidebar_pages.has_create_access;
if (!this.sidebar_pages.workspace_setup_completed) {
frappe.quick_edit("Workspace Settings");
}
this.all_pages.forEach((page) => {
page.is_editable = !page.public || this.has_access;
});
if (this.all_pages) {
frappe.workspaces = {};
frappe.workspace_list = [];
for (let page of this.all_pages) {
frappe.workspaces[frappe.router.slug(page.name)] = {
title: page.title,
public: page.public,
};
frappe.workspace_list.push(page);
}
this.make_sidebar();
}
}
make_sidebar() {
if (this.wrapper.find(".standard-sidebar-section")[0]) {
this.wrapper.find(".standard-sidebar-section").remove();
}
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),
];
this.build_sidebar_section("All", parent_pages);
// Scroll sidebar to selected page if it is not in viewport.
this.wrapper.find(".selected").length &&
!frappe.dom.is_element_in_viewport(this.wrapper.find(".selected")) &&
this.wrapper.find(".selected")[0].scrollIntoView();
}
build_sidebar_section(title, root_pages) {
let sidebar_section = $(
`<div class="standard-sidebar-section nested-container" data-title="${title}"></div>`
);
this.prepare_sidebar(root_pages, sidebar_section, this.wrapper.find(".sidebar-items"));
if (Object.keys(root_pages).length === 0) {
sidebar_section.addClass("hidden");
}
$(".item-anchor").on("click", () => {
$(".list-sidebar.hidden-xs.hidden-sm").removeClass("opened");
$(".close-sidebar").css("display", "none");
$("body").css("overflow", "auto");
});
if (
sidebar_section.find(".sidebar-item-container").length &&
sidebar_section.find("> [item-is-hidden='0']").length == 0
) {
sidebar_section.addClass("hidden show-in-edit-mode");
}
}
prepare_sidebar(items, child_container, item_container) {
for (let item of items) {
// visibility not explicitly set to 0
if (item.visibility !== 0) {
this.append_item(item, child_container);
}
}
child_container.appendTo(item_container);
}
append_item(item, container) {
let is_current_page = false;
item.selected = is_current_page;
if (is_current_page) {
this.current_page = { name: item.title, public: item.public };
}
let $item_container = this.sidebar_item_container(item);
let sidebar_control = $item_container.find(".sidebar-item-control");
let child_items = this.all_pages.filter((page) => page.parent_page == item.title);
if (child_items.length > 0) {
let child_container = $item_container.find(".sidebar-child-item");
child_container.addClass("hidden");
this.prepare_sidebar(child_items, child_container, $item_container);
}
$item_container.appendTo(container);
this.sidebar_items[item.public ? "public" : "private"][item.title] = $item_container;
if ($item_container.parent().hasClass("hidden") && is_current_page) {
$item_container.parent().toggleClass("hidden");
}
this.add_toggle_children(item, sidebar_control, $item_container);
if (child_items.length > 0) {
$item_container.find(".drop-icon").first().addClass("show-in-edit-mode");
}
}
sidebar_item_container(item) {
item.indicator_color =
item.indicator_color || this.indicator_colors[Math.floor(Math.random() * 12)];
return $(`
<div
class="sidebar-item-container ${item.is_editable ? "is-draggable" : ""}"
item-parent="${item.parent_page}"
item-name="${item.title}"
item-public="${item.public || 0}"
item-is-hidden="${item.is_hidden || 0}"
>
<div class="standard-sidebar-item ${item.selected ? "selected" : ""}">
<a
href="/app/${
item.public
? frappe.router.slug(item.title)
: "private/" + frappe.router.slug(item.title)
}"
class="item-anchor ${item.is_editable ? "" : "block-click"}" title="${__(item.title)}"
>
<span class="sidebar-item-icon" item-icon=${item.icon || "folder-normal"}>
${
item.public || item.icon
? frappe.utils.icon(item.icon || "folder-normal", "md")
: `<span class="indicator ${item.indicator_color}"></span>`
}
</span>
<span class="sidebar-item-label">${__(item.title)}<span>
</a>
<div class="sidebar-item-control"></div>
</div>
<div class="sidebar-child-item nested-container"></div>
</div>
`);
this.$sidebar = this.wrapper.find("." + this.css_class);
}
add_item(item, section, h6 = false) {
let $section, $li_item;
if (!section && this.wrapper.find(".sidebar-menu").length === 0) {
// if no section, add section with no heading
$section = this.get_section();
} else {
$section = this.get_section(section);
add_toggle_children(item, sidebar_control, item_container) {
let drop_icon = "es-line-down";
if (
this.current_page &&
item_container.find(`[item-name="${this.current_page.name}"]`).length
) {
drop_icon = "small-up";
}
if (item instanceof jQuery) {
$li_item = $(`<li>`);
item.appendTo($li_item);
} else {
const className = h6 ? "h6" : "";
const html = `<li class=${className}>
<a ${item.href ? `href="${item.href}"` : ""}>${item.label}</a>
</li>`;
$li_item = $(html).click(() => item.on_click && item.on_click());
let $child_item_section = item_container.find(".sidebar-child-item");
let $drop_icon = $(`<button class="btn-reset drop-icon hidden">`)
.html(frappe.utils.icon(drop_icon, "sm"))
.appendTo(sidebar_control);
if (
this.all_pages.some(
(e) => e.parent_page == item.title && (e.is_hidden == 0 || !this.is_read_only)
)
) {
$drop_icon.removeClass("hidden");
}
$section.append($li_item);
if (item.name) {
this.items[item.name] = $li_item;
}
}
remove_item(name) {
if (this.items[name]) {
this.items[name].remove();
}
}
get_section(section_heading = "") {
let $section = $(this.wrapper.find(`[data-section-heading="${section_heading}"]`));
if ($section.length) {
return $section;
}
const $section_heading = section_heading ? `<li class="h6">${section_heading}</li>` : "";
$section = $(`
<ul class="list-unstyled sidebar-menu" data-section-heading="${section_heading || "default"}">
${$section_heading}
</ul>
`);
this.$sidebar.append($section);
return $section;
$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);
$child_item_section.toggleClass("hidden");
});
}
};

View file

@ -14,7 +14,7 @@ frappe.ui.Tags = class {
setup(parent, placeholder) {
this.$ul = parent;
this.$input = $(`<input class="tags-input form-control"></input>`);
this.$input = $(`<input class="tags-input form-control mt-2"></input>`);
this.$inputWrapper = this.get_list_element(this.$input);
this.$placeholder =
@ -94,7 +94,7 @@ frappe.ui.Tags = class {
}
get_list_element($element, class_name = "") {
let $li = $(`<li class="${class_name}"></li>`);
let $li = $(`<div class="${class_name}"></div>`);
$element.appendTo($li);
return $li;
}

View file

@ -1,13 +1,13 @@
<div class="sticky-top">
<header class="navbar navbar-expand" role="navigation">
<div class="container">
<a class="navbar-brand navbar-home" href="/app">
<!-- <a class="navbar-brand navbar-home" href="/app">
<img
class="app-logo"
src="{{ frappe.boot.app_logo_url }}"
alt="{{ __("App Logo") }}"
>
</a>
</a> -->
<ul class="nav navbar-nav d-none d-sm-flex" id="navbar-breadcrumbs"></ul>
<div class="collapse navbar-collapse justify-content-end">
<form class="form-inline fill-width justify-content-end" role="search" onsubmit="return false;">

View file

@ -3,6 +3,8 @@ import KanbanSettings from "./kanban_settings";
frappe.provide("frappe.views");
frappe.views.KanbanView = class KanbanView extends frappe.views.ListView {
static full_page = true;
static load_last_view() {
const route = frappe.get_route();
if (route.length === 3) {

View file

@ -1995,7 +1995,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
}
message_div(message) {
return `<div class='flex justify-center align-center text-muted' style='height: 50vh;'>
return `<div class='flex justify-center align-center text-muted' style='height: calc(100vh - 280px);'>
<div>${message}</div>
</div>`;
}

View file

@ -8,6 +8,7 @@ frappe.standard_pages["Workspaces"] = function () {
parent: wrapper,
name: "Workspaces",
title: __("Workspace"),
single_column: true,
});
frappe.workspace = new frappe.views.Workspace(wrapper);
@ -30,10 +31,6 @@ frappe.views.Workspace = class Workspace {
public: {},
private: {},
};
this.sidebar_categories = [
{ id: "Personal", label: __("Personal", null, "Workspace Category") },
{ id: "Public", label: __("Public", null, "Workspace Category") },
];
this.indicator_colors = [
"green",
"cyan",
@ -50,64 +47,20 @@ frappe.views.Workspace = class Workspace {
];
this.prepare_container();
this.setup_pages();
this.sidebar = frappe.app.sidebar;
this.sidebar.setup_pages();
this.cached_pages = $.extend(true, {}, frappe.boot.sidebar_pages);
this.has_access = frappe.boot.sidebar_pages.has_access;
this.has_create_access = frappe.boot.sidebar_pages.has_create_access;
this.show();
this.register_awesomebar_shortcut();
}
prepare_container() {
let list_sidebar = $(`
<div class="list-sidebar overlay-sidebar hidden-xs hidden-sm">
<div class="desk-sidebar list-unstyled sidebar-menu">
</div>
<div class="mb-4">
<a class="edit-sidebar-link text-extra-muted">Edit sidebar</a>
</div>
</div>
`).appendTo(this.wrapper.find(".layout-side-section"));
this.sidebar = list_sidebar.find(".desk-sidebar");
this.body = this.wrapper.find(".layout-main-section");
this.prepare_new_and_edit();
}
async setup_pages(reload) {
!this.discard && this.create_page_skeleton();
!this.discard && this.create_sidebar_skeleton();
this.sidebar_pages = !this.discard ? await this.get_pages() : this.sidebar_pages;
this.cached_pages = $.extend(true, {}, this.sidebar_pages);
this.all_pages = this.sidebar_pages.pages;
this.has_access = this.sidebar_pages.has_access;
this.has_create_access = this.sidebar_pages.has_create_access;
if (!this.sidebar_pages.workspace_setup_completed) {
frappe.quick_edit("Workspace Settings");
}
this.all_pages.forEach((page) => {
page.is_editable = !page.public || this.has_access;
});
this.public_pages = this.all_pages.filter((page) => page.public);
this.private_pages = this.all_pages.filter((page) => !page.public);
if (this.all_pages) {
frappe.workspaces = {};
frappe.workspace_list = [];
for (let page of this.all_pages) {
frappe.workspaces[frappe.router.slug(page.name)] = {
title: page.title,
public: page.public,
};
frappe.workspace_list.push(page);
}
this.make_sidebar();
reload && this.show();
}
this.wrapper.find(".layout-side-section .edit-sidebar-link").on("click", () => {
frappe.quick_edit("Workspace Settings");
});
}
prepare_new_and_edit() {
this.$page = $(`
<div class="editor-js-container"></div>
@ -134,13 +87,11 @@ frappe.views.Workspace = class Workspace {
this.body.find(".btn-edit-workspace").on("click", async () => {
if (!this.editor || !this.editor.readOnly) return;
this.is_read_only = false;
this.toggle_hidden_workspaces(true);
await this.editor.readOnly.toggle();
this.editor.isReady.then(() => {
this.body.addClass("edit-mode");
this.initialize_editorjs_undo();
this.setup_customization_buttons(this._page);
this.show_sidebar_actions();
this.make_blocks_sortable();
});
});
@ -150,188 +101,8 @@ frappe.views.Workspace = class Workspace {
return frappe.xcall("frappe.desk.desktop.get_workspace_sidebar_items");
}
sidebar_item_container(item) {
item.indicator_color =
item.indicator_color || this.indicator_colors[Math.floor(Math.random() * 12)];
return $(`
<div
class="sidebar-item-container ${item.is_editable ? "is-draggable" : ""}"
item-parent="${item.parent_page}"
item-name="${item.title}"
item-public="${item.public || 0}"
item-is-hidden="${item.is_hidden || 0}"
>
<div class="desk-sidebar-item standard-sidebar-item ${item.selected ? "selected" : ""}">
<a
href="/app/${
item.public
? frappe.router.slug(item.title)
: "private/" + frappe.router.slug(item.title)
}"
class="item-anchor ${item.is_editable ? "" : "block-click"}" title="${__(item.title)}"
>
<span class="sidebar-item-icon" item-icon=${item.icon || "folder-normal"}>
${
item.public
? frappe.utils.icon(item.icon || "folder-normal", "md")
: `<span class="indicator ${item.indicator_color}"></span>`
}
</span>
<span class="sidebar-item-label">${__(item.title)}<span>
</a>
<div class="sidebar-item-control"></div>
</div>
<div class="sidebar-child-item nested-container"></div>
</div>
`);
}
make_sidebar() {
if (this.sidebar.find(".standard-sidebar-section")[0]) {
this.sidebar.find(".standard-sidebar-section").remove();
}
this.sidebar_categories.forEach((category) => {
let root_pages = this.public_pages.filter(
(page) => page.parent_page == "" || page.parent_page == null
);
if (category.id != "Public") {
root_pages = this.private_pages.filter(
(page) => page.parent_page == "" || page.parent_page == null
);
}
root_pages = root_pages.uniqBy((d) => d.title);
this.build_sidebar_section(category, root_pages);
});
// Scroll sidebar to selected page if it is not in viewport.
this.sidebar.find(".selected").length &&
!frappe.dom.is_element_in_viewport(this.sidebar.find(".selected")) &&
this.sidebar.find(".selected")[0].scrollIntoView();
this.remove_sidebar_skeleton();
}
build_sidebar_section(category, root_pages) {
let sidebar_section = $(
`<div class="standard-sidebar-section nested-container" data-title="${category.id}"></div>`
);
let $title = $(`<button class="btn-reset standard-sidebar-label">
<span>${frappe.utils.icon("es-line-down", "xs")}</span>
<span class="section-title">${category.label}<span>
</div>`).appendTo(sidebar_section);
$title.attr({
"aria-label": __("Toggle Section: {0}", [category.label]),
"aria-expanded": "true",
});
this.prepare_sidebar(root_pages, sidebar_section, this.sidebar);
$title.on("click", (e) => {
const $e = $(e.target);
const href = $e.find("span use").attr("href");
const isCollapsed = href === "#es-line-down";
let icon = isCollapsed ? "#es-line-right-chevron" : "#es-line-down";
$e.find("span use").attr("href", icon);
$e.parent().find(".sidebar-item-container").toggleClass("hidden");
$e.attr("aria-expanded", String(!isCollapsed));
});
if (Object.keys(root_pages).length === 0) {
sidebar_section.addClass("hidden");
}
$(".item-anchor").on("click", () => {
$(".list-sidebar.hidden-xs.hidden-sm").removeClass("opened");
$(".close-sidebar").css("display", "none");
$("body").css("overflow", "auto");
});
if (
sidebar_section.find(".sidebar-item-container").length &&
sidebar_section.find("> [item-is-hidden='0']").length == 0
) {
sidebar_section.addClass("hidden show-in-edit-mode");
}
}
prepare_sidebar(items, child_container, item_container) {
for (let item of items) {
// visibility not explicitly set to 0
if (item.visibility !== 0) {
this.append_item(item, child_container);
}
}
child_container.appendTo(item_container);
}
append_item(item, container) {
let is_current_page =
frappe.router.slug(item.title) == frappe.router.slug(this.get_page_to_show().name) &&
item.public == this.get_page_to_show().public;
item.selected = is_current_page;
if (is_current_page) {
this.current_page = { name: item.title, public: item.public };
}
let $item_container = this.sidebar_item_container(item);
let sidebar_control = $item_container.find(".sidebar-item-control");
this.add_sidebar_actions(item, sidebar_control);
let pages = item.public ? this.public_pages : this.private_pages;
let child_items = pages.filter((page) => page.parent_page == item.title);
if (child_items.length > 0) {
let child_container = $item_container.find(".sidebar-child-item");
child_container.addClass("hidden");
this.prepare_sidebar(child_items, child_container, $item_container);
}
$item_container.appendTo(container);
this.sidebar_items[item.public ? "public" : "private"][item.title] = $item_container;
if ($item_container.parent().hasClass("hidden") && is_current_page) {
$item_container.parent().toggleClass("hidden");
}
this.add_drop_icon(item, sidebar_control, $item_container);
if (child_items.length > 0) {
$item_container.find(".drop-icon").first().addClass("show-in-edit-mode");
}
}
add_drop_icon(item, sidebar_control, item_container) {
let drop_icon = "es-line-down";
if (item_container.find(`[item-name="${this.current_page.name}"]`).length) {
drop_icon = "small-up";
}
let $child_item_section = item_container.find(".sidebar-child-item");
let $drop_icon = $(`<button class="btn-reset drop-icon hidden">`)
.html(frappe.utils.icon(drop_icon, "sm"))
.appendTo(sidebar_control);
let pages = item.public ? this.public_pages : this.private_pages;
if (
pages.some(
(e) => e.parent_page == item.title && (e.is_hidden == 0 || !this.is_read_only)
)
) {
$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);
$child_item_section.toggleClass("hidden");
});
}
show() {
if (!this.all_pages) {
if (!this.sidebar.all_pages) {
// pages not yet loaded, call again after a bit
setTimeout(() => this.show(), 100);
return;
@ -359,8 +130,7 @@ frappe.views.Workspace = class Workspace {
this.sidebar_items[section][page.name]
) {
let $sidebar = this.sidebar_items[section][page.name];
let pages = page.public ? this.public_pages : this.private_pages;
let sidebar_page = pages.find((p) => p.title == page.name);
let sidebar_page = this.sidebar.all_pages.find((p) => p.title == page.name);
if (add) {
$sidebar[0].firstElementChild.classList.add("selected");
@ -420,14 +190,18 @@ frappe.views.Workspace = class Workspace {
};
} else if (
localStorage.current_page &&
this.all_pages.filter((page) => page.title == localStorage.current_page).length != 0
this.sidebar.all_pages.filter((page) => page.title == localStorage.current_page)
.length != 0
) {
default_page = {
name: localStorage.current_page,
public: localStorage.is_current_page_public != "false",
};
} else if (Object.keys(this.all_pages).length !== 0) {
default_page = { name: this.all_pages[0].title, public: this.all_pages[0].public };
} else if (Object.keys(this.sidebar.all_pages).length !== 0) {
default_page = {
name: this.sidebar.all_pages[0].title,
public: this.sidebar.all_pages[0].public,
};
} else {
default_page = { name: "Build", public: true };
}
@ -445,12 +219,10 @@ frappe.views.Workspace = class Workspace {
`).appendTo(this.body.find(".editor-js-container"));
}
if (this.all_pages.length) {
if (this.sidebar.all_pages.length) {
this.create_page_skeleton();
let pages =
page.public && this.public_pages.length ? this.public_pages : this.private_pages;
let current_page = pages.filter((p) => p.title == page.name)[0];
let current_page = this.sidebar.all_pages.filter((p) => p.title == page.name)[0];
this._page = current_page;
this.content = current_page && JSON.parse(current_page.content);
@ -508,8 +280,7 @@ frappe.views.Workspace = class Workspace {
}
setup_actions(page) {
let pages = page.public ? this.public_pages : this.private_pages;
let current_page = pages.filter((p) => p.title == page.name)[0];
let current_page = this.sidebar.all_pages.filter((p) => p.title == page.name)[0];
if (!this.is_read_only) {
this.setup_customization_buttons(current_page);
@ -564,12 +335,10 @@ frappe.views.Workspace = class Workspace {
this.page.set_secondary_action(__("Discard"), async () => {
this.body.removeClass("edit-mode");
this.discard = true;
this.clear_page_actions();
this.toggle_hidden_workspaces(false);
await this.editor.readOnly.toggle();
this.is_read_only = true;
this.sidebar_pages = this.cached_pages;
frappe.boot.sidebar_pages = this.cached_pages;
this.reload();
frappe.show_alert({ message: __("Customizations Discarded"), indicator: "info" });
});
@ -581,78 +350,10 @@ frappe.views.Workspace = class Workspace {
}
}
toggle_hidden_workspaces(show) {
$(".desk-sidebar").toggleClass("show-hidden-workspaces", show);
}
show_sidebar_actions() {
this.sidebar.find(".standard-sidebar-section").addClass("show-control");
this.make_sidebar_sortable();
}
add_sidebar_actions(item, sidebar_control, is_new) {
if (!item.is_editable) {
sidebar_control.parent().click(() => {
!this.is_read_only &&
frappe.show_alert(
{
message: __("Only Workspace Manager can sort or edit this page"),
indicator: "info",
},
5
);
});
frappe.utils.add_custom_button(
frappe.utils.icon("es-line-duplicate", "sm"),
() => this.duplicate_page(item),
"duplicate-page",
__("Duplicate Workspace"),
null,
sidebar_control
);
} else if (item.is_hidden) {
frappe.utils.add_custom_button(
frappe.utils.icon("es-line-preview", "sm"),
(e) => this.unhide_workspace(item, e),
"unhide-workspace-btn",
__("Unhide Workspace"),
null,
sidebar_control
);
} else {
frappe.utils.add_custom_button(
frappe.utils.icon("es-line-drag", "xs"),
null,
"drag-handle",
__("Drag"),
null,
sidebar_control
);
!is_new && this.add_settings_button(item, sidebar_control);
}
}
get_parent_pages(page) {
this.public_parent_pages = [
"",
...this.public_pages.filter((p) => !p.parent_page).map((p) => p.title),
];
this.private_parent_pages = [
"",
...this.private_pages.filter((p) => !p.parent_page).map((p) => p.title),
];
if (page) {
return page.public ? this.public_parent_pages : this.private_parent_pages;
}
}
edit_page(item) {
var me = this;
let old_item = item;
let parent_pages = this.get_parent_pages(item);
let parent_pages = this.sidebar.all_pages;
let idx = parent_pages.findIndex((x) => x == item.title);
if (idx !== -1) parent_pages.splice(idx, 1);
const d = new frappe.ui.Dialog({
@ -710,15 +411,6 @@ frappe.views.Workspace = class Workspace {
primary_action_label: __("Update"),
primary_action: (values) => {
values.title = strip_html(values.title);
let is_title_changed = values.title != old_item.title;
let is_section_changed = Boolean(values.is_public) != Boolean(old_item.public);
if (
(is_title_changed || is_section_changed) &&
!this.validate_page(values, old_item)
)
return;
d.hide();
frappe.call({
method: "frappe.desk.doctype.workspace.workspace.update_page",
args: {
@ -739,8 +431,6 @@ frappe.views.Workspace = class Workspace {
},
});
this.update_sidebar(old_item, values);
if (this.make_page_selected) {
let pre_url = values.is_public ? "" : "private/";
let route = pre_url + frappe.router.slug(values.title);
@ -749,200 +439,12 @@ frappe.views.Workspace = class Workspace {
this.make_page_selected = false;
}
this.make_sidebar();
this.show_sidebar_actions();
this.sidebar.make_sidebar();
},
});
d.show();
}
update_sidebar(old_item, new_item) {
let is_section_changed = old_item.public != (new_item.is_public || 0);
let is_title_changed = old_item.title != new_item.title;
let new_updated_item = { ...old_item };
let pages = old_item.public ? this.public_pages : this.private_pages;
let child_items = pages.filter((page) => page.parent_page == old_item.title);
this.make_page_selected = old_item.selected;
new_updated_item.title = new_item.title;
new_updated_item.icon = new_item.icon;
new_updated_item.indicator_color = new_item.indicator_color;
new_updated_item.parent_page = new_item.parent || "";
new_updated_item.public = new_item.is_public;
if (is_title_changed || is_section_changed) {
if (new_item.is_public) {
new_updated_item.name = new_item.title;
new_updated_item.label = new_item.title;
new_updated_item.for_user = "";
} else {
let user = frappe.session.user;
new_updated_item.name = `${new_item.title}-${user}`;
new_updated_item.label = `${new_item.title}-${user}`;
new_updated_item.for_user = user;
}
}
this.update_cached_values(old_item, new_updated_item);
if (child_items.length) {
child_items.forEach((child) => {
child.parent_page = new_item.title;
is_section_changed && this.update_child_sidebar(child, new_item);
});
}
}
update_child_sidebar(child, new_item) {
let old_child = { ...child };
this.make_page_selected = child.selected;
child.public = new_item.is_public;
if (new_item.is_public) {
child.name = child.title;
child.label = child.title;
child.for_user = "";
} else {
let user = frappe.session.user;
child.name = `${child.title}-${user}`;
child.label = `${child.title}-${user}`;
child.for_user = user;
}
this.update_cached_values(old_child, child);
}
update_cached_values(old_item, new_item, duplicate, new_page) {
let [from_pages, to_pages] = old_item.public
? [this.public_pages, this.private_pages]
: [this.private_pages, this.public_pages];
let old_item_index = from_pages.findIndex((page) => page.title == old_item.title);
duplicate && old_item_index++;
// update frappe.workspaces
if (frappe.workspaces[frappe.router.slug(old_item.name)] || new_page) {
!duplicate && delete frappe.workspaces[frappe.router.slug(old_item.name)];
if (new_item) {
frappe.workspaces[frappe.router.slug(new_item.name)] = { title: new_item.title };
}
}
// update page block data
if ((this.pages && this.pages[old_item.name]) || new_page) {
if (new_item) {
this.pages[new_item.name] = this.pages[old_item.name] || {};
}
!duplicate && delete this.pages[old_item.name];
}
// update public and private pages
if (new_item) {
let is_section_changed =
old_item.public != (new_item.is_public || new_item.public || 0);
if (is_section_changed) {
!duplicate && from_pages.splice(old_item_index, 1);
to_pages.push(new_item);
} else if (new_page) {
from_pages.push(new_item);
} else {
from_pages.splice(old_item_index, duplicate ? 0 : 1, new_item);
}
} else {
from_pages.splice(old_item_index, 1);
}
this.sidebar_pages.pages = [...this.public_pages, ...this.private_pages];
this.cached_pages = this.sidebar_pages;
}
add_settings_button(item, sidebar_control) {
this.dropdown_list = [
{
label: __("Edit"),
title: __("Edit Workspace"),
icon: frappe.utils.icon("es-line-edit", "sm"),
action: () => this.edit_page(item),
},
{
label: __("Duplicate"),
title: __("Duplicate Workspace"),
icon: frappe.utils.icon("es-line-duplicate", "sm"),
action: () => this.duplicate_page(item),
},
{
label: __("Hide"),
title: __("Hide Workspace"),
icon: frappe.utils.icon("es-line-hide", "sm"),
action: (e) => this.hide_workspace(item, e),
},
];
if (this.is_item_deletable(item)) {
this.dropdown_list.push({
label: __("Delete"),
title: __("Delete Workspace"),
icon: frappe.utils.icon("delete-active", "sm"),
action: () => this.delete_page(item),
});
}
let $button = $(`
<div class="btn btn-xs setting-btn dropdown-btn" title="${__("Setting")}">
${frappe.utils.icon("es-line-dot-horizontal", "xs")}
</div>
<div class="dropdown-list hidden"></div>
`);
let dropdown_item = function (label, title, icon, action) {
let html = $(`
<div class="dropdown-item" title="${title}">
<span class="dropdown-item-icon">${icon}</span>
<span class="dropdown-item-label">${label}</span>
</div>
`);
html.click((event) => {
event.stopPropagation();
action && action(event);
});
return html;
};
$button.filter(".dropdown-btn").click((event) => {
event.stopPropagation();
if ($button.filter(".dropdown-list.hidden").length) {
$(".dropdown-list:not(.hidden)").addClass("hidden");
}
$button.filter(".dropdown-list").toggleClass("hidden");
});
sidebar_control.append($button);
this.dropdown_list.forEach((i) => {
$button
.filter(".dropdown-list")
.append(dropdown_item(i.label, i.title, i.icon, i.action));
});
}
is_item_deletable(item) {
// if item is private
// if item is public but doesn't have module set
// if item is public and has module set but developer mode is on
// then item is deletable
if (
!item.public ||
(item.public && (!item.module || (item.module && frappe.boot.developer_mode)))
)
return true;
return false;
}
delete_page(page) {
frappe.confirm(
__("Are you sure you want to delete page {0}?", [page.title.bold()]),
@ -972,7 +474,6 @@ frappe.views.Workspace = class Workspace {
}
this.make_sidebar();
this.show_sidebar_actions();
}
);
}
@ -1037,7 +538,6 @@ frappe.views.Workspace = class Workspace {
],
primary_action_label: __("Duplicate"),
primary_action: (values) => {
if (!this.validate_page(values)) return;
d.hide();
frappe.call({
method: "frappe.desk.doctype.workspace.workspace.duplicate_page",
@ -1074,142 +574,12 @@ frappe.views.Workspace = class Workspace {
let route = pre_url + frappe.router.slug(values.title);
frappe.set_route(route);
me.make_sidebar();
me.show_sidebar_actions();
me.sidebar.make_sidebar();
},
});
d.show();
}
hide_unhide_workspace(page, event, hide) {
page.is_hidden = hide;
let sidebar_control = event.target.closest(".sidebar-item-control");
let sidebar_item_container = sidebar_control.closest(".sidebar-item-container");
$(sidebar_item_container).attr("item-is-hidden", hide);
$(sidebar_control).empty();
this.add_sidebar_actions(page, $(sidebar_control));
this.add_drop_icon(page, $(sidebar_control), $(sidebar_item_container));
let cached_page = this.cached_pages.pages.findIndex((p) => p.name === page.name);
if (cached_page !== -1) {
this.cached_pages.pages[cached_page].is_hidden = hide;
}
let method = hide ? "hide_page" : "unhide_page";
frappe.call({
method: "frappe.desk.doctype.workspace.workspace." + method,
args: {
page_name: page.name,
},
callback: (r) => {
if (!r.message) return;
let message = hide ? "{0} is hidden successfully" : "{0} is unhidden successfully";
message = __(message, [page.title.bold()]);
frappe.show_alert({ message: message, indicator: "green" });
},
});
}
hide_workspace(page, event) {
this.hide_unhide_workspace(page, event, 1);
}
unhide_workspace(page, event) {
this.hide_unhide_workspace(page, event, 0);
}
make_sidebar_sortable() {
let me = this;
$(".nested-container").each(function () {
new Sortable(this, {
handle: ".drag-handle",
draggable: ".sidebar-item-container.is-draggable",
group: "nested",
animation: 150,
fallbackOnBody: true,
swapThreshold: 0.65,
onEnd: function (evt) {
let is_public = $(evt.item).attr("item-public") == "1";
me.prepare_sorted_sidebar(is_public);
me.update_sorted_sidebar();
},
});
});
}
prepare_sorted_sidebar(is_public) {
let pages = is_public ? this.public_pages : this.private_pages;
if (is_public) {
this.sorted_public_items = this.sort_sidebar(
this.sidebar.find(".standard-sidebar-section").last(),
pages
);
} else {
this.sorted_private_items = this.sort_sidebar(
this.sidebar.find(".standard-sidebar-section").first(),
pages
);
}
this.sidebar_pages.pages = [...this.public_pages, ...this.private_pages];
this.cached_pages = this.sidebar_pages;
}
sort_sidebar($sidebar_section, pages) {
let sorted_items = [];
Array.from($sidebar_section.find(".sidebar-item-container")).forEach((page, i) => {
let parent_page = "";
if (page.closest(".nested-container").classList.contains("sidebar-child-item")) {
parent_page = page.parentElement.parentElement.attributes["item-name"].value;
}
sorted_items.push({
title: page.attributes["item-name"].value,
parent_page: parent_page,
public: page.attributes["item-public"].value,
});
let $drop_icon = $(page).find(".sidebar-item-control .drop-icon").first();
if ($(page).find(".sidebar-child-item > *").length != 0) {
$drop_icon.removeClass("hidden");
} else {
$drop_icon.addClass("hidden");
}
let from_index = pages.findIndex((p) => p.title == page.attributes["item-name"].value);
let element = pages[from_index];
element.parent_page = parent_page;
if (from_index != i) {
pages.splice(from_index, 1);
pages.splice(i, 0, element);
}
});
return sorted_items;
}
update_sorted_sidebar() {
if (this.sorted_public_items || this.sorted_private_items) {
frappe.call({
method: "frappe.desk.doctype.workspace.workspace.sort_pages",
args: {
sb_public_items: this.sorted_public_items,
sb_private_items: this.sorted_private_items,
},
callback: function (res) {
if (res.message) {
let message = `Sidebar Updated Successfully`;
frappe.show_alert({ message: __(message), indicator: "green" });
}
},
});
}
}
make_blocks_sortable() {
let me = this;
this.page_sortable = Sortable.create(
@ -1268,7 +638,6 @@ frappe.views.Workspace = class Workspace {
label: __("Icon"),
fieldtype: "Icon",
fieldname: "icon",
hidden: 1,
},
{
label: __("Indicator color"),
@ -1280,7 +649,6 @@ frappe.views.Workspace = class Workspace {
primary_action_label: __("Create"),
primary_action: (values) => {
values.title = strip_html(values.title);
if (!this.validate_page(values)) return;
d.hide();
this.initialize_editorjs_undo();
this.setup_customization_buttons({ is_editable: true });
@ -1341,8 +709,7 @@ frappe.views.Workspace = class Workspace {
let route = pre_url + frappe.router.slug(new_page.title);
frappe.set_route(route);
this.make_sidebar();
this.show_sidebar_actions();
this.sidebar.make_sidebar();
localStorage.setItem("new_workspace", JSON.stringify(new_page));
});
},
@ -1350,88 +717,6 @@ frappe.views.Workspace = class Workspace {
d.show();
}
validate_page(new_page, old_page) {
let message = "";
let [from_pages, to_pages] = new_page.is_public
? [this.private_pages, this.public_pages]
: [this.public_pages, this.private_pages];
let section = this.sidebar_categories[new_page.is_public];
if (to_pages && to_pages.filter((p) => p.title == new_page.title)[0]) {
message = __("Page with title {0} already exist.", [new_page.title.bold()]);
}
if (frappe.router.doctype_route_exist(frappe.router.slug(new_page.title))) {
message = __("Doctype with same route already exist. Please choose different title.");
}
let child_pages = old_page && from_pages.filter((p) => p.parent_page == old_page.title);
if (child_pages) {
child_pages.every((child_page) => {
if (to_pages && to_pages.find((p) => p.title == child_page.title)) {
message = __(
"One of the child page with name {0} already exist in {1} Section. Please update the name of the child page first before moving",
[child_page.title.bold(), section.bold()]
);
cur_dialog.hide();
return false;
}
return true;
});
}
if (message) {
frappe.throw(__(message));
return false;
}
return true;
}
add_page_to_sidebar(page) {
let $sidebar = $(".standard-sidebar-section");
let item = { ...page };
item.selected = true;
item.is_editable = true;
let $sidebar_item = this.sidebar_item_container(item);
this.add_sidebar_actions(item, $sidebar_item.find(".sidebar-item-control"), true);
$sidebar_item.find(".sidebar-item-control .drag-handle").css("margin-right", "8px");
let sidebar_section = item.is_public ? $sidebar[1] : $sidebar[0];
if (!item.parent) {
!item.is_public && $sidebar.first().removeClass("hidden");
$sidebar_item.appendTo(sidebar_section);
} else {
let $item_container = $(sidebar_section).find(`[item-name="${item.parent}"]`);
let $child_section = $item_container.find(".sidebar-child-item");
let $drop_icon = $item_container.find(".drop-icon");
if (!$child_section[0]) {
$child_section = $(
`<div class="sidebar-child-item hidden nested-container"></div>`
).appendTo($item_container);
$drop_icon.toggleClass("hidden");
}
$sidebar_item.appendTo($child_section);
$child_section.removeClass("hidden");
$item_container.find(".drop-icon.hidden").removeClass("hidden");
$item_container.find(".drop-icon use").attr("href", "#es-line-up");
}
let section = item.is_public ? "public" : "private";
if (
this.sidebar_items &&
this.sidebar_items[section] &&
!this.sidebar_items[section][item.title]
) {
this.sidebar_items[section][item.title] = $sidebar_item;
}
}
initialize_editorjs(blocks) {
this.tools = {
header: {
@ -1573,12 +858,35 @@ frappe.views.Workspace = class Workspace {
});
}
reload() {
async reload() {
this.sorted_public_items = [];
this.sorted_private_items = [];
this.setup_pages(true);
this.discard = false;
this.undo.readOnly = true;
this.get_pages().then((r) => {
frappe.boot.sidebar_pages = r;
this.sidebar.setup_pages();
this.show();
this.undo.readOnly = true;
});
}
get_parent_pages(page) {
this.public_parent_pages = [
"",
...this.sidebar.all_pages
.filter((p) => p.public && !p.parent_page)
.map((p) => p.title),
];
this.private_parent_pages = [
"",
...this.sidebar.all_pages
.filter((p) => !p.public && !p.parent_page)
.map((p) => p.title),
];
if (page) {
return page.public ? this.public_parent_pages : this.private_parent_pages;
}
}
create_page_skeleton() {
@ -1593,18 +901,6 @@ frappe.views.Workspace = class Workspace {
this.body.find(".workspace-skeleton").remove();
}
create_sidebar_skeleton() {
if ($(".workspace-sidebar-skeleton").length) return;
$(frappe.render_template("workspace_sidebar_loading_skeleton")).insertBefore(this.sidebar);
this.sidebar.addClass("hidden");
}
remove_sidebar_skeleton() {
this.sidebar.removeClass("hidden");
$(".workspace-sidebar-skeleton").remove();
}
register_awesomebar_shortcut() {
"abcdefghijklmnopqrstuvwxyz".split("").forEach((letter) => {
const default_shortcut = {

View file

@ -1,5 +1,7 @@
// REDESIGN-TODO: Review
.awesomplete {
position: relative;
[hidden] {
display: none;
}

View file

@ -19,6 +19,8 @@ $disabled-input-height: 22px;
--margin-xl: 30px;
--margin-2xl: 40px;
--page-max-width: 900px;
--modal-shadow: var(--shadow-md);
--card-shadow: var(--shadow-sm);
--btn-shadow: var(--shadow-xs);
@ -79,7 +81,7 @@ $disabled-input-height: 22px;
--fg-color: white;
--subtle-accent: var(--gray-50);
--subtle-fg: var(--gray-100);
--navbar-bg: var(--subtle-accent);
--navbar-bg: var(--neutral);
--fg-hover-color: var(--gray-100);
--card-bg: var(--fg-color);
--disabled-text-color: var(--gray-600);

View file

@ -31,7 +31,7 @@ body {
.frappe-card {
@include card();
box-shadow: none;
border: 1px solid var(--gray-300);
border: 1px solid var(--border-color);
}
.frappe-control[data-fieldtype="Select"].frappe-control[data-fieldname="color"] {

View file

@ -52,7 +52,7 @@
.frappe-control[data-fieldtype="Icon"] {
input {
padding-left: 40px;
padding-left: 30px;
}
.selected-icon {
cursor: pointer;
@ -75,6 +75,13 @@
}
}
.horizontal {
.selected-icon {
top: 4px !important;
left: calc(50% + 5px) !important;
}
}
.data-row.row {
.selected-icon {
top: calc(50% - 11px);

View file

@ -48,6 +48,10 @@ body.modal-open[style^="padding-right"] {
max-width: calc(100% - 80px);
}
.btn-modal-close {
padding: 4px 0;
}
.modal-actions {
.btn-modal-minimize {
padding-right: 0;

View file

@ -6,7 +6,6 @@
}
/*! This comment will be included even in compressed mode. */
#navbar-breadcrumbs {
margin-left: var(--margin-md);
@include get_textstyle("base", "regular");
a {
margin-right: 10px;

View file

@ -52,7 +52,7 @@
--progress-bar-bg: var(--primary);
// list
--list-row-height: 40px;
--list-row-height: 30px;
--list-checkbox-padding: calc((var(--list-row-height) - var(--checkbox-size)) / 2);
@include media-breakpoint-down(md) {

View file

@ -7,33 +7,6 @@ body {
}
}
.desk-sidebar {
@extend .standard-sidebar;
.standard-sidebar-label {
font-size: var(--text-xs);
text-transform: uppercase;
cursor: pointer;
margin-bottom: var(--margin-xs) !important;
.section-title {
margin-left: 4px;
}
span {
pointer-events: none;
}
}
.standard-sidebar-section {
margin-bottom: var(--margin-xl);
&:last-of-type {
margin-bottom: var(--margin-sm);
}
}
}
.widget-group {
@include get_textstyle("base", "regular");
margin-bottom: var(--margin-2xl);
@ -930,6 +903,7 @@ body {
}
.workspace-skeleton {
width: 100%;
transition: ease;
.widget-group-title {
height: 15px;
@ -948,59 +922,16 @@ body {
}
}
.workspace-sidebar-skeleton {
transition: ease;
.sidebar-box {
height: 40px;
margin-bottom: 10px;
margin-left: 10px;
background-color: var(--skeleton-bg);
&.child {
margin-left: 30px;
}
&.section {
height: 25px;
margin-left: 0px;
}
}
}
[data-page-route="Workspaces"] {
@media (min-width: map-get($grid-breakpoints, "lg")) {
.layout-main {
height: calc(100vh - var(--navbar-height) - var(--page-head-height) - 5px);
.layout-side-section,
.layout-main-section-wrapper {
height: 100%;
overflow-y: auto;
scrollbar-color: var(--gray-200) transparent;
[data-theme="dark"] & {
scrollbar-color: var(--gray-800) transparent;
}
&::-webkit-scrollbar-track {
background: transparent;
}
&::-webkit-scrollbar-thumb {
background: var(--gray-200);
[data-theme="dark"] & {
background: var(--gray-800);
}
}
}
.layout-side-section {
padding-right: 15px;
}
max-width: var(--page-max-width);
margin: auto;
.layout-main-section {
border: 1px solid var(--border-color);
border-top: 0px;
border: 0px;
padding: var(--padding-sm) var(--padding-md);
margin-bottom: var(--margin-sm);
&.edit-mode {
background-color: var(--subtle-fg) !important;
@ -1013,10 +944,6 @@ body {
}
}
}
.desk-sidebar {
margin-bottom: var(--margin-2xl);
}
}
}
@ -1044,139 +971,6 @@ body {
margin-right: 5px;
}
.standard-sidebar-item {
justify-content: space-between;
padding: 0px;
.sidebar-item-control {
> * {
align-self: center;
margin-left: 3px;
box-shadow: none;
}
.drag-handle {
cursor: all-scroll;
cursor: grabbing;
display: none;
}
.setting-btn,
.duplicate-page {
display: none;
}
.drop-icon {
padding: 0px 12px 0px 2px;
}
svg {
margin-right: 0;
margin-top: -3px;
}
.dropdown-list {
top: 42px;
}
}
.sidebar-item-label {
flex: 1;
}
.item-anchor {
display: flex;
overflow: hidden;
padding: 3px 0px 3px 8px;
flex: 1;
}
}
.sidebar-item-container {
position: relative;
margin-left: -10px;
&[item-is-hidden="1"] {
display: none;
opacity: 0.4;
&:hover {
opacity: 1;
}
}
.sidebar-item-container {
margin-left: 10px;
.standard-sidebar-item {
justify-content: start;
}
}
.indicator {
margin-left: 5px;
}
}
.desk-sidebar {
&.show-hidden-workspaces {
.unhide-workspace-btn {
display: none;
}
.standard-sidebar-section {
display: block;
.sidebar-item-container {
&[item-is-hidden="1"] {
display: block;
}
&[item-is-hidden="0"] {
.drop-icon {
display: inline-block;
}
}
}
}
.show-in-edit-mode {
display: block !important;
&.drop-icon {
display: inline-block !important;
}
}
}
.standard-sidebar-section.show-control {
.desk-sidebar-item.standard-sidebar-item {
&:hover,
&.selected {
.drag-handle {
display: inline-block;
background-color: var(--bg-color);
}
.setting-btn,
.duplicate-page,
.unhide-workspace-btn {
display: inline-block;
margin-right: 8px;
}
.drop-icon {
padding: 10px 8px 10px 2px;
margin-left: -8px;
}
}
.block-click {
pointer-events: none;
}
}
}
}
// widgets
.widget.number-widget-box {
border: 1px solid var(--border-color);
@ -1187,7 +981,7 @@ body {
}
.codex-editor {
min-height: calc(100vh - var(--navbar-height) - var(--page-head-height));
min-height: calc(100vh - 165px);
.codex-editor__redactor {
display: flex;
@ -1216,7 +1010,7 @@ body {
.new-block-button {
position: absolute;
top: 14px;
left: -22px;
left: -5px;
cursor: pointer;
visibility: hidden;
opacity: 0;
@ -1326,7 +1120,6 @@ body {
}
.ce-header {
padding-left: 7px !important;
margin-bottom: 0 !important;
flex: 1;
@ -1394,7 +1187,6 @@ body {
}
&.spacer {
border-bottom: 1px solid var(--border-color);
margin: 0px -15px;
height: 0px;
padding: 0px;

View file

@ -23,15 +23,14 @@
}
.std-form-layout > .form-layout > .form-page {
border: 1px solid var(--border-color);
border-top: 0px;
box-shadow: none;
background-color: var(--card-bg);
}
.form-section,
.form-dashboard-section {
margin: 0px;
max-width: var(--page-max-width);
margin: auto;
.form-section-description {
margin-bottom: 10px;
@ -98,7 +97,7 @@
.form-section.card-section,
.form-dashboard-section {
border-bottom: 1px solid var(--border-color);
padding: var(--padding-xs);
padding: var(--padding-md) 0;
}
.row.form-section.card-section.visible-section:last-child {
@ -435,13 +434,21 @@
justify-content: space-between;
vertical-align: middle;
.clearfix {
width: 38%;
}
.control-input-wrapper {
width: 50%;
width: 62%; // golden ratio
}
}
}
.form-footer {
max-width: var(--page-max-width);
margin: auto;
padding-left: 15px;
h5 {
margin: 15px 0px;
font-weight: bold;
@ -459,7 +466,6 @@
background-color: var(--card-bg);
z-index: 5;
transition: 0.5s top;
padding-left: var(--padding-xs);
border-bottom: 1px solid var(--border-color);
border-radius: var(--border-radius-md) var(--border-radius-md) 0 0;
@ -486,6 +492,7 @@
}
}
.form-tab-content {
margin-top: var(--margin-md);
scroll-margin-top: calc(var(--navbar-height) + 52px);
}
.form-tabs-sticky-up {

View file

@ -9,15 +9,6 @@ body {
font-weight: var(--weight-regular);
letter-spacing: 0.02em;
background-color: var(--bg-color);
&.full-width {
@include media-breakpoint-up(md) {
.container {
width: 90%;
max-width: 100%;
}
}
}
}
a {
@ -96,6 +87,10 @@ pre {
white-space: pre-wrap;
}
.container {
max-width: 100%;
}
.col-xs-1 {
@extend .col-1;
}
@ -585,10 +580,6 @@ body.no-sidebar {
.layout-side-section {
display: none;
}
.layout-main-section-wrapper {
width: 100% !important;
}
}
}

View file

@ -52,7 +52,6 @@
.layout-main-section-wrapper {
flex: 1;
max-width: 100%;
}
}
}
@ -61,6 +60,7 @@
.list-row-container {
display: flex;
flex-direction: column;
padding: var(--padding-xs) var(--padding-md);
border-bottom: 1px solid $border-color;
&:focus {
@ -69,6 +69,11 @@
}
}
&:first-child {
padding-top: 0;
border-bottom: none;
}
&:last-child {
border-bottom: none;
}
@ -77,6 +82,7 @@
.list-row {
padding-right: 15px;
height: var(--list-row-height);
border-radius: var(--border-radius);
cursor: pointer;
transition: color 0.2s;
-webkit-transition: color 0.2s;
@ -164,7 +170,8 @@
@extend .list-row;
cursor: default;
background-color: var(--subtle-fg);
height: 36px;
border-radius: var(--border-radius);
height: var(--list-row-height);
.list-subject {
font-weight: normal;
@ -225,7 +232,7 @@ $level-margin-right: 8px;
}
}
.frappe-card {
.layout-main-list {
.list-paging-area,
.footnote-area {
padding: var(--padding-md);

View file

@ -127,29 +127,6 @@ body {
.sidebar .user-menu img {
margin-top: -1px;
}
// body[data-route=""],
// body[data-route="desktop"] {
// .navbar .navbar-home {
// // display: none !important;
// &:before {
// display: none;
// }
// img {
// margin-top: 0;
// }
// }
// .toggle-sidebar {
// display: none !important;
// }
// }
// body[data-sidebar="0"] {
// .toggle-sidebar {
// display: none !important;
// }
// }
}
@media (max-width: 767px) {
@ -198,6 +175,10 @@ body {
border-right-color: transparent !important;
}
.layout-side-section {
display: none;
}
// listviews
.select-like {
margin-right: unset !important;
@ -264,48 +245,6 @@ body {
}
}
#page-chat {
.layout-side-section {
position: relative;
left: 0px;
border-right: 1px solid var(--border-color);
padding-left: 0px;
float: left;
// hack! to prevent overlap of borders
width: 76px;
}
.layout-main-section-wrapper {
position: absolute;
left: 75px;
right: 0px;
border-left: 1px solid var(--border-color);
float: left;
}
.module-sidebar-item {
margin: 0px;
.chat-sidebar-link {
padding: var(--padding-md);
}
}
.timeline-head {
padding: 15px 15px 7px;
}
.list-row {
padding: 7px 0px;
}
.message-row-right {
margin-top: var(--margin-sm);
text-align: left;
}
}
.timeline {
&::before {
content: none;

View file

@ -1,6 +1,6 @@
.navbar {
height: $navbar-height;
background: var(--subtle-accent);
background: var(--navbar-bg);
border-bottom: 1px solid var(--border-color);
padding: 0;
.navbar-brand {

View file

@ -24,6 +24,14 @@
}
}
}
@include media-breakpoint-up(md) {
// only for small screens
.sidebar-toggle-btn {
display: none;
}
}
.title-area {
display: inline-flex;
align-items: center;
@ -46,9 +54,9 @@
.page-container {
background-color: var(--bg-color);
.page-body.full-width {
.page-body {
width: 100%;
max-width: 100%;
padding: 0px;
}
}
@ -85,16 +93,14 @@
}
.layout-main-section-wrapper {
min-width: 0;
width: 100%;
}
.layout-main-section.frappe-card {
overflow: hidden;
@include card($padding: 0px);
box-shadow: none;
border: 1px solid var(--border-color);
border-top: 0px;
border-radius: 0px;
border: none;
}
.page-head {
@ -112,15 +118,14 @@
.page-form {
margin: 0;
padding: var(--padding-xs);
padding: var(--padding-xs) var(--padding-md);
display: flex;
flex-wrap: wrap;
background-color: var(--card-bg);
border-bottom: 1px solid var(--border-color);
.form-group {
padding: 0px 5px;
margin: 5px 0px;
padding: 0 10px 0 0;
margin: 5px 0;
}
.checkbox {
margin-top: 4px;
@ -164,6 +169,12 @@
margin-right: var(--margin-sm);
}
}
.layout-main {
display: flex;
flex-direction: row;
}
.layout-main-section {
scroll-margin-top: var(--navbar-height);
@ -172,7 +183,7 @@
.result,
.no-result,
.freeze {
min-height: #{"calc(100vh - 284px)"};
min-height: #{"calc(100vh - 224px)"};
}
}

View file

@ -89,6 +89,7 @@
}
.report-view {
max-width: 100%;
.result {
.dt-row:last-child:not(.dt-row-filter) {
.dt-cell {

View file

@ -14,10 +14,6 @@
body[data-route=""] .main-menu,
body[data-route="desk"] .main-menu {
.desk-sidebar {
display: block !important;
}
@extend .hide-form-sidebar;
}
@ -37,6 +33,149 @@ body[data-route^="Module"] .main-menu {
@extend .hide-form-sidebar;
}
.main-section {
width: 100%;
height: 100vh;
overflow: scroll;
overflow-x: hidden;
overflow-y: visible;
}
@include media-breakpoint-up(md) {
body {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: flex-start;
justify-content: flex-start;
position: relative;
}
}
.body-sidebar-container {
display: flex;
flex-direction: column;
height: fit-content;
width: fit-content;
}
.body-sidebar {
min-width: 50px;
background: var(--subtle-accent);
border: 1px solid var(--border-color);
display: flex;
align-items: flex-start;
flex-direction: column;
flex-wrap: nowrap;
gap: 2px;
height: 100vh;
justify-content: flex-start;
overflow-x: hidden;
overflow-y: auto;
padding: 15px;
z-index: 100;
position: static;
font-size: var(--text-base);
transition: transform 200ms ease-in-out;
.app-logo {
width: 21px;
}
.sidebar-items {
width: 100%;
}
.standard-sidebar-section {
margin-bottom: var(--margin-xl);
&:last-of-type {
margin-bottom: var(--margin-sm);
}
&:first-child {
margin-top: var(--margin-xl);
}
}
.standard-sidebar-item {
display: flex;
justify-content: space-between;
padding: 0px;
.sidebar-item-control {
display: none;
> * {
align-self: center;
margin-left: 3px;
box-shadow: none;
}
.drop-icon {
padding: 0px 12px 0px 2px;
}
svg {
margin-right: 0;
margin-top: 3px;
}
}
.sidebar-item-label {
display: none;
}
.item-anchor {
display: flex;
overflow: hidden;
gap: 5px;
align-items: center;
padding: 3px 0px 3px 11px;
flex: 1;
}
}
.edit-sidebar-link {
display: none;
}
&:hover {
width: 200px;
.sidebar-item-label {
display: flex;
}
.sidebar-item-control {
display: block;
}
.edit-sidebar-link {
display: block;
}
}
.sidebar-item-container {
position: relative;
margin-left: -10px;
margin-bottom: 6px;
/* nested container */
.sidebar-item-container {
margin-left: 20px;
.standard-sidebar-item {
justify-content: start;
}
}
.indicator {
margin-left: 5px;
}
}
}
.sidebar-menu {
.data-pill {
width: 100%;
@ -65,16 +204,13 @@ body[data-route^="Module"] .main-menu {
// form sidebar
.form-sidebar {
.sidebar-menu {
margin-bottom: var(--margin-sm);
.sidebar-section {
.form-sidebar-items {
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
padding-bottom: var(--padding-sm);
color: var(--text-light);
.icon-btn {
@ -88,10 +224,6 @@ body[data-route^="Module"] .main-menu {
}
.form-tags {
.form-tag-row:not(:last-child) {
margin-bottom: var(--margin-sm);
}
.tag-area {
margin-top: -3px;
margin-left: -4px;
@ -124,14 +256,17 @@ body[data-route^="Module"] .main-menu {
}
}
hr {
margin: 15px -15px;
}
.sidebar-image-section {
width: min(100%, 200px);
width: min(100%, 220px);
cursor: pointer;
border-radius: var(--border-radius-lg);
.sidebar-image {
height: auto;
max-height: 200px;
max-height: 220px;
object-fit: cover;
}
@ -183,12 +318,16 @@ body[data-route^="Module"] .main-menu {
.layout-side-section {
@include get_textstyle("sm", "regular");
// padding-right: 30px;
padding-top: 15px;
padding-right: 5px;
width: 220px;
border-left: 1px solid var(--border-color);
&.right {
padding-left: 5px;
padding-right: 15px;
.sidebar-section {
padding: var(--padding-md);
border-bottom: 1px solid var(--border-color);
}
.sidebar-section:last-child {
border-bottom: none;
}
&.hide-sidebar {
@ -209,13 +348,10 @@ body[data-route^="Module"] .main-menu {
}
.sidebar-label {
font-size: var(--text-xs);
font-weight: var(--weight-regular);
margin-bottom: var(--margin-sm);
text-transform: uppercase;
font-size: var(--text-md);
font-weight: var(--weight-medium);
display: flex;
align-items: center;
color: var(--text-muted);
cursor: pointer;
.es-icon {
@ -313,12 +449,8 @@ body[data-route^="Module"] .main-menu {
}
.list-sidebar {
.sidebar-section {
margin-bottom: 30px;
a {
font-size: var(--text-xs);
}
.sidebar-section a {
font-size: var(--text-xs);
}
.list-link {
@ -348,7 +480,7 @@ body[data-route^="Module"] .main-menu {
max-height: 300px;
overflow-y: auto;
min-width: 180px;
max-width: 250px;
max-width: 220px;
z-index: 100;
.dropdown-item {
@ -421,7 +553,7 @@ body[data-route^="Module"] .main-menu {
.attachment-row,
.form-tag-row {
margin-bottom: 3px;
margin: var(--margin-xs) 0;
max-width: 100%;
.data-pill {
@include get_textstyle("sm", "regular");
@ -435,6 +567,12 @@ body[data-route^="Module"] .main-menu {
background-color: unset;
box-shadow: none;
padding: 0 var(--padding-xs) 0 var(--padding-md) !important;
.attachment-file-label {
width: 94px;
display: block;
margin-left: var(--margin-xs);
}
}
}
@ -464,9 +602,7 @@ body[data-route^="Module"] .main-menu {
margin-bottom: var(--margin-sm);
color: var(--text-light);
}
.form-assignments {
margin-top: 12px;
}
.form-assignments,
.form-shared {
.assignments,