added library tab to tabs menu, minor refactoring

- library tab appears in the tabs menu without the close button
and no drag functionality
- minor refactoring to simplify how the tabs icons are fetched for
items with item.getItemTypeIconName()
- added focus-ring to tabs and filter field via @focus-ring mixin
so that the focus outline looks the same for the input field and buttons
- removed default margin from the <description> component of the tab
titles that moved titles up by a bit
This commit is contained in:
abaevbog 2024-01-07 04:17:40 -05:00 committed by Dan Stillman
parent ef062b9a8d
commit d8a00610da
2 changed files with 56 additions and 32 deletions

View file

@ -823,12 +823,9 @@ var Zotero_Tabs = new function () {
this.refreshTabsMenuList = () => { this.refreshTabsMenuList = () => {
// Empty existing nodes // Empty existing nodes
this.tabsMenuList.replaceChildren(); this.tabsMenuList.replaceChildren();
this._tabsMenuFocusedIndex = 0;
let index = 1; let index = 1;
for (let tab of this._tabs) { for (let tab of this._tabs) {
// Skip tabs when we have no itemID, like library
if (!tab.data?.itemID) {
continue;
}
// Skip tabs whose title wasn't added yet // Skip tabs whose title wasn't added yet
if (tab.title == "") { if (tab.title == "") {
continue; continue;
@ -841,6 +838,14 @@ var Zotero_Tabs = new function () {
let row = document.createXULElement('toolbaritem'); let row = document.createXULElement('toolbaritem');
let rowIndex = this._tabs.findIndex(x => x.id === tab.id); let rowIndex = this._tabs.findIndex(x => x.id === tab.id);
row.setAttribute("index", rowIndex); row.setAttribute("index", rowIndex);
// Title of the tab
let tabName = document.createXULElement('toolbarbutton');
tabName.setAttribute('flex', '1');
tabName.setAttribute('class', 'zotero-tabs-menu-entry title');
tabName.setAttribute('tabindex', `${index++}`);
tabName.setAttribute('aria-label', tab.title);
// Cross button to close a tab // Cross button to close a tab
let closeButton = document.createXULElement('toolbarbutton'); let closeButton = document.createXULElement('toolbarbutton');
closeButton.setAttribute('class', 'zotero-tabs-menu-entry zotero-clicky-cross close'); closeButton.setAttribute('class', 'zotero-tabs-menu-entry zotero-clicky-cross close');
@ -852,35 +857,33 @@ var Zotero_Tabs = new function () {
} }
this.close(tab.id); this.close(tab.id);
}); });
// Title of the tab // Library tab has no close button
let tabName = document.createXULElement('toolbarbutton'); if (tab.id == "zotero-pane") {
tabName.setAttribute('flex', '1'); closeButton.hidden = true;
tabName.setAttribute('class', 'zotero-tabs-menu-entry title'); closeButton.disabled = true;
tabName.setAttribute('tabindex', `${index++}`); }
tabName.setAttribute('aria-label', tab.title);
closeButton.setAttribute('tabindex', `${index++}`); closeButton.setAttribute('tabindex', `${index++}`);
// Item type icon // Item type icon
let span = document.createElement("span"); let span = document.createElement("span");
span.className = "icon icon-css cell-icon"; span.className = "icon icon-css tab-icon";
span.classList.add("icon-item-type"); if (tab.id == 'zotero-pane') {
let item = Zotero.Items.get(tab.data.itemID); // Determine which icon from the collection view rows to use (same as in _update())
let dataTypeLabel = ""; let index = ZoteroPane.collectionsView?.selection?.focused;
if (item.isPDFAttachment()) { if (typeof index !== 'undefined' && ZoteroPane.collectionsView.getRow(index)) {
dataTypeLabel = "attachmentPDF"; let iconName = ZoteroPane.collectionsView.getIconName(index);
span.classList.add(`icon-${iconName}`);
}
} }
else if (item.isEPUBAttachment()) { else {
dataTypeLabel = "attachmentEPUB"; span.classList.add("icon-item-type");
let item = Zotero.Items.get(tab.data.itemID);
let dataTypeLabel = item.getItemTypeIconName();
span.setAttribute("data-item-type", dataTypeLabel);
} }
else if (item.isSnapshotAttachment()) {
dataTypeLabel = "attachmentSnapshot";
}
else if (item.isFileAttachment()) {
dataTypeLabel = "attachmentFile";
}
span.setAttribute("data-item-type", dataTypeLabel);
tabName.appendChild(span); tabName.appendChild(span);
// Actual label with bolded substrings matching the filter // Actual label with bolded substrings matching the filter
let tabLabel = createTabsMenuLabel(tab.title, this._tabsMenuFilter); let tabLabel = createTabsMenuLabel(tab.title, this._tabsMenuFilter);
@ -920,8 +923,8 @@ var Zotero_Tabs = new function () {
row.appendChild(closeButton); row.appendChild(closeButton);
row.addEventListener("dragstart", (e) => { row.addEventListener("dragstart", (e) => {
// No drag-drop on the cross button // No drag-drop on the cross button or the library tab
if (e.target.classList.contains("close")) { if (tab.id == 'zotero-pane' || e.target.classList.contains("close")) {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
return; return;
@ -959,7 +962,9 @@ var Zotero_Tabs = new function () {
row.addEventListener('drop', (e) => { row.addEventListener('drop', (e) => {
let tabId = e.dataTransfer.getData("zotero/tab"); let tabId = e.dataTransfer.getData("zotero/tab");
this.move(tabId, parseInt(row.getAttribute("index"))); let rowIndex = parseInt(row.getAttribute("index"));
if (rowIndex == 0) return;
this.move(tabId, rowIndex);
}); });
row.addEventListener('dragend', (_) => { row.addEventListener('dragend', (_) => {
@ -1050,11 +1055,15 @@ var Zotero_Tabs = new function () {
if (event.key == "Tab") { if (event.key == "Tab") {
event.preventDefault(); event.preventDefault();
let isShift = event.shiftKey; let isShift = event.shiftKey;
let moveTabIndex = () => tabindex++;
if (isShift) { if (isShift) {
tabindex -= 1; moveTabIndex = () => tabindex--;
} }
else { moveTabIndex();
tabindex += 1; let candidate = this.tabsMenuList.parentElement.querySelector(`[tabindex="${tabindex}"]`);
// If the candidate is disabled (e.g. close button of library tab), skip it
if (candidate && candidate.disabled) {
moveTabIndex();
} }
focusTabsMenuEntry(tabindex); focusTabsMenuEntry(tabindex);
} }
@ -1078,6 +1087,12 @@ var Zotero_Tabs = new function () {
else { else {
tabindex -= step; tabindex -= step;
} }
// If the candidate is a disabled element (e.g. close button of the library tab),
// move focus to the element before it
let candidate = this.tabsMenuList.parentElement.querySelector(`[tabindex="${tabindex}"]`);
if (candidate && candidate.disabled) {
tabindex--;
}
if (tabindex <= 0) { if (tabindex <= 0) {
// ArrowUp from the first tab or the first close button focuses the filter field. // ArrowUp from the first tab or the first close button focuses the filter field.
// ArrowUp from the filter field focuses the last tab // ArrowUp from the filter field focuses the last tab

View file

@ -10,6 +10,7 @@
border: 1px solid transparent; border: 1px solid transparent;
padding-inline-start: 5px !important; padding-inline-start: 5px !important;
padding: 2px; padding: 2px;
@include focus-ring(3px, 5px);
} }
#zotero-tabs-menu-list { #zotero-tabs-menu-list {
@ -17,6 +18,7 @@
margin: 0; margin: 0;
.zotero-tabs-menu-entry { .zotero-tabs-menu-entry {
@include focus-ring(3px, 5px);
border-radius: 6px; border-radius: 6px;
height: 22px; height: 22px;
margin: 0; margin: 0;
@ -32,6 +34,13 @@
} }
description { description {
overflow: hidden; overflow: hidden;
// override default description margins
margin-bottom: 0;
margin-block-start: 0;
}
.tab-icon {
width: 16px;
height: 16px;
} }
} }