Skip to content

Commit 2233e18

Browse files
committed
refactor: replace split-pane-react with the SplitPane component
1 parent e419848 commit 2233e18

File tree

12 files changed

+333
-88
lines changed

12 files changed

+333
-88
lines changed

package-lock.json

Lines changed: 0 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
"react": "18.3.1",
3939
"react-dom": "18.3.1",
4040
"react-icons": "3.5.0",
41-
"split-pane-react": "0.1.3",
4241
"svelte": "3.59.2",
4342
"tslib": "2.8.1",
4443
"vue": "3.5.13",

packages/overmind-devtools-client/src/components/Actions/index.tsx

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from 'react'
2-
import SplitPane, { Pane } from 'split-pane-react'
2+
import SplitPane, { Pane } from '../common/SplitPane'
33
import { useAppState, useActions } from '../../overmind'
44
import ActionsList from '../ActionsList'
55
import Action from '../Action'
@@ -20,17 +20,8 @@ const Actions: React.FunctionComponent = () => {
2020
split="vertical"
2121
sizes={[state.actionsSplitSize]}
2222
onChange={(size) => actions.updateActionsSplitSize(size[0])}
23-
sashRender={() => (
24-
<div
25-
style={{
26-
cursor: 'col-resize',
27-
width: '5px',
28-
background: '#ccc',
29-
}}
30-
/>
31-
)}
3223
>
33-
<Pane minSize={100}>
24+
<Pane minSize={150}>
3425
<ActionsList />
3526
</Pane>
3627
<Pane>

packages/overmind-devtools-client/src/components/ActionsList/styles.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export const separator = css({
99
})
1010

1111
export const wrapper = css({
12-
padding: '1rem',
12+
padding: '0.3rem',
1313
height: '100%',
1414
overflowY: 'auto',
1515
boxSizing: 'border-box',

packages/overmind-devtools-client/src/components/Charts/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { css } from 'emotion'
22
import * as React from 'react'
3-
import SplitPane, { Pane } from 'split-pane-react'
3+
import SplitPane, { Pane } from '../common/SplitPane'
44

55
import { useAppState, useActions } from '../../overmind'
66
import { nameToColor } from '../../overmind/utils'
@@ -24,7 +24,7 @@ const Charts: React.FunctionComponent = () => {
2424
sizes={[state.chartsSplitSize]}
2525
onChange={(size) => actions.updateChartsSplitSize(size[0])}
2626
>
27-
<Pane minSize={100}>
27+
<Pane minSize={150}>
2828
<div className={styles.listWrapper}>
2929
{chartKeys.map((path) => {
3030
return (

packages/overmind-devtools-client/src/components/Charts/styles.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const wrapper = css({
1010
})
1111

1212
export const listWrapper = css({
13-
padding: '1rem',
13+
padding: '0.3rem',
1414
})
1515

1616
export const centerWrapper = css({
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import * as React from 'react'
2+
import { useRef, useEffect, useCallback } from 'react'
3+
import { useAppState, useActions, useEffects } from '../../../overmind'
4+
import * as styles from './styles'
5+
6+
interface PaneProps {
7+
minSize?: number
8+
children: React.ReactNode
9+
style?: React.CSSProperties
10+
}
11+
12+
export const Pane: React.FC<PaneProps> = ({ minSize, children, style }) => {
13+
return (
14+
<div
15+
className={styles.paneBase}
16+
style={{
17+
minWidth: minSize,
18+
minHeight: minSize,
19+
...style,
20+
}}
21+
>
22+
{children}
23+
</div>
24+
)
25+
}
26+
27+
interface SplitPaneProps {
28+
split: 'vertical' | 'horizontal'
29+
sizes: number[]
30+
onChange?: (sizes: number[]) => void
31+
children: React.ReactNode
32+
sashRender?: () => React.ReactNode
33+
}
34+
35+
const SplitPane: React.FC<SplitPaneProps> = ({
36+
split,
37+
sizes: initialSizes,
38+
onChange,
39+
children,
40+
sashRender,
41+
}) => {
42+
const containerRef = useRef<HTMLDivElement>(null)
43+
const state = useAppState()
44+
const actions = useActions()
45+
const effects = useEffects()
46+
47+
// Convert children to array
48+
const childrenArray = React.Children.toArray(children)
49+
50+
// Extract minSize values from Pane children
51+
const minSizes = childrenArray.map((child) => {
52+
if (
53+
React.isValidElement(child) &&
54+
typeof child.type !== 'string' &&
55+
child.props
56+
) {
57+
const props = child.props as { minSize?: number }
58+
return typeof props.minSize !== 'undefined' ? props.minSize : 0
59+
}
60+
return 0
61+
})
62+
63+
// Initialize current sizes from initialSizes or use from Overmind state
64+
const currentSizes =
65+
state.splitPane.isDragging && state.splitPane.splitType === split
66+
? state.splitPane.currentSizes
67+
: initialSizes
68+
69+
const isDragging =
70+
state.splitPane.isDragging && state.splitPane.splitType === split
71+
72+
const handleMouseDown = (e: React.MouseEvent) => {
73+
e.preventDefault()
74+
actions.startSplitPaneDrag({
75+
startPos: split === 'vertical' ? e.clientX : e.clientY,
76+
sizes: currentSizes,
77+
split,
78+
minSizes,
79+
})
80+
}
81+
82+
// Define handlers with useCallback to maintain reference equality
83+
const handleMouseMove = useCallback(
84+
(e: MouseEvent) => {
85+
if (!state.splitPane.isDragging || !containerRef.current) return
86+
87+
const containerRect = containerRef.current.getBoundingClientRect()
88+
const containerSize =
89+
split === 'vertical' ? containerRect.width : containerRect.height
90+
const currentPos = split === 'vertical' ? e.clientX : e.clientY
91+
92+
actions.handleSplitPaneMove({
93+
currentPos,
94+
containerSize,
95+
onChange,
96+
minSizes,
97+
})
98+
},
99+
[split, onChange, actions, state.splitPane.isDragging, minSizes]
100+
)
101+
102+
const handleMouseUp = useCallback(() => {
103+
actions.stopSplitPaneDrag()
104+
}, [actions])
105+
106+
// Effect for attaching/detaching global event listeners
107+
useEffect(() => {
108+
if (state.splitPane.isDragging && state.splitPane.splitType === split) {
109+
effects.splitPane.addDragListeners(handleMouseMove, handleMouseUp)
110+
}
111+
112+
return () => {
113+
if (state.splitPane.splitType === split) {
114+
effects.splitPane.removeDragListeners(handleMouseMove, handleMouseUp)
115+
}
116+
}
117+
}, [
118+
state.splitPane.isDragging,
119+
state.splitPane.splitType,
120+
split,
121+
handleMouseMove,
122+
handleMouseUp,
123+
effects.splitPane,
124+
])
125+
126+
// Effect to add VSCode-like body class when dragging
127+
useEffect(() => {
128+
if (isDragging) {
129+
document.body.classList.add('react-split--disabled')
130+
} else {
131+
document.body.classList.remove('react-split--disabled')
132+
}
133+
134+
return () => {
135+
document.body.classList.remove('react-split--disabled')
136+
}
137+
}, [isDragging])
138+
139+
return (
140+
<div
141+
ref={containerRef}
142+
className={`${styles.container(split)} ${isDragging ? 'dragging' : ''}`}
143+
>
144+
<div className={styles.paneFirst(currentSizes[0])}>
145+
{childrenArray[0]}
146+
</div>
147+
<div
148+
className={styles.divider(split, isDragging)}
149+
onMouseDown={handleMouseDown}
150+
>
151+
{sashRender ? sashRender() : null}
152+
</div>
153+
<div className={styles.paneSecond}>{childrenArray[1]}</div>
154+
</div>
155+
)
156+
}
157+
158+
export default SplitPane
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { css, injectGlobal } from 'emotion'
2+
import { colors } from '../../../theme'
3+
4+
injectGlobal`
5+
body.splitpane-dragging {
6+
user-select: none;
7+
}
8+
`
9+
10+
export const container = (split: 'vertical' | 'horizontal') =>
11+
css({
12+
display: 'flex',
13+
flexDirection: split === 'vertical' ? 'row' : 'column',
14+
height: '100%',
15+
width: '100%',
16+
overflow: 'hidden',
17+
position: 'relative',
18+
'&.dragging': {
19+
cursor: split === 'vertical' ? 'col-resize' : 'row-resize',
20+
userSelect: 'none',
21+
},
22+
})
23+
24+
export const paneFirst = (size: number) =>
25+
css({
26+
flex: `0 0 ${size}px`,
27+
position: 'relative',
28+
width: `${size}px`,
29+
overflow: 'hidden',
30+
})
31+
32+
export const paneSecond = css({
33+
flex: 1,
34+
position: 'relative',
35+
})
36+
37+
export const divider = (
38+
split: 'vertical' | 'horizontal',
39+
isDragging: boolean
40+
) =>
41+
css({
42+
flex: isDragging ? '0 0 3px' : '0 0 1px',
43+
backgroundColor: isDragging ? '#175ede' : colors.border || '#8c8c8c',
44+
cursor: split === 'vertical' ? 'col-resize' : 'row-resize',
45+
userSelect: 'none',
46+
position: 'relative',
47+
transition: 'background-color 0.1s, flex 0.1s',
48+
'&::after': {
49+
content: '""',
50+
position: 'absolute',
51+
top: 0,
52+
left: 0,
53+
right: 0,
54+
bottom: 0,
55+
// Invisible overlay to make the divider easier to grab
56+
width: split === 'vertical' ? '5px' : '100%',
57+
height: split === 'vertical' ? '100%' : '5px',
58+
transform: split === 'vertical' ? 'translateX(-2px)' : 'translateY(-2px)',
59+
zIndex: 1,
60+
},
61+
'&:hover': {
62+
backgroundColor: '#175ede',
63+
},
64+
})
65+
66+
export const paneBase = css({
67+
overflow: 'auto',
68+
width: '100%',
69+
height: '100%',
70+
'& *': {
71+
overflow: 'hidden',
72+
textOverflow: 'ellipsis',
73+
maxWidth: '100%',
74+
},
75+
})

packages/overmind-devtools-client/src/index.tsx

Lines changed: 0 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -28,67 +28,6 @@ injectGlobal`
2828
#app {
2929
height: 100%;
3030
}
31-
32-
33-
.react-split {
34-
flex: 1;
35-
height: 100%;
36-
position: relative;
37-
width: 100%;
38-
}
39-
.react-split__pane {
40-
height: 100%;
41-
position: absolute;
42-
white-space: normal;
43-
width: 100%;
44-
overflow: hidden;
45-
}
46-
.react-split__sash {
47-
height: 100%;
48-
position: absolute;
49-
top: 0;
50-
transition: background-color 0.1s;
51-
width: 100%;
52-
z-index: 2;
53-
}
54-
.react-split__sash--disabled {
55-
pointer-events: none;
56-
}
57-
.react-split__sash--vertical {
58-
transition: opacity 0.5s ease;
59-
cursor: col-resize;
60-
background-color: var(--colors-border);
61-
opacity: 0.8;
62-
}
63-
.react-split__sash--horizontal {
64-
cursor: row-resize;
65-
}
66-
.react-split__sash-content {
67-
width: 100%;
68-
height: 100%;
69-
}
70-
.react-split__sash-content--active {
71-
background-color: #175ede;
72-
}
73-
.react-split--dragging.react-split--vertical {
74-
cursor: col-resize;
75-
}
76-
.react-split--dragging.react-split--horizontal {
77-
cursor: row-resize;
78-
}
79-
80-
body.react-split--disabled {
81-
user-select: none;
82-
}
83-
84-
.split-sash-content {
85-
width: 100%;
86-
height: 100%;
87-
}
88-
.split-sash-content.split-sash-content-vscode.split-sash-content-active {
89-
background-color: var(--colors-border);
90-
opacity: 1;
91-
}
9231
`
9332

9433
window.onerror = (_, _2, _3, _4, error) => {

0 commit comments

Comments
 (0)