103 lines
3.6 KiB
TypeScript
103 lines
3.6 KiB
TypeScript
import { useDialogueStore } from '../../store/dialogueStore';
|
|
import { LineInspector } from './inspectors/LineInspector';
|
|
import { ChoiceInspector } from './inspectors/ChoiceInspector';
|
|
import { ConditionInspector } from './inspectors/ConditionInspector';
|
|
import { SetFlagInspector } from './inspectors/SetFlagInspector';
|
|
import { CutsceneStartInspector, EndInspector } from './inspectors/OtherInspectors';
|
|
import { DialogueNode } from '../../types/dialogue';
|
|
import styles from './RightPanel.module.css';
|
|
|
|
// Helper to update start node ID
|
|
function useUpdateStart() {
|
|
const store = useDialogueStore;
|
|
return (dialogueId: string, start: string) => {
|
|
store.setState(state => {
|
|
const d = state.file?.dialogues.find(x => x.id === dialogueId);
|
|
if (d) d.start = start;
|
|
});
|
|
};
|
|
}
|
|
|
|
export function RightPanel() {
|
|
const { file, selectedDialogueId, selectedNodeId } = useDialogueStore();
|
|
const updateStart = useUpdateStart();
|
|
|
|
const dialogue = file?.dialogues.find(d => d.id === selectedDialogueId);
|
|
|
|
if (!dialogue) {
|
|
return (
|
|
<div className={styles.panel}>
|
|
<div className={styles.emptyMsg}>No dialogue selected</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!selectedNodeId) {
|
|
return (
|
|
<div className={styles.panel}>
|
|
<div className={styles.dialogueMeta}>
|
|
<div className={styles.metaHeader}>Dialogue Properties</div>
|
|
<div>
|
|
<label className={styles.label}>ID</label>
|
|
<input className={styles.input} value={dialogue.id} readOnly />
|
|
</div>
|
|
<div style={{ marginTop: 8 }}>
|
|
<label className={styles.label}>Start node</label>
|
|
<input
|
|
className={styles.input}
|
|
value={dialogue.start}
|
|
onChange={e => {
|
|
if (selectedDialogueId) updateStart(selectedDialogueId, e.target.value);
|
|
}}
|
|
list="startNodeList"
|
|
/>
|
|
<datalist id="startNodeList">
|
|
{dialogue.nodes.map(n => <option key={n.id} value={n.id} />)}
|
|
</datalist>
|
|
</div>
|
|
<div className={styles.emptyHint} style={{ marginTop: 16 }}>
|
|
Click a node in the graph to inspect it.
|
|
</div>
|
|
<div style={{ marginTop: 12 }}>
|
|
<div className={styles.metaHeader}>Node Count</div>
|
|
<div className={styles.statRow}>
|
|
{(['Line', 'Choice', 'Condition', 'SetFlag', 'CutsceneStart', 'End'] as DialogueNode['type'][]).map(t => {
|
|
const count = dialogue.nodes.filter(n => n.type === t).length;
|
|
return count > 0 ? (
|
|
<span key={t} className={styles.statPill}>{t}: {count}</span>
|
|
) : null;
|
|
})}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const node = dialogue.nodes.find(n => n.id === selectedNodeId);
|
|
if (!node) return null;
|
|
|
|
return (
|
|
<div className={styles.panel}>
|
|
{node.type === 'Line' && (
|
|
<LineInspector node={node} dialogueId={dialogue.id} />
|
|
)}
|
|
{node.type === 'Choice' && (
|
|
<ChoiceInspector node={node} dialogueId={dialogue.id} />
|
|
)}
|
|
{node.type === 'Condition' && (
|
|
<ConditionInspector node={node} dialogueId={dialogue.id} />
|
|
)}
|
|
{node.type === 'SetFlag' && (
|
|
<SetFlagInspector node={node} dialogueId={dialogue.id} />
|
|
)}
|
|
{node.type === 'CutsceneStart' && (
|
|
<CutsceneStartInspector node={node} dialogueId={dialogue.id} />
|
|
)}
|
|
{node.type === 'End' && (
|
|
<EndInspector node={node} dialogueId={dialogue.id} />
|
|
)}
|
|
</div>
|
|
);
|
|
}
|