diff --git a/chrome/content/zotero-platform/mac/overlay.css b/chrome/content/zotero-platform/mac/overlay.css
index 7a8aa294db..1dc1f9fc1a 100644
--- a/chrome/content/zotero-platform/mac/overlay.css
+++ b/chrome/content/zotero-platform/mac/overlay.css
@@ -255,13 +255,6 @@ treechildren::-moz-tree-image {
list-style-image: url('chrome://zotero/skin/mac/toolbar-note-add.png');
}
-/* Hide icons on macOS. We use :is() to work around weird behavior in Fx101 where a regular child
- selector doesn't match the first time the menu is opened. */
-:is(#zotero-collectionmenu, #zotero-itemmenu) > :is(.menuitem-iconic, .menu-iconic) {
- list-style-image: none !important;
-}
-
-
/* BEGIN 2X BLOCK -- DO NOT EDIT MANUALLY -- USE 2XIZE */
@media (min-resolution: 1.25dppx) {
.zotero-tb-button,.zotero-tb-button:first-child,.zotero-tb-button:last-child { background: url("chrome://zotero/skin/mac/menubutton-end@2x.png") right center/auto 24px no-repeat; }
diff --git a/chrome/content/zotero/elements/collapsibleSection.js b/chrome/content/zotero/elements/collapsibleSection.js
index 2a1da837cb..6b29801c01 100644
--- a/chrome/content/zotero/elements/collapsibleSection.js
+++ b/chrome/content/zotero/elements/collapsibleSection.js
@@ -47,9 +47,10 @@
let open = this.open;
if (open === val || this.empty) return;
this.render();
- let openHeight = this._head?.nextSibling?.scrollHeight;
- if (openHeight) {
- this.style.setProperty('--open-height', `${openHeight}px`);
+ if (this._head?.nextSibling
+ && this._head.nextSibling.getBoundingClientRect().height
+ && this._head.nextSibling.scrollHeight) {
+ this.style.setProperty('--open-height', `${this._head.nextSibling.scrollHeight}px`);
}
else {
this.style.setProperty('--open-height', 'auto');
@@ -80,7 +81,9 @@
setCount(count) {
this.setAttribute('data-l10n-args', JSON.stringify({ count }));
- this.empty = !count;
+ this._runWithTransitionsDisabled(() => {
+ this.empty = !count;
+ });
}
get label() {
@@ -119,6 +122,7 @@
this._head.className = 'head';
this._head.addEventListener('click', this._handleClick);
this._head.addEventListener('keydown', this._handleKeyDown);
+ this._head.addEventListener('contextmenu', this._handleContextMenu);
this._title = document.createElement('span');
this._title.className = 'title';
@@ -131,13 +135,21 @@
});
this._head.append(this._addButton);
+ this._contextMenu = this._buildContextMenu();
+ let popupset = document.createXULElement('popupset');
+ popupset.append(this._contextMenu);
+ this._head.append(popupset);
+
let twisty = document.createXULElement('toolbarbutton');
twisty.className = 'twisty';
this._head.append(twisty);
this.prepend(this._head);
- this._restoreOpenState();
- this.render();
+
+ this._runWithTransitionsDisabled(() => {
+ this._restoreOpenState();
+ this.render();
+ });
this._notifierID = Zotero.Prefs.registerObserver(`panes.${this.dataset.pane}.open`, this._restoreOpenState.bind(this));
@@ -146,9 +158,79 @@
}
}
+ _buildContextMenu() {
+ let containerRoot = this.closest('.zotero-view-item-container');
+
+ let contextMenu = document.createXULElement('menupopup');
+ let collapseOtherSections = document.createXULElement('menuitem');
+ collapseOtherSections.classList.add('menuitem-iconic', 'zotero-menuitem-collapse-others');
+ collapseOtherSections.setAttribute('data-l10n-id', 'collapse-other-sections');
+ collapseOtherSections.addEventListener('command', () => {
+ for (let section of containerRoot.querySelectorAll('collapsible-section')) {
+ if (section !== this) {
+ section.open = false;
+ }
+ }
+ });
+ contextMenu.append(collapseOtherSections);
+
+ let expandAllSections = document.createXULElement('menuitem');
+ expandAllSections.classList.add('menuitem-iconic', 'zotero-menuitem-expand-all');
+ expandAllSections.setAttribute('data-l10n-id', 'expand-all-sections');
+ expandAllSections.addEventListener('command', () => {
+ for (let section of containerRoot.querySelectorAll('collapsible-section')) {
+ section.open = true;
+ }
+ });
+ contextMenu.append(expandAllSections);
+
+ let pinSection, unpinSection;
+ let pinUnpinSeparator = document.createXULElement('menuseparator');
+ contextMenu.append(pinUnpinSeparator);
+
+ pinSection = document.createXULElement('menuitem');
+ pinSection.classList.add('menuitem-iconic', 'zotero-menuitem-pin');
+ pinSection.setAttribute('data-l10n-id', 'pin-section');
+ pinSection.addEventListener('command', () => {
+ let sidenav = this._getSidenav();
+ sidenav.scrollToPane(this.dataset.pane, 'smooth');
+ sidenav.pinnedPane = this.dataset.pane;
+ });
+ contextMenu.append(pinSection);
+
+ unpinSection = document.createXULElement('menuitem');
+ unpinSection.classList.add('menuitem-iconic', 'zotero-menuitem-unpin');
+ unpinSection.setAttribute('data-l10n-id', 'unpin-section');
+ unpinSection.addEventListener('command', () => {
+ this._getSidenav().pinnedPane = null;
+ });
+ contextMenu.append(unpinSection);
+
+ contextMenu.addEventListener('popupshowing', () => {
+ let sections = Array.from(containerRoot.querySelectorAll('collapsible-section'));
+ collapseOtherSections.disabled = sections.every(section => section === this || !section.open);
+ expandAllSections.disabled = sections.every(section => section.open);
+
+ let sidenav = this._getSidenav();
+ if (sidenav?.isPanePinnable(this.dataset.pane)) {
+ pinUnpinSeparator.hidden = false;
+ pinSection.hidden = sidenav.pinnedPane == this.dataset.pane;
+ unpinSection.hidden = sidenav.pinnedPane != this.dataset.pane;
+ }
+ else {
+ pinUnpinSeparator.hidden = true;
+ pinSection.hidden = true;
+ unpinSection.hidden = true;
+ }
+ });
+
+ return contextMenu;
+ }
+
destroy() {
this._head.removeEventListener('click', this._handleClick);
this._head.removeEventListener('keydown', this._handleKeyDown);
+ this._head.removeEventListener('contextmenu', this._handleContextMenu);
Zotero.Prefs.unregisterObserver(this._notifierID);
}
@@ -160,6 +242,15 @@
_restoreOpenState() {
this.open = Zotero.Prefs.get(`panes.${this.dataset.pane}.open`) ?? true;
}
+
+ _runWithTransitionsDisabled(fn) {
+ this.classList.add('disable-transitions');
+ fn();
+ // Need to wait a tick before re-enabling - forcing style recalculation isn't enough here
+ requestAnimationFrame(() => {
+ this.classList.remove('disable-transitions');
+ });
+ }
_handleClick = (event) => {
if (event.target.closest('.add')) return;
@@ -174,6 +265,16 @@
}
};
+ _handleContextMenu = (event) => {
+ if (event.target.closest('.add')) return;
+ event.preventDefault();
+ this._contextMenu.openPopupAtScreen(event.screenX, event.screenY, true);
+ };
+
+ _getSidenav() {
+ return this.closest('.zotero-view-item-container')?.querySelector('item-pane-sidenav');
+ }
+
render() {
if (!this.initialized) return;
diff --git a/chrome/content/zotero/elements/itemPaneSidenav.js b/chrome/content/zotero/elements/itemPaneSidenav.js
index 10b3950c1e..3b76e3193e 100644
--- a/chrome/content/zotero/elements/itemPaneSidenav.js
+++ b/chrome/content/zotero/elements/itemPaneSidenav.js
@@ -28,31 +28,50 @@
{
class ItemPaneSidenav extends XULElementBase {
content = MozXULElement.parseXULToFragment(`
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
`, ['chrome://zotero/locale/zotero.dtd']);
_container = null;
- _currentSmoothScrollTarget = null;
-
- _smoothScrollTargetTimeout = null;
+ _contextMenuTarget = null;
+
+ _preserveMinScrollHeightTimeout = null;
get container() {
return this._container;
@@ -60,26 +79,33 @@
set container(val) {
if (this._container == val) return;
-
- this._container?.removeEventListener('scroll', this._updateSelectedPane.bind(this), { passive: true });
+ this._container?.removeEventListener('scroll', this._handleContainerScroll);
this._container = val;
- this._container.addEventListener('scroll', this._updateSelectedPane.bind(this), { passive: true });
-
- this._updateSelectedPane();
+ this._container.addEventListener('scroll', this._handleContainerScroll);
this.render();
}
- get selectedPane() {
- return this.getAttribute('selectedPane');
+ get pinnedPane() {
+ return this.getAttribute('pinnedPane');
}
- set selectedPane(val) {
- this.setAttribute('selectedPane', val);
- this.render();
+ set pinnedPane(val) {
+ this.setAttribute('pinnedPane', val || '');
+ if (val) {
+ this._pinnedPaneMinScrollHeight = this._getMinScrollHeightForPane(this.getPane(val));
+ }
+ }
+
+ get _minScrollHeight() {
+ return parseFloat(this._container.style.getPropertyValue('--min-scroll-height') || 0);
+ }
+
+ set _minScrollHeight(val) {
+ this._container.style.setProperty('--min-scroll-height', val + 'px');
}
static get observedAttributes() {
- return ['selectedPane'];
+ return ['pinnedPane'];
}
attributeChangedCallback() {
@@ -87,75 +113,134 @@
}
scrollToPane(id, behavior = 'smooth') {
- let pane = this._getPane(id);
- if (pane) {
- this._currentSmoothScrollTarget = id;
- pane.scrollIntoView({ block: 'start', behavior });
-
- if (this._smoothScrollTargetTimeout) {
- clearTimeout(this._smoothScrollTargetTimeout);
+ let pane = this.getPane(id);
+ if (!pane) return;
+
+ // The pane should always be at the very top
+ // If there isn't enough stuff below it for it to be at the top, we add padding
+ // We use a ::before pseudo-element for this so that we don't need to add another level to the DOM
+ this._makeSpaceForPane(pane);
+ if (behavior == 'smooth' && pane.getBoundingClientRect().top > this._container.getBoundingClientRect().top) {
+ if (this._preserveMinScrollHeightTimeout) {
+ clearTimeout(this._preserveMinScrollHeightTimeout);
}
- this._smoothScrollTargetTimeout = setTimeout(() => {
- this._currentSmoothScrollTarget = null;
+ this._preserveMinScrollHeightTimeout = setTimeout(() => {
+ this._preserveMinScrollHeightTimeout = null;
+ this._handleContainerScroll();
+ }, 1000);
+ }
+ pane.scrollIntoView({ block: 'start', behavior });
+ pane.focus();
+ }
+
+ _flashPane(id) {
+ let paneSection = this.getPane(id)?.querySelector('collapsible-section');
+ if (paneSection) {
+ paneSection.classList.add('highlight');
+ setTimeout(() => {
+ paneSection.classList.remove('highlight');
}, 1000);
}
}
- _updateSelectedPane() {
- let topPane = null;
- let containerBoundingRect = this.container.getBoundingClientRect();
- // If not initialized with content, just select the first pane
- if (containerBoundingRect.height == 0) {
- this.selectedPane = 'info';
- return;
+ _makeSpaceForPane(pane) {
+ let oldMinScrollHeight = this._minScrollHeight;
+ let newMinScrollHeight = this._getMinScrollHeightForPane(pane);
+ if (newMinScrollHeight > oldMinScrollHeight) {
+ this._minScrollHeight = newMinScrollHeight;
}
- for (let box of this._getPanes()) {
- // Allow a little padding to deal with floating-point imprecision
- if (box.getBoundingClientRect().top > containerBoundingRect.top + 5) {
- break;
- }
- topPane = box.dataset.pane;
- }
- if (this._currentSmoothScrollTarget) {
- if (this._currentSmoothScrollTarget == topPane) {
- this._currentSmoothScrollTarget = null;
- }
- else {
- return;
- }
- }
- this.selectedPane = topPane || 'info';
- }
-
- _getPanes() {
- return Array.from(this.container.querySelectorAll('[data-pane]'));
}
- _getPane(id) {
- return this.container.querySelector(':scope > [data-pane="' + id + '"]');
+ _getMinScrollHeightForPane(pane) {
+ let paneRect = pane.getBoundingClientRect();
+ let containerRect = this._container.getBoundingClientRect();
+ // No offsetTop property for XUL elements
+ let offsetTop = paneRect.top - containerRect.top + this._container.scrollTop;
+ return offsetTop + containerRect.height;
+ }
+
+ _handleContainerScroll = () => {
+ if (this._preserveMinScrollHeightTimeout) return;
+ let minHeight = this._minScrollHeight;
+ if (minHeight) {
+ let newMinScrollHeight = this._container.scrollTop + this._container.clientHeight;
+ if (this.pinnedPane && newMinScrollHeight < this._pinnedPaneMinScrollHeight) {
+ return;
+ }
+ this._minScrollHeight = newMinScrollHeight;
+ }
+ };
+
+ getPanes() {
+ return Array.from(this.container.querySelectorAll(':scope > [data-pane]'));
+ }
+
+ getPane(id) {
+ return this.container.querySelector(`:scope > [data-pane="${CSS.escape(id)}"]`);
+ }
+
+ isPanePinnable(id) {
+ return id !== 'info';
}
init() {
if (!this.container) {
this.container = document.getElementById('zotero-view-item');
}
-
- for (let toolbarbutton of this.children) {
- toolbarbutton.addEventListener('command', () => {
- this.selectedPane = toolbarbutton.dataset.pane;
- this.scrollToPane(toolbarbutton.dataset.pane);
+
+ for (let toolbarbutton of this.querySelectorAll('toolbarbutton')) {
+ let pane = toolbarbutton.dataset.pane;
+ let pinnable = this.isPanePinnable(pane);
+ toolbarbutton.parentElement.classList.toggle('pinnable', pinnable);
+
+ toolbarbutton.addEventListener('click', (event) => {
+ switch (event.detail) {
+ case 1:
+ this.scrollToPane(pane, 'smooth');
+ this._flashPane(pane);
+ break;
+ case 2:
+ if (this.pinnedPane == pane || !pinnable) {
+ this.pinnedPane = null;
+ }
+ else {
+ this.pinnedPane = pane;
+ }
+ break;
+ }
});
+
+ if (pinnable) {
+ toolbarbutton.addEventListener('contextmenu', (event) => {
+ this._contextMenuTarget = pane;
+ this.querySelector('.zotero-menuitem-pin').hidden = this.pinnedPane == pane;
+ this.querySelector('.zotero-menuitem-unpin').hidden = this.pinnedPane != pane;
+ this.querySelector('.context-menu')
+ .openPopupAtScreen(event.screenX, event.screenY, true);
+ });
+ }
}
+
+ this.querySelector('.zotero-menuitem-pin').addEventListener('command', () => {
+ this.scrollToPane(this._contextMenuTarget, 'smooth');
+ this.pinnedPane = this._contextMenuTarget;
+ });
+ this.querySelector('.zotero-menuitem-unpin').addEventListener('command', () => {
+ this.pinnedPane = null;
+ });
+
this.render();
}
render() {
- let currentPane = this.selectedPane;
- for (let toolbarbutton of this.children) {
+ let pinnedPane = this.pinnedPane;
+ for (let toolbarbutton of this.querySelectorAll('toolbarbutton')) {
let pane = toolbarbutton.dataset.pane;
- toolbarbutton.hidden = !this._getPane(pane);
- toolbarbutton.toggleAttribute('selected', pane == currentPane);
- toolbarbutton.setAttribute('aria-selected', pane == currentPane);
+ toolbarbutton.setAttribute('aria-selected', pane == pinnedPane);
+ toolbarbutton.parentElement.hidden = !this.getPane(pane);
+
+ // Set .pinned on the container, for pin styling
+ toolbarbutton.parentElement.classList.toggle('pinned', pane == pinnedPane);
}
}
}
diff --git a/chrome/content/zotero/elements/tagsBox.js b/chrome/content/zotero/elements/tagsBox.js
index 4d2c5a223d..0b3ff4dfc9 100644
--- a/chrome/content/zotero/elements/tagsBox.js
+++ b/chrome/content/zotero/elements/tagsBox.js
@@ -84,7 +84,7 @@
let removeAllItemTags = this._id('remove-all-item-tags');
this._id('remove-all-item-tags').addEventListener('command', this.removeAll);
- this.addEventListener('contextmenu', (event) => {
+ this.querySelector('.body').addEventListener('contextmenu', (event) => {
removeAllItemTags.disabled = !this.count;
this._id('tags-context-menu').openPopupAtScreen(event.screenX, event.screenY, true);
});
diff --git a/chrome/content/zotero/itemPane.js b/chrome/content/zotero/itemPane.js
index 217aab0d8b..4d0b2f2e0a 100644
--- a/chrome/content/zotero/itemPane.js
+++ b/chrome/content/zotero/itemPane.js
@@ -62,12 +62,8 @@ var ZoteroItemPane = new function() {
/*
* Load a top-level item
*/
- this.viewItem = Zotero.Promise.coroutine(function* (item, mode, pane) {
- if (!pane) {
- pane = 'info';
- }
-
- Zotero.debug('Viewing item in pane ' + pane);
+ this.viewItem = Zotero.Promise.coroutine(function* (item, mode, pinnedPane) {
+ Zotero.debug('Viewing item');
_notesBox.parentItem = item;
@@ -108,14 +104,20 @@ var ZoteroItemPane = new function() {
box.inTrash = inTrash;
}
- _sidenav.selectedPane = pane;
- _sidenav.scrollToPane(pane, 'instant');
+ _scrollParent.style.paddingBottom = '';
+ if (pinnedPane) {
+ _sidenav.scrollToPane(pinnedPane, 'instant');
+ _sidenav.pinnedPane = pinnedPane;
+ }
+ else if (pinnedPane !== false) {
+ _sidenav.scrollToPane('info', 'instant');
+ }
});
this.notify = Zotero.Promise.coroutine(function* (action, type, ids, extraData) {
if (action == 'refresh' && _lastItem) {
- yield this.viewItem(_lastItem, null, _sidenav.selectedPane);
+ yield this.viewItem(_lastItem, null, false);
}
});
@@ -250,8 +252,8 @@ var ZoteroItemPane = new function() {
};
- this.getSidenavSelectedPane = function () {
- return _sidenav.selectedPane;
+ this.getPinnedPane = function () {
+ return _sidenav.pinnedPane;
};
diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js
index 05ba347db1..97b5dc7c0e 100644
--- a/chrome/content/zotero/zoteroPane.js
+++ b/chrome/content/zotero/zoteroPane.js
@@ -1818,14 +1818,9 @@ var ZoteroPane = new function()
var isCommons = collectionTreeRow.isBucket();
let deck = document.getElementById('zotero-item-pane-content');
- let pane;
- if (deck.selectedIndex == 1) {
- pane = ZoteroItemPane.getSidenavSelectedPane();
- }
- else {
- deck.selectedIndex = 1;
- pane = ZoteroItemPane.getSidenavSelectedPane();
- }
+ deck.selectedIndex = 1;
+
+ let pane = ZoteroItemPane.getPinnedPane();
var button = document.getElementById('zotero-item-show-original');
if (isCommons) {
diff --git a/chrome/locale/en-US/zotero/zotero.ftl b/chrome/locale/en-US/zotero/zotero.ftl
index 3124d1af0c..37de54deda 100644
--- a/chrome/locale/en-US/zotero/zotero.ftl
+++ b/chrome/locale/en-US/zotero/zotero.ftl
@@ -267,6 +267,16 @@ sidenav-tags =
sidenav-related =
.tooltiptext = { pane-related }
+pin-section =
+ .label = Pin Section
+unpin-section =
+ .label = Unpin Section
+
+collapse-other-sections =
+ .label = Collapse Other Sections
+expand-all-sections =
+ .label = Expand All Sections
+
abstract-field =
.label = Add abstract…
diff --git a/chrome/skin/default/zotero/16/universal/collapse-others.svg b/chrome/skin/default/zotero/16/universal/collapse-others.svg
new file mode 100644
index 0000000000..829091a133
--- /dev/null
+++ b/chrome/skin/default/zotero/16/universal/collapse-others.svg
@@ -0,0 +1,5 @@
+
diff --git a/chrome/skin/default/zotero/16/universal/expand-all.svg b/chrome/skin/default/zotero/16/universal/expand-all.svg
new file mode 100644
index 0000000000..f3072b3969
--- /dev/null
+++ b/chrome/skin/default/zotero/16/universal/expand-all.svg
@@ -0,0 +1,5 @@
+
diff --git a/chrome/skin/default/zotero/16/universal/pin-remove.svg b/chrome/skin/default/zotero/16/universal/pin-remove.svg
new file mode 100644
index 0000000000..42052788d7
--- /dev/null
+++ b/chrome/skin/default/zotero/16/universal/pin-remove.svg
@@ -0,0 +1,10 @@
+
diff --git a/chrome/skin/default/zotero/16/universal/pin.svg b/chrome/skin/default/zotero/16/universal/pin.svg
new file mode 100644
index 0000000000..6e8bad4834
--- /dev/null
+++ b/chrome/skin/default/zotero/16/universal/pin.svg
@@ -0,0 +1,10 @@
+
diff --git a/chrome/skin/default/zotero/8/universal/pin-remove.svg b/chrome/skin/default/zotero/8/universal/pin-remove.svg
new file mode 100644
index 0000000000..f223852f6b
--- /dev/null
+++ b/chrome/skin/default/zotero/8/universal/pin-remove.svg
@@ -0,0 +1,3 @@
+
diff --git a/chrome/skin/default/zotero/8/universal/pin.svg b/chrome/skin/default/zotero/8/universal/pin.svg
new file mode 100644
index 0000000000..ef08d39da6
--- /dev/null
+++ b/chrome/skin/default/zotero/8/universal/pin.svg
@@ -0,0 +1,5 @@
+
diff --git a/chrome/skin/default/zotero/itemPane.css b/chrome/skin/default/zotero/itemPane.css
index 58b1d8b107..3deb0f8285 100644
--- a/chrome/skin/default/zotero/itemPane.css
+++ b/chrome/skin/default/zotero/itemPane.css
@@ -15,6 +15,7 @@
.zotero-view-item-container {
display: flex;
flex-direction: row;
+ height: 0; /* Prevent overflow, somehow */
}
.zotero-view-item-main {
@@ -24,10 +25,22 @@
}
.zotero-view-item {
+ position: relative;
flex: 1;
overflow-y: auto;
padding: 0 8px;
overflow-anchor: none; /* Work around tags box causing scroll to jump - figure this out */
+ scrollbar-color: var(--color-scrollbar) var(--color-scrollbar-background);
+}
+
+.zotero-view-item::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: var(--min-scroll-height);
+ pointer-events: none;
}
.zotero-view-item-container.feed-item .zotero-view-item > :is(attachments-box, notes-box, tags-box, related-box),
@@ -40,10 +53,6 @@
width: 100%;
}
-.zotero-view-item > :last-child {
- min-height: 100%;
-}
-
.zotero-view-item > vbox
{
margin-left: 5px;
diff --git a/scss/abstracts/_mixins.scss b/scss/abstracts/_mixins.scss
index 622b8987d1..7de825cc24 100644
--- a/scss/abstracts/_mixins.scss
+++ b/scss/abstracts/_mixins.scss
@@ -108,3 +108,22 @@
}
}
}
+
+
+/* Hide icons on macOS. We use :is() to work around weird behavior in Fx101 where a regular child
+ selector doesn't match the first time the menu is opened. */
+@mixin macOS-hide-menu-icons {
+ $selector: &;
+ @at-root {
+ @media (-moz-platform: macos) {
+ // Yes, every single one of these :is-es is necessary!
+ :is(:is(#{$selector}) .menuitem-iconic, :is(#{$selector}) .menu-iconic) {
+ list-style-image: none !important;
+
+ .menu-iconic-left {
+ display: none !important;
+ }
+ }
+ }
+ }
+}
diff --git a/scss/components/_menu.scss b/scss/components/_menu.scss
index 9ee14361d7..daa401cc95 100644
--- a/scss/components/_menu.scss
+++ b/scss/components/_menu.scss
@@ -57,7 +57,10 @@ $menu-icons: (
add-to-collection: "new-collection",
new-tab: "new-tab",
new-window: "new-window",
-
+ pin: "pin",
+ unpin: "pin-remove",
+ expand-all: "expand-all",
+ collapse-others: "collapse-others",
);
@each $cls, $icon in $menu-icons {
@@ -73,3 +76,7 @@ $menu-icons: (
}
}
};
+
+#zotero-collectionmenu, #zotero-itemmenu {
+ @include macOS-hide-menu-icons;
+}
diff --git a/scss/elements/_collapsibleSection.scss b/scss/elements/_collapsibleSection.scss
index 64b907d22e..f3ddd53a6d 100644
--- a/scss/elements/_collapsibleSection.scss
+++ b/scss/elements/_collapsibleSection.scss
@@ -24,6 +24,7 @@ collapsible-section {
gap: 4px;
color: var(--fill-secondary);
font-weight: 590;
+ transition: color 0.2s ease-out;
&::before {
content: '';
@@ -59,6 +60,14 @@ collapsible-section {
transform-origin: center;
transition: transform 0.2s ease-in-out;
}
+
+ popupset > menupopup {
+ @include macOS-hide-menu-icons;
+ }
+ }
+
+ &.highlight > .head > .title {
+ color: var(--fill-primary);
}
&[open]:not([empty]) > .head {
@@ -97,4 +106,9 @@ collapsible-section {
transition: max-height 0.2s ease-in-out, opacity 0.2s ease-in-out, visibility 0s 0.2s, overflow-y 0s 0.2s;
}
}
+
+ &.disable-transitions * {
+ transition: none !important;
+ color: red;
+ }
}
diff --git a/scss/elements/_itemPaneSidenav.scss b/scss/elements/_itemPaneSidenav.scss
index fbf2414856..59506650b9 100644
--- a/scss/elements/_itemPaneSidenav.scss
+++ b/scss/elements/_itemPaneSidenav.scss
@@ -6,24 +6,53 @@ item-pane-sidenav {
gap: 6px;
border-left: var(--material-panedivider);
+ .toolbarbutton-container {
+ position: relative;
+ // Disable pointer events here, re-enable on the button, so that :hover styles are only applied
+ // when the button itself is hovered
+ pointer-events: none;
+
+ &::after {
+ content: '';
+ position: absolute;
+ top: 1px;
+ inset-inline-end: 1px;
+ width: 12px;
+ height: 12px;
+ border-radius: 50%;
+ }
+
+ &.pinnable {
+ &.pinned::after {
+ background-color: var(--accent-blue);
+ background-image: icon-url("8/universal/pin.svg");
+ background-position: center;
+ background-repeat: no-repeat;
+ }
+ }
+ }
+
toolbarbutton {
// TODO: Extract button styles?
width: 28px;
height: 28px;
+ margin: 0;
padding: 4px;
flex-shrink: 0;
border-radius: 5px;
-moz-context-properties: fill, fill-opacity, stroke, stroke-opacity;
+ pointer-events: all;
+
&:hover {
background-color: var(--fill-quinary);
}
-
- &:active, &[selected] {
+
+ &:active {
background-color: var(--fill-quarternary);
}
-
+
&:disabled {
background-color: transparent;
fill: var(--fill-tertiary);
@@ -37,4 +66,8 @@ item-pane-sidenav {
}
}
}
+
+ menupopup {
+ @include macOS-hide-menu-icons;
+ }
}