Skip to content

Commit 36bc401

Browse files
authored
feat(editor): add resizable editor/preview panes (#279)
* feat(editor): add resizable editor/preview panes for improved flexibility Signed-off-by: surya4419 <[email protected]> * feat(editor): Add auto-pairing for curly braces in Concerto model (#281) * feat(editor): Add auto-pairing for curly braces in Concerto model section Signed-off-by: surya4419 <[email protected]> * feat(editor): Add auto-pairing for curly braces in Concerto model section Signed-off-by: surya4419 <[email protected]> * feat(editor): Add auto-pairing for curly braces in Concerto model section Signed-off-by: surya4419 <[email protected]> --------- Signed-off-by: surya4419 <[email protected]> * feat(editor): add resizable editor/preview panes for improved flexibility Signed-off-by: surya4419 <[email protected]> --------- Signed-off-by: surya4419 <[email protected]>
1 parent df0f13a commit 36bc401

File tree

2 files changed

+134
-12
lines changed

2 files changed

+134
-12
lines changed

src/App.tsx

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import SampleDropdown from "./components/SampleDropdown";
1616
import UseShare from "./components/UseShare";
1717
import LearnContent from "./components/Content";
1818
import FloatingFAB from "./components/FabButton";
19+
import ResizableContainer from "./components/ResizableContainer";
1920

2021
const { Content } = Layout;
2122

@@ -171,18 +172,20 @@ const App = () => {
171172
background: backgroundColor,
172173
}}
173174
>
174-
<Row gutter={24}>
175-
<Col xs={24} sm={16} style={{ paddingBottom: "20px" }}>
176-
<Collapse
177-
defaultActiveKey={activePanel}
178-
onChange={onChange}
179-
items={panels}
180-
/>
181-
</Col>
182-
<Col xs={24} sm={8}>
183-
<AgreementHtml loading={loading} isModal={false} />
184-
</Col>
185-
</Row>
175+
<ResizableContainer
176+
leftPane={
177+
<Collapse
178+
defaultActiveKey={activePanel}
179+
onChange={onChange}
180+
items={panels}
181+
style={{ marginBottom: "24px" }}
182+
/>
183+
}
184+
rightPane={<AgreementHtml loading={loading} isModal={false} />}
185+
initialLeftWidth={66}
186+
minLeftWidth={30}
187+
minRightWidth={30}
188+
/>
186189
</div>
187190
<FloatingFAB />
188191
</div>
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import { useEffect, useRef, useState } from 'react';
2+
import useAppStore from '../store/store';
3+
4+
interface ResizableContainerProps {
5+
leftPane: React.ReactNode;
6+
rightPane: React.ReactNode;
7+
initialLeftWidth?: number;
8+
minLeftWidth?: number;
9+
minRightWidth?: number;
10+
}
11+
12+
const ResizableContainer: React.FC<ResizableContainerProps> = ({
13+
leftPane,
14+
rightPane,
15+
initialLeftWidth = 66,
16+
minLeftWidth = 30,
17+
minRightWidth = 30,
18+
}) => {
19+
const [leftWidth, setLeftWidth] = useState(
20+
Number(localStorage.getItem('editorPaneWidth')) || initialLeftWidth
21+
);
22+
const containerRef = useRef<HTMLDivElement>(null);
23+
const isDragging = useRef(false);
24+
const backgroundColor = useAppStore((state) => state.backgroundColor);
25+
const [isHovered, setIsHovered] = useState(false);
26+
27+
useEffect(() => {
28+
const handleMouseMove = (e: MouseEvent) => {
29+
if (!isDragging.current || !containerRef.current) return;
30+
31+
const container = containerRef.current;
32+
const containerRect = container.getBoundingClientRect();
33+
const containerWidth = containerRect.width;
34+
const mouseX = e.clientX - containerRect.left;
35+
const newLeftWidth = (mouseX / containerWidth) * 100;
36+
37+
if (newLeftWidth >= minLeftWidth && (100 - newLeftWidth) >= minRightWidth) {
38+
setLeftWidth(newLeftWidth);
39+
localStorage.setItem('editorPaneWidth', newLeftWidth.toString());
40+
}
41+
};
42+
43+
const handleMouseUp = () => {
44+
isDragging.current = false;
45+
document.body.style.cursor = 'default';
46+
};
47+
48+
document.addEventListener('mousemove', handleMouseMove);
49+
document.addEventListener('mouseup', handleMouseUp);
50+
51+
return () => {
52+
document.removeEventListener('mousemove', handleMouseMove);
53+
document.removeEventListener('mouseup', handleMouseUp);
54+
};
55+
}, [minLeftWidth, minRightWidth]);
56+
57+
const [isMobile, setIsMobile] = useState(window.innerWidth <= 575);
58+
59+
useEffect(() => {
60+
const handleResize = () => {
61+
setIsMobile(window.innerWidth <= 575);
62+
};
63+
64+
window.addEventListener('resize', handleResize);
65+
return () => window.removeEventListener('resize', handleResize);
66+
}, []);
67+
68+
return (
69+
<div
70+
ref={containerRef}
71+
style={{
72+
display: 'flex',
73+
width: '100%',
74+
position: 'relative',
75+
backgroundColor,
76+
flexDirection: isMobile ? 'column' : 'row',
77+
height: '100%',
78+
overflow: isMobile ? 'auto' : 'hidden'
79+
}}
80+
>
81+
<div style={{
82+
width: isMobile ? '100%' : `${leftWidth}%`,
83+
height: isMobile ? 'auto' : '100%',
84+
overflow: 'hidden'
85+
}}>
86+
{leftPane}
87+
</div>
88+
{!isMobile && (
89+
<div
90+
style={{
91+
width: '8px',
92+
cursor: 'col-resize',
93+
background: isHovered || isDragging.current ? '#999' : '#ccc',
94+
position: 'relative',
95+
zIndex: 10,
96+
userSelect: 'none',
97+
transition: 'background-color 0.2s'
98+
}}
99+
onMouseDown={() => {
100+
isDragging.current = true;
101+
document.body.style.cursor = 'col-resize';
102+
}}
103+
onMouseEnter={() => setIsHovered(true)}
104+
onMouseLeave={() => setIsHovered(false)}
105+
/>
106+
)}
107+
<div style={{
108+
width: isMobile ? '100%' : `${100 - leftWidth}%`,
109+
height: isMobile ? 'auto' : '100%',
110+
overflow: 'hidden',
111+
marginTop: isMobile ? '4px' : '0'
112+
}}>
113+
{rightPane}
114+
</div>
115+
</div>
116+
);
117+
};
118+
119+
export default ResizableContainer;

0 commit comments

Comments
 (0)