From 026b49a89f9c6c57b2fde75017b2524abd4605f5 Mon Sep 17 00:00:00 2001 From: ngc2207 Date: Sat, 23 Nov 2024 13:25:41 +0800 Subject: [PATCH] feat: add language selector and Monaco editor components; update answer page with language selection and editor functionality --- .../code-editor/code-language-selector.tsx | 0 .../[slug]/components/language-selector.tsx | 49 +++++++ .../[slug]/components/monaco-editor.tsx | 28 ++++ .../problems/[slug]/components/tabs-card.tsx | 6 +- .../[slug]/components/tooltip-button.tsx | 27 ++++ src/app/(app)/problems/[slug]/layout.tsx | 7 + .../problems/[slug]/views/answer-page.tsx | 135 +++++++++++++++++- .../[slug]/views/code-editor-page.tsx | 3 + 8 files changed, 252 insertions(+), 3 deletions(-) create mode 100644 src/app/(app)/problems/[slug]/components/code-editor/code-language-selector.tsx create mode 100644 src/app/(app)/problems/[slug]/components/language-selector.tsx create mode 100644 src/app/(app)/problems/[slug]/components/monaco-editor.tsx create mode 100644 src/app/(app)/problems/[slug]/components/tooltip-button.tsx create mode 100644 src/app/(app)/problems/[slug]/views/code-editor-page.tsx diff --git a/src/app/(app)/problems/[slug]/components/code-editor/code-language-selector.tsx b/src/app/(app)/problems/[slug]/components/code-editor/code-language-selector.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/app/(app)/problems/[slug]/components/language-selector.tsx b/src/app/(app)/problems/[slug]/components/language-selector.tsx new file mode 100644 index 0000000..417067a --- /dev/null +++ b/src/app/(app)/problems/[slug]/components/language-selector.tsx @@ -0,0 +1,49 @@ +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; + +interface LanguageOption { + value: string; + label: string; +} + +interface LanguageSelectorProps { + value: string; + onChange: (language: string) => void; + options: LanguageOption[]; +} + +const LanguageSelector = ({ + value, + onChange, + options, +}: LanguageSelectorProps) => { + // 检查 options 数组是否为空或未定义 + if (!options || options.length === 0) { + return null; + } + + return ( + + ); +}; + +export default LanguageSelector; diff --git a/src/app/(app)/problems/[slug]/components/monaco-editor.tsx b/src/app/(app)/problems/[slug]/components/monaco-editor.tsx new file mode 100644 index 0000000..d26cc8c --- /dev/null +++ b/src/app/(app)/problems/[slug]/components/monaco-editor.tsx @@ -0,0 +1,28 @@ +"use client"; + +import Editor from "@monaco-editor/react"; + +interface MonacoEditorProps { + language: string; + template: string; + value: string; + onChange: (value: string) => void; +} + +export default function MonacoEditor({ + language, + value, + onChange, +}: MonacoEditorProps) { + return ( +
+ onChange(value ?? "")} + options={{ minimap: { enabled: false } }} + /> +
+ ); +} diff --git a/src/app/(app)/problems/[slug]/components/tabs-card.tsx b/src/app/(app)/problems/[slug]/components/tabs-card.tsx index 660e194..0450f67 100644 --- a/src/app/(app)/problems/[slug]/components/tabs-card.tsx +++ b/src/app/(app)/problems/[slug]/components/tabs-card.tsx @@ -32,7 +32,11 @@ export function TabsCard({ tabsItems }: TabsCardProps) { ))} {tabsItems.map((tabsItem) => ( - + {tabsItem.content} ))} diff --git a/src/app/(app)/problems/[slug]/components/tooltip-button.tsx b/src/app/(app)/problems/[slug]/components/tooltip-button.tsx new file mode 100644 index 0000000..e8d9776 --- /dev/null +++ b/src/app/(app)/problems/[slug]/components/tooltip-button.tsx @@ -0,0 +1,27 @@ +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; + +interface TooltipButtonProps { + icon: React.ReactNode; + tooltipText: string; + onClick?: () => void; +} + +const TooltipButton = ({ icon, tooltipText, onClick }: TooltipButtonProps) => ( + + + + {icon} + + +

{tooltipText}

+
+
+
+); + +export default TooltipButton; diff --git a/src/app/(app)/problems/[slug]/layout.tsx b/src/app/(app)/problems/[slug]/layout.tsx index 0c6932a..132bf94 100644 --- a/src/app/(app)/problems/[slug]/layout.tsx +++ b/src/app/(app)/problems/[slug]/layout.tsx @@ -13,6 +13,7 @@ import CommitPage from "./views/commit-page"; import AnswerPage from "./views/answer-page"; import ConsolePage from "./views/console-page"; import { TabsCard } from "./components/tabs-card"; +import CodeEditorPage from "./views/code-editor-page"; import DescriptionPage from "./views/description-page"; export default async function ProblemLayout({ @@ -44,6 +45,12 @@ export default async function ProblemLayout({ value: "code", content: , }, + { + icon: CodeXmlIcon, + label: "代码编辑器", + value: "editor", + content: , + }, ]; const consoleTabsItems = [ { diff --git a/src/app/(app)/problems/[slug]/views/answer-page.tsx b/src/app/(app)/problems/[slug]/views/answer-page.tsx index 6acd362..7e3d16d 100644 --- a/src/app/(app)/problems/[slug]/views/answer-page.tsx +++ b/src/app/(app)/problems/[slug]/views/answer-page.tsx @@ -1,7 +1,138 @@ +"use client"; + +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { useState } from "react"; +import MonacoEditor from "../components/monaco-editor"; +import TooltipButton from "../components/tooltip-button"; +import { Play, RefreshCw, Save, Settings } from "lucide-react"; + export default function AnswerPage({ slug }: { slug: string }) { + const templates = [ + { + language: "c", + template: `/** + * Note: The returned array must be malloced, assume caller calls free(). + */ +int* twoSum(int* nums, int numsSize, int target, int* returnSize) { + +}`, + }, + { + language: "java", + template: `class Solution { + public int[] twoSum(int[] nums, int target) { + + } +}`, + }, + ]; + + // 初始化当前选择的语言和模板 + const [selectedLanguage, setSelectedLanguage] = useState( + templates[0].language + ); + const [languageValues, setLanguageValues] = useState<{ + [key: string]: string; + }>({}); + + // 根据选择的语言更新模板 + const handleLanguageChange = (language: string) => { + setSelectedLanguage(language); + }; + + // 获取当前语言的值,如果状态中没有,则使用模板 + const getCurrentValue = () => { + return ( + languageValues[selectedLanguage] || + templates.find((t) => t.language === selectedLanguage)?.template || + "" + ); + }; + + // 处理编辑器内容变化 + const handleEditorChange = (value: string | undefined) => { + if (value !== undefined) { + setLanguageValues((prev) => ({ + ...prev, + [selectedLanguage]: value, + })); + } + }; + + // 重置代码为当前语言的模板 + const handleResetCode = () => { + const template = templates.find( + (t) => t.language === selectedLanguage + )?.template; + if (template) { + setLanguageValues((prev) => ({ + ...prev, + [selectedLanguage]: template, + })); + } + }; + + // 从 templates 中提取语言作为选项 + const options = templates.map((template) => ({ + value: template.language, + label: template.language.toUpperCase(), + })); + return ( -
-

Answer Page: {slug}

+
+
+
+ +
+
+ } + tooltipText="重置" + onClick={handleResetCode} + /> + } + tooltipText="设置" + /> + } + tooltipText="运行" + /> + } + tooltipText="保存" + /> +
+
+
+ t.language === selectedLanguage)?.template || + "" + } + value={getCurrentValue()} + onChange={handleEditorChange} + /> +
+
); } diff --git a/src/app/(app)/problems/[slug]/views/code-editor-page.tsx b/src/app/(app)/problems/[slug]/views/code-editor-page.tsx new file mode 100644 index 0000000..9dc6f46 --- /dev/null +++ b/src/app/(app)/problems/[slug]/views/code-editor-page.tsx @@ -0,0 +1,3 @@ +export default function CodeEditorPage({ slug }: { slug: string }) { + return
; +}