Skip to content

Commit 92bd56e

Browse files
committed
Replace SplitterLayout with a new ResizableWithSplitter component.
This replacement has the following advantages: It's compatible with React 18, and it supports making "max-height" the controlled property, which is useful for our timeline splitter. In the past, we needed to compute the height of the timeline contents, and then make it so you couldn't move the splitter down more than the full timeline height, because we didn't want there to be a gap above the splitter, or even initialize the splitter at a position that creates such a gap. By making the splitter control max-height instead, even if the splitter is dragged low enough to say max-height:1000px, the splitter won't actually move that low because the timeline won't grow beyond its full size (because there's no longer a height property forcing it to grow).
1 parent 38336c8 commit 92bd56e

18 files changed

Lines changed: 339 additions & 133 deletions

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@
9898
"react-dom": "^18.3.1",
9999
"react-intersection-observer": "^10.0.3",
100100
"react-redux": "^9.2.0",
101-
"react-splitter-layout": "^4.0.0",
102101
"redux": "^5.0.1",
103102
"redux-logger": "^3.0.6",
104103
"redux-thunk": "^3.1.0",

res/css/global.css

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -184,15 +184,3 @@ a:active {
184184
.colored-border.ellipsis {
185185
opacity: 0;
186186
}
187-
188-
.splitter-layout.splitter-layout > .layout-splitter {
189-
background-color: var(--wide-splitter-color);
190-
}
191-
192-
.splitter-layout.splitter-layout > .layout-splitter:hover {
193-
background-color: var(--wide-splitter-hover-color);
194-
}
195-
196-
.splitter-layout.splitter-layout.layout-changing > .layout-splitter {
197-
background-color: var(--wide-splitter-pressed-color);
198-
}

src/components/app/BottomBox.css

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,21 @@
22
* License, v. 2.0. If a copy of the MPL was not distributed with this
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

5+
.bottom-box {
6+
display: flex;
7+
min-height: 0;
8+
flex-flow: row nowrap;
9+
}
10+
511
.bottom-box-pane {
612
--internal-sourceview-background-color: var(--grey-20);
713
--internal-close-icon: url(../../../res/img/svg/close-dark.svg);
814
--internal-assembly-icon: url(../../../res/img/svg/asm-icon.svg);
915

1016
display: flex;
11-
height: 100%; /* direct child of SplitterLayout */
17+
overflow: hidden;
18+
min-width: 0;
19+
flex: 1;
1220
flex-flow: column nowrap;
1321
color: var(--base-foreground-color);
1422
}
@@ -23,15 +31,15 @@
2331
background: var(--internal-sourceview-background-color);
2432
}
2533

26-
.bottom-box .layout-splitter {
34+
.bottom-box .resizableWithSplitterSplitter {
2735
position: relative; /* containing block for absolute ::before */
2836
width: 1px;
2937
border: none;
3038
background-color: var(--base-border-color) !important;
3139
}
3240

3341
/* Provide 3px extra grabbable surface on each side of the splitter */
34-
.bottom-box .layout-splitter::before {
42+
.bottom-box .resizableWithSplitterSplitter::before {
3543
position: absolute;
3644
z-index: var(--z-bottom-box);
3745
display: block;
@@ -44,6 +52,7 @@
4452
overflow: hidden;
4553
height: 24px;
4654
flex-flow: row;
55+
flex-shrink: 0;
4756
align-items: center;
4857
justify-content: space-between;
4958
border-bottom: 1px solid var(--panel-border-color);

src/components/app/BottomBox.tsx

Lines changed: 42 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

55
import React from 'react';
6-
import SplitterLayout from 'react-splitter-layout';
76
import classNames from 'classnames';
87

98
import { SourceView } from '../shared/SourceView';
109
import { AssemblyView } from '../shared/AssemblyView';
10+
import { ResizableWithSplitter } from '../shared/ResizableWithSplitter';
1111
import { AssemblyViewToggleButton } from './AssemblyViewToggleButton';
1212
import { AssemblyViewNativeSymbolNavigator } from './AssemblyViewNativeSymbolNavigator';
1313
import { IonGraphView } from '../shared/IonGraphView';
@@ -217,43 +217,47 @@ class BottomBoxImpl extends React.PureComponent<Props> {
217217

218218
return (
219219
<div className="bottom-box">
220-
<SplitterLayout customClassName="bottom-box" percentage>
221-
<div className="bottom-box-pane">
222-
<div className="bottom-box-bar">
223-
<h3 className="bottom-box-title">{path ?? '(no source file)'}</h3>
224-
{assemblyViewIsOpen ? null : trailingHeaderButtons}
225-
</div>
226-
<div className="bottom-sourceview-wrapper">
227-
{displayIonGraph ? (
228-
<IonGraphView
229-
timings={globalLineTimings}
230-
sourceCode={sourceCode}
231-
/>
232-
) : null}
233-
{displaySourceView ? (
234-
<SourceView
235-
timings={globalLineTimings}
236-
sourceCode={sourceCode}
237-
filePath={path}
238-
scrollGeneration={sourceViewScrollGeneration}
239-
scrollToLineNumber={sourceViewScrollToLineNumber}
240-
highlightedLine={sourceViewHighlightedLine}
241-
startLine={sourceViewStartLine}
242-
ref={this._sourceView}
243-
/>
244-
) : null}
245-
{sourceViewCode !== undefined &&
246-
sourceViewCode.type === 'LOADING' ? (
247-
<CodeLoadingOverlay source={sourceViewCode.source} />
248-
) : null}
249-
{sourceViewCode !== undefined &&
250-
sourceViewCode.type === 'ERROR' ? (
251-
<SourceCodeErrorOverlay errors={sourceViewCode.errors} />
252-
) : null}
253-
</div>
220+
<div className="bottom-box-pane">
221+
<div className="bottom-box-bar">
222+
<h3 className="bottom-box-title">{path ?? '(no source file)'}</h3>
223+
{assemblyViewIsOpen ? null : trailingHeaderButtons}
224+
</div>
225+
<div className="bottom-sourceview-wrapper">
226+
{displayIonGraph ? (
227+
<IonGraphView
228+
timings={globalLineTimings}
229+
sourceCode={sourceCode}
230+
/>
231+
) : null}
232+
{displaySourceView ? (
233+
<SourceView
234+
timings={globalLineTimings}
235+
sourceCode={sourceCode}
236+
filePath={path}
237+
scrollGeneration={sourceViewScrollGeneration}
238+
scrollToLineNumber={sourceViewScrollToLineNumber}
239+
highlightedLine={sourceViewHighlightedLine}
240+
startLine={sourceViewStartLine}
241+
ref={this._sourceView}
242+
/>
243+
) : null}
244+
{sourceViewCode !== undefined &&
245+
sourceViewCode.type === 'LOADING' ? (
246+
<CodeLoadingOverlay source={sourceViewCode.source} />
247+
) : null}
248+
{sourceViewCode !== undefined && sourceViewCode.type === 'ERROR' ? (
249+
<SourceCodeErrorOverlay errors={sourceViewCode.errors} />
250+
) : null}
254251
</div>
252+
</div>
255253

256-
{assemblyViewIsOpen ? (
254+
{assemblyViewIsOpen ? (
255+
<ResizableWithSplitter
256+
splitterPosition="start"
257+
controlledProperty="width"
258+
percent={true}
259+
initialSize="50%"
260+
>
257261
<div className="bottom-box-pane">
258262
<div className="bottom-box-bar">
259263
<h3 className="bottom-box-title">
@@ -289,8 +293,8 @@ class BottomBoxImpl extends React.PureComponent<Props> {
289293
) : null}
290294
</div>
291295
</div>
292-
) : null}
293-
</SplitterLayout>
296+
</ResizableWithSplitter>
297+
) : null}
294298
</div>
295299
);
296300
}

src/components/app/Details.css

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
--internal-sidebar-expand-icon: url(../../../res/img/svg/pane-expand.svg);
44

55
display: flex;
6+
7+
/* If .Details is squished to a very small size between the sidebar and/or bottom bar,
8+
don't spill out our contents over those other elements. */
69
overflow: clip;
7-
flex: auto;
10+
flex: 1;
811
flex-direction: column;
912
}
1013

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,29 @@
1-
.DetailsContainer .layout-pane > * {
2-
width: 100%;
3-
height: 100%;
1+
.DetailsContainer {
2+
position: relative;
3+
z-index: 0;
4+
display: flex;
45
box-sizing: border-box;
6+
flex: 1;
7+
flex-flow: row nowrap;
8+
contain: size;
59
}
610

7-
.DetailsContainer .layout-pane:not(.layout-pane-primary) {
8-
max-width: 600px;
11+
.DetailsContainer .resizableWithSplitterInner > * {
12+
min-width: 0;
13+
flex: 1;
914
}
1015

11-
/* overriding defaults from splitter-layout */
12-
.DetailsContainer {
13-
/* SplitterLayout injects position: absolute, this conflicts with our using
14-
* Flex Layout. */
15-
position: unset;
16+
.DetailsContainerResizableSidebarWrapper {
17+
max-width: 600px;
1618
}
1719

18-
.DetailsContainer.splitter-layout > .layout-splitter {
20+
/* overriding defaults from ResizableWithSplitter.css */
21+
.DetailsContainer .resizableWithSplitterSplitter {
1922
border-top: 1px solid var(--panel-border-color);
2023
border-left: 1px solid var(--panel-border-color);
21-
background: var(--panel-background-color);
24+
background: var(--panel-background-color); /* Same background as sidebars */
2225
}
2326

24-
.DetailsContainer.splitter-layout > .layout-splitter:hover {
25-
background: var(--panel-background-color);
27+
.DetailsContainer .resizableWithSplitterSplitter:hover {
28+
background: var(--panel-background-color); /* same as the border above */
2629
}

src/components/app/DetailsContainer.tsx

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
/* This Source Code Form is subject to the terms of the Mozilla Public
22
* License, v. 2.0. If a copy of the MPL was not distributed with this
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4-
// React imported for JSX
5-
import SplitterLayout from 'react-splitter-layout';
64

75
import { Details } from './Details';
6+
import { ResizableWithSplitter } from 'firefox-profiler/components/shared/ResizableWithSplitter';
87
import { selectSidebar } from 'firefox-profiler/components/sidebar';
98

109
import { getSelectedTab } from 'firefox-profiler/selectors/url-state';
@@ -27,14 +26,20 @@ function DetailsContainerImpl({ selectedTab, isSidebarOpen }: Props) {
2726
const Sidebar = selectSidebar(selectedTab);
2827

2928
return (
30-
<SplitterLayout
31-
customClassName="DetailsContainer"
32-
percentage
33-
secondaryInitialSize={20}
34-
>
29+
<div className="DetailsContainer">
3530
<Details />
36-
{Sidebar && isSidebarOpen ? <Sidebar /> : null}
37-
</SplitterLayout>
31+
{Sidebar && isSidebarOpen ? (
32+
<ResizableWithSplitter
33+
className="DetailsContainerResizableSidebarWrapper"
34+
percent={false}
35+
splitterPosition="start"
36+
controlledProperty="width"
37+
initialSize="300px"
38+
>
39+
<Sidebar />
40+
</ResizableWithSplitter>
41+
) : null}
42+
</div>
3843
);
3944
}
4045

src/components/app/ProfileViewer.css

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@
4747
}
4848

4949
.profileViewer {
50+
/* Create a stacking context, so that the KeyboardShortcut can overlay the profile
51+
viewer. */
52+
position: relative;
53+
z-index: 0;
5054
display: flex;
5155
min-width: 0; /* This allows Flexible Layout to shrink this further than its min-content */
5256
flex: 1;
@@ -109,21 +113,6 @@
109113
}
110114
}
111115

112-
.profileViewerSplitter {
113-
/* Position relative for layout. Don't set z-index here to avoid creating a
114-
stacking context that would trap the context menu below the screenshot hover.
115-
This allows both the screenshot hover and context menu to be compared
116-
at the .profileViewer level, ensuring the context menu appears on top. */
117-
position: relative;
118-
}
119-
120-
.profileViewerSplitter > .layout-pane:not(.layout-pane-primary) {
121-
display: flex;
122-
overflow: hidden;
123-
max-height: var(--profile-viewer-splitter-max-height);
124-
flex-direction: column;
125-
}
126-
127116
.profileViewerTopBar {
128117
display: flex;
129118
height: 24px;

src/components/app/ProfileViewer.tsx

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import { PureComponent } from 'react';
66
import explicitConnect from 'firefox-profiler/utils/connect';
77

8+
import { ResizableWithSplitter } from 'firefox-profiler/components/shared/ResizableWithSplitter';
89
import { DetailsContainer } from './DetailsContainer';
910
import { SourceCodeFetcher } from './SourceCodeFetcher';
1011
import { AssemblyCodeFetcher } from './AssemblyCodeFetcher';
@@ -20,7 +21,6 @@ import { KeyboardShortcut } from './KeyboardShortcut';
2021
import { returnToZipFileList } from 'firefox-profiler/actions/zipped-profiles';
2122
import { Timeline } from 'firefox-profiler/components/timeline';
2223
import { getHasZipFile } from 'firefox-profiler/selectors/zipped-profiles';
23-
import SplitterLayout from 'react-splitter-layout';
2424
import { getTimelineHeight } from 'firefox-profiler/selectors/app';
2525
import { getIsBottomBoxOpen } from 'firefox-profiler/selectors/url-state';
2626
import {
@@ -125,28 +125,25 @@ class ProfileViewerImpl extends PureComponent<Props> {
125125
/>
126126
) : null}
127127
</div>
128-
<SplitterLayout
129-
customClassName="profileViewerSplitter"
130-
vertical
131-
percentage={false}
132-
// The DetailsContainer is primary.
133-
primaryIndex={1}
134-
// The Timeline is secondary.
135-
secondaryInitialSize={270}
128+
<ResizableWithSplitter
129+
splitterPosition="end"
130+
controlledProperty="max-height"
131+
percent={false}
132+
initialSize="270px"
136133
>
137134
<Timeline />
138-
<SplitterLayout
139-
vertical
140-
percentage={true}
141-
// The DetailsContainer is primary.
142-
primaryIndex={0}
143-
// The BottomBox is secondary.
144-
secondaryInitialSize={40}
135+
</ResizableWithSplitter>
136+
<DetailsContainer />
137+
{isBottomBoxOpen ? (
138+
<ResizableWithSplitter
139+
splitterPosition="start"
140+
controlledProperty="height"
141+
percent={true}
142+
initialSize="40%"
145143
>
146-
<DetailsContainer />
147-
{isBottomBoxOpen ? <BottomBox /> : null}
148-
</SplitterLayout>
149-
</SplitterLayout>
144+
<BottomBox />
145+
</ResizableWithSplitter>
146+
) : null}
150147
<div id="screenshot-hover"></div>
151148
<SymbolicationStatusOverlay />
152149
<BeforeUnloadManager />

0 commit comments

Comments
 (0)