Skip to content

Commit 07fcfd0

Browse files
authored
Merge pull request #727 from integer32llc/less-popping
2 parents 7472bcf + 51bf6bc commit 07fcfd0

File tree

5 files changed

+2079
-3017
lines changed

5 files changed

+2079
-3017
lines changed

tests/Gemfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ GEM
1919
launchy (2.5.0)
2020
addressable (~> 2.7)
2121
mini_mime (1.1.0)
22-
mini_portile2 (2.5.1)
23-
nokogiri (1.11.4)
22+
mini_portile2 (2.5.3)
23+
nokogiri (1.11.7)
2424
mini_portile2 (~> 2.5.0)
2525
racc (~> 1.4)
2626
public_suffix (4.0.6)

ui/frontend/Playground.module.css

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,29 @@
55
padding-bottom: 1em;
66
}
77

8-
.-parent {
8+
.-resizeableArea {
99
composes: -autoSize from './shared.module.css';
1010
display: grid;
1111
}
1212

13-
$plainPrimaryDimension: 1fr auto;
13+
.resizeableAreaRowOutputUnfocused {
14+
composes: -resizeableArea;
15+
grid-template-rows: 1fr auto;
16+
}
17+
18+
.resizeableAreaRowOutputFocused {
19+
composes: -resizeableArea;
20+
grid-template-rows: 1fr 12px 1fr;
21+
}
1422

15-
.plainRows {
16-
composes: -parent;
17-
grid-template-rows: $plainPrimaryDimension;
23+
.resizeableAreaColumnOutputUnfocused {
24+
composes: -resizeableArea;
25+
grid-template-columns: 1fr auto;
1826
}
1927

20-
.plainColumns {
21-
composes: -parent;
22-
grid-template-columns: $plainPrimaryDimension;
28+
.resizeableAreaColumnOutputFocused {
29+
composes: -resizeableArea;
30+
grid-template-columns: 1fr 12px 1fr;
2331
}
2432

2533
.-gutter {
@@ -28,15 +36,6 @@ $plainPrimaryDimension: 1fr auto;
2836
justify-content: center;
2937
}
3038

31-
$splitPrimaryDimension: 1fr 12px 1fr;
32-
$splitSecondaryDimension: 1fr;
33-
34-
.splitRows {
35-
composes: -parent;
36-
grid-template-columns: $splitSecondaryDimension;
37-
grid-template-rows: $splitPrimaryDimension;
38-
}
39-
4039
.splitRowsGutter {
4140
composes: -gutter;
4241
cursor: row-resize;
@@ -47,12 +46,6 @@ $splitSecondaryDimension: 1fr;
4746
transform: rotate(90deg);
4847
}
4948

50-
.splitColumns {
51-
composes: -parent;
52-
grid-template-columns: $splitPrimaryDimension;
53-
grid-template-rows: $splitSecondaryDimension;
54-
}
55-
5649
.splitColumnsGutter {
5750
composes: -gutter;
5851
cursor: col-resize;

ui/frontend/Playground.tsx

Lines changed: 66 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import React, { useCallback } from 'react';
1+
import React, { useCallback, useEffect, useRef } from 'react';
22
import { useDispatch, useSelector } from 'react-redux';
3-
import Split from 'react-split-grid';
3+
import Split from 'split-grid';
44

55
import Editor from './Editor';
66
import Header from './Header';
@@ -12,103 +12,91 @@ import * as actions from './actions';
1212

1313
import styles from './Playground.module.css';
1414

15-
const NoOutput: React.SFC = () => (
16-
<div className={styles.editor}><Editor /></div>
17-
);
18-
19-
const PlainRows: React.SFC = () => (
20-
<div className={styles.plainRows}>
21-
<div className={styles.editor}><Editor /></div>
22-
<div className={styles.output}><Output /></div>
23-
</div>
24-
);
25-
26-
const PlainColumns: React.SFC = () => (
27-
<div className={styles.plainColumns}>
28-
<div className={styles.editor}><Editor /></div>
29-
<div className={styles.output}><Output /></div>
30-
</div>
31-
);
32-
33-
interface SplitProps {
34-
resizeComplete: () => void;
15+
const TRACK_OPTION_NAME = {
16+
[Orientation.Horizontal]: 'rowGutters',
17+
[Orientation.Vertical]: 'columnGutters',
3518
}
3619

37-
const SplitRows: React.SFC<SplitProps> = ({ resizeComplete }) => (
38-
<Split
39-
minSize={100}
40-
onDragEnd={resizeComplete}
41-
render={({
42-
getGridProps,
43-
getGutterProps,
44-
}) => (
45-
<div className={styles.splitRows} {...getGridProps()}>
46-
<div className={styles.editor}><Editor /></div>
47-
<div className={styles.splitRowsGutter} {...getGutterProps('row', 1)}>
48-
<span className={styles.splitRowsGutterHandle}></span>
49-
</div>
50-
<div className={styles.output}><Output /></div>
51-
</div>
52-
)} />
53-
)
54-
55-
const SplitColumns: React.SFC<SplitProps> = ({ resizeComplete }) => (
56-
<Split
57-
minSize={100}
58-
onDragEnd={resizeComplete}
59-
render={({
60-
getGridProps,
61-
getGutterProps,
62-
}) => (
63-
<div className={styles.splitColumns} {...getGridProps()}>
64-
<div className={styles.editor}><Editor /></div>
65-
<div className={styles.splitColumnsGutter} {...getGutterProps('column', 1)}></div>
66-
<div className={styles.output}><Output /></div>
67-
</div>
68-
)} />
69-
)
20+
const FOCUSED_GRID_STYLE = {
21+
[Orientation.Horizontal]: styles.resizeableAreaRowOutputFocused,
22+
[Orientation.Vertical]: styles.resizeableAreaColumnOutputFocused,
23+
}
7024

71-
const ORIENTATION_PLAIN_MAP = {
72-
[Orientation.Horizontal]: PlainRows,
73-
[Orientation.Vertical]: PlainColumns,
25+
const UNFOCUSED_GRID_STYLE = {
26+
[Orientation.Horizontal]: styles.resizeableAreaRowOutputUnfocused,
27+
[Orientation.Vertical]: styles.resizeableAreaColumnOutputUnfocused,
7428
}
7529

76-
const ORIENTATION_SPLIT_MAP = {
77-
[Orientation.Horizontal]: SplitRows,
78-
[Orientation.Vertical]: SplitColumns,
30+
const HANDLE_STYLES = {
31+
[Orientation.Horizontal]: [styles.splitRowsGutter, styles.splitRowsGutterHandle],
32+
[Orientation.Vertical]: [styles.splitColumnsGutter, ''],
7933
}
8034

81-
const Playground: React.SFC = () => {
82-
const showNotifications = useSelector(selectors.anyNotificationsToShowSelector);
35+
// We drop down to lower-level split-grid code and use some hooks
36+
// because we want to reduce the number of times that the Editor
37+
// component is remounted. Each time it's remounted, we see a flicker and
38+
// lose state (like undo history).
39+
const ResizableArea: React.SFC = () => {
8340
const somethingToShow = useSelector(selectors.getSomethingToShow);
8441
const isFocused = useSelector(selectors.isOutputFocused);
8542
const orientation = useSelector(selectors.orientation);
8643

8744
const dispatch = useDispatch();
8845
const resizeComplete = useCallback(() => dispatch(actions.splitRatioChanged()), [dispatch]);
8946

90-
let Foo;
91-
if (!somethingToShow) {
92-
Foo = NoOutput;
93-
} else {
94-
if (isFocused) {
95-
Foo = ORIENTATION_SPLIT_MAP[orientation];
96-
} else {
97-
Foo = ORIENTATION_PLAIN_MAP[orientation];
98-
}
99-
}
47+
const grid = useRef(null);
48+
const dragHandle = useRef(null);
49+
50+
// Reset styles left on the grid from split-grid when we change orientation or focus.
51+
useEffect(() => {
52+
grid.current.style['grid-template-columns'] = null;
53+
grid.current.style['grid-template-rows'] = null;
54+
55+
resizeComplete();
56+
}, [orientation, isFocused, resizeComplete])
57+
58+
useEffect(() => {
59+
const split = Split({
60+
minSize: 100,
61+
[TRACK_OPTION_NAME[orientation]]: [{
62+
track: 1,
63+
element: dragHandle.current,
64+
}],
65+
onDragEnd: resizeComplete,
66+
});
67+
68+
return () => split.destroy();
69+
}, [orientation, isFocused, somethingToShow, resizeComplete])
70+
71+
const gridStyles = isFocused ? FOCUSED_GRID_STYLE : UNFOCUSED_GRID_STYLE;
72+
const gridStyle = gridStyles[orientation];
73+
const [handleOuterStyle, handleInnerStyle] = HANDLE_STYLES[orientation];
74+
75+
return (
76+
<div ref={grid} className={gridStyle}>
77+
<div className={styles.editor}><Editor /></div>
78+
{ isFocused &&
79+
<div ref={dragHandle} className={handleOuterStyle}>
80+
<span className={handleInnerStyle}></span>
81+
</div>
82+
}
83+
{ somethingToShow && <div className={styles.output}><Output /></div>}
84+
</div>
85+
);
86+
};
87+
88+
const Playground: React.SFC = () => {
89+
const showNotifications = useSelector(selectors.anyNotificationsToShowSelector);
10090

10191
return (
10292
<>
10393
<div className={styles.container}>
104-
<div>
105-
<Header />
106-
</div>
107-
<Foo resizeComplete={resizeComplete} />
94+
<Header />
95+
<ResizableArea />
10896
</div>
10997
{ showNotifications && <Notifications />}
11098
</>
11199
);
112-
};
100+
}
113101

114102
export default Playground;

ui/frontend/package.json

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@
2121
"react-prism": "^4.0.0",
2222
"react-redux": "^7.0.0",
2323
"react-shadow": "^19.0.2",
24-
"react-split-grid": "^1.0.3",
2524
"redux": "^4.0.0",
2625
"redux-thunk": "^2.1.0",
2726
"regenerator-runtime": "^0.13.2",
2827
"reselect": "^4.0.0",
2928
"route-parser": "^0.0.5",
29+
"split-grid": "^1.0.9",
3030
"url": "^0.11.0"
3131
},
3232
"devDependencies": {
@@ -47,30 +47,30 @@
4747
"babel-loader": "^8.0.0",
4848
"babel-plugin-lodash": "^3.3.4",
4949
"basename": "^0.1.2",
50-
"compression-webpack-plugin": "^7.1.2",
51-
"copy-webpack-plugin": "^8.0.0",
50+
"compression-webpack-plugin": "^8.0.0",
51+
"copy-webpack-plugin": "^9.0.0",
5252
"css-loader": "^5.1.1",
5353
"eslint": "^7.4.0",
5454
"eslint-plugin-react": "^7.14.3",
5555
"eslint-plugin-react-hooks": "^4.0.0",
5656
"glob": "^7.0.5",
5757
"html-webpack-plugin": "^5.2.0",
58-
"jest": "^26.0.0",
58+
"jest": "^27.0.0",
5959
"json-loader": "^0.5.4",
6060
"mini-css-extract-plugin": "^1.3.9",
6161
"normalize.css": "^8.0.0",
6262
"postcss": "^8.2.7",
63-
"postcss-loader": "^5.0.0",
64-
"postcss-nesting": "^7.0.1",
63+
"postcss-loader": "^6.1.0",
64+
"postcss-nesting": "^8.0.1",
6565
"postcss-simple-vars": "^6.0.3",
66-
"prettier": "2.2.1",
66+
"prettier": "^2.2.1",
6767
"style-loader": "^2.0.0",
6868
"stylelint": "^13.12.0",
6969
"stylelint-config-css-modules": "^2.2.0",
7070
"stylelint-config-idiomatic-order": "^8.1.0",
71-
"stylelint-config-standard": "^21.0.0",
72-
"ts-jest": "^26.0.0",
73-
"ts-loader": "^8.0.0",
71+
"stylelint-config-standard": "^22.0.0",
72+
"ts-jest": "^27.0.0",
73+
"ts-loader": "^9.2.3",
7474
"typescript": "^4.2.2",
7575
"typescript-plugin-css-modules": "^3.2.0",
7676
"webpack": "^5.24.3",

0 commit comments

Comments
 (0)