From 4b5bee37b131dae7c6c5ad75bc1bdc11ecd2bb70 Mon Sep 17 00:00:00 2001 From: Abe Jellinek Date: Tue, 6 May 2025 12:05:31 -0400 Subject: [PATCH] Show item pane toggle everywhere --- chrome/content/zotero/contextPane.js | 80 +++++++++---------- .../zotero/elements/itemPaneSidenav.js | 23 +++++- chrome/content/zotero/xpcom/reader.js | 13 +++ chrome/content/zotero/zoteroPane.js | 10 ++- chrome/content/zotero/zoteroPane.xhtml | 9 +++ chrome/locale/en-US/zotero/zotero.ftl | 5 ++ .../zotero/20/universal/sidebar-bottom.svg | 5 ++ .../default/zotero/20/universal/sidebar.svg | 6 ++ scss/components/_toolbar.scss | 14 ++++ scss/components/_toolbarbutton.scss | 1 + scss/elements/_itemPaneSidenav.scss | 25 +++++- 11 files changed, 140 insertions(+), 51 deletions(-) create mode 100644 chrome/skin/default/zotero/20/universal/sidebar-bottom.svg create mode 100644 chrome/skin/default/zotero/20/universal/sidebar.svg diff --git a/chrome/content/zotero/contextPane.js b/chrome/content/zotero/contextPane.js index 2cc4876b38..41c5254820 100644 --- a/chrome/content/zotero/contextPane.js +++ b/chrome/content/zotero/contextPane.js @@ -57,12 +57,10 @@ var ZoteroContextPane = new function () { _contextPaneInner.setAttribute('collapsed', !!collapsed); _contextPaneSplitter.setAttribute('state', collapsed ? 'collapsed' : 'open'); _contextPaneSplitterStacked.setAttribute('state', collapsed ? 'collapsed' : 'open'); - _update(); + this.update(); } }); - this.update = _update; - this.focus = () => { return _contextPaneInner.handleFocus(); }; @@ -71,10 +69,6 @@ var ZoteroContextPane = new function () { _loadingMessageContainer.classList.toggle('hidden', !isShow); }; - this.updateAddToNote = _updateAddToNote; - - this.togglePane = _togglePane; - this.init = function () { if (!Zotero) { return; @@ -93,18 +87,18 @@ var ZoteroContextPane = new function () { this.context = _contextPaneInner; - window.addEventListener('resize', _update); - Zotero.Reader.onChangeSidebarWidth = _updatePaneWidth; - Zotero.Reader.onToggleSidebar = _updatePaneWidth; + window.addEventListener('resize', this.update); + Zotero.Reader.onChangeSidebarWidth = this._updatePaneWidth; + Zotero.Reader.onToggleSidebar = this._updatePaneWidth; }; this.destroy = function () { - window.removeEventListener('resize', _update); + window.removeEventListener('resize', this.update); Zotero.Reader.onChangeSidebarWidth = () => {}; Zotero.Reader.onToggleSidebar = () => {}; }; - function _updateAddToNote() { + this.updateAddToNote = () => { let reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID); if (reader) { let editor = ZoteroContextPane.activeEditor; @@ -113,34 +107,28 @@ var ZoteroContextPane = new function () { && (editor.item.deleted || editor.item.parentItem && editor.item.parentItem.deleted); reader.enableAddToNote(!!editor && !libraryReadOnly && !noteReadOnly); } - } + }; - function _updatePaneWidth() { + this._updatePaneWidth = () => { let stacked = _isStacked(); - let width = Zotero.Reader.getSidebarWidth() + 'px'; - if (!Zotero.Reader.getSidebarOpen()) { - width = 0; - } + let readerSidebarWidth = (Zotero.Reader.getSidebarOpen() ? Zotero.Reader.getSidebarWidth() : 0) + + 'px'; let contextPaneWidth = _contextPane.getAttribute("width"); if (contextPaneWidth && !_contextPane.style.width) { _contextPane.style.width = `${contextPaneWidth}px`; } if (Zotero.rtl) { _contextPane.style.left = 0; - _contextPane.style.right = stacked ? width : 'unset'; + _contextPane.style.right = stacked ? readerSidebarWidth : 'unset'; } else { - _contextPane.style.left = stacked ? width : 'unset'; + _contextPane.style.left = stacked ? readerSidebarWidth : 'unset'; _contextPane.style.right = 0; } - } + }; - function _isStacked() { - return Zotero.Prefs.get('layout') == 'stacked'; - } - - function _update() { - if (Zotero_Tabs.selectedIndex == 0) { + this.update = () => { + if (Zotero_Tabs.selectedType === 'library') { return; } if (_isStacked()) { @@ -174,28 +162,32 @@ var ZoteroContextPane = new function () { _contextPane.style.width = `${_contextPane.getAttribute("width")}px`; } - if (Zotero_Tabs.selectedIndex > 0) { - var height = null; - if (_isStacked()) { - height = 0; - if (_contextPane.getAttribute('collapsed') != 'true') { - height = _contextPaneInner.getBoundingClientRect().height; - } - } - Zotero.Reader.setBottomPlaceholderHeight(height); - } + Zotero.Reader.setContextPaneOpen(!this.collapsed); - _updatePaneWidth(); - _updateAddToNote(); + var height = null; + if (_isStacked()) { + height = 0; + if (_contextPane.getAttribute('collapsed') != 'true') { + height = _contextPaneInner.getBoundingClientRect().height; + } + } + Zotero.Reader.setBottomPlaceholderHeight(height); + + this._updatePaneWidth(); + this.updateAddToNote(); ZoteroPane.updateLayoutConstraints(); + }; + + this.togglePane = () => { + this.collapsed = !this.collapsed; + }; + + function _isStacked() { + return Zotero.Prefs.get('layout') == 'stacked'; } - + function _isLibraryReadOnly(libraryID) { return !Zotero.Libraries.get(libraryID).editable; } - - function _togglePane() { - this.collapsed = !this.collapsed; - } }; diff --git a/chrome/content/zotero/elements/itemPaneSidenav.js b/chrome/content/zotero/elements/itemPaneSidenav.js index 6b4195a7e1..b0146082b7 100644 --- a/chrome/content/zotero/elements/itemPaneSidenav.js +++ b/chrome/content/zotero/elements/itemPaneSidenav.js @@ -28,6 +28,14 @@ { class ItemPaneSidenav extends XULElementBase { content = MozXULElement.parseXULToFragment(` + + + + + @@ -215,6 +223,11 @@ for (let button of this.querySelectorAll('.btn[data-action]')) { let action = button.dataset.action; + if (action === 'toggle-pane') { + button.addEventListener('command', () => { + this._collapsed = !this._collapsed; + }); + } if (action === 'locate') { button.addEventListener('mousedown', async (event) => { if (event.button !== 0 || button.open) { @@ -313,9 +326,15 @@ for (let button of this.querySelectorAll('.btn[data-action]')) { let action = button.dataset.action; - if (action == 'locate') { + if (action == 'toggle-pane' || action == 'locate') { button.parentElement.hidden = false; } + if (action == 'toggle-pane') { + document.l10n.setAttributes(button, + Zotero_Tabs.selectedType === 'library' + ? 'toggle-item-pane' + : 'toggle-context-pane'); + } } this.querySelector('.highlight-notes-active').classList.toggle('highlight', contextNotesPaneVisible); @@ -601,7 +620,7 @@ return { index, position: index === 0 ? 0 : index * (btnSize + btnGap) + btnGap / 2 - } + }; }; handleKeyDown = (event) => { diff --git a/chrome/content/zotero/xpcom/reader.js b/chrome/content/zotero/xpcom/reader.js index eb1fbd3d4f..14a849aad4 100644 --- a/chrome/content/zotero/xpcom/reader.js +++ b/chrome/content/zotero/xpcom/reader.js @@ -201,6 +201,7 @@ class ReaderInstance { sidebarWidth: this._sidebarWidth, sidebarOpen: this._sidebarOpen, bottomPlaceholderHeight: this._bottomPlaceholderHeight, + contextPaneOpen: this._contextPaneOpen, rtl: Zotero.rtl, fontSize: Zotero.Prefs.get('fontSize'), localizedStrings: { @@ -1236,6 +1237,7 @@ class ReaderTab extends ReaderInstance { super(options); this._sidebarWidth = options.sidebarWidth; this._sidebarOpen = options.sidebarOpen; + this._contextPaneOpen = options.bottomPlaceholderHeight; this._bottomPlaceholderHeight = options.bottomPlaceholderHeight; this._showContextPaneToggle = true; this._onToggleSidebarCallback = options.onToggleSidebar; @@ -1388,6 +1390,7 @@ class ReaderWindow extends ReaderInstance { super(options); this._sidebarWidth = options.sidebarWidth; this._sidebarOpen = options.sidebarOpen; + this._contextPaneOpen = false; this._bottomPlaceholderHeight = 0; this._onClose = options.onClose; @@ -1800,6 +1803,7 @@ class Reader { constructor() { this._sidebarWidth = 240; this._sidebarOpen = false; + this._contextPaneOpen = false; this._bottomPlaceholderHeight = 0; this._readers = []; this._notifierID = Zotero.Notifier.registerObserver(this, ['item', 'setting', 'tab'], 'reader'); @@ -1925,6 +1929,14 @@ class Reader { } this._setSidebarState(); } + + setContextPaneOpen(open) { + this._contextPaneOpen = open; + let readers = this._readers.filter(r => r instanceof ReaderTab); + for (let reader of readers) { + reader.setContextPaneOpen(open); + } + } setBottomPlaceholderHeight(height) { this._bottomPlaceholderHeight = height; @@ -2107,6 +2119,7 @@ class Reader { background: openInBackground, sidebarWidth: this._sidebarWidth, sidebarOpen: this._sidebarOpen, + contextPaneOpen: this._contextPaneOpen, bottomPlaceholderHeight: this._bottomPlaceholderHeight, preventJumpback: preventJumpback, onToggleSidebar: (open) => { diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js index cdba26f7eb..ebf7afc17b 100644 --- a/chrome/content/zotero/zoteroPane.js +++ b/chrome/content/zotero/zoteroPane.js @@ -6853,8 +6853,14 @@ var ZoteroPane = new function() this.handleTagSelectorResize(); this.itemPane.handleResize(); - } - + }; + + + this.toggleItemPane = function () { + this.itemPane.collapsed = !this.itemPane.collapsed; + this.updateLayoutConstraints(); + }; + // Set the label of the dynamic tooltip. Can be used when we cannot set .tooltiptext // property, e.g. if we don't want the tooltip to be announced by screenreaders. diff --git a/chrome/content/zotero/zoteroPane.xhtml b/chrome/content/zotero/zoteroPane.xhtml index 595f90399f..2dfc4e7c77 100644 --- a/chrome/content/zotero/zoteroPane.xhtml +++ b/chrome/content/zotero/zoteroPane.xhtml @@ -1344,6 +1344,15 @@ onkeydown="ZoteroPane_Local.handleSearchKeypress(this, event)" oninput="ZoteroPane_Local.handleSearchInput(this, event)" oncommand="ZoteroPane_Local.search()"/> + + + diff --git a/chrome/locale/en-US/zotero/zotero.ftl b/chrome/locale/en-US/zotero/zotero.ftl index 51fee32b99..ceffca5850 100644 --- a/chrome/locale/en-US/zotero/zotero.ftl +++ b/chrome/locale/en-US/zotero/zotero.ftl @@ -537,6 +537,11 @@ sidenav-reorder-down = sidenav-reorder-reset = .label = Reset Section Order +toggle-item-pane = + .tooltiptext = Toggle Item Pane +toggle-context-pane = + .tooltiptext = Toggle Context Pane + pin-section = .label = Pin Section unpin-section = diff --git a/chrome/skin/default/zotero/20/universal/sidebar-bottom.svg b/chrome/skin/default/zotero/20/universal/sidebar-bottom.svg new file mode 100644 index 0000000000..c17e5f48df --- /dev/null +++ b/chrome/skin/default/zotero/20/universal/sidebar-bottom.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/chrome/skin/default/zotero/20/universal/sidebar.svg b/chrome/skin/default/zotero/20/universal/sidebar.svg new file mode 100644 index 0000000000..6ae4c33f0e --- /dev/null +++ b/chrome/skin/default/zotero/20/universal/sidebar.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/scss/components/_toolbar.scss b/scss/components/_toolbar.scss index a6615561f8..7265502e4e 100644 --- a/scss/components/_toolbar.scss +++ b/scss/components/_toolbar.scss @@ -122,6 +122,20 @@ margin-top: 1px; } +#zotero-tb-toggle-item-pane-stacked { + // Hidden except in Stacked mode + display: none; +} + +#main-window.stacked #zotero-toolbar-item-tree { + padding-inline-end: 0; + + #zotero-tb-toggle-item-pane-stacked { // Keep nested for specificity + display: flex; + margin-inline-start: 9px; + } +} + toolbox { @media (-moz-platform: linux) { background: Menu; diff --git a/scss/components/_toolbarbutton.scss b/scss/components/_toolbarbutton.scss index 5ea8e80731..13a136a2ba 100644 --- a/scss/components/_toolbarbutton.scss +++ b/scss/components/_toolbarbutton.scss @@ -74,6 +74,7 @@ $toolbarbutton-icons: ( tabs-menu: "chevron", sync-error: "error", sync: "sync", + toggle-item-pane-stacked: "sidebar-bottom", ); @each $cls, $icon in $toolbarbutton-icons { diff --git a/scss/elements/_itemPaneSidenav.scss b/scss/elements/_itemPaneSidenav.scss index c1a23a5c5e..c06e1f069f 100644 --- a/scss/elements/_itemPaneSidenav.scss +++ b/scss/elements/_itemPaneSidenav.scss @@ -108,16 +108,35 @@ item-pane-sidenav { stroke: map.get($item-pane-sections, "notes"); } - // Locate button - &[data-action="locate"] { + &[data-action="locate"], &[data-action="toggle-pane"] { color: var(--fill-secondary); + } + + &[data-action="locate"] { @include svgicon-menu("go-to", "universal", "20"); - + + // Locate is flipped in RTL &:-moz-locale-dir(rtl) { transform: scaleX(-1); } } + &[data-action="toggle-pane"] { + @include svgicon-menu("sidebar", "universal", "20"); + + // ...And Toggle Item/Context Pane is flipped in LTR + &:-moz-locale-dir(ltr) { + transform: scaleX(-1); + } + + // Hide in Stacked mode, since we show it in the toolbar instead + @include state("item-pane-sidenav.stacked") { + &, & + .divider { + display: none; + } + } + } + &[custom] { @media (prefers-color-scheme: light) { background-image: var(--custom-sidenav-icon-light);