Bug 1930201 - Add option to expand sidebar on hover r=desktop-theme-reviewers,sidebar-reviewers,emilio,tabbrowser-reviewers,flod,sclements,dao

Differential Revision: https://phabricator.services.mozilla.com/D233098
This commit is contained in:
Kelly Cochrane
2025-02-05 14:05:08 +00:00
parent 58049f9bc7
commit 03c6b75a99
13 changed files with 394 additions and 77 deletions

View File

@@ -2076,6 +2076,7 @@ pref("sidebar.revamp.round-content-area", false);
#endif
pref("sidebar.animation.enabled", true);
pref("sidebar.animation.duration-ms", 200);
pref("sidebar.animation.expand-on-hover.duration-ms", 400);
pref("sidebar.main.tools", "aichat,syncedtabs,history");
pref("sidebar.verticalTabs", false);
pref("sidebar.visibility", "always-show");
@@ -2083,6 +2084,7 @@ pref("sidebar.visibility", "always-show");
// as a backup to restore the sidebar UI state when a user has PPB mode on
// or has history cleared on browser close.
pref("sidebar.backupState", "{}");
pref("sidebar.expandOnHover", false);
pref("browser.ml.chat.enabled", true);
pref("browser.ml.chat.hideLocalhost", true);

View File

@@ -3,26 +3,29 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
<hbox flex="1" id="browser">
<box context="sidebar-context-menu" id="sidebar-main" hidden="true">
<html:sidebar-main flex="1">
<box id="vertical-tabs" slot="tabstrip" customizable="true" contextmenu="toolbar-context-menu"></box>
</html:sidebar-main>
</box>
<splitter id="sidebar-launcher-splitter" class="chromeclass-extrachrome sidebar-splitter" resizebefore="sibling" resizeafter="none" hidden="true"/>
<vbox id="sidebar-box" hidden="true" class="chromeclass-extrachrome">
<box id="sidebar-header" align="center">
<toolbarbutton id="sidebar-switcher-target" class="tabbable" aria-expanded="false">
<image id="sidebar-icon" consumeanchor="sidebar-switcher-target"/>
<label id="sidebar-title" crop="end" control="sidebar"/>
<image id="sidebar-switcher-arrow"/>
</toolbarbutton>
<image id="sidebar-throbber"/>
<spacer id="sidebar-spacer"/>
<toolbarbutton id="sidebar-close" class="close-icon tabbable" data-l10n-id="sidebar-close-button"/>
<div id="sidebar-wrapper">
<box context="sidebar-context-menu" id="sidebar-main" hidden="true">
<html:sidebar-main flex="1">
<box id="vertical-tabs" slot="tabstrip" customizable="true" contextmenu="toolbar-context-menu"></box>
</html:sidebar-main>
</box>
<browser id="sidebar" autoscroll="false" disablehistory="true" disablefullscreen="true" tooltip="aHTMLTooltip"/>
</vbox>
<splitter id="sidebar-splitter" class="chromeclass-extrachrome sidebar-splitter" resizebefore="sibling" resizeafter="none" hidden="true"/>
<splitter id="sidebar-launcher-splitter" class="chromeclass-extrachrome sidebar-splitter" resizebefore="sibling" resizeafter="none" hidden="true"/>
<vbox id="sidebar-box" hidden="true" class="chromeclass-extrachrome">
<box id="sidebar-header" align="center">
<toolbarbutton id="sidebar-switcher-target" class="tabbable" aria-expanded="false">
<image id="sidebar-icon" consumeanchor="sidebar-switcher-target"/>
<label id="sidebar-title" crop="end" control="sidebar"/>
<image id="sidebar-switcher-arrow"/>
</toolbarbutton>
<image id="sidebar-throbber"/>
<spacer id="sidebar-spacer"/>
<toolbarbutton id="sidebar-close" class="close-icon tabbable" data-l10n-id="sidebar-close-button"/>
</box>
<browser id="sidebar" autoscroll="false" disablehistory="true" disablefullscreen="true" tooltip="aHTMLTooltip"/>
</vbox>
<splitter id="sidebar-splitter" class="chromeclass-extrachrome sidebar-splitter" resizebefore="sibling" resizeafter="none" hidden="true"/>
<box id="after-splitter"></box>
</div>
<tabbox id="tabbrowser-tabbox" flex="1" tabcontainer="tabbrowser-tabs">
<tabpanels id="tabbrowser-tabpanels" flex="1" selectedIndex="0"/>
</tabbox>

View File

@@ -33,6 +33,8 @@ XPCOMUtils.defineLazyPreferenceGetter(
* Current width of the sidebar launcher.
* @property {number} expandedLauncherWidth
* Width that the sidebar launcher should expand to.
* @property {number} collapsedLauncherWidth
* Width that the sidebar launcher should collapse to.
*/
const LAUNCHER_MINIMUM_WIDTH = 100;
@@ -52,6 +54,7 @@ export class SidebarState {
launcherExpanded: false,
launcherDragActive: false,
launcherHoverActive: false,
collapsedLauncherWidth: undefined,
};
#previousLauncherVisible = undefined;
@@ -180,6 +183,10 @@ export class SidebarState {
expandedLauncherWidth: convertToInt(this.expandedLauncherWidth),
launcherExpanded: this.launcherExpanded,
launcherVisible: this.launcherVisible,
collapsedLauncherWidth:
typeof this.collapsedLauncherWidth === "number"
? Math.round(this.collapsedLauncherWidth)
: this.collapsedLauncherWidth,
};
}
@@ -218,7 +225,8 @@ export class SidebarState {
updateVisibility(
visible,
openedByToolbarButton = false,
onToolbarButtonRemoval = false
onToolbarButtonRemoval = false,
forceExpandValue = null
) {
switch (this.revampVisibility) {
case "hide-sidebar":
@@ -248,8 +256,10 @@ export class SidebarState {
this.launcherVisible = visible;
break;
case "always-show":
case "expand-on-hover":
this.launcherVisible = true;
this.launcherExpanded = !this.launcherExpanded;
this.launcherExpanded =
forceExpandValue !== null ? forceExpandValue : !this.launcherExpanded;
break;
}
}
@@ -299,8 +309,6 @@ export class SidebarState {
this.#props.launcherDragActive = active;
if (active) {
this.#launcherEl.toggleAttribute("customWidth", true);
} else if (!lazy.verticalTabsEnabled) {
this.launcherExpanded = false;
} else if (this.launcherWidth < LAUNCHER_MINIMUM_WIDTH) {
// Snap back to collapsed state when the new width is too narrow.
this.launcherExpanded = false;
@@ -313,6 +321,14 @@ export class SidebarState {
}
}
get launcherHoverActive() {
return this.#props.launcherHoverActive;
}
set launcherHoverActive(active) {
this.#props.launcherHoverActive = active;
}
get launcherWidth() {
return this.#props.launcherWidth;
}
@@ -343,6 +359,14 @@ export class SidebarState {
this.#updateLauncherWidth();
}
get collapsedLauncherWidth() {
return this.#props.collapsedLauncherWidth;
}
set collapsedLauncherWidth(width) {
this.#props.collapsedLauncherWidth = width;
}
/**
* If the sidebar is expanded, resize the launcher to the user-preferred
* width (if available). If it is collapsed, reset the launcher width.

View File

@@ -293,6 +293,20 @@ var SidebarController = {
return this._sidebarMain;
},
get sidebarWrapper() {
if (!this._sidebarWrapper) {
this._sidebarWrapper = document.getElementById("sidebar-wrapper");
}
return this._sidebarWrapper;
},
get contentArea() {
if (!this._contentArea) {
this._contentArea = document.getElementById("tabbrowser-tabbox");
}
return this._contentArea;
},
get toolbarButton() {
if (!this._toolbarButton) {
this._toolbarButton = document.getElementById("sidebar-button");
@@ -494,6 +508,9 @@ var SidebarController = {
if (this.isLauncherDragging) {
this._state.launcherDragActive = true;
}
if (this._state.visibilitySetting === "expand-on-hover") {
this.setLauncherInlineMargin();
}
},
getUIState() {
@@ -667,6 +684,9 @@ var SidebarController = {
*/
reversePosition() {
Services.prefs.setBoolPref(this.POSITION_START_PREF, !this._positionStart);
if (this.sidebarRevampVisibility === "expand-on-hover") {
this.setLauncherInlineMargin();
}
},
/**
@@ -675,37 +695,51 @@ var SidebarController = {
*/
setPosition() {
// First reset all ordinals to match DOM ordering.
let contentArea = document.getElementById("tabbrowser-tabbox");
let browser = document.getElementById("browser");
[...browser.children].forEach((node, i) => {
node.style.order = i + 1;
});
[...this.sidebarWrapper.children].forEach((node, i) => {
node.style.order = i + 1;
});
let sidebarContainer = document.getElementById("sidebar-main");
let sidebarMain = document.querySelector("sidebar-main");
if (!this._positionStart) {
// DOM ordering is: sidebar-main | launcher-splitter | sidebar-box | splitter | tabbrowser-tabbox |
// Want to display as: | tabbrowser-tabbox | splitter | sidebar-box | launcher-splitter | sidebar-main
// So we just swap box and tabbrowser-tabbox ordering and move sidebar-main to the end
let tabbox = document.getElementById("tabbrowser-tabbox");
let boxOrdinal = this._box.style.order;
this._box.style.order = tabbox.style.order;
// First switch order of sidebar-wrapper and tabbrowser-tabbox
let wrapperOrdinal = this.sidebarWrapper.style.order;
this.sidebarWrapper.style.order = contentArea.style.order;
contentArea.style.order = wrapperOrdinal;
tabbox.style.order = boxOrdinal;
// DOM ordering is: sidebar-main | launcher-splitter | sidebar-box | splitter | after-splitter |
// Want to display as: after-splitter | splitter | sidebar-box | launcher-splitter | sidebar-main
// First swap sidebar-box and after-splitter
let afterSplitter = document.getElementById("after-splitter");
let boxOrdinal = this._box.style.order;
this._box.style.order = afterSplitter.style.order;
afterSplitter.style.order = boxOrdinal;
// Then move the launcher-splitter to the right of sidebar-box
const launcherSplitterOrdinal = parseInt(this._box.style.order) + 1;
this._launcherSplitter.style.order = launcherSplitterOrdinal;
// the launcher should be on the right of the launcher-splitter
sidebarContainer.style.order = launcherSplitterOrdinal + 1;
// Finally move the launcher to the right of the launcher-splitter
sidebarContainer.style.order = parseInt(launcherSplitterOrdinal) + 1;
// Indicate we've switched ordering to the box
this._box.toggleAttribute("positionend", true);
sidebarMain.toggleAttribute("positionend", true);
sidebarContainer.toggleAttribute("positionend", true);
this.toolbarButton &&
this.toolbarButton.toggleAttribute("positionend", true);
this.sidebarWrapper.toggleAttribute("positionend", true);
} else {
this._box.toggleAttribute("positionend", false);
sidebarMain.toggleAttribute("positionend", false);
sidebarContainer.toggleAttribute("positionend", false);
this.toolbarButton &&
this.toolbarButton.toggleAttribute("positionend", false);
this.sidebarWrapper.toggleAttribute("positionend", false);
}
this.hideSwitcherPanel();
@@ -954,14 +988,19 @@ var SidebarController = {
return this.show(commandID, triggerNode);
},
_getRects(animatingElements) {
return animatingElements.map(e => [
e.hidden,
e.getBoundingClientRect().toJSON(),
]);
},
async _animateSidebarMain() {
let tabbox = document.getElementById("tabbrowser-tabbox");
let animatingElements = [
this.sidebarContainer,
this._box,
this._splitter,
tabbox,
];
let animatingElements = [this.sidebarContainer, this._box, this._splitter];
if (!this.sidebarRevampVisibility === "expand-on-hover") {
animatingElements.push(tabbox);
}
let resetElements = () => {
for (let el of animatingElements) {
el.style.minWidth =
@@ -971,30 +1010,29 @@ var SidebarController = {
el.style.display =
"";
}
this.sidebarWrapper.classList.remove("ongoing-animations");
};
if (this._ongoingAnimations.length) {
this._ongoingAnimations.forEach(a => a.cancel());
this._ongoingAnimations = [];
resetElements();
}
let getRects = () => {
return animatingElements.map(e => [
e.hidden,
e.getBoundingClientRect().toJSON(),
]);
};
let fromRects = getRects();
let fromRects = this._getRects(animatingElements);
// We need to wait for rAF for lit to re-render, and us to get the final
// width. This is a bit unfortunate but alas...
let toRects = await new Promise(resolve => {
requestAnimationFrame(() => {
resolve(getRects());
resolve(this._getRects(animatingElements));
});
});
const options = {
duration: this._animationDurationMs,
duration:
this.sidebarRevampVisibility === "expand-on-hover"
? this._animationExpandOnHoverDurationMs
: this._animationDurationMs,
easing: "ease-in-out",
};
let animations = [];
@@ -1038,6 +1076,7 @@ var SidebarController = {
if (isHidden && !wasHidden) {
el.style.display = "flex";
}
if (widthGrowth < 0) {
el.style.minWidth = el.style.maxWidth = from.width + "px";
el.style["margin-" + (sidebarOnLeft ? "right" : "left")] =
@@ -1057,6 +1096,7 @@ var SidebarController = {
} else if (isSidebar && !this._positionStart) {
fromTranslate += sidebarOnLeft ? -widthGrowth : widthGrowth;
}
animations.push(
el.animate(
[
@@ -1079,6 +1119,7 @@ var SidebarController = {
);
}
this._ongoingAnimations = animations;
this.sidebarWrapper.classList.add("ongoing-animations");
await Promise.allSettled(animations.map(a => a.finished));
if (this._ongoingAnimations === animations) {
this._ongoingAnimations = [];
@@ -1117,6 +1158,7 @@ var SidebarController = {
}
switch (this.sidebarRevampVisibility) {
case "always-show":
case "expand-on-hover":
// Toolbar button controls expanded state.
toolbarButton.checked = this.sidebarMain.expanded;
toolbarButton.dataset.l10nId = toolbarButton.checked
@@ -1761,15 +1803,102 @@ var SidebarController = {
this.sidebarMain.requestUpdate();
},
onMouseOver() {
SidebarController._state.launcherHoverActive = true;
SidebarController.sidebarMain.addEventListener(
"mouseout",
SidebarController.onMouseOut,
{ once: true }
);
if (SidebarController._animationEnabled && !window.gReduceMotion) {
SidebarController._animateSidebarMain();
}
SidebarController._state.updateVisibility(true, false, false, true);
SidebarController.sidebarMain.removeEventListener(
"mouseover",
SidebarController.onMouseOver
);
},
onMouseOut() {
SidebarController._state.launcherHoverActive = false;
SidebarController.sidebarMain.addEventListener(
"mouseover",
SidebarController.onMouseOver,
{ once: true }
);
if (SidebarController._animationEnabled && !window.gReduceMotion) {
SidebarController._animateSidebarMain();
}
SidebarController._state.updateVisibility(true, false, false, false);
SidebarController.sidebarMain.removeEventListener(
"mouseout",
SidebarController.onMouseOut
);
},
async setLauncherInlineMargin() {
let collapsedWidth;
if (this._state.launcherExpanded) {
this._state.launcherExpanded = false;
await this.sidebarMain.updateComplete;
collapsedWidth = await new Promise(resolve => {
requestAnimationFrame(() => {
resolve(this._getRects([this.sidebarMain])[0][1].width);
});
});
} else {
collapsedWidth = this._getRects([this.sidebarMain])[0][1].width;
}
this._state.collapsedLauncherWidth = collapsedWidth;
// Make sure sidebar doesn't overlay content area outline
const CONTENT_AREA_OUTLINE_WIDTH = 1;
if (this._positionStart) {
this.contentArea.style.marginInlineStart = `${this._state.collapsedLauncherWidth + CONTENT_AREA_OUTLINE_WIDTH}px`;
this.contentArea.style.marginInlineEnd = "";
} else {
this.contentArea.style.marginInlineEnd = `${this._state.collapsedLauncherWidth + CONTENT_AREA_OUTLINE_WIDTH}px`;
this.contentArea.style.marginInlineStart = "";
}
},
async toggleExpandOnHover(isEnabled) {
if (isEnabled) {
this.sidebarWrapper.classList.add("expandOnHover");
if (!this._state) {
this._state = new this.SidebarState(this);
}
await this.setLauncherInlineMargin();
this.sidebarMain.addEventListener("mouseover", this.onMouseOver, {
once: true,
});
} else {
this.sidebarWrapper.classList.remove("expandOnHover");
this.sidebarMain.removeEventListener("mouseover", this.onMouseOver);
this.contentArea.style.marginInlineStart = "";
this.contentArea.style.marginInlineEnd = "";
}
},
/**
* Report visibility preference to Glean.
*
* @param {string} [value] - The preference value.
*/
recordVisibilitySetting(value = this.sidebarRevampVisibility) {
Glean.sidebar.displaySettings.set(
value === "always-show" ? "always" : "hide"
);
let visibilitySetting = "hide";
if (value === "always-show") {
visibilitySetting = "always";
} else if (value === "expand-on-hover") {
visibilitySetting = "expand-on-hover";
}
Glean.sidebar.displaySettings.set(visibilitySetting);
},
/**
@@ -1822,6 +1951,12 @@ XPCOMUtils.defineLazyPreferenceGetter(
"sidebar.animation.duration-ms",
200
);
XPCOMUtils.defineLazyPreferenceGetter(
SidebarController,
"_animationExpandOnHoverDurationMs",
"sidebar.animation.expand-on-hover.duration-ms",
400
);
XPCOMUtils.defineLazyPreferenceGetter(
SidebarController,
"sidebarRevampEnabled",
@@ -1852,12 +1987,16 @@ XPCOMUtils.defineLazyPreferenceGetter(
"always-show",
(_aPreference, _previousValue, newValue) => {
if (!SidebarController.inPopup && !SidebarController.uninitializing) {
SidebarController.toggleExpandOnHover(newValue === "expand-on-hover");
SidebarController.recordVisibilitySetting(newValue);
SidebarController._state.revampVisibility = newValue;
SidebarController._state.updateVisibility(
(newValue != "hide-sidebar" &&
SidebarController.sidebarVerticalTabsEnabled) ||
!SidebarController.sidebarVerticalTabsEnabled
!SidebarController.sidebarVerticalTabsEnabled,
false,
false,
newValue !== "expand-on-hover"
);
SidebarController.updateToolbarButton();
}

View File

@@ -55,7 +55,7 @@ sidebar:
type: string
lifetime: application
description: >
Setting for sidebar display (either "always" or "hide").
Setting for sidebar display (either "always", "expand-on-hover", or "hide").
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1898250
data_reviews:
@@ -627,3 +627,22 @@ sidebar.customize:
- vsabino@mozilla.com
send_in_pings:
- events
expand_on_hover_enabled:
type: event
description: >
User clicked on the checkbox corresponding to expand on hover on sidebar customization settings.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1930201
data_reviews:
- https://phabricator.services.mozilla.com/D233098
data_sensitivity:
- interaction
expires: never
notification_emails:
- vsabino@mozilla.com
send_in_pings:
- events
extra_keys:
checked:
type: boolean
description: Whether the box was checked.

View File

@@ -88,6 +88,10 @@ moz-checkbox {
> moz-checkbox:last-of-type {
padding-block: 0;
}
&[disabled]::part(label) {
opacity: 0.5;
}
}
.no-label::part(label) {

View File

@@ -18,6 +18,7 @@ const l10nMap = new Map([
["viewBookmarksSidebar", "sidebar-menu-bookmarks-label"],
]);
const VISIBILITY_SETTING_PREF = "sidebar.visibility";
const EXPAND_ON_HOVER_PREF = "sidebar.expandOnHover";
const POSITION_SETTING_PREF = "sidebar.position_start";
const TAB_DIRECTION_SETTING_PREF = "sidebar.verticalTabs";
@@ -52,9 +53,19 @@ export class SidebarCustomize extends SidebarPage {
this.verticalTabsEnabled = newValue;
}
);
XPCOMUtils.defineLazyPreferenceGetter(
this.#prefValues,
"expandOnHoverEnabled",
EXPAND_ON_HOVER_PREF,
false,
(_aPreference, _previousValue, newValue) => {
this.expandOnHoverEnabled = newValue;
}
);
this.visibility = this.#prefValues.visibility;
this.isPositionStart = this.#prefValues.isPositionStart;
this.verticalTabsEnabled = this.#prefValues.verticalTabsEnabled;
this.expandOnHoverEnabled = this.#prefValues.expandOnHoverEnabled;
this.boundObserve = (...args) => this.observe(...args);
}
@@ -65,6 +76,7 @@ export class SidebarCustomize extends SidebarPage {
visibility: { type: String },
isPositionStart: { type: Boolean },
verticalTabsEnabled: { type: Boolean },
expandOnHoverEnabled: { type: Boolean },
};
static queries = {
@@ -110,7 +122,7 @@ export class SidebarCustomize extends SidebarPage {
}
}
async onToggleInput(e) {
async onToggleToolInput(e) {
e.preventDefault();
this.getWindow().SidebarController.toggleTool(e.target.id);
switch (e.target.id) {
@@ -154,7 +166,7 @@ export class SidebarCustomize extends SidebarPage {
}
}
inputTemplate(tool) {
toolInputTemplate(tool) {
if (tool.hidden) {
return null;
}
@@ -166,7 +178,7 @@ export class SidebarCustomize extends SidebarPage {
name=${tool.view}
iconsrc=${tool.iconUrl}
data-l10n-id=${this.getInputL10nId(tool.view)}
@change=${this.onToggleInput}
@change=${this.onToggleToolInput}
?checked=${!tool.disabled}
/>
`;
@@ -253,20 +265,38 @@ export class SidebarCustomize extends SidebarPage {
@change=${this.#handleTabDirectionChange}
?checked=${this.verticalTabsEnabled}
>
${when(
this.verticalTabsEnabled,
() => html`
<moz-checkbox
slot="nested"
type="checkbox"
id="hide-sidebar"
name="hideSidebar"
data-l10n-id="sidebar-hide-tabs-and-sidebar"
@change=${this.#handleVisibilityChange}
?checked=${this.visibility == "hide-sidebar"}
></moz-checkbox>
`
)}
${when(
this.verticalTabsEnabled,
() => html`
${when(
this.expandOnHoverEnabled,
() => html`
<moz-checkbox
slot="nested"
type="checkbox"
id="expand-on-hover"
name="expand-on-hover"
data-l10n-id="expand-sidebar-on-hover"
@change=${this.#toggleExpandOnHover}
?checked=${this.getWindow().SidebarController._state
.revampVisibility === "expand-on-hover"}
?disabled=${this.visibility == "hide-sidebar"}
/>
`
)}
<moz-checkbox
slot="nested"
type="checkbox"
id="hide-sidebar"
name="hideSidebar"
data-l10n-id="sidebar-hide-tabs-and-sidebar"
@change=${this.#handleVisibilityChange}
?checked=${this.visibility == "hide-sidebar"}
?disabled=${this.getWindow().SidebarController._state
.revampVisibility === "expand-on-hover"}
></moz-checkbox>
`
)}
</moz-checkbox>
</moz-fieldset>
<moz-fieldset class="customize-group medium-top-margin no-label">
@@ -282,7 +312,7 @@ export class SidebarCustomize extends SidebarPage {
<moz-fieldset class="customize-group" data-l10n-id="sidebar-customize-firefox-tools-header">
${this.getWindow()
.SidebarController.getTools()
.map(tool => this.inputTemplate(tool))}
.map(tool => this.toolInputTemplate(tool))}
</moz-fieldset>
${when(
extensions.length,
@@ -325,6 +355,18 @@ export class SidebarCustomize extends SidebarPage {
});
}
#toggleExpandOnHover(e) {
e.stopPropagation();
if (e.target.checked) {
Services.prefs.setStringPref("sidebar.visibility", "expand-on-hover");
Glean.sidebarCustomize.expandOnHoverEnabled.record({
checked: true,
});
} else {
Services.prefs.setStringPref("sidebar.visibility", "always-show");
}
}
#handleTabDirectionChange({ target: { checked } }) {
const verticalTabsEnabled = checked;
Services.prefs.setBoolPref(TAB_DIRECTION_SETTING_PREF, verticalTabsEnabled);

View File

@@ -2197,6 +2197,18 @@
browserSidebarContainer.className = "browserSidebarContainer";
browserSidebarContainer.appendChild(browserContainer);
let visibility = Services.prefs.getStringPref(
"sidebar.visibility",
"always-show"
);
let expandOnHover = Services.prefs.getBoolPref(
"sidebar.expandOnHover",
false
);
if (visibility === "expand-on-hover" && expandOnHover) {
SidebarController.toggleExpandOnHover(true);
}
// Prevent the superfluous initial load of a blank document
// if we're going to load something other than about:blank.
if (!uriIsAboutBlank || skipLoad) {

View File

@@ -58,6 +58,10 @@ sidebar-show-on-the-right =
.label = Move sidebar to the right
sidebar-show-on-the-left =
.label = Move sidebar to the left
# Option to automatically expand the collapsed sidebar when the mouse pointer
# hovers over it.
expand-sidebar-on-hover =
.label = Expand sidebar on hover
## Labels for sidebar context menu items

View File

@@ -70,7 +70,7 @@ body {
--browser-area-z-index-toolbox: 0;
--browser-area-z-index-sidebar: 1;
--browser-area-z-index-tabbox: 2;
--browser-area-z-index-sidebar-splitter: 3;
--browser-area-z-index-sidebar-wrapper: 3;
/* on macOS the animation of the toolbox in fullscreen needs the toolbox to be higher */
--browser-area-z-index-toolbox-while-animating: 4;

View File

@@ -21,11 +21,10 @@
padding-block-end: var(--space-small);
padding-inline-end: var(--space-small);
position: relative;
z-index: var(--browser-area-z-index-sidebar);
&[positionend] {
padding-inline-end: 0;
padding-inline-start: var(--space-small);
padding-inline: 0;
margin-inline-start: var(--space-small);
}
}
@@ -76,9 +75,6 @@
.sidebar-splitter {
--splitter-width: 4px;
/* Ensure the splitter is painted on top of the sidebar box it overlaps.
Otherwise, the user may be unable to drag the splitter to resize the sidebar. */
z-index: var(--browser-area-z-index-sidebar-splitter);
/* stylelint-disable-next-line media-query-no-invalid */
@media (-moz-bool-pref: "sidebar.revamp") or (not (-moz-platform: linux)) {
@@ -224,3 +220,60 @@ sidebar-main,
/* Prevent overflow during sidebar animation when the sidebar is reordered */
overflow: clip;
}
/* Expand on hover */
#sidebar-wrapper {
position: relative;
z-index: var(--browser-area-z-index-sidebar-wrapper);
&.expandOnHover {
position: absolute;
inset: 0;
inset-inline-end: unset;
&:not([positionend]):hover {
border-start-end-radius: var(--border-radius-medium);
border-end-end-radius: var(--border-radius-medium);
border-inline-end: 0.01px solid var(--chrome-content-separator-color);
}
&[positionend]:hover {
border-start-start-radius: var(--border-radius-medium);
border-start-end-radius: var(--border-radius-medium);
border-inline-start: 0.01px solid var(--chrome-content-separator-color);
}
:root[lwtheme] & {
border-radius: unset;
border-inline: unset;
color: var(--toolbox-textcolor);
background-color: var(--toolbox-bgcolor);
&:-moz-window-inactive {
color: var(--toolbox-textcolor-inactive);
background-color: var(--toolbox-bgcolor-inactive);
}
}
&:hover,
&.ongoing-animations {
color: var(--toolbox-textcolor);
background-color: var(--toolbox-bgcolor);
}
#sidebar-main {
border-radius: inherit;
background-color: inherit;
&:hover {
box-shadow: var(--content-area-shadow);
}
}
&[positionend] {
inset-inline-start: unset;
inset-inline-end: 0;
}
}
}

View File

@@ -40,6 +40,11 @@
}
}
/* Needed to ensure #sidebar-wrapper is full height when vertical tabs are not enabled */
#browser {
position: relative;
}
#browser,
#tabbrowser-tabbox,
#tabbrowser-tabpanels,

View File

@@ -1382,6 +1382,16 @@
.tabbrowser-tab:not([pinned]):hover & {
opacity: 1;
}
/* stylelint-disable-next-line media-query-no-invalid */
@media (-moz-bool-pref: "sidebar.expandOnHover") {
/* Tab close button when the sidebar is collapsed should
not be shown when expand on hover is enabled because once
you hover over the launcher to use the button, the launcher
starts to expand and the button is no longer shown. Users can
rely on the close button within the tab once expanded. */
display: none;
}
}
}