@@ -28,6 +28,7 @@ import { pythonGenerator } from 'blockly/python';
2828import Header from './reactComponents/Header' ;
2929import * as Menu from './reactComponents/Menu' ;
3030import CodeDisplay from './reactComponents/CodeDisplay' ;
31+ import SiderCollapseTrigger from './reactComponents/SiderCollapseTrigger' ;
3132import BlocklyComponent , { BlocklyComponentType } from './reactComponents/BlocklyComponent' ;
3233import ToolboxSettingsModal from './reactComponents/ToolboxSettings' ;
3334import * as Tabs from './reactComponents/Tabs' ;
@@ -81,7 +82,7 @@ const FULL_HEIGHT = '100%';
8182const CODE_PANEL_DEFAULT_SIZE = '25%' ;
8283
8384/** Minimum size for code panel. */
84- const CODE_PANEL_MIN_SIZE = 80 ;
85+ const CODE_PANEL_MIN_SIZE = 100 ;
8586
8687/** Background color for testing layout. */
8788const LAYOUT_BACKGROUND_COLOR = '#0F0' ;
@@ -167,7 +168,10 @@ const AppContent: React.FC<AppContentProps> = ({ project, setProject }): React.J
167168 const [ shownPythonToolboxCategories , setShownPythonToolboxCategories ] = React . useState < Set < string > > ( new Set ( ) ) ;
168169 const [ triggerPythonRegeneration , setTriggerPythonRegeneration ] = React . useState ( 0 ) ;
169170 const [ leftCollapsed , setLeftCollapsed ] = React . useState ( false ) ;
170- const [ rightCollapsed , setRightCollapsed ] = React . useState ( false ) ;
171+ const [ codePanelSize , setCodePanelSize ] = React . useState < string | number > ( CODE_PANEL_DEFAULT_SIZE ) ;
172+ const [ codePanelCollapsed , setCodePanelCollapsed ] = React . useState ( false ) ;
173+ const [ codePanelExpandedSize , setCodePanelExpandedSize ] = React . useState < string | number > ( CODE_PANEL_DEFAULT_SIZE ) ;
174+ const [ codePanelAnimating , setCodePanelAnimating ] = React . useState ( false ) ;
171175 const [ theme , setTheme ] = React . useState ( 'dark' ) ;
172176 const [ languageInitialized , setLanguageInitialized ] = React . useState ( false ) ;
173177 const [ themeInitialized , setThemeInitialized ] = React . useState ( false ) ;
@@ -379,6 +383,30 @@ const AppContent: React.FC<AppContentProps> = ({ project, setProject }): React.J
379383 setToolboxSettingsModalIsOpen ( false ) ;
380384 } ;
381385
386+ /** Toggles the code panel between collapsed and expanded states. */
387+ const toggleCodePanelCollapse = ( ) : void => {
388+ setCodePanelAnimating ( true ) ;
389+
390+ if ( codePanelCollapsed ) {
391+ // Expand to previous size
392+ setCodePanelSize ( codePanelExpandedSize ) ;
393+ setCodePanelCollapsed ( false ) ;
394+ } else {
395+ // Collapse to minimum size - convert current size to pixels for storage
396+ const currentSizePx = typeof codePanelSize === 'string'
397+ ? ( parseFloat ( codePanelSize ) / 100 ) * window . innerWidth
398+ : codePanelSize ;
399+ setCodePanelExpandedSize ( currentSizePx ) ;
400+ setCodePanelSize ( CODE_PANEL_MIN_SIZE ) ;
401+ setCodePanelCollapsed ( true ) ;
402+ }
403+
404+ // Reset animation flag after transition completes
405+ setTimeout ( ( ) => {
406+ setCodePanelAnimating ( false ) ;
407+ } , 200 ) ;
408+ } ;
409+
382410 /** Handles toolbox settings modal OK with updated categories. */
383411 const handleToolboxSettingsConfirm = ( updatedShownCategories : Set < string > ) : void => {
384412 setToolboxSettingsModalIsOpen ( false ) ;
@@ -733,6 +761,8 @@ const AppContent: React.FC<AppContentProps> = ({ project, setProject }): React.J
733761 collapsible
734762 collapsed = { leftCollapsed }
735763 onCollapse = { ( collapsed : boolean ) => setLeftCollapsed ( collapsed ) }
764+ trigger = { null }
765+ style = { { position : 'relative' } }
736766 >
737767 < Menu . Component
738768 storage = { storage }
@@ -744,6 +774,10 @@ const AppContent: React.FC<AppContentProps> = ({ project, setProject }): React.J
744774 theme = { theme }
745775 setTheme = { setTheme }
746776 />
777+ < SiderCollapseTrigger
778+ collapsed = { leftCollapsed }
779+ onToggle = { ( ) => setLeftCollapsed ( ! leftCollapsed ) }
780+ />
747781 </ Sider >
748782 < Antd . Layout >
749783 < Tabs . Component
@@ -757,8 +791,8 @@ const AppContent: React.FC<AppContentProps> = ({ project, setProject }): React.J
757791 setProject = { setProject }
758792 storage = { storage }
759793 />
760- < Antd . Layout >
761- < Content >
794+ < div style = { { display : 'flex' , height : FULL_HEIGHT } } >
795+ < Content style = { { flex : 1 , height : '100%' } } >
762796 { modulePaths . current . map ( ( modulePath ) => (
763797 < BlocklyComponent
764798 key = { modulePath }
@@ -769,22 +803,69 @@ const AppContent: React.FC<AppContentProps> = ({ project, setProject }): React.J
769803 />
770804 ) ) }
771805 </ Content >
772- < Sider
773- collapsible
774- reverseArrow = { true }
775- collapsed = { rightCollapsed }
776- collapsedWidth = { CODE_PANEL_MIN_SIZE }
777- width = { CODE_PANEL_DEFAULT_SIZE }
778- onCollapse = { ( collapsed : boolean ) => setRightCollapsed ( collapsed ) }
806+ < div
807+ style = { {
808+ width : typeof codePanelSize === 'string' ? codePanelSize : `${ codePanelSize } px` ,
809+ minWidth : CODE_PANEL_MIN_SIZE ,
810+ height : '100%' ,
811+ borderLeft : '1px solid #d9d9d9' ,
812+ position : 'relative' ,
813+ transition : codePanelAnimating ? 'width 0.2s ease' : 'none'
814+ } }
779815 >
816+ < div
817+ style = { {
818+ position : 'absolute' ,
819+ left : 0 ,
820+ top : 0 ,
821+ width : '4px' ,
822+ height : '100%' ,
823+ cursor : 'ew-resize' ,
824+ backgroundColor : 'transparent' ,
825+ zIndex : 10 ,
826+ transform : 'translateX(-2px)'
827+ } }
828+ onMouseDown = { ( e ) => {
829+ e . preventDefault ( ) ;
830+ const startX = e . clientX ;
831+ const startWidth = codePanelSize ;
832+
833+ const handleMouseMove = ( e : MouseEvent ) => {
834+ const deltaX = startX - e . clientX ;
835+ // Convert startWidth to number if it's a percentage
836+ const startWidthPx = typeof startWidth === 'string'
837+ ? ( parseFloat ( startWidth ) / 100 ) * window . innerWidth
838+ : startWidth ;
839+ const newWidth = Math . max ( CODE_PANEL_MIN_SIZE , startWidthPx + deltaX ) ;
840+ setCodePanelSize ( newWidth ) ;
841+ // Update expanded size if not at minimum
842+ if ( newWidth > CODE_PANEL_MIN_SIZE ) {
843+ setCodePanelExpandedSize ( newWidth ) ;
844+ setCodePanelCollapsed ( false ) ;
845+ } else {
846+ setCodePanelCollapsed ( true ) ;
847+ }
848+ } ;
849+
850+ const handleMouseUp = ( ) => {
851+ document . removeEventListener ( 'mousemove' , handleMouseMove ) ;
852+ document . removeEventListener ( 'mouseup' , handleMouseUp ) ;
853+ } ;
854+
855+ document . addEventListener ( 'mousemove' , handleMouseMove ) ;
856+ document . addEventListener ( 'mouseup' , handleMouseUp ) ;
857+ } }
858+ />
780859 < CodeDisplay
781860 generatedCode = { generatedCode }
782861 messageApi = { messageApi }
783862 setAlertErrorMessage = { setAlertErrorMessage }
784863 theme = { theme }
864+ isCollapsed = { codePanelCollapsed }
865+ onToggleCollapse = { toggleCodePanelCollapse }
785866 />
786- </ Sider >
787- </ Antd . Layout >
867+ </ div >
868+ </ div >
788869 </ Antd . Layout >
789870 </ Antd . Layout >
790871 </ Antd . Layout >
0 commit comments