Skip to content
This repository was archived by the owner on Sep 30, 2024. It is now read-only.

Commit 57abbb6

Browse files
authored
✨ Allow vertical tabs to be resized (#199)
1 parent 8123157 commit 57abbb6

File tree

2 files changed

+148
-15
lines changed

2 files changed

+148
-15
lines changed

src/browser/base/content/browser-verticaltabs.js

Lines changed: 117 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,47 @@
55

66
const VERTICAL_TABS_POSITION = 'pulse.tabs.vertical'
77
const VERTICAL_TABS_COLLAPSE = 'pulse.tabs.vertical.collapse'
8+
const VERTICAL_TABS_WIDTH = 'pulse.tabs.vertical.width'
9+
10+
/**
11+
* @param {HTMLElement} toInsertAfter This is the element that I want to insert content after
12+
* @param {HTMLElement} toInsert The element to insert
13+
*
14+
* @throws {Error} If the element you want me to base insertions on has no parent
15+
*/
16+
function insertAfter(toInsertAfter, toInsert) {
17+
const parent = toInsertAfter.parentNode
18+
19+
if (!parent) {
20+
throw new Error(
21+
'The element you want me to base insertions on has no parent'
22+
)
23+
}
24+
25+
if (toInsertAfter.nextSibling) {
26+
parent.insertBefore(toInsert, toInsertAfter.nextSibling)
27+
} else {
28+
parent.appendChild(toInsert)
29+
}
30+
}
31+
32+
/**
33+
* Replace a tag with another tag with a different name
34+
* @param {string} tagName The new tag name
35+
* @param {HTMLElement?} initialTag The tag to be changed
36+
*/
37+
function changeXULTagName(tagName, initialTag) {
38+
if (!initialTag) return
39+
if (initialTag.tagName == tagName) return
40+
41+
const newParent = document.createXULElement(tagName)
42+
43+
for (const attr of initialTag.attributes)
44+
newParent.setAttribute(attr.name, attr.value)
45+
while (initialTag.firstChild) newParent.appendChild(initialTag.firstChild)
46+
47+
initialTag.replaceWith(newParent)
48+
}
849

950
var VerticalTabs = {
1051
/**
@@ -35,6 +76,13 @@ var VerticalTabs = {
3576
return document.getElementById('browser')
3677
},
3778

79+
/**
80+
* @return {HTMLElement?}
81+
*/
82+
get splitter() {
83+
return document.getElementById('verticaltabs-splitter')
84+
},
85+
3886
/**
3987
* @return {Boolean}
4088
*/
@@ -48,6 +96,8 @@ var VerticalTabs = {
4896
tabBrowserTabs: null,
4997

5098
_initialized: false,
99+
/** @type {MutationObserver?} */
100+
_widthObserver: null,
51101

52102
init() {
53103
if (this._initialized) {
@@ -79,9 +129,40 @@ var VerticalTabs = {
79129
.querySelector('#TabsToolbar .toolbar-items')
80130
?.setAttribute('align', 'start')
81131

82-
document
83-
.getElementById('TabsToolbar')
84-
?.setAttribute('collapse', this.browserCollapseTabs ? 'true' : 'false')
132+
this.tabsToolbar?.setAttribute(
133+
'collapse',
134+
this.browserCollapseTabs ? 'true' : 'false'
135+
)
136+
this.tabsToolbar?.removeAttribute('flex')
137+
changeXULTagName('vbox', this.tabsToolbar)
138+
139+
this._widthObserver = new MutationObserver(this._mutationObserverCallback)
140+
if (this.tabsToolbar)
141+
this._widthObserver.observe(this.tabsToolbar, { attributes: true })
142+
143+
this.tabsToolbar?.setAttribute(
144+
'width',
145+
Services.prefs.getIntPref(VERTICAL_TABS_WIDTH, 200)
146+
)
147+
if (this.tabsToolbar)
148+
this.tabsToolbar.style.width = `${Services.prefs.getIntPref(
149+
VERTICAL_TABS_WIDTH,
150+
200
151+
)}px`
152+
153+
if (!this.splitter) {
154+
const separator = document.createXULElement('splitter')
155+
separator.setAttribute('id', 'verticaltabs-splitter')
156+
separator.setAttribute(
157+
'class',
158+
'chromeclass-extrachrome verticaltabs-splitter'
159+
)
160+
separator.setAttribute('resizebefore', 'none')
161+
separator.setAttribute('resizeafter', 'sibling')
162+
163+
const tabs = this.tabsToolbar
164+
if (tabs) tabs.parentElement?.insertBefore(separator, tabs)
165+
}
85166
},
86167

87168
disableVerticalTabs() {
@@ -96,6 +177,39 @@ var VerticalTabs = {
96177
document
97178
.querySelector('#TabsToolbar .toolbar-items')
98179
?.setAttribute('align', 'end')
180+
181+
if (this.tabsToolbar) {
182+
changeXULTagName('toolbar', this.tabsToolbar)
183+
this.tabsToolbar.setAttribute('flex', '1')
184+
// Reset the resize value, or else the tabs will end up squished
185+
this.tabsToolbar.style.width = ''
186+
}
187+
188+
if (this.splitter) {
189+
this.splitter.remove()
190+
}
191+
192+
if (this._widthObserver) {
193+
this._widthObserver.disconnect()
194+
this._widthObserver = null
195+
}
196+
},
197+
198+
/**
199+
* @param {MutationRecord[]} mutationsList
200+
* @param {MutationObserver} _observer
201+
*/
202+
_mutationObserverCallback(mutationsList, _observer) {
203+
for (const mutation of mutationsList) {
204+
if (mutation.type === 'attributes' && mutation.attributeName == 'width') {
205+
const tabsToolbar = document.getElementById('TabsToolbar')
206+
207+
Services.prefs.setIntPref(
208+
VERTICAL_TABS_WIDTH,
209+
parseInt(tabsToolbar?.getAttribute('width') || '100')
210+
)
211+
}
212+
}
99213
},
100214

101215
/**

src/browser/themes/pulse/vertical_tabs.css

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,40 @@
33
* License, v. 2.0. If a copy of the MPL was not distributed with this
44
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
55
*/
6+
:root {
7+
--vertical-tabs-max-width: 350px;
8+
}
9+
610
#browser #TabsToolbar .titlebar-spacer,
711
#browser #TabsToolbar .titlebar-buttonbox-container,
812
#browser #TabsToolbar #alltabs-button {
913
display: none;
1014
}
1115

1216
#navigator-toolbox-background[verticaltabs='true'] #titlebar {
13-
display: none;
17+
display: none;
1418
}
1519

1620
#browser #TabsToolbar {
1721
background-color: -moz-Dialog;
18-
max-width: 350px;
22+
max-width: var(--vertical-tabs-max-width);
23+
-moz-box-ordinal-group: 0;
24+
}
25+
26+
#browser #TabsToolbar:not([collapse='true']) {
27+
min-width: 76px;
28+
}
1929

20-
transition: all 200ms ease-in-out;
30+
#browser #TabsToolbar[collapse='true'] {
31+
max-width: calc(
32+
16px + 2 * var(--inline-tab-padding) + 2 * var(--tab-block-margin)
33+
);
34+
}
35+
36+
/** On collapse hover of tabs or splitter, the max width should increase again */
37+
#browser #TabsToolbar[collapse='true']:hover,
38+
#verticaltabs-splitter:hover ~ #TabsToolbar[collapse='true'] {
39+
max-width: var(--vertical-tabs-max-width);
2140
}
2241

2342
#browser #TabsToolbar:-moz-lwtheme {
@@ -36,9 +55,6 @@
3655
/* Stops the tabs from getting squished or stretched */
3756
min-height: unset;
3857
max-height: var(--tab-min-height);
39-
40-
/* Animate tabs out vertically */
41-
transition: min-height 100ms ease-out, max-height 100ms ease-out;
4258
}
4359

4460
/* Put the new tab button on the bottom */
@@ -93,12 +109,15 @@
93109
margin-inline-start: none;
94110
}
95111

96-
#browser #TabsToolbar[collapse='true']:not(:hover) {
97-
max-width: calc(
98-
16px + 2 * var(--inline-tab-padding) + 2 * var(--tab-block-margin)
99-
);
112+
#browser #TabsToolbar[collapse='true'] .tabbrowser-tab {
113+
/* Animate tabs out vertically */
114+
transition: min-height 100ms ease-out, max-height 100ms ease-out;
115+
}
116+
117+
#verticaltabs-splitter {
118+
-moz-box-ordinal-group: 1;
100119
}
101120

102121
:root[sizemode='fullscreen'] #browser #TabsToolbar {
103-
display: none;
104-
}
122+
display: none;
123+
}

0 commit comments

Comments
 (0)