feat: Sidebar Attachments accessibility

- Issue: With a large volume of attachments, the "Attach File" button is pushed to the bottom
- "Attach File" stays at the top of the pile
- Small explore files button added so that users can use the File View to navigate/filter through files
- Expanded Explore Files button when attach file action is hidden
- Added `file_type` to Files, this is useful for filtering and visibility
- Added "Type" to File List View
- Patch to set File Type  in all files
This commit is contained in:
marination 2023-07-26 19:49:28 +05:30
parent 89b9b64a55
commit fa241580d9
9 changed files with 126 additions and 16 deletions

View file

@ -24,8 +24,7 @@ frappe.ui.form.on("File", {
preview_file: function (frm) {
let $preview = "";
let file_name = frm.doc.file_name.split("?")[0];
let file_extension = file_name.split(".").pop()?.toLowerCase();
let file_extension = frm.doc.file_type.toLowerCase();
if (frappe.utils.is_image_file(frm.doc.file_url)) {
$preview = $(`<div class="img_preview">

View file

@ -8,6 +8,8 @@
"field_order": [
"file_name",
"is_private",
"column_break_7jmm",
"file_type",
"preview",
"preview_html",
"section_break_5",
@ -168,13 +170,25 @@
"fieldtype": "Check",
"label": "Uploaded To Google Drive",
"read_only": 1
},
{
"fieldname": "column_break_7jmm",
"fieldtype": "Column Break"
},
{
"fieldname": "file_type",
"fieldtype": "Data",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "File Type",
"read_only": 1
}
],
"force_re_route_to_default_view": 1,
"icon": "fa fa-file",
"idx": 1,
"links": [],
"modified": "2023-05-02 15:42:14.274901",
"modified": "2023-07-26 14:03:49.456951",
"modified_by": "Administrator",
"module": "Core",
"name": "File",

View file

@ -44,6 +44,7 @@ class File(Document):
content_hash: DF.Data | None
file_name: DF.Data | None
file_size: DF.Int
file_type: DF.Data | None
file_url: DF.Code | None
folder: DF.Link | None
is_attachments_folder: DF.Check
@ -86,6 +87,7 @@ class File(Document):
self.set_folder_name()
self.set_file_name()
self.validate_attachment_limit()
self.set_file_type()
if self.is_folder:
return
@ -330,6 +332,15 @@ class File(Document):
elif not self.is_home_folder:
self.folder = "Home"
def set_file_type(self):
if self.is_folder:
return
file_name = self.file_name.split("?")[0]
file_extension = file_name.split(".")[-1].upper() if file_name.split(".")[-1] else None
if file_extension:
self.file_type = file_extension
def validate_file_on_disk(self):
"""Validates existence file"""
full_path = self.get_full_path()

View file

@ -227,3 +227,4 @@ execute:frappe.delete_doc_if_exists("Workspace", "Customization")
execute:frappe.db.set_single_value("Document Naming Settings", "default_amend_naming", "Amend Counter")
execute:frappe.delete_doc_if_exists("DocType", "Error Snapshot")
frappe.patches.v15_0.move_event_cancelled_to_status
frappe.patches.v15_0.set_file_type

View file

@ -0,0 +1,18 @@
import frappe
def execute():
"""Set 'File Type' for all files based on file extension."""
files = frappe.db.get_all(
"File",
fields=["name", "file_name", "is_folder"],
)
for file in files:
if file.get("is_folder"):
continue
file_name = file.get("file_name").split("?")[0]
file_extension = file_name.split(".")[-1].upper() if file_name.split(".")[-1] else None
if file_extension:
frappe.db.set_value("File", file.get("name"), "file_type", file_extension)

View file

@ -11,7 +11,16 @@ frappe.ui.form.Attachments = class Attachments {
this.parent.find(".add-attachment-btn").click(function () {
me.new_attachment();
});
this.add_attachment_wrapper = this.parent.find(".add-attachment-btn");
this.parent.find(".explore-btn").click(() => {
frappe.open_in_new_tab = true;
frappe.set_route("List", "File", {
attached_to_doctype: this.frm.doctype,
attached_to_name: this.frm.docname,
});
});
this.add_attachment_wrapper = this.parent.find(".attachments-actions");
this.attachments_label = this.parent.find(".attachments-label");
}
max_reached(raise_exception = false) {
@ -42,6 +51,7 @@ frappe.ui.form.Attachments = class Attachments {
var max_reached = this.max_reached();
this.add_attachment_wrapper.toggle(!max_reached);
this.setup_expanded_explore_button(max_reached);
// add attachment objects
var attachments = this.get_attachments();
@ -57,11 +67,29 @@ frappe.ui.form.Attachments = class Attachments {
});
} else {
this.attachments_label.removeClass("has-attachments");
this.parent.find(".explore-btn").toggle(false); // hide explore icon button
}
}
setup_expanded_explore_button(max_reached) {
if (!max_reached) {
this.parent.find(".explore-full-btn").addClass("hidden");
return;
}
this.parent.find(".explore-full-btn").removeClass("hidden");
this.parent.find(".explore-full-btn").click(() => {
frappe.set_route("List", "File", {
attached_to_doctype: this.frm.doctype,
attached_to_name: this.frm.docname,
});
});
}
get_attachments() {
return this.frm.get_docinfo().attachments || [];
}
add_attachment(attachment) {
var file_name = attachment.file_name;
var file_url = this.get_file_url(attachment);
@ -101,8 +129,11 @@ frappe.ui.form.Attachments = class Attachments {
$(`<li class="attachment-row">`)
.append(frappe.get_data_pill(file_label, fileid, remove_action, icon))
.insertAfter(this.attachments_label.addClass("has-attachments"));
.insertAfter(this.add_attachment_wrapper);
this.parent.find(".explore-btn").toggle(true); // show explore icon button if hidden
}
get_file_url(attachment) {
var file_url = attachment.file_url;
if (!file_url) {

View file

@ -54,17 +54,37 @@
<li class="sidebar-label attachments-label">
<svg class="icon icon-sm"><use href="#icon-attachment"></use></svg>
{%= __("Attachments") %}
<li class="explore-full-btn hidden">
<button class="data-pill btn">
<span class="pill-label ellipsis">
{%= __("Explore Files") %}
</span>
<svg class="icon icon-sm">
<use href="#icon-projects"></use>
</svg>
</button>
</li>
<li class="attachments-actions">
<button class="data-pill btn add-attachment-btn">
<span class="pill-label ellipsis">
{%= __("Attach File") %}
</span>
<svg class="icon icon-sm">
<use href="#icon-add"></use>
</svg>
</button>
<button class="text-muted btn btn-default icon-btn explore-btn">
<svg class="icon icon-sm">
<use href="#icon-projects"></use>
</svg>
</button>
</li>
</li>
<li class="add-attachment-btn">
<button class="data-pill btn">
<span class="pill-label ellipsis">
{%= __("Attach File") %}
</span>
<svg class="icon icon-sm">
<use href="#icon-add"></use>
</svg>
</button>
</li>
</ul>
<ul class="list-unstyled sidebar-menu form-reviews">
<li class="sidebar-label reviews-label">

View file

@ -319,6 +319,9 @@ frappe.views.FileView = class FileView extends frappe.views.ListView {
? `<div class="list-row-col ellipsis hidden-xs">
<span>${__("Size")}</span>
</div>
<div class="list-row-col ellipsis hidden-xs">
<span>${__("Type")}</span>
</div>
<div class="list-row-col ellipsis hidden-xs">
<span>${__("Created")}</span>
</div>`
@ -370,6 +373,9 @@ frappe.views.FileView = class FileView extends frappe.views.ListView {
<div class="list-row-col ellipsis hidden-xs text-muted">
<span>${file_size}</span>
</div>
<div class="list-row-col ellipsis hidden-xs text-muted">
<span>${file.file_type || ""}</span>
</div>
<div class="list-row-col ellipsis hidden-xs text-muted">
<span>${this.get_creation_date(file)}</span>
</div>

View file

@ -389,7 +389,17 @@ body[data-route^="Module"] .main-menu {
display: inline-flex;
}
.add-attachment-btn,
.attachments-actions {
display: flex;
gap: 5px;
max-width: 100%;
}
.explore-full-btn,
.attachments-actions {
margin-bottom: var(--margin-md);
}
.shares,
.followed-by {
max-width: 100%;