Implement new pane selection algorithm, remove bottom padding

This commit is contained in:
Abe Jellinek 2023-12-01 02:43:43 -05:00 committed by Dan Stillman
parent cc9d58e6be
commit 19b78e2b20
18 changed files with 427 additions and 121 deletions

View file

@ -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; }

View file

@ -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;

View file

@ -28,31 +28,50 @@
{
class ItemPaneSidenav extends XULElementBase {
content = MozXULElement.parseXULToFragment(`
<toolbarbutton
data-l10n-id="sidenav-info"
data-pane="info"/>
<toolbarbutton
data-l10n-id="sidenav-abstract"
data-pane="abstract"/>
<toolbarbutton
data-l10n-id="sidenav-attachments"
data-pane="attachments"/>
<toolbarbutton
data-l10n-id="sidenav-notes"
data-pane="notes"/>
<toolbarbutton
data-l10n-id="sidenav-tags"
data-pane="tags"/>
<toolbarbutton
data-l10n-id="sidenav-related"
data-pane="related"/>
<html:div class="toolbarbutton-container">
<toolbarbutton
data-l10n-id="sidenav-info"
data-pane="info"/>
</html:div>
<html:div class="toolbarbutton-container">
<toolbarbutton
data-l10n-id="sidenav-abstract"
data-pane="abstract"/>
</html:div>
<html:div class="toolbarbutton-container">
<toolbarbutton
data-l10n-id="sidenav-attachments"
data-pane="attachments"/>
</html:div>
<html:div class="toolbarbutton-container">
<toolbarbutton
data-l10n-id="sidenav-notes"
data-pane="notes"/>
</html:div>
<html:div class="toolbarbutton-container">
<toolbarbutton
data-l10n-id="sidenav-tags"
data-pane="tags"/>
</html:div>
<html:div class="toolbarbutton-container">
<toolbarbutton
data-l10n-id="sidenav-related"
data-pane="related"/>
</html:div>
<popupset>
<menupopup class="context-menu">
<menuitem class="menuitem-iconic zotero-menuitem-pin" data-l10n-id="pin-section"/>
<menuitem class="menuitem-iconic zotero-menuitem-unpin" data-l10n-id="unpin-section"/>
</menupopup>
</popupset>
`, ['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);
}
}
}

View file

@ -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);
});

View file

@ -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;
};

View file

@ -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) {

View file

@ -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…

View file

@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g>
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.9999 1.70711L8.00004 5.70709L4 1.70711L4.70711 1L8.00004 4.29288L11.2928 1L11.9999 1.70711ZM11.9999 15.2929L8.00004 11.2929L4 15.2929L4.70711 16L8.00004 12.7071L11.2928 16L11.9999 15.2929ZM14 8H2V9H14V8Z" fill="context-fill"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 392 B

View file

@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.00004 0.292908L11.9999 4.29289L11.2928 5L8.00004 1.70712L4.70711 5L4 4.29289L8.00004 0.292908ZM8.00004 15.7071L11.9999 11.7071L11.2928 11L8.00004 14.2929L4.70711 11L4 11.7071L8.00004 15.7071Z" fill="context-fill"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 379 B

View file

@ -0,0 +1,10 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_4465_26581)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0.70706L15.293 16L16 15.2928L0.707197 0L0 0.70706ZM10.499 12.6203L9.25753 15.1031C8.95228 15.7136 8.13864 15.8457 7.65599 15.363L4.50008 12.2071L0.707182 16L9.87947e-05 16L0 15.2929L3.79297 11.5L0.637052 8.34408C0.15441 7.86144 0.286449 7.04779 0.896946 6.74255L3.37981 5.50111L4.12517 6.24647L1.34416 7.63697L8.3631 14.6559L9.7536 11.8749L10.499 12.6203ZM11.9132 9.79184L12.3489 8.92033L14.8204 8.2142C15.5535 8.00473 15.7919 7.08473 15.2528 6.54558L9.4545 0.747313C8.91534 0.208154 7.99534 0.446553 7.78587 1.1797L7.07974 3.65115L6.20824 4.0869L6.95359 4.83226L7.72368 4.44721L7.92041 4.34885L7.98084 4.13736L8.74739 1.45442L14.5457 7.25268L11.8627 8.01923L11.6512 8.07966L11.5529 8.27639L11.1678 9.04648L11.9132 9.79184Z" fill="context-fill"/>
</g>
<defs>
<clipPath id="clip0_4465_26581">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1,10 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_4465_26580)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.45442 0.747313C8.91526 0.208154 7.99526 0.446553 7.78579 1.1797L7.07966 3.65115L0.896869 6.74255C0.286372 7.04779 0.154333 7.86144 0.636975 8.34408L3.79293 11.5L0 15.2929L9.87947e-05 16L0.707182 16L4.50004 12.2071L7.65592 15.363C8.13856 15.8457 8.9522 15.7136 9.25745 15.1031L12.3488 8.92033L14.8203 8.21421C15.5534 8.00474 15.7918 7.08473 15.2527 6.54558L9.45442 0.747313ZM8.74732 1.45442L14.5456 7.25268L11.8626 8.01924L11.6512 8.07966L11.5528 8.27639L8.36302 14.6559L1.34408 7.63697L7.72361 4.44721L7.92034 4.34885L7.98076 4.13736L8.74732 1.45442Z" fill="context-fill"/>
</g>
<defs>
<clipPath id="clip0_4465_26580">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 877 B

View file

@ -0,0 +1,3 @@
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.00002 4L5.68694 4.62617L3.37386 2.31308L4.00002 2L4.50002 0.5L7.50002 3.5L6.00002 4ZM4.97983 6.04038L1.95964 3.02019L1.00002 3.5L2.50002 5L1.00002 6.5V7H1.50002L3.00002 5.5L4.50002 7L4.97983 6.04038ZM1 1.35355L6.64648 7L7.00004 6.64645L1.35355 1L1 1.35355Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 424 B

View file

@ -0,0 +1,5 @@
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="pin">
<path id="Vector" d="M7.5 3.5L4.5 0.5L4 2L1 3.5L2.5 5L1 6.5V7H1.5L3 5.5L4.5 7L6 4L7.5 3.5Z" fill="white"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 224 B

View file

@ -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;

View file

@ -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;
}
}
}
}
}

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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;
}
}