Skip to content

Commit 4cc4765

Browse files
committed
feat(dojo): improve code editor styling and add horizontal tab layout
Signed-off-by: Tyler Slaton <[email protected]>
1 parent b709007 commit 4cc4765

File tree

3 files changed

+82
-30
lines changed

3 files changed

+82
-30
lines changed

typescript-sdk/apps/dojo/src/components/code-viewer/code-editor.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ export function CodeEditor({ file, onFileChange }: CodeEditorProps) {
1616

1717
const theme = useTheme();
1818

19+
if (file?.language === "ts") file.language = "typescript";
20+
1921
return file ? (
2022
<div className="h-full flex flex-col">
2123
<Editor
@@ -25,6 +27,7 @@ export function CodeEditor({ file, onFileChange }: CodeEditorProps) {
2527
onChange={handleEditorChange}
2628
options={{
2729
minimap: { enabled: false },
30+
padding: { top: 30, bottom: 30 },
2831
fontSize: 16,
2932
lineNumbers: "on",
3033
readOnly: true,
Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,64 @@
11
import { useMemo, useState } from "react";
22
import { FileTree } from "@/components/file-tree/file-tree";
3-
import { CodeEditor } from './code-editor'
3+
import { CodeEditor } from "./code-editor";
44
import { FeatureFile } from "@/types/feature";
55
import { useURLParams } from "@/contexts/url-params-context";
6+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
67

7-
export default function CodeViewer({
8-
codeFiles
9-
}: {
10-
codeFiles: FeatureFile[];
11-
}) {
12-
const { file, setCodeFile } = useURLParams();
8+
export default function CodeViewer({ codeFiles }: { codeFiles: FeatureFile[] }) {
9+
const { file, setCodeFile, codeLayout } = useURLParams();
1310

14-
const selectedFile = useMemo(() => (
15-
codeFiles.find(f => f.name === file) ?? codeFiles[0]
16-
), [codeFiles, file])
11+
const selectedFile = useMemo(
12+
() => codeFiles.find((f) => f.name === file) ?? codeFiles[0],
13+
[codeFiles, file],
14+
);
15+
16+
if (codeLayout === "tabs") {
17+
return (
18+
<div className="flex flex-col h-full">
19+
<Tabs
20+
value={selectedFile?.name}
21+
onValueChange={setCodeFile}
22+
className="flex-1 flex flex-col"
23+
>
24+
<TabsList className="w-full justify-start h-auto flex-wrap p-1">
25+
{codeFiles.map((file) => (
26+
<TabsTrigger
27+
key={file.name}
28+
value={file.name}
29+
className="border border-transparent data-[state=active]:border-indigo-200"
30+
>
31+
{file.name.split("/").pop()}
32+
</TabsTrigger>
33+
))}
34+
</TabsList>
35+
{codeFiles.map((file) => (
36+
<TabsContent
37+
key={file.name}
38+
value={file.name}
39+
className="flex-1 mt-0 data-[state=inactive]:hidden"
40+
>
41+
<div className="h-full bg-[#1e1e1e]">
42+
<CodeEditor file={file} />
43+
</div>
44+
</TabsContent>
45+
))}
46+
</Tabs>
47+
</div>
48+
);
49+
}
1750

1851
return (
1952
<div className="flex h-full">
2053
<div className="w-72 border-r flex flex-col bg-background">
2154
<div className="flex-1 overflow-auto">
22-
<FileTree
23-
files={codeFiles}
24-
selectedFile={selectedFile}
25-
onFileSelect={setCodeFile}
26-
/>
55+
<FileTree files={codeFiles} selectedFile={selectedFile} onFileSelect={setCodeFile} />
2756
</div>
2857
</div>
2958
<div className="flex-1 h-full py-5 bg-[#1e1e1e]">
3059
{selectedFile ? (
3160
<div className="h-full">
32-
<CodeEditor
33-
file={selectedFile}
34-
/>
61+
<CodeEditor file={selectedFile} />
3562
</div>
3663
) : (
3764
<div className="flex items-center justify-center h-full text-muted-foreground">
@@ -40,5 +67,5 @@ export default function CodeViewer({
4067
)}
4168
</div>
4269
</div>
43-
)
44-
}
70+
);
71+
}

typescript-sdk/apps/dojo/src/contexts/url-params-context.tsx

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
'use client';
1+
"use client";
22

3-
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
4-
import { useRouter, usePathname, useSearchParams } from 'next/navigation';
3+
import React, { createContext, useContext, useState, useEffect, ReactNode } from "react";
4+
import { useRouter, usePathname, useSearchParams } from "next/navigation";
55
import { View } from "@/types/interface";
66

77
interface URLParamsState {
@@ -11,6 +11,7 @@ interface URLParamsState {
1111
viewPickerHidden: boolean;
1212
featurePickerHidden: boolean;
1313
file?: string;
14+
codeLayout: "sidebar" | "tabs";
1415
}
1516

1617
interface URLParamsContextType extends URLParamsState {
@@ -20,6 +21,7 @@ interface URLParamsContextType extends URLParamsState {
2021
setViewPickerHidden: (disabled: boolean) => void;
2122
setFeaturePickerHidden: (disabled: boolean) => void;
2223
setCodeFile: (fileName: string) => void;
24+
setCodeLayout: (layout: "sidebar" | "tabs") => void;
2325
}
2426

2527
const URLParamsContext = createContext<URLParamsContextType | undefined>(undefined);
@@ -40,6 +42,7 @@ export function URLParamsProvider({ children }: URLParamsProviderProps) {
4042
frameworkPickerHidden: searchParams.get("frameworkPicker") === "false",
4143
viewPickerHidden: searchParams.get("viewPicker") === "false",
4244
featurePickerHidden: searchParams.get("featurePicker") === "false",
45+
codeLayout: (searchParams.get("codeLayout") as "sidebar" | "tabs") || "sidebar",
4346
}));
4447

4548
// Update URL when state changes
@@ -90,8 +93,22 @@ export function URLParamsProvider({ children }: URLParamsProviderProps) {
9093
}
9194
}
9295

96+
// Update codeLayout param
97+
if (newState.codeLayout !== undefined) {
98+
if (newState.codeLayout === "sidebar") {
99+
params.delete("codeLayout");
100+
} else {
101+
params.set("codeLayout", newState.codeLayout);
102+
}
103+
}
104+
105+
// Update file param
106+
if (newState.file !== undefined) {
107+
params.set("file", newState.file);
108+
}
109+
93110
const queryString = params.toString();
94-
router.push(pathname + (queryString ? '?' + queryString : ''));
111+
router.push(pathname + (queryString ? "?" + queryString : ""));
95112
};
96113

97114
// Sync state with URL changes (e.g., browser back/forward)
@@ -102,6 +119,8 @@ export function URLParamsProvider({ children }: URLParamsProviderProps) {
102119
frameworkPickerHidden: searchParams.get("frameworkPicker") === "false",
103120
viewPickerHidden: searchParams.get("viewPicker") === "false",
104121
featurePickerHidden: searchParams.get("featurePicker") === "false",
122+
file: searchParams.get("file") || undefined,
123+
codeLayout: (searchParams.get("codeLayout") as "sidebar" | "tabs") || "sidebar",
105124
};
106125

107126
setState(newState);
@@ -144,6 +163,12 @@ export function URLParamsProvider({ children }: URLParamsProviderProps) {
144163
updateURL({ file: fileName });
145164
};
146165

166+
const setCodeLayout = (codeLayout: "sidebar" | "tabs") => {
167+
const newState = { ...state, codeLayout };
168+
setState(newState);
169+
updateURL({ codeLayout });
170+
};
171+
147172
const contextValue: URLParamsContextType = {
148173
...state,
149174
setView,
@@ -152,19 +177,16 @@ export function URLParamsProvider({ children }: URLParamsProviderProps) {
152177
setViewPickerHidden,
153178
setFeaturePickerHidden,
154179
setCodeFile,
180+
setCodeLayout,
155181
};
156182

157-
return (
158-
<URLParamsContext.Provider value={contextValue}>
159-
{children}
160-
</URLParamsContext.Provider>
161-
);
183+
return <URLParamsContext.Provider value={contextValue}>{children}</URLParamsContext.Provider>;
162184
}
163185

164186
export function useURLParams(): URLParamsContextType {
165187
const context = useContext(URLParamsContext);
166188
if (context === undefined) {
167-
throw new Error('useURLParams must be used within a URLParamsProvider');
189+
throw new Error("useURLParams must be used within a URLParamsProvider");
168190
}
169191
return context;
170192
}

0 commit comments

Comments
 (0)