diff --git a/app/components/ContentViewer/ContentViewer.module.css b/app/components/ContentViewer/ContentViewer.module.css index 8e98663..95b8bad 100644 --- a/app/components/ContentViewer/ContentViewer.module.css +++ b/app/components/ContentViewer/ContentViewer.module.css @@ -1,20 +1,18 @@ .content { display: flex; flex-direction: column; - overflow-y: auto; flex: 1; - padding-right: 14px; - padding-bottom: 12px; - padding-left: 14px; + padding: 14px; + height: 100%; + width: 100%; } + .contentWrapper { border-right: 1px solid hsl(var(--border-color)); - display: flex; flex-direction: column; - - height: 100%; - flex: 1; height: 100%; + width: 100%; + overflow: hidden; } diff --git a/app/components/EditorNOutput/EditorNOutput.module.css b/app/components/EditorNOutput/EditorNOutput.module.css index 79a8640..c670801 100644 --- a/app/components/EditorNOutput/EditorNOutput.module.css +++ b/app/components/EditorNOutput/EditorNOutput.module.css @@ -3,14 +3,12 @@ height: 100%; display: flex; flex-direction: column; - - max-width: 50%; + width: 100%; + overflow: hidden; } .outputWrapper { width: 100%; - - /* padding: 16px; */ display: flex; flex-direction: column; align-items: flex-start; @@ -19,11 +17,13 @@ border-left: none; height: 100%; } + .divider { width: 100%; height: 6px; background-color: hsl(var(--border-color)); cursor: row-resize; + flex-shrink: 0; } .divider:hover, diff --git a/app/components/ResizableContent/ResizableContent.module.css b/app/components/ResizableContent/ResizableContent.module.css new file mode 100644 index 0000000..3a494b3 --- /dev/null +++ b/app/components/ResizableContent/ResizableContent.module.css @@ -0,0 +1,82 @@ +.mainArea { + display: flex; + flex-direction: row; + height: 100vh; + width: 100%; + overflow: hidden; + position: relative; +} + +.leftPane { + height: 100%; + overflow: hidden; + min-width: 300px; + max-width: 70%; + display: flex; +} + +.rightPane { + flex: 1; + height: 100%; + min-width: 300px; + overflow: hidden; + display: flex; +} + +.divider { + width: 4px; + height: 100%; + background-color: hsl(var(--border-color) / 0.5); + cursor: col-resize; + transition: all 0.2s ease; + flex-shrink: 0; + user-select: none; + -webkit-user-select: none; + position: relative; + border-left: 1px solid hsl(var(--border-color)); + border-right: 1px solid hsl(var(--border-color)); +} + +.divider::after { + content: ''; + position: absolute; + top: 0; + left: 50%; + transform: translateX(-50%); + width: 2px; + height: 100%; + background-color: hsl(var(--primary) / 0.3); + opacity: 0; + transition: opacity 0.2s ease; +} + +.divider:hover::after, +.divider:active::after { + opacity: 1; + background-color: hsl(var(--primary) / 0.5); +} + +.divider:hover, +.divider:active { + background-color: hsl(var(--primary) / 0.2); + border-left: 1px solid hsl(var(--primary) / 0.4); + border-right: 1px solid hsl(var(--primary) / 0.4); +} + +.mainArea ::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +.mainArea ::-webkit-scrollbar-track { + background: hsl(var(--background)); +} + +.mainArea ::-webkit-scrollbar-thumb { + background: hsl(var(--border-color)); + border-radius: 4px; +} + +.mainArea ::-webkit-scrollbar-thumb:hover { + background: hsl(var(--primary) / 0.5); +} diff --git a/app/components/ResizableContent/ResizableContent.tsx b/app/components/ResizableContent/ResizableContent.tsx new file mode 100644 index 0000000..c9f9829 --- /dev/null +++ b/app/components/ResizableContent/ResizableContent.tsx @@ -0,0 +1,88 @@ +"use client"; + +import React, { useState, useRef, useEffect } from "react"; +import styles from "./ResizableContent.module.css"; +import ContentViewer from "../ContentViewer"; +import EditorNOutput from "../EditorNOutput"; +import { CodeFile } from "@/lib/types"; + +interface ResizableContentProps { + content: React.ReactNode; + codeFile: CodeFile; + nextStepPath: string | undefined; + stepIndex: number; + chapterIndex: number; +} + +export default function ResizableContent({ + content, + codeFile, + nextStepPath, + stepIndex, + chapterIndex, +}: ResizableContentProps) { + const [leftWidth, setLeftWidth] = useState(600); + const containerRef = useRef(null); + const isDraggingRef = useRef(false); + + const handleMouseDown = () => { + isDraggingRef.current = true; + }; + + const handleMouseUp = () => { + isDraggingRef.current = false; + }; + + const handleMouseMove = ( + e: React.MouseEvent | MouseEvent, + ) => { + if (!isDraggingRef.current) return; + + const containerRect = containerRef.current!.getBoundingClientRect(); + const newLeftWidth = e.clientX - containerRect.left; + + const minWidth = 300; + const maxWidth = containerRect.width * 0.7; + + if (newLeftWidth >= minWidth && newLeftWidth <= maxWidth) { + setLeftWidth(newLeftWidth); + localStorage.setItem("horizontalLeftWidth", String(newLeftWidth)); + } + }; + + useEffect(() => { + const savedWidth = localStorage.getItem("horizontalLeftWidth"); + if (savedWidth) { + setLeftWidth(Number(savedWidth)); + } + }, []); + + return ( +
+
+ {content} +
+
+
+ +
+
+ ); +} diff --git a/app/content/[...markdownPath]/page.module.css b/app/content/[...markdownPath]/page.module.css index 7669d38..10e3323 100644 --- a/app/content/[...markdownPath]/page.module.css +++ b/app/content/[...markdownPath]/page.module.css @@ -1,7 +1,41 @@ .mainArea { display: flex; flex-direction: row; - flex: 12; + height: calc(100vh - 60px); + width: 100%; + overflow: hidden; + position: relative; +} + +.leftPane { height: 100%; - overflow-y: auto; + overflow: hidden; + min-width: 300px; + max-width: 70%; + display: flex; +} + +.rightPane { + flex: 1; + height: 100%; + min-width: 300px; + overflow: hidden; + display: flex; +} + +.divider { + width: 4px; + height: 100%; + background-color: hsl(var(--border-color)); + cursor: col-resize; + transition: background-color 0.2s ease; + flex-shrink: 0; +} + +.divider:hover { + background-color: hsl(var(--primary) / 0.75); +} + +.divider:active { + background-color: hsl(var(--primary) / 0.75); } diff --git a/app/content/[...markdownPath]/page.tsx b/app/content/[...markdownPath]/page.tsx index c2b94a4..21caaca 100644 --- a/app/content/[...markdownPath]/page.tsx +++ b/app/content/[...markdownPath]/page.tsx @@ -1,9 +1,7 @@ import { contentManager } from "@/lib/contentManager"; -import styles from "./page.module.css"; import React from "react"; import { parseLessonFolder } from "@/lib/server-functions"; -import ContentViewer from "@/app/components/ContentViewer"; -import EditorNOutput from "@/app/components/EditorNOutput"; +import ResizableContent from "@/app/components/ResizableContent/ResizableContent"; export function generateMetadata({ params, @@ -38,28 +36,28 @@ export default async function Content({ contentManager.getPageMeta(urlPath); const { Page, metadata, codeFile } = parseLessonFolder(mdPath, codePath); + const pageContent = ; + return ( -
- - - - -
+ ); } + export async function generateStaticParams() { const outline = contentManager.getOutline(); const pathList: { markdownPath: string[] }[] = []; outline.map((item) => { item.steps.map((step) => { + const pathSegments = step.fullPath.split("/"); pathList.push({ - markdownPath: [item.folderName, step.fileName], + markdownPath: pathSegments, }); }); });