nitro-v36-with-renderer-v36
/
src /
components /
wired /
views /
extras /
WiredContractRulesEditor.tsx
WiredContractRulesEditor.tsx 4.3 Ko
Télécharger le fichier
import { FC } from 'react';
import { Button, Text } from '../../../../common';
import {
CONTRACT_MAX_NODES,
CONTRACT_MAX_RULES,
ContractTermRow,
emptyRow,
} from './contractTermWire';
import { WiredContractTermRow } from './WiredContractTermRow';
interface WiredContractRulesEditorProps {
/** Alternatives: satisfying any one of them satisfies the contract. */
rules: ContractTermRow[][];
direction: number;
/** Off for the reward side, which is a single rule rather than a set of alternatives. */
allowAlternatives?: boolean;
onChange: (rules: ContractTermRow[][]) => void;
}
/**
* Edits the requirement grammar: rows inside one alternative must all be met, and the alternatives
* are joined by "or". It is the same shape the player's requirements bubble renders, so what an
* owner builds here is literally what a visitor reads.
*/
export const WiredContractRulesEditor: FC<WiredContractRulesEditorProps> = ({
rules,
direction,
allowAlternatives = true,
onChange,
}) => {
const replaceRule = (index: number, rule: ContractTermRow[]) =>
onChange(rules.map((existing, position) => (position === index ? rule : existing)));
const patchRow = (ruleIndex: number, rowIndex: number, patch: Partial<ContractTermRow>) =>
replaceRule(
ruleIndex,
rules[ruleIndex].map((row, position) => (position === rowIndex ? { ...row, ...patch } : row)),
);
const addRow = (ruleIndex: number) => replaceRule(ruleIndex, [...rules[ruleIndex], emptyRow(direction)]);
const removeRow = (ruleIndex: number, rowIndex: number) =>
replaceRule(
ruleIndex,
rules[ruleIndex].filter((unused, position) => position !== rowIndex),
);
const addRule = () => onChange([...rules, [emptyRow(direction)]]);
const removeRule = (index: number) => onChange(rules.filter((unused, position) => position !== index));
return (
<div className="flex flex-col gap-2">
{rules.map((rule, ruleIndex) => (
<div key={ruleIndex} className="flex flex-col gap-2">
{ruleIndex > 0 && (
<div className="flex items-center gap-2">
<div className="h-px flex-1 bg-black/10" />
<Text small bold>
or
</Text>
<div className="h-px flex-1 bg-black/10" />
</div>
)}
<div className="flex flex-col gap-2 rounded border border-black/10 p-2">
{!rule.length && <Text small>Nothing in this option yet.</Text>}
{rule.map((row, rowIndex) => (
<div key={rowIndex} className="flex flex-col gap-1">
{rowIndex > 0 && <Text small bold>and</Text>}
<WiredContractTermRow row={row} onChange={(patch) => patchRow(ruleIndex, rowIndex, patch)} />
<div className="flex justify-end">
<Button variant="danger" onClick={() => removeRow(ruleIndex, rowIndex)}>
Remove
</Button>
</div>
</div>
))}
<div className="flex flex-wrap justify-between gap-1">
<Button disabled={rule.length >= CONTRACT_MAX_NODES} variant="secondary" onClick={() => addRow(ruleIndex)}>
Add requirement
</Button>
{allowAlternatives && rules.length > 1 && (
<Button variant="danger" onClick={() => removeRule(ruleIndex)}>
Remove option
</Button>
)}
</div>
</div>
</div>
))}
{allowAlternatives && (
<Button disabled={rules.length >= CONTRACT_MAX_RULES} variant="secondary" onClick={addRule}>
Add another option
</Button>
)}
</div>
);
};
