diff --git a/packages/react/src/Panels/WorkspacePanel.tsx b/packages/react/src/Panels/WorkspacePanel.tsx index 3fb8a3328..3badbba45 100644 --- a/packages/react/src/Panels/WorkspacePanel.tsx +++ b/packages/react/src/Panels/WorkspacePanel.tsx @@ -3,10 +3,6 @@ import { TutorialStore } from '@tutorialkit/runtime'; import type { I18n } from '@tutorialkit/types'; import { useCallback, useEffect, useRef, useState } from 'react'; import { Panel, PanelGroup, PanelResizeHandle, type ImperativePanelHandle } from 'react-resizable-panels'; -import type { - OnChangeCallback as OnEditorChange, - OnScrollCallback as OnEditorScroll, -} from '../core/CodeMirrorEditor/index.js'; import type { Theme } from '../core/types.js'; import resizePanelStyles from '../styles/resize-panel.module.css'; import { classNames } from '../utils/classnames.js'; @@ -21,42 +17,82 @@ interface Props { theme: Theme; } +interface PanelProps extends Props { + hasEditor: boolean; + hasPreviews: boolean; + hideTerminalPanel: boolean; +} + +interface TerminalProps extends PanelProps { + terminalPanelRef: React.RefObject; + terminalExpanded: React.MutableRefObject; +} + /** * This component is the orchestrator between various interactive components. */ export function WorkspacePanel({ tutorialStore, theme }: Props) { - const fileTree = tutorialStore.hasFileTree(); const hasEditor = tutorialStore.hasEditor(); const hasPreviews = tutorialStore.hasPreviews(); const hideTerminalPanel = !tutorialStore.hasTerminalPanel(); - const editorPanelRef = useRef(null); - const previewPanelRef = useRef(null); const terminalPanelRef = useRef(null); - const previewRef = useRef(null); const terminalExpanded = useRef(false); - const [helpAction, setHelpAction] = useState<'solve' | 'reset'>('reset'); + return ( + + + + + + + + + + + + ); +} +function EditorSection({ theme, tutorialStore, hasEditor }: PanelProps) { + const [helpAction, setHelpAction] = useState<'solve' | 'reset'>('reset'); const selectedFile = useStore(tutorialStore.selectedFile); const currentDocument = useStore(tutorialStore.currentDocument); const lessonFullyLoaded = useStore(tutorialStore.lessonFullyLoaded); const lesson = tutorialStore.lesson!; - const onEditorChange = useCallback((update) => { - tutorialStore.setCurrentDocumentContent(update.content); - }, []); - - const onEditorScroll = useCallback((position) => { - tutorialStore.setCurrentDocumentScrollPosition(position); - }, []); - - const onFileSelect = useCallback((filePath: string | undefined) => { - tutorialStore.setSelectedFile(filePath); - }, []); - - const onHelpClick = useCallback(() => { + function onHelpClick() { if (tutorialStore.hasSolution()) { setHelpAction((action) => { if (action === 'reset') { @@ -72,40 +108,57 @@ export function WorkspacePanel({ tutorialStore, theme }: Props) { } else { tutorialStore.reset(); } - }, [tutorialStore.ref]); + } useEffect(() => { - const lesson = tutorialStore.lesson!; - - const unsubscribe = tutorialStore.lessonFullyLoaded.subscribe((loaded) => { - if (loaded && lesson.data.autoReload) { - previewRef.current?.reload(); - } - }); - if (tutorialStore.hasSolution()) { setHelpAction('solve'); } else { setHelpAction('reset'); } - - if (tutorialStore.terminalConfig.value?.defaultOpen) { - showTerminal(); - } - - return () => unsubscribe(); }, [tutorialStore.ref]); - useEffect(() => { - if (hideTerminalPanel) { - // force hide the terminal if we don't have any panels to show - hideTerminal(); + return ( + + tutorialStore.setSelectedFile(filePath)} + selectedFile={selectedFile} + onEditorScroll={(position) => tutorialStore.setCurrentDocumentScrollPosition(position)} + onEditorChange={(update) => tutorialStore.setCurrentDocumentContent(update.content)} + /> + + ); +} - terminalExpanded.current = false; - } - }, [hideTerminalPanel]); +function PreviewsSection({ + tutorialStore, + terminalPanelRef, + terminalExpanded, + hideTerminalPanel, + hasPreviews, + hasEditor, +}: TerminalProps) { + const previewRef = useRef(null); + const lesson = tutorialStore.lesson!; + const terminalConfig = useStore(tutorialStore.terminalConfig); - const showTerminal = useCallback(() => { + function showTerminal() { const { current: terminal } = terminalPanelRef; if (!terminal) { @@ -118,110 +171,109 @@ export function WorkspacePanel({ tutorialStore, theme }: Props) { } else { terminal.expand(); } - }, []); + } - const hideTerminal = useCallback(() => { - terminalPanelRef.current?.collapse(); + const toggleTerminal = useCallback(() => { + if (terminalPanelRef.current?.isCollapsed()) { + showTerminal(); + } else if (terminalPanelRef.current) { + terminalPanelRef.current.collapse(); + } }, []); - const toggleTerminal = useCallback(() => { - const { current: terminal } = terminalPanelRef; + useEffect(() => { + if (hideTerminalPanel) { + // force hide the terminal if we don't have any panels to show + terminalPanelRef.current?.collapse(); - if (!terminal) { - return; + terminalExpanded.current = false; } + }, [hideTerminalPanel]); - if (terminalPanelRef.current?.isCollapsed()) { + useEffect(() => { + if (terminalConfig.defaultOpen) { showTerminal(); - } else { - hideTerminal(); } - }, []); + }, [terminalConfig.defaultOpen]); + + useEffect(() => { + const lesson = tutorialStore.lesson!; + + const unsubscribe = tutorialStore.lessonFullyLoaded.subscribe((loaded) => { + if (loaded && lesson.data.autoReload) { + previewRef.current?.reload(); + } + }); + + return () => unsubscribe(); + }, [tutorialStore.ref]); return ( - - - - - - - - - + - { - terminalExpanded.current = true; - }} - className={classNames( - 'transition-theme bg-tk-elements-panel-backgroundColor text-tk-elements-panel-textColor', - { - 'border-t border-tk-elements-app-borderColor': hasPreviews, - }, - )} - > - - - + + ); +} + +function TerminalSection({ + tutorialStore, + theme, + terminalPanelRef, + terminalExpanded, + hideTerminalPanel, + hasEditor, + hasPreviews, +}: TerminalProps) { + let id = 'terminal-closed'; + + if (hideTerminalPanel) { + id = 'terminal-none'; + } else if (!hasPreviews && !hasEditor) { + id = 'terminal-full'; + } else if (!hasPreviews) { + id = 'terminal-opened'; + } + + let defaultSize = 0; + + if (hideTerminalPanel) { + defaultSize = 0; + } else if (!hasPreviews && !hasEditor) { + defaultSize = 100; + } else if (!hasPreviews) { + defaultSize = DEFAULT_TERMINAL_SIZE; + } + + return ( + { + terminalExpanded.current = true; + }} + className={classNames('transition-theme bg-tk-elements-panel-backgroundColor text-tk-elements-panel-textColor', { + 'border-t border-tk-elements-app-borderColor': hasPreviews, + })} + > + + ); }