feat: port TechLog Studio editors

This commit is contained in:
DongHyeonka
2026-08-15 23:41:34 +09:00
parent 887f5e6eb1
commit 5933265975
16 changed files with 879 additions and 3 deletions
@@ -0,0 +1,34 @@
import type { components } from "../../../contracts/studio/generated.ts";
import { createLocalId } from "../../../domain/studio/local-id.ts";
type CatalogEntry = components["schemas"]["CatalogEntry"];
type RelationInput = components["schemas"]["RelationInput"];
function ordered(relations: RelationInput[]): RelationInput[] {
return relations.map((relation, order) => ({ ...relation, order }));
}
export function RelationEditor({ relations, catalog, onChange }: { relations: RelationInput[]; catalog: CatalogEntry[]; onChange(relations: RelationInput[]): void }) {
const update = (index: number, patch: Partial<RelationInput>) => onChange(ordered(relations.map((relation, candidateIndex) => candidateIndex === index ? { ...relation, ...patch } : relation)));
const move = (index: number, delta: -1 | 1) => {
const target = index + delta;
if (target < 0 || target >= relations.length) return;
const next = [...relations];
[next[index], next[target]] = [next[target]!, next[index]!];
onChange(ordered(next));
};
return (
<fieldset className="studio-ordered-list">
<legend></legend>
{relations.length === 0 ? <p> .</p> : null}
{relations.map((relation, index) => (
<div className="studio-relation-item" key={relation.id ?? `relation-${index}`}>
<label><span> {index + 1} </span><select value={relation.targetId ?? ""} onChange={(event) => update(index, { targetId: event.currentTarget.value || null })}><option value=""> </option>{catalog.map((entry) => <option key={entry.id} value={entry.id} disabled={relations.some((candidate, candidateIndex) => candidateIndex !== index && candidate.targetId === entry.id)}>{entry.label}</option>)}</select></label>
<label><span> {index + 1} </span><input value={relation.reason} onChange={(event) => update(index, { reason: event.currentTarget.value })} /></label>
<div className="studio-item-actions"><button type="button" onClick={() => move(index, -1)} disabled={index === 0}></button><button type="button" onClick={() => move(index, 1)} disabled={index === relations.length - 1}></button><button type="button" onClick={() => onChange(ordered(relations.filter((_, candidateIndex) => candidateIndex !== index)))}></button></div>
</div>
))}
<button className="studio-add-item" type="button" disabled={relations.length >= 20} onClick={() => { if (relations.length < 20) onChange(ordered([...relations, { id: createLocalId("relation"), targetId: null, reason: "", order: relations.length }])); }}> </button>
</fieldset>
);
}