diff --git a/chrome/content/zotero/collectionTree.jsx b/chrome/content/zotero/collectionTree.jsx
index a0472c0f09..1719cc5bf3 100644
--- a/chrome/content/zotero/collectionTree.jsx
+++ b/chrome/content/zotero/collectionTree.jsx
@@ -1293,6 +1293,45 @@ var CollectionTree = class CollectionTree extends LibraryTree {
return false;
}
+ getIconName(index) {
+ const treeRow = this.getRow(index);
+ let collectionType = treeRow.type;
+ let icon = collectionType;
+
+ switch (collectionType) {
+ case 'group':
+ icon = 'library-group';
+ break;
+ case 'feed':
+ // Better alternative needed: https://github.com/zotero/zotero/pull/902#issuecomment-183185973
+ /*
+ if (treeRow.ref.updating) {
+ collectionType += 'Updating';
+ } else */if (treeRow.ref.lastCheckError) {
+ icon += '-error';
+ }
+ break;
+ case 'trash':
+ if (this._trashNotEmpty[treeRow.ref.libraryID]) {
+ icon += '-full';
+ }
+ break;
+
+ case 'feeds':
+ icon = 'feed-library';
+ break;
+
+ case 'header':
+ if (treeRow.ref.id == 'group-libraries-header') {
+ icon = 'groups';
+ }
+ break;
+ case 'separator':
+ return null;
+ }
+ return icon;
+ }
+
////////////////////////////////////////////////////////////////////////////////
///
@@ -2234,47 +2273,11 @@ var CollectionTree = class CollectionTree extends LibraryTree {
}
_getIcon(index) {
- const treeRow = this.getRow(index);
- var collectionType = treeRow.type;
-
- if (collectionType == 'separator') {
- return document.createElement('span');
- }
+ let iconName = this.getIconName(index);
- let icon = collectionType;
-
- switch (collectionType) {
- case 'group':
- icon = 'library-group';
- break;
- case 'feed':
- // Better alternative needed: https://github.com/zotero/zotero/pull/902#issuecomment-183185973
- /*
- if (treeRow.ref.updating) {
- collectionType += 'Updating';
- } else */if (treeRow.ref.lastCheckError) {
- icon += '-error';
- }
- break;
-
- case 'trash':
- if (this._trashNotEmpty[treeRow.ref.libraryID]) {
- icon += '-full';
- }
- break;
-
- case 'feeds':
- icon = 'feed-library';
- break;
-
- case 'header':
- if (treeRow.ref.id == 'group-libraries-header') {
- icon = 'groups';
- }
- break;
- }
-
- return getCSSIcon(icon);
+ return iconName
+ ? getCSSIcon(iconName)
+ : document.createElement('span');
}
/**
diff --git a/chrome/content/zotero/components/icons.jsx b/chrome/content/zotero/components/icons.jsx
index 6cd0913df8..76cb63ecb3 100644
--- a/chrome/content/zotero/components/icons.jsx
+++ b/chrome/content/zotero/components/icons.jsx
@@ -109,22 +109,6 @@ i('Twisty', (
));
-i('ArrowLeft', (
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-));
-i('ArrowRight', (
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-));
i('Cross', "chrome://zotero/skin/cross.png");
i('Tick', "chrome://zotero/skin/tick.png");
i('ArrowRefresh', "chrome://zotero/skin/arrow_refresh.png");
@@ -253,14 +237,14 @@ module.exports.getCSSIcon = function (key) {
let iconEl = document.createElement('span');
iconEl.classList.add('icon');
iconEl.classList.add('icon-css');
- iconEl.classList.add(key);
+ iconEl.classList.add(`icon-${key}`);
cssIconsCache.set(key, iconEl);
}
return cssIconsCache.get(key).cloneNode(true);
};
-module.exports.getCSSItemTypeIcon = function (itemType, key = 'icon-item-type') {
+module.exports.getCSSItemTypeIcon = function (itemType, key = 'item-type') {
let icon = module.exports.getCSSIcon(key);
icon.dataset.itemType = itemType;
return icon;
diff --git a/chrome/content/zotero/components/tabBar.jsx b/chrome/content/zotero/components/tabBar.jsx
index ae06bfb401..7fa6743277 100644
--- a/chrome/content/zotero/components/tabBar.jsx
+++ b/chrome/content/zotero/components/tabBar.jsx
@@ -27,7 +27,7 @@
import React, { forwardRef, useState, useRef, useImperativeHandle, useEffect, useLayoutEffect } from 'react';
import cx from 'classnames';
-const { IconXmark, IconArrowLeft, IconArrowRight } = require('./icons');
+const { CSSIcon } = require('./icons');
const SCROLL_ARROW_SCROLL_BY = 222;
@@ -312,7 +312,7 @@ const TabBar = forwardRef(function (props, ref) {
}
}
- function renderTab({ id, title, selected, iconBackgroundImage }, index) {
+ function renderTab({ id, title, selected, icon }, index) {
return (
-
{iconBackgroundImage &&
- }{title}
+ {icon}
+
{title}
handleTabClose(event, id)}
>
-
+
);
}
-
+
return (
);
});
+TabBar.displayName = 'TabBar';
+
export default TabBar;
diff --git a/chrome/content/zotero/itemTree.jsx b/chrome/content/zotero/itemTree.jsx
index 2bfeaf6ad6..a2f068cb67 100644
--- a/chrome/content/zotero/itemTree.jsx
+++ b/chrome/content/zotero/itemTree.jsx
@@ -2744,19 +2744,19 @@ var ItemTree = class ItemTree extends LibraryTree {
// If the item has a child attachment
if (type !== null && type != 'none') {
if (type == 'pdf') {
- icon = getCSSItemTypeIcon('attachmentPDF', 'icon-attachment-type');
+ icon = getCSSItemTypeIcon('attachmentPDF', 'attachment-type');
ariaLabel = Zotero.getString('pane.item.attachments.hasPDF');
}
else if (type == 'snapshot') {
- icon = getCSSItemTypeIcon('attachmentSnapshot', 'icon-attachment-type');
+ icon = getCSSItemTypeIcon('attachmentSnapshot', 'attachment-type');
ariaLabel = Zotero.getString('pane.item.attachments.hasSnapshot');
}
else if (type == 'epub') {
- icon = getCSSItemTypeIcon('attachmentEPUB', 'icon-attachment-type');
+ icon = getCSSItemTypeIcon('attachmentEPUB', 'attachment-type');
ariaLabel = Zotero.getString('pane.item.attachments.hasEPUB');
}
else {
- icon = getCSSItemTypeIcon('attachmentFile', 'icon-attachment-type');
+ icon = getCSSItemTypeIcon('attachmentFile', 'attachment-type');
ariaLabel = Zotero.getString('pane.item.attachments.has');
}
diff --git a/chrome/content/zotero/tabs.js b/chrome/content/zotero/tabs.js
index b3462d3963..4ca49e8a2e 100644
--- a/chrome/content/zotero/tabs.js
+++ b/chrome/content/zotero/tabs.js
@@ -29,6 +29,7 @@
var React = require('react');
var ReactDOM = require('react-dom');
import TabBar from 'components/tabBar';
+import { CSSIcon, CSSItemTypeIcon } from 'components/icons';
// Reduce loaded tabs limit if the system has 8 GB or less memory.
// TODO: Revise this after upgrading to Zotero 7
@@ -81,13 +82,33 @@ var Zotero_Tabs = new function () {
};
this._update = function () {
- this._tabBarRef.current.setTabs(this._tabs.map(tab => ({
- id: tab.id,
- type: tab.type,
- title: tab.title,
- selected: tab.id == this._selectedID,
- iconBackgroundImage: tab.iconBackgroundImage
- })));
+ this._tabBarRef.current.setTabs(this._tabs.map((tab) => {
+ let icon = null;
+ if (tab.id === 'zotero-pane') {
+ let index = ZoteroPane.collectionsView?.selection?.focused;
+ if (typeof index !== 'undefined' && ZoteroPane.collectionsView.getRow(index)) {
+ let iconName = ZoteroPane.collectionsView.getIconName(index);
+ icon = ;
+ }
+ }
+ else if (tab.data?.itemID) {
+ try {
+ let item = Zotero.Items.get(tab.data.itemID);
+ icon = ;
+ }
+ catch (e) {
+ // item might not yet be loaded, we will get the icon on the next update
+ }
+ }
+
+ return {
+ id: tab.id,
+ type: tab.type,
+ title: tab.title,
+ selected: tab.id == this._selectedID,
+ icon,
+ };
+ }));
// Disable File > Close menuitem if multiple tabs are open
const multipleTabsOpen = this._tabs.length > 1;
document.getElementById('cmd_close').setAttribute('disabled', multipleTabsOpen);
@@ -243,23 +264,9 @@ var Zotero_Tabs = new function () {
return;
}
tab.title = title;
- Zotero_Tabs.updateLibraryTabIcon();
this._update();
};
- this.updateLibraryTabIcon = () => {
- let index = ZoteroPane.collectionsView.selection.focused;
- if (!ZoteroPane.collectionsView.getRow(index)) {
- return;
- }
- let icon = ZoteroPane.collectionsView._getIcon(index);
- var { tab } = this._getTab('zotero-pane');
- if (!tab || !icon.style.backgroundImage) {
- return;
- }
- tab.iconBackgroundImage = icon.style.backgroundImage;
- };
-
/**
* Close tabs
*
diff --git a/chrome/content/zotero/zoteroPane.xhtml b/chrome/content/zotero/zoteroPane.xhtml
index 0378c0c170..2ed1a7df8c 100644
--- a/chrome/content/zotero/zoteroPane.xhtml
+++ b/chrome/content/zotero/zoteroPane.xhtml
@@ -754,7 +754,7 @@
-
+
diff --git a/chrome/skin/default/zotero/20/universal/chevron-tabs.svg b/chrome/skin/default/zotero/20/universal/chevron-tabs.svg
new file mode 100644
index 0000000000..1f78adc320
--- /dev/null
+++ b/chrome/skin/default/zotero/20/universal/chevron-tabs.svg
@@ -0,0 +1,5 @@
+
diff --git a/scss/base/_base.scss b/scss/base/_base.scss
index 2784558c89..b89644c6e0 100644
--- a/scss/base/_base.scss
+++ b/scss/base/_base.scss
@@ -19,3 +19,7 @@
width: 290px;
background: var(--material-background);
}
+
+#zotero-title-bar {
+ border-bottom: var(--material-panedivider);
+}
\ No newline at end of file
diff --git a/scss/components/_collection-tree.scss b/scss/components/_collection-tree.scss
index de2473d695..fb940e0382 100644
--- a/scss/components/_collection-tree.scss
+++ b/scss/components/_collection-tree.scss
@@ -3,6 +3,14 @@ $icons: (
library-group, publications, trash-full, trash, unfiled, retracted, search,
);
+@each $icon in $icons {
+ .icon-css.icon-#{$icon} {
+ @include focus-states using ($color) {
+ @include svgicon($icon, $color, "16", "collection-tree");
+ }
+ }
+}
+
#zotero-collections-tree-container {
height: 5.2em;
}
@@ -22,7 +30,7 @@ $icons: (
}
@each $icon in $icons {
- .icon-css.#{$icon} {
+ .icon-css.icon-#{$icon} {
@include focus-states using ($color) {
@include svgicon($icon, $color, "16", "collection-tree");
}
diff --git a/scss/components/_icons.scss b/scss/components/_icons.scss
index 48adf1c131..27d1a055c1 100644
--- a/scss/components/_icons.scss
+++ b/scss/components/_icons.scss
@@ -25,6 +25,11 @@
-moz-appearance: none !important;
}
+.icon-20 {
+ width: 20px;
+ height: 20px;
+}
+
.icon-16 {
width: 16px;
height: 16px;
@@ -40,6 +45,8 @@ $-icons: (
chevron-6: 8,
filter: 16,
note: 16,
+ x-8: 16,
+ chevron-tabs: 20
);
@each $icon, $size in $-icons {
diff --git a/scss/components/_mainWindow.scss b/scss/components/_mainWindow.scss
index 5828608eb2..c3f4c31447 100644
--- a/scss/components/_mainWindow.scss
+++ b/scss/components/_mainWindow.scss
@@ -1,12 +1,7 @@
// Styling for displaying tabs in the title bar
:root:not([legacytoolbar="true"]) {
& {
- @media (-moz-platform: windows) {
- --tab-min-height: 36px;
- }
- @media not (-moz-platform: windows) {
- --tab-min-height: 30px;
- }
+ --tab-min-height: 36px;
--tabs-border-color: rgba(0,0,0,.3);
--tabline-color: #0a84ff;
@@ -138,14 +133,6 @@
background: var(--material-tabbar);
-moz-window-dragging: drag;
}
-
- #tab-bar-container .tab {
- background: transparent;
- }
-
- #tab-bar-container .tab.selected {
- background: var(--material-tabbar)
- }
.zotero-toolbar {
-moz-appearance: none;
diff --git a/scss/components/_tabBar.scss b/scss/components/_tabBar.scss
index 3b60ca0c1a..d416eb34e3 100644
--- a/scss/components/_tabBar.scss
+++ b/scss/components/_tabBar.scss
@@ -4,7 +4,7 @@
--safe-area-end: 0;
--tab-border: .5px solid var(--tab-border);
- min-height: 30px;
+ min-height: var(--tab-min-height);
&:not([hidden]) {
// display: flex overrides the hidden attribute
@@ -39,51 +39,77 @@
}
.scroll-start-arrow, .scroll-end-arrow {
- height: 30px;
- padding: 0 3px;
+ padding: 0 4px;
align-items: center;
- z-index: 1;
- color: var(--fill-tertiary);
+ color: var(--fill-secondary);
display: none;
box-shadow: none;
- // Add bottom border to scroll arrows,
- // and negate the extra vertical pixel added by the border with a negative margin
- border-bottom: var(--tab-border);
- margin-bottom: -1px;
- box-sizing: border-box;
+ z-index: 1;
+
+ > button {
+ width: auto;
+ padding: 0;
+ border: 0;
+ height: 28px;
+ border-radius: 5px;
+ background: transparent;
+ }
.icon {
display: flex;
+ fill: var(--fill-tertiary);
}
&.active {
- color: var(--fill-secondary);
- &:hover {
- background-color: var(--fill-quinary);
+ button {
+ &:hover {
+ background-color: var(--fill-quinary);
+ }
+
+ &:active {
+ background-color: var(--fill-quarternary);
+ }
+ }
+
+ .icon {
+ fill: var(--fill-secondary);
}
}
}
.scroll-start-arrow {
border-right: var(--material-border-transparent);
+ border-left: var(--material-border-quarternary);
+
&.active {
border-right: var(--material-panedivider);
- box-shadow: 3px 0 3px -3px rgba(0, 0, 0, .3);
+ box-shadow: 0.5px 0.5px 0px -0.5px rgba(0, 0, 0, 0.05), 3px 0px 3px -3px rgba(0, 0, 0, .3)
}
}
.scroll-end-arrow {
border-left: var(--material-border-transparent);
+
&.active {
border-left: var(--material-panedivider);
- box-shadow: -3px 0 3px -3px rgba(0, 0, 0, .3)
+ box-shadow: -0.5px 0.5px 0px -0.5px rgba(0, 0, 0, 0.05), -3px 0 3px -3px rgba(0, 0, 0, .3)
+ }
+
+ .icon {
+ transform: rotate(180deg);
}
}
.pinned-tabs {
display: none;
+ padding-right: 8px;
+
+ .tabs {
+ overflow: unset;
+ }
+
.tab {
- max-width: 110px;
+ max-width: 100px;
}
}
@@ -108,74 +134,76 @@
overflow: hidden;
left: 0;
right: 0;
+ padding: 4px 1px;
+ column-gap: 4px;
- &:before {
- content: "";
- width: 0;
- min-width: 0;
- border-inline-end: var(--tab-border);
- }
-
- &:after {
- content: "";
- flex: 1 0 0;
- width: 100%;
- min-width: 0;
- // Add bottom border to the space between the last tab and the spacer in #tab-bar-container::after
- border-bottom: var(--tab-border);
- border-inline-start: var(--tab-border);
+ .pinned-tabs & {
+ padding: 4px 0;
}
}
.tab {
box-sizing: border-box;
- -moz-appearance: none;
max-width: 200px;
flex: 1 1 200px;
- height: 30px;
- line-height: 30px;
position: relative;
color: var(--fill-primary);
text-align: center;
- padding: 0 22px;
- min-width: 110px;
-
- &:not(:last-child) {
- border-inline-end: var(--tab-border);
- }
-
+ min-width: 100px;
+ height: 28px;
+ line-height: 16px;
+ padding: 6px 22px 6px 6px;
+ display: flex;
+ border-radius: 5px;
+ transition: background-color 0.1s ease-out;
+
&.selected {
background: var(--material-button);
box-shadow: 0px 0px 0px 0.5px rgba(0, 0, 0, 0.05), 0px 0.5px 2.5px 0px rgba(0, 0, 0, 0.30);
}
-
+
&.dragging {
- border-inline-start: var(--tab-border);
z-index: 1;
}
- &.dragging + & {
- border-inline-start: var(--tab-border);
+ &:not(:last-child) {
+ border-inline-end: var(--tab-border);
+ }
+
+ &:not(.selected):hover {
+ background-color: var(--fill-quinary);
+ }
+
+ &:first-child {
+ padding: 6px; // no 22px padding for X button because pinned tab cannot be closed
+
+ .tab-close {
+ display: none;
+ }
}
.tab-name {
- line-height: 30px;
- overflow-x: hidden;
- text-overflow: ellipsis;
- display: -moz-box;
- -moz-box-pack: center;
- position: relative;
- top: -2px;
- overflow-y: hidden;
+ background: linear-gradient(to left, transparent 0px, var(--fill-primary) 20px) text;
+ flex: 1 1 100%;
+ line-height: 16px;
+ margin-inline-start: 4px;
+ overflow: hidden;
+ text-align: left;
+ -webkit-text-fill-color: transparent;
+ white-space: nowrap;
- .icon-bg {
- margin-inline-end: 7px;
- margin-top: -2px;
+ @include state('.tab:first-child') {
+ background: linear-gradient(to left, transparent 0px, var(--fill-primary) 10px) text;
}
}
+ .tab-icon {
+ flex: 0 0 16px;
+ }
+
.tab-close {
position: absolute;
+ transition: background-color 0.1s ease-out;
&:dir(ltr) {
right: 6px;
@@ -205,10 +233,6 @@
}
}
- &:first-child .tab-close {
- display: none;
- }
-
&:not(.selected) {
border-bottom: var(--tab-border);
}
diff --git a/scss/mac/_tabBar.scss b/scss/mac/_tabBar.scss
index c11f6cd4be..7db31ab7f9 100644
--- a/scss/mac/_tabBar.scss
+++ b/scss/mac/_tabBar.scss
@@ -1,5 +1,5 @@
#tab-bar-container {
- --safe-area-start: 78px;
+ --safe-area-start: 73px;
--safe-area-end: 20px;
}