Skip to content

Commit c58e274

Browse files
committed
feat: add resizeable splitter feature
1 parent 02fb9cf commit c58e274

File tree

2 files changed

+173
-10
lines changed

2 files changed

+173
-10
lines changed

src/App.css

Lines changed: 99 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -183,10 +183,60 @@ body {
183183
background-color: var(--color-background-primary);
184184
}
185185

186+
.splitter {
187+
width: 1px;
188+
background-color: var(--color-border-secondary);
189+
cursor: col-resize;
190+
flex-shrink: 0;
191+
position: relative;
192+
transition: background-color 0.2s ease, margin 0.2s ease;
193+
z-index: 10;
194+
min-height: 100%;
195+
}
196+
197+
.splitter::before {
198+
content: '';
199+
position: absolute;
200+
top: 0;
201+
left: -6px;
202+
right: -6px;
203+
bottom: 0;
204+
background: transparent;
205+
cursor: col-resize;
206+
}
207+
208+
.splitter:hover {
209+
background-color: var(--color-button-hover-border);
210+
width: 2px;
211+
margin-left: -1px;
212+
margin-right: -1px;
213+
}
214+
215+
.splitter:active,
216+
.splitter.resizing {
217+
background-color: var(--color-link-primary);
218+
width: 2px;
219+
margin-left: -1px;
220+
margin-right: -1px;
221+
}
222+
223+
.dark-theme .splitter {
224+
background-color: var(--color-border-primary);
225+
}
226+
227+
.dark-theme .splitter:hover {
228+
background-color: var(--color-button-hover-border);
229+
}
230+
231+
.dark-theme .splitter:active,
232+
.dark-theme .splitter.resizing {
233+
background-color: var(--color-link-primary);
234+
}
235+
186236
.editor-section {
187237
display: flex;
188238
flex-direction: column;
189-
flex: 1 1 50%;
239+
flex-shrink: 0;
190240
min-width: 0;
191241
height: 100%;
192242
background-color: var(--color-background-primary);
@@ -296,10 +346,6 @@ body {
296346
background-color: var(--color-background-primary);
297347
}
298348

299-
.editor-section:first-child {
300-
border-right: 1px solid var(--color-border-primary);
301-
}
302-
303349
.output.error .monaco-editor .view-lines span {
304350
color: var(--color-error);
305351
}
@@ -506,13 +552,59 @@ body {
506552

507553
.editor-section {
508554
flex: 1;
509-
width: 100%;
555+
width: 100% !important;
510556
min-height: 0;
557+
height: auto;
511558
}
512559

513560
.editor-section:first-child {
514561
border-right: none;
515-
border-bottom: 1px solid var(--color-border-primary);
562+
height: var(--left-panel-height, 50%);
563+
flex: none;
564+
}
565+
566+
.editor-section:last-child {
567+
height: var(--right-panel-height, 50%);
568+
flex: none;
569+
}
570+
571+
.splitter {
572+
display: block;
573+
width: 100%;
574+
height: 1px;
575+
cursor: row-resize;
576+
min-height: 1px;
577+
background-color: var(--color-border-secondary);
578+
transition: background-color 0.2s ease, height 0.2s ease, margin 0.2s ease;
579+
}
580+
581+
.splitter::before {
582+
top: -6px;
583+
bottom: -6px;
584+
left: 0;
585+
right: 0;
586+
cursor: row-resize;
587+
}
588+
589+
.splitter:hover {
590+
background-color: var(--color-button-hover-border);
591+
height: 2px;
592+
margin-top: -1px;
593+
margin-bottom: -1px;
594+
width: 100%;
595+
margin-left: 0;
596+
margin-right: 0;
597+
}
598+
599+
.splitter:active,
600+
.splitter.resizing {
601+
background-color: var(--color-link-primary);
602+
height: 2px;
603+
margin-top: -1px;
604+
margin-bottom: -1px;
605+
width: 100%;
606+
margin-left: 0;
607+
margin-right: 0;
516608
}
517609
}
518610

src/App.tsx

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,54 @@ function App() {
207207
const [canUndo, setCanUndo] = useState(false);
208208
const [canRedo, setCanRedo] = useState(false);
209209
const [isResetConfirmOpen, setIsResetConfirmOpen] = useState(false);
210+
const [leftPanelWidth, setLeftPanelWidth] = useState(50);
211+
const [isResizing, setIsResizing] = useState(false);
212+
const [isMobile, setIsMobile] = useState(false);
210213
const editorRef = useRef<any>(null);
214+
const containerRef = useRef<HTMLDivElement>(null);
215+
216+
useEffect(() => {
217+
const checkMobile = () => setIsMobile(window.innerWidth <= 768);
218+
219+
checkMobile();
220+
window.addEventListener('resize', checkMobile);
221+
return () => window.removeEventListener('resize', checkMobile);
222+
}, []);
223+
224+
const handleMouseDown = (event: React.MouseEvent) => {
225+
event.preventDefault();
226+
setIsResizing(true);
227+
228+
const container = containerRef.current;
229+
if (!container) return;
230+
231+
const handleMouseMove = (e: MouseEvent) => {
232+
if (!container) return;
233+
234+
const containerRect = container.getBoundingClientRect();
235+
const newWidth = isMobile
236+
? Math.min(Math.max(((e.clientY - containerRect.top) / containerRect.height) * 100, 15), 85)
237+
: Math.min(
238+
Math.max(((e.clientX - containerRect.left) / containerRect.width) * 100, 15),
239+
85,
240+
);
241+
242+
setLeftPanelWidth(newWidth);
243+
};
244+
245+
const handleMouseUp = () => {
246+
setIsResizing(false);
247+
document.removeEventListener('mousemove', handleMouseMove);
248+
document.removeEventListener('mouseup', handleMouseUp);
249+
document.body.style.cursor = '';
250+
document.body.style.userSelect = '';
251+
};
252+
253+
document.addEventListener('mousemove', handleMouseMove);
254+
document.addEventListener('mouseup', handleMouseUp);
255+
document.body.style.cursor = isMobile ? 'row-resize' : 'col-resize';
256+
document.body.style.userSelect = 'none';
257+
};
211258

212259
const getSystemTheme = () => {
213260
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
@@ -587,8 +634,21 @@ function App() {
587634
</div>
588635

589636
<div className="content-area">
590-
<div className="container">
591-
<div className="editor-section">
637+
<div
638+
className="container"
639+
ref={containerRef}
640+
style={
641+
{
642+
'--left-panel-height': `${leftPanelWidth}%`,
643+
'--right-panel-height': `${100 - leftPanelWidth}%`,
644+
} as React.CSSProperties
645+
}>
646+
<div
647+
className="editor-section"
648+
style={{
649+
width: !isMobile ? `${leftPanelWidth}%` : '100%',
650+
height: isMobile ? `${leftPanelWidth}%` : '100%',
651+
}}>
592652
<div className={getThemeClass('editor-header', theme)}>
593653
<div className="editor-actions-left">
594654
<button
@@ -645,7 +705,18 @@ function App() {
645705
/>
646706
</div>
647707
</div>
648-
<div className="editor-section">
708+
709+
<div
710+
className={getThemeClass('splitter', theme, isResizing ? 'resizing' : '')}
711+
onMouseDown={handleMouseDown}
712+
/>
713+
714+
<div
715+
className="editor-section"
716+
style={{
717+
width: !isMobile ? `${100 - leftPanelWidth}%` : '100%',
718+
height: isMobile ? `${100 - leftPanelWidth}%` : '100%',
719+
}}>
649720
<div className={getThemeClass('editor-header', theme)}>
650721
<div className="editor-actions-left">
651722
<button

0 commit comments

Comments
 (0)