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 = () => {
// Empty existing nodes
this.tabsMenuList.replaceChildren();
this._tabsMenuFocusedIndex = 0;
let index = 1;
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
if (tab.title == "") {
continue;
@ -841,6 +838,14 @@ var Zotero_Tabs = new function () {
let row = document.createXULElement('toolbaritem');
let rowIndex = this._tabs.findIndex(x => x.id === tab.id);
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
let closeButton = document.createXULElement('toolbarbutton');
closeButton.setAttribute('class', 'zotero-tabs-menu-entry zotero-clicky-cross close');
@ -852,35 +857,33 @@ var Zotero_Tabs = new function () {
}
this.close(tab.id);
});
// 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);
// Library tab has no close button
if (tab.id == "zotero-pane") {
closeButton.hidden = true;
closeButton.disabled = true;
}
closeButton.setAttribute('tabindex', `${index++}`);
// Item type icon
let span = document.createElement("span");
span.className = "icon icon-css cell-icon";
span.classList.add("icon-item-type");
let item = Zotero.Items.get(tab.data.itemID);
let dataTypeLabel = "";
if (item.isPDFAttachment()) {
dataTypeLabel = "attachmentPDF";
span.className = "icon icon-css tab-icon";
if (tab.id == 'zotero-pane') {
// Determine which icon from the collection view rows to use (same as in _update())
let index = ZoteroPane.collectionsView?.selection?.focused;
if (typeof index !== 'undefined' && ZoteroPane.collectionsView.getRow(index)) {
let iconName = ZoteroPane.collectionsView.getIconName(index);
span.classList.add(`icon-${iconName}`);
}
}
else if (item.isEPUBAttachment()) {
dataTypeLabel = "attachmentEPUB";
else {
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);
// Actual label with bolded substrings matching the filter
let tabLabel = createTabsMenuLabel(tab.title, this._tabsMenuFilter);
@ -920,8 +923,8 @@ var Zotero_Tabs = new function () {
row.appendChild(closeButton);
row.addEventListener("dragstart", (e) => {
// No drag-drop on the cross button
if (e.target.classList.contains("close")) {
// No drag-drop on the cross button or the library tab
if (tab.id == 'zotero-pane' || e.target.classList.contains("close")) {
e.preventDefault();
e.stopPropagation();
return;
@ -959,7 +962,9 @@ var Zotero_Tabs = new function () {
row.addEventListener('drop', (e) => {
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', (_) => {
@ -1050,11 +1055,15 @@ var Zotero_Tabs = new function () {
if (event.key == "Tab") {
event.preventDefault();
let isShift = event.shiftKey;
let moveTabIndex = () => tabindex++;
if (isShift) {
tabindex -= 1;
moveTabIndex = () => tabindex--;
}
else {
tabindex += 1;
moveTabIndex();
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);
}
@ -1078,6 +1087,12 @@ var Zotero_Tabs = new function () {
else {
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) {
// ArrowUp from the first tab or the first close button focuses the filter field.
// ArrowUp from the filter field focuses the last tab

View file

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