Merge pull request #27499 from rmehta/espresso-sidebar

feat(design): espresso style sidebar
This commit is contained in:
Rushabh Mehta 2024-08-27 09:16:57 +05:30 committed by GitHub
commit dfd7606d03
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
59 changed files with 1030 additions and 1978 deletions

View file

@ -10,8 +10,10 @@ context("Awesome Bar", () => {
});
beforeEach(() => {
cy.get(".navbar .navbar-home").click();
cy.findByPlaceholderText("Search or type a command (Ctrl + G)").as("awesome_bar");
let txt = `Search or type a command (${
window.navigator.platform === "MacIntel" ? "⌘" : "Ctrl"
} + G)`;
cy.findByPlaceholderText(txt).as("awesome_bar");
cy.get("@awesome_bar").type("{selectall}");
});
@ -35,13 +37,13 @@ context("Awesome Bar", () => {
cy.get("@awesome_bar").type("{enter}");
cy.get(".title-text").should("contain", "To Do");
cy.wait(400); // Wait a bit longer before checking the filter.
cy.get('[data-original-title="ID"] > input').should("have.value", "%test%");
cy.get('[data-original-title="ID"]:visible > input').should("have.value", "%test%");
// filter preserved, now finds something else
cy.visit("/app/todo");
cy.get(".title-text").should("contain", "To Do");
cy.wait(200); // Wait a bit longer before checking the filter.
cy.get('[data-original-title="ID"] > input').as("filter");
cy.get('[data-original-title="ID"]:visible > input').as("filter");
cy.get("@filter").should("have.value", "%test%");
cy.get("@awesome_bar").type("anothertest in todo");
cy.wait(200); // Wait a bit longer before hitting enter.

View file

@ -71,6 +71,8 @@ context("Form", () => {
let expectBackgroundColor = "rgb(255, 245, 245)";
cy.visit("/app/contact/new");
cy.fill_field("company_name", "Test Company");
cy.get('.frappe-control[data-fieldname="email_ids"]').as("table");
cy.get("@table").find("button.grid-add-row").click();
cy.get("@table").find("button.grid-add-row").click();
@ -80,7 +82,6 @@ context("Form", () => {
cy.get("@row1").find("input.input-with-feedback.form-control").as("email_input1");
cy.get("@email_input1").type(website_input, { waitForAnimations: false });
cy.fill_field("company_name", "Test Company");
cy.get("@row2").click();
cy.get("@row2").find("input.input-with-feedback.form-control").as("email_input2");

View file

@ -20,6 +20,8 @@ context("Workspace 2.0", () => {
cy.get(".codex-editor__redactor .ce-block");
cy.get(".btn-new-workspace").click();
cy.fill_field("title", "Test Private Page", "Data");
cy.wait(300);
cy.get_open_dialog().find(".modal-header").click();
cy.get_open_dialog().find(".btn-primary").click();
@ -29,7 +31,7 @@ context("Workspace 2.0", () => {
"item-public",
"0"
);
cy.wait(300);
cy.get('.standard-actions .btn-primary[data-label="Save"]').click();
cy.wait(300);
cy.get('.sidebar-item-container[item-name="Test Private Page"]').should(
@ -52,6 +54,7 @@ context("Workspace 2.0", () => {
cy.fill_field("title", "Test Child Page", "Data");
cy.fill_field("parent", "Test Private Page", "Select");
cy.get_open_dialog().find(".modal-header").click();
cy.wait(300);
cy.get_open_dialog().find(".btn-primary").click();
// check if sidebar item is added in pubic section
@ -60,7 +63,7 @@ context("Workspace 2.0", () => {
"item-public",
"0"
);
cy.wait(300);
cy.get('.standard-actions .btn-primary[data-label="Save"]').click();
cy.wait(300);
cy.get('.sidebar-item-container[item-name="Test Child Page"]').should(
@ -72,84 +75,12 @@ context("Workspace 2.0", () => {
cy.wait("@new_page");
});
it("Duplicate Page", () => {
cy.intercept({
method: "POST",
url: "api/method/frappe.desk.doctype.workspace.workspace.duplicate_page",
}).as("page_duplicated");
cy.get(".codex-editor__redactor .ce-block");
cy.get(".btn-edit-workspace").click();
cy.get('.sidebar-item-container[item-name="Test Private Page"]').as("sidebar-item");
cy.get("@sidebar-item").find(".standard-sidebar-item").first().click();
cy.get("@sidebar-item").find(".dropdown-btn").first().click();
cy.get("@sidebar-item")
.find(".dropdown-list .dropdown-item")
.contains("Duplicate")
.first()
.click({ force: true });
cy.get_open_dialog().fill_field("title", "Duplicate Page", "Data");
cy.click_modal_primary_button("Duplicate");
cy.wait("@page_duplicated");
});
it("Drag Sidebar Item", () => {
cy.intercept({
method: "POST",
url: "api/method/frappe.desk.doctype.workspace.workspace.sort_pages",
}).as("page_sorted");
cy.get('.sidebar-item-container[item-name="Duplicate Page"]').as("sidebar-item");
cy.get("@sidebar-item").find(".standard-sidebar-item").first().click();
cy.get("@sidebar-item").find(".drag-handle").first().move({ deltaX: 0, deltaY: 100 });
cy.get('.sidebar-item-container[item-name="Build"]').as("sidebar-item");
cy.get("@sidebar-item").find(".standard-sidebar-item").first().click();
cy.get("@sidebar-item").find(".drag-handle").first().move({ deltaX: 0, deltaY: 100 });
cy.wait("@page_sorted");
});
it("Edit Page Detail", () => {
cy.intercept({
method: "POST",
url: "api/method/frappe.desk.doctype.workspace.workspace.update_page",
}).as("page_updated");
cy.get('.sidebar-item-container[item-name="Test Private Page"]').as("sidebar-item");
cy.get("@sidebar-item").find(".standard-sidebar-item").first().click();
cy.get("@sidebar-item").find(".dropdown-btn").first().click();
cy.get("@sidebar-item")
.find(".dropdown-list .dropdown-item")
.contains("Edit")
.first()
.click({ force: true });
cy.get_open_dialog().fill_field("title", " 1", "Data");
cy.get_open_dialog().find('input[data-fieldname="is_public"]').check();
cy.click_modal_primary_button("Update");
cy.get(
'.standard-sidebar-section:first .sidebar-item-container[item-name="Test Private Page"]'
).should("not.exist");
cy.get(
'.standard-sidebar-section:last .sidebar-item-container[item-name="Test Private Page 1"]'
).should("exist");
cy.wait("@page_updated");
});
it("Add New Block", () => {
cy.get('.sidebar-item-container[item-name="Duplicate Page"]').as("sidebar-item");
cy.get('.sidebar-item-container[item-name="Test Private Page"]').as("sidebar-item");
cy.get("@sidebar-item").find(".standard-sidebar-item").first().click();
cy.get("@sidebar-item").find(".standard-sidebar-item").first().click({ force: true });
cy.get(".btn-edit-workspace").click({ force: true });
cy.get(".ce-block").click().type("{enter}");
cy.get(".block-list-container .block-list-item").contains("Heading").click();
@ -184,71 +115,7 @@ context("Workspace 2.0", () => {
cy.get(".ce-block:last").should("have.class", "col-xs-11");
cy.get(".ce-block:last .dropdown-item").contains("Expand").click();
cy.get(".ce-block:last").should("have.class", "col-xs-12");
cy.wait(300);
cy.get('.standard-actions .btn-primary[data-label="Save"]').click();
});
it("Hide/Unhide Workspaces", () => {
// hide
cy.intercept({
method: "POST",
url: "api/method/frappe.desk.doctype.workspace.workspace.hide_page",
}).as("hide_page");
cy.get(".codex-editor__redactor .ce-block");
cy.get(".btn-edit-workspace").click();
cy.get('.sidebar-item-container[item-name="Duplicate Page"]')
.find(".sidebar-item-control .setting-btn")
.click();
cy.get('.sidebar-item-container[item-name="Duplicate Page"]')
.find('.dropdown-item[title="Hide Workspace"]')
.click({ force: true });
cy.wait(300);
cy.get('.standard-actions .btn-secondary[data-label="Discard"]').click();
cy.get('.sidebar-item-container[item-name="Duplicate Page"]').should("not.be.visible");
cy.wait("@hide_page");
// unhide
cy.intercept({
method: "POST",
url: "api/method/frappe.desk.doctype.workspace.workspace.unhide_page",
}).as("unhide_page");
cy.get(".codex-editor__redactor .ce-block");
cy.get(".btn-edit-workspace").click();
cy.get('.sidebar-item-container[item-name="Duplicate Page"]')
.find('[title="Unhide Workspace"]')
.click({ force: true });
cy.wait(300);
cy.get('.standard-actions .btn-secondary[data-label="Discard"]').click();
cy.get('.sidebar-item-container[item-name="Duplicate Page"]').should("be.visible");
cy.wait("@unhide_page");
});
it("Delete Duplicate Page", () => {
cy.intercept({
method: "POST",
url: "api/method/frappe.desk.doctype.workspace.workspace.delete_page",
}).as("page_deleted");
cy.get(".codex-editor__redactor .ce-block");
cy.get(".btn-edit-workspace").click();
cy.get('.sidebar-item-container[item-name="Duplicate Page"]')
.find(".sidebar-item-control .setting-btn")
.click();
cy.get('.sidebar-item-container[item-name="Duplicate Page"]')
.find('.dropdown-item[title="Delete Workspace"]')
.click({ force: true });
cy.wait(300);
cy.get(".modal-footer > .standard-actions > .btn-modal-primary:visible").first().click();
cy.get('.sidebar-item-container[item-name="Duplicate Page"]').should("not.exist");
cy.wait("@page_deleted");
});
});

View file

@ -29,7 +29,7 @@ context("Workspace Blocks", () => {
"item-public",
"0"
);
cy.wait(300);
cy.get('.standard-actions .btn-primary[data-label="Save"]').click();
cy.wait(300);
cy.get('.sidebar-item-container[item-name="Test Block Page"]').should(

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

@ -77,10 +77,6 @@ frappe.ui.form.on("DocType", {
let msg = __(
"This site is running in developer mode. Any change made here will be updated in code."
);
msg += "<br>";
msg += __("If you just want to customize for your site, use {0} instead.", [
customize_form_link,
]);
frm.dashboard.add_comment(msg, "yellow", true);
}

View file

@ -7,7 +7,7 @@
"doctype": "Workspace",
"for_user": "",
"hide_custom": 0,
"icon": "tool",
"icon": "organization",
"idx": 1,
"is_hidden": 0,
"label": "Build",
@ -301,7 +301,7 @@
"type": "Link"
}
],
"modified": "2024-08-16 12:31:51.279839",
"modified": "2024-08-26 15:14:40.193261",
"modified_by": "Administrator",
"module": "Core",
"name": "Build",
@ -312,7 +312,7 @@
"quick_lists": [],
"restrict_to_domain": "",
"roles": [],
"sequence_id": 27.0,
"sequence_id": 2.0,
"shortcuts": [
{
"color": "Grey",

View file

@ -20,15 +20,11 @@ frappe.ui.form.on("Workspace", {
.attr("target", "_blank");
frm.layout.message.empty();
let message = __(
"This document allows you to edit limited fields. For all kinds of workspace customization, use the Edit button located on the workspace page"
);
let message = __("Please click Edit on the Workspace for best results");
if (
frm.doc.for_user ||
(frm.doc.public &&
!frm.has_perm("write") &&
!frappe.user.has_role("Workspace Manager"))
(frm.doc.for_user && frm.doc.for_user !== frappe.session.user) ||
(frm.doc.public && !frappe.user.has_role("Workspace Manager"))
) {
frm.trigger("disable_form");

View file

@ -77,8 +77,7 @@
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Module",
"options": "Module Def",
"read_only": 1
"options": "Module Def"
},
{
"fieldname": "column_break_3",
@ -101,8 +100,7 @@
{
"fieldname": "for_user",
"fieldtype": "Data",
"label": "For User",
"read_only": 1
"label": "For User"
},
{
"default": "0",
@ -114,8 +112,7 @@
{
"fieldname": "icon",
"fieldtype": "Icon",
"label": "Icon",
"read_only": 1
"label": "Icon"
},
{
"fieldname": "links",
@ -137,7 +134,6 @@
"fieldname": "title",
"fieldtype": "Data",
"label": "Title",
"read_only": 1,
"reqd": 1
},
{
@ -156,8 +152,7 @@
{
"fieldname": "sequence_id",
"fieldtype": "Float",
"label": "Sequence Id",
"read_only": 1
"label": "Sequence Id"
},
{
"fieldname": "roles",
@ -219,7 +214,7 @@
],
"in_create": 1,
"links": [],
"modified": "2024-05-30 17:30:36.791171",
"modified": "2024-08-26 17:16:05.820503",
"modified_by": "Administrator",
"module": "Desk",
"name": "Workspace",

View file

@ -6,7 +6,7 @@ from json import loads
import frappe
from frappe import _
from frappe.desk.desktop import save_new_widget
from frappe.desk.desktop import get_workspace_sidebar_items, save_new_widget
from frappe.desk.utils import validate_route_conflict
from frappe.model.document import Document
from frappe.model.rename_doc import rename_doc
@ -261,7 +261,7 @@ def new_page(new_page):
doc = frappe.new_doc("Workspace")
doc.title = page.get("title")
doc.icon = page.get("icon")
doc.icon = page.get("icon") or "dashboard"
doc.indicator_color = page.get("indicator_color")
doc.content = page.get("content")
doc.parent_page = page.get("parent_page")
@ -271,7 +271,7 @@ def new_page(new_page):
doc.sequence_id = last_sequence_id(doc) + 1
doc.save(ignore_permissions=True)
return doc
return get_workspace_sidebar_items()
@frappe.whitelist()
@ -341,125 +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):
return
new_page = loads(new_page)
if new_page.get("is_public") and not is_workspace_manager():
return
old_doc = frappe.get_doc("Workspace", page_name)
doc = frappe.copy_doc(old_doc)
doc.title = new_page.get("title")
doc.icon = new_page.get("icon")
doc.indicator_color = new_page.get("indicator_color")
doc.parent_page = new_page.get("parent") or ""
doc.public = new_page.get("is_public")
doc.for_user = ""
doc.label = doc.title
doc.module = ""
if not doc.public:
doc.for_user = doc.for_user or frappe.session.user
doc.label = f"{doc.title}-{doc.for_user}"
doc.name = doc.label
if old_doc.public == doc.public:
doc.sequence_id += 0.1
else:
doc.sequence_id = last_sequence_id(doc) + 1
doc.insert(ignore_permissions=True)
return doc
@frappe.whitelist()
def delete_page(page):
if not loads(page):
return
page = loads(page)
if page.get("public") and not is_workspace_manager():
frappe.throw(
_("Cannot delete public workspace without Workspace Manager role"),
frappe.PermissionError,
)
elif not page.get("public") and not is_workspace_manager():
workspace_owner = frappe.get_value("Workspace", page.get("name"), "for_user")
if workspace_owner != frappe.session.user:
frappe.throw(
_("Cannot delete private workspace of other users"),
frappe.PermissionError,
)
if frappe.db.exists("Workspace", page.get("name")):
frappe.get_doc("Workspace", page.get("name")).delete(ignore_permissions=True)
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

@ -397,7 +397,6 @@ frappe.setup.slides_settings = [
fieldtype: "Select",
reqd: 1,
},
{ fieldtype: "Column Break" },
{
fieldname: "currency",
label: __("Currency"),

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

@ -1,6 +1,7 @@
frappe.ui.form.ControlTableMultiSelect = class ControlTableMultiSelect extends (
frappe.ui.form.ControlLink
) {
static horizontal = false;
make_input() {
super.make_input();

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

@ -27,8 +27,8 @@ export default class Section {
make() {
let make_card = this.card_layout;
this.wrapper = $(`<div class="row
${this.df.is_dashboard_section ? "form-dashboard-section" : "form-section"}
this.wrapper = $(`<div class=
"${this.df.is_dashboard_section ? "form-dashboard-section" : "form-section"}
${make_card ? "card-section" : ""}" data-fieldname="${this.df.fieldname}">
`).appendTo(this.parent);

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

@ -71,6 +71,7 @@ frappe.ui.form.Sidebar = class {
frappe.utils.get_page_view_count(route).then((res) => {
this.sidebar
.find(".pageview-count")
.removeClass("hidden")
.html(__("{0} Web page views", [String(res.message).bold()]));
});
}
@ -145,6 +146,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);
@ -198,7 +200,9 @@ frappe.ui.form.Sidebar = class {
return $("<a>")
.html(label)
.appendTo(
$('<li class="user-action-row">').appendTo(this.user_actions.removeClass("hidden"))
$('<div class="user-action-row"></div>').appendTo(
this.user_actions.removeClass("hidden")
)
)
.on("click", click);
}

View file

@ -3,7 +3,7 @@
<div class="comment-box"></div>
<div class="timeline"></div>
</div>
<button class="scroll-to-top btn btn-default icon-btn" onclick="frappe.utils.scroll_to(0)">
<button class="scroll-to-top btn btn-default icon-btn" onclick="frappe.utils.scroll_page_to_top()">
<svg class="icon icon-xs"><use href="#icon-up-line"></use></svg>
</button>
</div>

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,32 +16,34 @@
</div>
</div>
{% endif %}
</li>
</ul>
</div>
</div>
{% if frm.meta.beta %}
<div class="sidebar-menu">
<div class="sidebar-section">
<p><label class="indicator-pill yellow" title="{{ __("This feature is brand new and still experimental") }}">{{ __("Experimental") }}</label></p>
<p><a class="small text-muted" href="https://github.com/frappe/{{ frappe.boot.module_app[frappe.scrub(frm.meta.module)] }}/issues/new"
<div><a class="small text-muted" href="https://github.com/frappe/{{ frappe.boot.module_app[frappe.scrub(frm.meta.module)] }}/issues/new"
target="_blank">
{{ __("Click here to post bugs and suggestions") }}</a></p>
{{ __("Report bug") }}</a></div>
</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 hidden"></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");
@ -435,6 +435,20 @@ frappe.views.BaseList = class BaseList {
});
}
set_result_height() {
// place it at the footer of the page
this.$result.css({
height:
window.innerHeight -
this.$result.get(0).offsetTop -
this.$paging_area.get(0).offsetHeight +
"px",
});
this.$no_result.css({
height: window.innerHeight - this.$no_result.get(0).offsetTop + "px",
});
}
get_fields() {
// convert [fieldname, Doctype] => tabDoctype.fieldname
return this.fields.map((f) => frappe.model.get_full_column_name(f[0], f[1]));
@ -516,6 +530,7 @@ frappe.views.BaseList = class BaseList {
this.before_render();
this.render();
this.after_render();
this.set_result_height();
this.freeze(false);
this.reset_defaults();
if (this.settings.refresh) {
@ -581,8 +596,10 @@ frappe.views.BaseList = class BaseList {
this.$paging_area.toggle(this.data.length > 0);
this.$no_result.toggle(this.data.length == 0);
const show_more = this.start + this.page_length <= this.data.length;
this.$paging_area.find(".btn-more").toggle(show_more);
if (this.data.length) {
const show_more = this.start + this.page_length <= this.data.length;
this.$paging_area.find(".btn-more").toggle(show_more);
}
}
call_for_selected_items(method, args = {}) {

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) {
@ -297,16 +298,8 @@ frappe.views.ListSidebar = class ListSidebar {
const cta = "Frappe Insights";
this.insights_banner = $(`
<div style="position: relative;">
<div class="pr-3">
${message} <a href="${link}" target="_blank" style="color: var(--text-color)">${cta} &rarr; </a>
</div>
<div style="position: absolute; top: -1px; right: -4px; cursor: pointer;" title="Dismiss"
onclick="localStorage.setItem('show_insights_banner', 'false') || this.parentElement.remove()">
<svg class="icon icon-sm" style="">
<use class="" href="#icon-close"></use>
</svg>
</div>
<div class="sidebar-section">
${message} <a href="${link}" target="_blank" style="color: var(--text-color)">${cta} &rarr; </a>
</div>
`).appendTo(this.sidebar);
} catch (error) {

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

@ -106,8 +106,6 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
});
}
if (this.view_name == "List") this.toggle_paging = true;
this.patch_refresh_and_load_lib();
return this.get_list_view_settings();
}
@ -566,6 +564,11 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
if (this.$result.find(".list-row-head").length === 0) {
// append header once
this.$result.prepend(this.get_header_html());
if (this.filter_area.filter_list.get_filter_value("_liked_by")) {
// if there is a liked fitler, then add liked
this.$result.find(".list-liked-by-me").addClass("liked");
}
}
}
@ -584,17 +587,11 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
sort_by: this.sort_selector && this.sort_selector.sort_by,
sort_order: this.sort_selector && this.sort_selector.sort_order,
});
this.toggle_paging && this.$paging_area.toggle(false);
}
after_render() {
this.$no_result.html(`
<div class="no-result text-muted flex justify-center align-center">
${this.get_no_result_message()}
</div>
`);
this.$no_result.html(this.get_no_result_message());
this.setup_new_doc_event();
this.toggle_paging && this.$paging_area.toggle(true);
}
render() {
@ -605,6 +602,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
render_list() {
// clear rows
this.$result.find(".list-row-container").remove();
this.render_header();
if (this.data.length > 0) {
// append rows
@ -1464,9 +1462,9 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
this.$result.on("click", ".list-liked-by-me", (e) => {
const $this = $(e.currentTarget);
$this.toggleClass("active");
$this.toggleClass("liked");
if ($this.hasClass("active")) {
if ($this.hasClass("liked")) {
this.filter_area.add(
this.doctype,
"_liked_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

@ -169,12 +169,6 @@ frappe.router = {
} else if (route[0] == "private") {
// private workspace
let private_workspace = route[1] && `${route[1]}-${frappe.user.name.toLowerCase()}`;
if (!frappe.workspaces[private_workspace] && localStorage.new_workspace) {
let new_workspace = JSON.parse(localStorage.new_workspace);
if (frappe.router.slug(new_workspace.title) === route[1]) {
frappe.workspaces[private_workspace] = new_workspace;
}
}
if (!frappe.workspaces[private_workspace]) {
frappe.msgprint(__("Workspace <b>{0}</b> does not exist", [route[1]]));
return ["Workspaces"];

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,249 @@
frappe.provide("frappe.ui");
frappe.ui.Sidebar = class Sidebar {
constructor({ wrapper, css_class }) {
this.wrapper = wrapper;
this.css_class = css_class;
constructor() {
this.items = {};
if (!frappe.boot.setup_complete) {
// no sidebar if setup is not complete
return;
}
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-placeholder"></div>
<div class="body-sidebar">
<a href="/app" style="text-decoration: none">
<div class="standard-sidebar-item">
<div class="sidebar-item-icon">
<img
class="app-logo"
src="${frappe.boot.app_logo_url}"
alt="${__("App Logo")}"
>
</div>
<div class="sidebar-item-label" style="margin-left: 5px; margin-top: 1px">
${__(frappe.boot.sysdefaults.app_name)}
</div>
</div>
</a>
<div class="sidebar-items">
</div>
<div class="mb-4">
<a class="edit-sidebar-link text-extra-muted">Edit sidebar</a>
</div>
</div>
</div>
`).prependTo("body");
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;
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

@ -299,6 +299,9 @@ Object.assign(frappe.utils, {
);
return content.html();
},
scroll_page_to_top() {
$(".main-section").scrollTop(0);
},
scroll_to: function (
element,
animate = true,

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

@ -49,7 +49,7 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
this.setup_columns();
super.setup_new_doc_event();
this.setup_events();
this.page.main.addClass("report-view");
this.page.main.parent().addClass("report-view");
}
setup_events() {
@ -98,7 +98,7 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
);
this.$paging_area
.find(".level-left")
.after(`<span class="comparison-message text-muted">${message}</span>`);
.after(`<span class="comparison-message text-extra-muted">${message}</span>`);
}
setup_sort_selector() {

File diff suppressed because it is too large Load diff

View file

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

View file

@ -6,7 +6,7 @@ $disabled-input-height: 22px;
--brand-color: var(--primary);
--padding-xs: 5px;
--padding-sm: 6px;
--padding-sm: 7px;
--padding-md: 15px;
--padding-lg: 20px;
--padding-xl: 30px;
@ -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

@ -17,7 +17,7 @@
}
min-height: var(--input-height);
border-radius: var(--border-radius-sm);
padding: var(--disabled-input-padding);
padding: 4px 8px;
cursor: default;
color: var(--disabled-text-color);
background-color: var(--disabled-control-bg);

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

@ -21,6 +21,8 @@
.grid-row {
background-color: var(--subtle-fg);
border-top-left-radius: var(--border-radius-md);
border-top-right-radius: var(--border-radius-md);
}
&.with-filter {
@ -123,7 +125,7 @@
.grid-static-col,
.row-index {
height: 38px;
// height: 38px;
max-height: 200px;
&.search {
@ -137,11 +139,12 @@
}
.row-check {
height: 38px;
// height: 38px;
text-align: center;
input {
margin-right: 0 !important;
margin-bottom: -3px;
}
&.search {
@ -150,7 +153,7 @@
}
.grid-row-check {
margin-top: 2px;
margin-top: 3px;
&::after {
// Extend the checkbox's clickable area
@ -290,7 +293,8 @@
}
.grid-static-col[data-fieldtype="Check"] .static-area {
padding-top: 2px;
padding-top: 4px;
margin-bottom: -4px;
}
.grid-static-col[data-fieldtype="Rating"] .field-area {
@ -360,7 +364,7 @@
}
.frappe-control {
margin-bottom: 0px;
margin-bottom: 0px !important;
position: relative;
}

View file

@ -52,7 +52,7 @@
.frappe-control[data-fieldtype="Icon"] {
input {
padding-left: 40px;
padding-left: 30px;
}
.selected-icon {
cursor: pointer;
@ -73,6 +73,17 @@
cursor: default;
}
}
.horizontal {
.selected-icon {
top: 4px;
left: calc(38% + 5px);
}
.like-disabled-input {
.selected-icon {
left: 8px;
}
}
}
}
.data-row.row {

View file

@ -43,8 +43,8 @@ use.like-icon {
.liked {
use.like-icon {
--icon-stroke: var(--red-500);
fill: var(--icon-stroke);
--icon-stroke: var(--invert-neutral);
fill: var(--invert-neutral);
}
}

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;
@ -234,11 +238,14 @@ body.modal-open[style^="padding-right"] {
&[data-fieldname="sender"] {
margin-right: 10px;
}
flex: 1;
.control-input-wrapper {
width: 60%;
}
}
.frappe-control:last-child {
margin-left: 10px;
margin-bottom: -24px;
button {
// same as form-control input
height: calc(1.5em + 0.7rem);
@ -257,10 +264,14 @@ body.modal-open[style^="padding-right"] {
margin-right: 10px;
}
flex: 1;
.control-input-wrapper {
width: 53%.6;
}
}
.frappe-control:last-child {
margin-bottom: -8px;
margin-top: -14px;
}
}
}

View file

@ -6,8 +6,7 @@
}
/*! This comment will be included even in compressed mode. */
#navbar-breadcrumbs {
margin-left: var(--margin-md);
@include get_textstyle("base", "regular");
@include get_textstyle("sm", "regular");
a {
margin-right: 10px;
&:before {
@ -17,6 +16,11 @@
}
}
li:first-child a:before {
// no angle before first item
content: none;
}
li.disabled {
a {
color: var(--gray-600);

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);
@ -465,7 +438,7 @@ body {
}
span {
color: var(--gray-700);
color: var(--text-color);
}
}
@ -600,7 +573,7 @@ body {
.link-item {
display: flex;
text-decoration: none;
@include get_textstyle("base", "regular");
@include get_textstyle("sm", "regular");
color: var(--text-color);
padding: 4px;
margin-left: -4px;
@ -650,8 +623,6 @@ body {
}
@include media-breakpoint-down(md) {
padding: 1px 8px;
.link-item {
&:first-child {
margin-top: 5px;
@ -930,6 +901,7 @@ body {
}
.workspace-skeleton {
width: 100%;
transition: ease;
.widget-group-title {
height: 15px;
@ -948,59 +920,15 @@ 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;
padding: var(--padding-sm) var(--padding-md);
margin-bottom: var(--margin-sm);
border: 0px;
&.edit-mode {
background-color: var(--subtle-fg) !important;
@ -1013,13 +941,14 @@ body {
}
}
}
.desk-sidebar {
margin-bottom: var(--margin-2xl);
}
}
}
.page-head .container {
max-width: var(--page-max-width);
margin: auto;
}
.layout-main-section-wrapper {
margin-top: -5px;
padding-top: 5px;
@ -1028,7 +957,7 @@ body {
.layout-main-section {
background-color: var(--fg-color);
box-shadow: none;
padding: var(--padding-sm);
padding: var(--padding-sm) var(--padding-md);
}
.workspace-footer {
@ -1044,139 +973,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,14 +983,14 @@ body {
}
.codex-editor {
min-height: calc(100vh - var(--navbar-height) - var(--page-head-height));
min-height: calc(100vh - 165px);
.codex-editor__redactor {
display: flex;
flex-wrap: wrap;
flex-direction: row;
margin: 0px -7px;
padding-bottom: 20px !important;
margin: 0 -4px;
.ce-block {
width: 100%;
@ -1216,7 +1012,7 @@ body {
.new-block-button {
position: absolute;
top: 14px;
left: -22px;
left: -5px;
cursor: pointer;
visibility: hidden;
opacity: 0;
@ -1298,10 +1094,6 @@ body {
height: 100%;
padding: 7px;
@include media-breakpoint-down(md) {
padding: 7px 3px;
}
& > div {
height: 100%;
}
@ -1326,7 +1118,6 @@ body {
}
.ce-header {
padding-left: 7px !important;
margin-bottom: 0 !important;
flex: 1;
@ -1394,7 +1185,6 @@ body {
}
&.spacer {
border-bottom: 1px solid var(--border-color);
margin: 0px -15px;
height: 0px;
padding: 0px;

View file

@ -4,7 +4,7 @@
position: absolute;
bottom: 100%;
left: 0;
z-index: 9999;
z-index: 1050;
padding: 2px 6px;
border-radius: var(--border-radius-sm);
background-color: var(--bg-dark-gray);
@ -23,16 +23,12 @@
}
.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;
.form-section-description {
margin-bottom: 10px;
@include get_textstyle("base", "regular");
@ -45,8 +41,6 @@
color: var(--text-color);
width: 100%;
padding: var(--padding-sm) var(--padding-md);
margin: 0;
margin-bottom: var(--margin-sm);
cursor: default;
.collapse-indicator {
@ -66,15 +60,27 @@
}
.section-body {
padding-top: var(--padding-sm);
width: 100%;
@extend .row;
margin: 0;
display: flex;
flex-wrap: wrap;
&:first-child {
margin-top: 1rem;
}
}
}
.section-head,
.section-body {
max-width: var(--page-max-width);
margin: auto !important;
}
#doctype-form_builder_tab .section-body {
max-width: 100% !important;
}
.form-column {
.form-column-description {
margin-bottom: 10px;
@ -98,11 +104,7 @@
.form-section.card-section,
.form-dashboard-section {
border-bottom: 1px solid var(--border-color);
padding: var(--padding-xs);
}
.row.form-section.card-section.visible-section:last-child {
border-bottom: none;
padding: var(--padding-md) 0;
}
.form-section.card-section.hide-border {
@ -227,7 +229,7 @@
font-weight: var(--weight-regular);
background-color: var(--subtle-fg);
border-radius: var(--border-radius-full);
color: var(--gray-700);
color: var(--text-color);
padding: 0 var(--padding-xs);
margin-right: var(--margin-xs);
}
@ -239,10 +241,10 @@
.open-notification {
display: inline-flex;
align-items: center;
background-color: var(--gray-100);
background-color: var(--subtle-bg);
font-size: var(--text-xs);
padding: 0 var(--padding-sm);
color: var(--gray-700);
color: var(--text-color);
border-radius: var(--border-radius);
cursor: pointer;
}
@ -435,13 +437,26 @@
justify-content: space-between;
vertical-align: middle;
.clearfix {
width: 38%;
}
.control-input-wrapper {
width: 50%;
width: 62%; // golden ratio
}
.control-label {
margin-top: 3px;
margin-bottom: 3px;
}
}
}
.form-footer {
max-width: var(--page-max-width);
margin: auto;
padding: 0 15px;
h5 {
margin: 15px 0px;
font-weight: bold;
@ -450,7 +465,8 @@
.scroll-to-top {
position: absolute;
height: 28px;
right: 0;
right: 15px;
bottom: 15px;
}
}
@ -459,7 +475,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;

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;
}
@ -519,7 +514,7 @@ kbd {
// freeze backdrop text
#freeze {
z-index: 1055;
z-index: 2000;
bottom: 0;
opacity: 0;
background-color: var(--bg-color);
@ -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,10 +232,10 @@ $level-margin-right: 8px;
}
}
.frappe-card {
.layout-main-list {
.list-paging-area,
.footnote-area {
padding: var(--padding-md);
padding: var(--padding-sm) 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 {
max-width: 0px;
}
// 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,11 +1,19 @@
.navbar {
height: $navbar-height;
background: var(--subtle-accent);
background: var(--navbar-bg);
border-bottom: 1px solid var(--border-color);
padding: 0;
.navbar-brand {
margin-right: 0;
margin-right: 15px;
}
@include media-breakpoint-up(md) {
.navbar-brand {
// not shown for full screen
display: none;
}
}
.navbar-nav {
.dropdown-item {
padding: 6px 8px !important;

View file

@ -24,11 +24,19 @@
}
}
}
@include media-breakpoint-up(md) {
// only for small screens
.sidebar-toggle-btn {
display: none;
}
}
.title-area {
display: inline-flex;
align-items: center;
.title-text {
@include get_textstyle("2xl", "bold");
@include get_textstyle("2xl", "semibold");
cursor: pointer;
margin-bottom: 0px;
margin-right: var(--margin-sm);
@ -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 {
@ -104,6 +110,7 @@
background: var(--bg-color);
border-bottom: 1px solid var(--border-color);
transition: 0.5s top;
.page-head-content {
height: var(--page-head-height);
padding: 8px 0px;
@ -112,15 +119,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 +170,12 @@
margin-right: var(--margin-sm);
}
}
.layout-main {
display: flex;
flex-direction: row;
}
.layout-main-section {
scroll-margin-top: var(--navbar-height);
@ -172,7 +184,11 @@
.result,
.no-result,
.freeze {
min-height: #{"calc(100vh - 284px)"};
min-height: "200px";
}
.result {
overflow: scroll;
}
}

View file

@ -89,6 +89,7 @@
}
.report-view {
width: calc(100% - 220px);
.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,44 +33,174 @@ body[data-route^="Module"] .main-menu {
@extend .hide-form-sidebar;
}
.sidebar-menu {
.data-pill {
body {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: flex-start;
justify-content: flex-start;
position: relative;
}
.main-section {
width: 100%;
height: 100vh;
overflow: scroll;
overflow-x: hidden;
overflow-y: visible;
}
.body-sidebar-container {
display: flex;
flex-direction: column;
height: fit-content;
width: fit-content;
.body-sidebar-placeholder {
display: none;
width: 53px;
height: 100vh;
}
&:hover {
.body-sidebar {
// make it an overlay on hover
position: absolute;
width: 200px;
.sidebar-item-label {
display: flex;
}
.sidebar-item-control {
display: block;
}
.edit-sidebar-link {
display: block;
}
}
// show placeholder so that main section remains static
.body-sidebar-placeholder {
display: flex;
}
}
}
.body-sidebar {
width: 53px;
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: 1030;
position: static;
font-size: var(--text-base);
transition: transform 200ms ease-in-out;
.app-logo {
width: 21px;
}
.sidebar-items {
width: 100%;
justify-content: unset;
.es-icon {
margin: 0px 6px;
}
}
&.user-actions {
margin-bottom: 15px;
a {
font-weight: bold;
.standard-sidebar-section {
margin-bottom: var(--margin-xl);
&:last-of-type {
margin-bottom: var(--margin-sm);
}
&:first-child {
margin-top: 24px;
}
}
h6,
.h6 {
margin-top: 0px;
margin-bottom: 7px;
.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;
&:hover {
text-decoration: none !important;
}
}
}
> li {
.edit-sidebar-link {
display: none;
}
.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;
}
}
}
// 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 +214,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 +246,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 +308,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 +338,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 {
@ -250,13 +376,12 @@ body[data-route^="Module"] .main-menu {
top: 0;
left: 0;
transform: translateX(-110%);
z-index: 9999;
z-index: 1300;
box-shadow: var(--shadow-base);
height: 100%;
width: 40%;
display: block !important;
transition: transform 200ms ease-in-out;
padding: var(--padding-md);
&.opened {
transform: translateX(0);
@ -281,7 +406,7 @@ body[data-route^="Module"] .main-menu {
right: 0;
opacity: 0.3;
background: #000;
z-index: 9998;
z-index: 1100;
height: 100%;
width: 100%;
}
@ -313,12 +438,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 +469,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 +542,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 +556,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 +591,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,

View file

@ -3,6 +3,5 @@
--input-padding: 6px 8px;
--dropdown-padding: 4px 8px;
--grid-padding: 10px 8px;
--disabled-input-padding: 3px 8px;
--number-card-padding: 8px 8px 8px 12px;
}

View file

@ -7,7 +7,7 @@
--text-2xs: 12px;
--text-xs: 12px;
--text-sm: 13px;
--text-md: 13px; // alias
--text-md: 14px; // alias
--text-base: 13px;
--text-lg: 16px;
--text-xl: 18px;