+
{heading}
+
+ 가능한 색깔
+ {possibleColors(card, activeColors, rainbowMode).map(color => (
+ {COLOR_LABEL[color]}
+ ))}
+
+
+ 가능한 숫자
+ {possibleValues(card).map(value => (
+ {value}
+ ))}
+
+
+);
+
+const OpponentCard = ({ card, activeColors, rainbowMode }: {
+ card: CardViewDto,
+ activeColors: HanabiColor[],
+ rainbowMode: RainbowMode,
+}) => {
+ // The badge is always rendered, only hidden when there is no clue, so gaining a clue does not
+ // re-center the value and shift it upward.
+ const clue = clueText(card, activeColors, rainbowMode);
+ const { ref, shift, below, clampIntoView } = useCandidateClamp();
+ return (
+
+ {only !== ClueType.VALUE && (
+
+ {colors.map(color => (
+
+ ))}
+
+ )}
+ {only !== ClueType.COLOR && (
+
+ {[1, 2, 3, 4, 5].map(value => (
+
+ ))}
+
+ )}
+
+);
+
+/**
+ * The pending bonus, shown to everyone: its owner gets the controls to resolve it, while the rest of
+ * the table sees only what was drawn and whose turn it is to act. Note this covers just the four
+ * effects that pend — GAIN_CLUE and REPAIR_AND_CLUE apply the moment they are drawn and never reach
+ * this state.
+ */
+const BonusBanner = ({ effect, isMine, ownerName, others, discardPile, colors, onResolve }: {
+ effect: BonusEffect,
+ isMine: boolean,
+ ownerName: string,
+ others: PlayerDto[],
+ discardPile: HanabiCardDto[],
+ colors: HanabiColor[],
+ onResolve: (payload: object) => void,
+}) => {
+ const [targetId, setTargetId] = useState