66 lines
2.4 KiB
TypeScript
66 lines
2.4 KiB
TypeScript
import { memo } from 'react';
|
|
import { Handle, Position, NodeProps } from '@xyflow/react';
|
|
import { ChoiceNode as ChoiceNodeData } from '../../types/dialogue';
|
|
import { useValidation } from '../../hooks/useValidation';
|
|
import { useDialogueStore } from '../../store/dialogueStore';
|
|
import styles from './nodes.module.css';
|
|
|
|
export const ChoiceNode = memo(({ data, selected }: NodeProps & { data: ChoiceNodeData }) => {
|
|
const { hasError, hasWarning } = useValidation();
|
|
const startId = useDialogueStore(s => {
|
|
const id = s.selectedDialogueId;
|
|
return s.file?.dialogues.find(d => d.id === id)?.start;
|
|
});
|
|
|
|
const isStart = data.id === startId;
|
|
const error = hasError(data.id);
|
|
const warning = !error && hasWarning(data.id);
|
|
|
|
return (
|
|
<div
|
|
className={[
|
|
styles.node,
|
|
styles.choiceNode,
|
|
selected ? styles.selected : '',
|
|
error ? styles.hasError : '',
|
|
warning ? styles.hasWarning : '',
|
|
].join(' ')}
|
|
>
|
|
{isStart && <div className={styles.startBadge}>▶ START</div>}
|
|
<Handle type="target" position={Position.Top} id="target" className={styles.handle} />
|
|
<div className={styles.nodeHeader}>
|
|
<span className={styles.speakerName}>{data.speaker || '(no speaker)'}</span>
|
|
<span className={styles.nodeType}>Choice</span>
|
|
</div>
|
|
<div className={styles.nodeId}>{data.id}</div>
|
|
<div className={styles.nodeBody}>
|
|
{data.choices.length === 0 ? (
|
|
<span className={styles.emptyText}>(no choices)</span>
|
|
) : (
|
|
data.choices.map((choice, i) => (
|
|
<div key={choice.id} className={styles.choiceRow} style={{ position: 'relative' }}>
|
|
<span className={[
|
|
styles.choiceKindBadge,
|
|
choice.kind === 'Main' ? styles.kindMain : styles.kindOptional,
|
|
].join(' ')}>
|
|
{choice.kind}
|
|
</span>
|
|
<span className={styles.choiceText}>{choice.text || '(empty)'}</span>
|
|
<Handle
|
|
type="source"
|
|
position={Position.Bottom}
|
|
id={choice.id}
|
|
className={styles.choiceHandle}
|
|
style={{
|
|
left: `${((i + 1) / (data.choices.length + 1)) * 100}%`,
|
|
bottom: -8,
|
|
}}
|
|
/>
|
|
</div>
|
|
))
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
});
|