ModReleaser/src/components/ConfigSelector.tsx

131 lines
3.4 KiB
TypeScript

"use client";
import { useState, useCallback } from "react";
import { Select, Button, Space, App } from "antd";
import { EditOutlined, PlusOutlined } from "@ant-design/icons";
import { ConfigModal } from "./ConfigModal";
import { getConfig } from "@/services/config";
import { parseProject } from "@/services/project";
import type { Config } from "@/types";
import type { ParsedProject } from "@/services/project";
interface ConfigEntry {
name: string;
file: string;
}
interface ConfigSelectorProps {
configs: ConfigEntry[];
selectedFile: string | null;
onSelect: (file: string, config: Config, project: ParsedProject) => void;
onConfigAdded: (entry: ConfigEntry) => void;
onConfigUpdated: (entry: ConfigEntry) => void;
}
export function ConfigSelector({
configs,
selectedFile,
onSelect,
onConfigAdded,
onConfigUpdated,
}: ConfigSelectorProps) {
const { message } = App.useApp();
const [selecting, setSelecting] = useState(false);
// Config modal state
const [modalOpen, setModalOpen] = useState(false);
const [modalMode, setModalMode] = useState<"add" | "edit">("add");
const [modalInitial, setModalInitial] = useState<{
file: string;
config: Config;
} | null>(null);
// Handle config selection
const handleChange = useCallback(
async (file: string) => {
setSelecting(true);
try {
const config = await getConfig(file);
const project = await parseProject(config);
onSelect(file, config, project);
} catch (err) {
message.error(
err instanceof Error ? err.message : "解析项目配置失败",
);
} finally {
setSelecting(false);
}
},
[onSelect],
);
// Open add modal
const openAdd = useCallback(() => {
setModalMode("add");
setModalInitial(null);
setModalOpen(true);
}, []);
// Open edit modal
const openEdit = useCallback(async () => {
if (!selectedFile) {
message.warning("请先选择一个配置");
return;
}
try {
const config = await getConfig(selectedFile);
setModalMode("edit");
setModalInitial({ file: selectedFile, config });
setModalOpen(true);
} catch (err) {
message.error("读取配置失败");
}
}, [selectedFile]);
// Modal save callback
const handleModalSaved = useCallback(
(file: string, name: string) => {
if (modalMode === "add") {
onConfigAdded({ name, file });
} else {
onConfigUpdated({ name, file });
}
setModalOpen(false);
},
[modalMode, onConfigAdded, onConfigUpdated],
);
return (
<>
<Space.Compact style={{ width: "100%" }}>
<Select
style={{ flex: 1 }}
placeholder="选择配置…"
loading={selecting}
value={selectedFile}
onChange={handleChange}
options={configs.map((c) => ({
value: c.file,
label: c.name,
}))}
/>
<Button icon={<EditOutlined />} onClick={openEdit}>
</Button>
<Button icon={<PlusOutlined />} onClick={openAdd}>
</Button>
</Space.Compact>
<ConfigModal
open={modalOpen}
mode={modalMode}
initialFile={modalInitial?.file ?? null}
initialConfig={modalInitial?.config ?? null}
onCancel={() => setModalOpen(false)}
onSaved={handleModalSaved}
/>
</>
);
}