Skip to content

Commit f562f14

Browse files
committed
出力ファイル追加機能
1 parent b2e6c06 commit f562f14

1 file changed

Lines changed: 84 additions & 4 deletions

File tree

app/(docs)/@docs/[lang]/sandbox/sandboxContent.tsx

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ export function SandboxContent(props: SandboxContentProps) {
3838
const [userFiles, setUserFiles] = useState<string[]>([]);
3939
const [newFilename, setNewFilename] = useState("");
4040
const [filenameError, setFilenameError] = useState<string | null>(null);
41+
42+
const [userOutputFiles, setUserOutputFiles] = useState<string[]>([]);
43+
const [newOutputFilename, setNewOutputFilename] = useState("");
44+
const [outputFilenameError, setOutputFilenameError] = useState<string | null>(null);
45+
4146
const [isFormVisible, setIsFormVisible] = useState(false);
4247

4348
// サイドバーの目次用セクション定義
@@ -52,7 +57,10 @@ export function SandboxContent(props: SandboxContentProps) {
5257
level: 2,
5358
});
5459
list.push({ id: "sandbox-exec" as SectionId, title: "実行", level: 2 });
55-
if (language.readonlyFiles && language.readonlyFiles.length > 0) {
60+
if (
61+
(language.readonlyFiles && language.readonlyFiles.length > 0) ||
62+
language.supportsFileOutput
63+
) {
5664
list.push({
5765
id: "sandbox-readonly" as SectionId,
5866
title: "出力ファイル",
@@ -119,7 +127,8 @@ export function SandboxContent(props: SandboxContentProps) {
119127
if (
120128
defaultFiles.includes(name) ||
121129
readonlyFiles.includes(name) ||
122-
userFiles.includes(name)
130+
userFiles.includes(name) ||
131+
userOutputFiles.includes(name)
123132
) {
124133
setFilenameError("同名のファイルがすでに存在します。");
125134
return;
@@ -135,6 +144,36 @@ export function SandboxContent(props: SandboxContentProps) {
135144
setUserFiles((prev) => prev.filter((f) => f !== filename));
136145
};
137146

147+
const handleAddOutputFile = (e: FormEvent) => {
148+
e.preventDefault();
149+
const name = newOutputFilename.trim();
150+
if (!name) return;
151+
152+
// 既存ファイルチェック
153+
const defaultFiles = language.sampleFiles
154+
? Object.keys(language.sampleFiles)
155+
: [];
156+
const readonlyFiles = language.readonlyFiles ?? [];
157+
if (
158+
defaultFiles.includes(name) ||
159+
readonlyFiles.includes(name) ||
160+
userFiles.includes(name) ||
161+
userOutputFiles.includes(name)
162+
) {
163+
setOutputFilenameError("同名のファイルがすでに存在します。");
164+
return;
165+
}
166+
167+
setOutputFilenameError(null);
168+
setUserOutputFiles((prev) => [...prev, name]);
169+
writeFile({ [name]: "" });
170+
setNewOutputFilename("");
171+
};
172+
173+
const handleRemoveOutputFile = (filename: string) => {
174+
setUserOutputFiles((prev) => prev.filter((f) => f !== filename));
175+
};
176+
138177
return (
139178
<div className="flex-1 p-4 pb-16 flex flex-col max-w-docs mx-auto w-full">
140179
<Heading level={1}>{langEntry?.name ?? langId} Sandbox</Heading>
@@ -298,23 +337,64 @@ export function SandboxContent(props: SandboxContentProps) {
298337
</section>
299338

300339
{/* 4. 出力ファイル */}
301-
{language.readonlyFiles && language.readonlyFiles.length > 0 && (
340+
{((language.readonlyFiles && language.readonlyFiles.length > 0) ||
341+
language.supportsFileOutput) && (
302342
<section
303343
id="sandbox-readonly"
304344
ref={(el) => {
305345
sectionRefs.current.set("sandbox-readonly", el);
306346
}}
307347
>
308348
<Heading level={2}>出力ファイル</Heading>
309-
{language.readonlyFiles.map((filename) => (
349+
{language.readonlyFiles &&
350+
language.readonlyFiles.map((filename) => (
351+
<EditorComponent
352+
key={filename}
353+
language={langConstants(runtimeLang)}
354+
filename={filename}
355+
initContent=""
356+
readonly
357+
/>
358+
))}
359+
{userOutputFiles.map((filename) => (
310360
<EditorComponent
311361
key={filename}
312362
language={langConstants(runtimeLang)}
313363
filename={filename}
314364
initContent=""
315365
readonly
366+
onDelete={() => handleRemoveOutputFile(filename)}
316367
/>
317368
))}
369+
370+
{language.supportsFileOutput && (
371+
<div className="mx-2 my-2 mt-4">
372+
<form
373+
onSubmit={handleAddOutputFile}
374+
className="flex items-center gap-2"
375+
>
376+
出力ファイルを追加:
377+
<input
378+
type="text"
379+
className="input input-bordered input-sm flex-1 font-mono"
380+
placeholder="追加するファイル名を入力"
381+
value={newOutputFilename}
382+
onChange={(e) => {
383+
setNewOutputFilename(e.target.value);
384+
setOutputFilenameError(null);
385+
}}
386+
/>
387+
<button type="submit" className="btn btn-sm btn-accent">
388+
出力ファイルを追加
389+
</button>
390+
</form>
391+
{outputFilenameError && (
392+
<p className="text-error text-sm mt-1">
393+
{outputFilenameError}
394+
</p>
395+
)}
396+
</div>
397+
)}
318398
</section>
319399
)}
320400

0 commit comments

Comments
 (0)