Commit c4c2246c authored by timel's avatar timel

optimize: card drapdown menu store

parent 2e712f62
...@@ -4,8 +4,9 @@ import { animated, to, useSpring } from "@react-spring/web"; ...@@ -4,8 +4,9 @@ import { animated, to, useSpring } from "@react-spring/web";
import { Dropdown, type MenuProps } from "antd"; import { Dropdown, type MenuProps } from "antd";
import classnames from "classnames"; import classnames from "classnames";
import React, { type CSSProperties, useEffect, useState } from "react"; import React, { type CSSProperties, useEffect, useState } from "react";
import { proxy, useSnapshot } from "valtio"; import { useSnapshot } from "valtio";
import type { CardMeta } from "@/api";
import { import {
fetchStrings, fetchStrings,
getCardStr, getCardStr,
...@@ -139,7 +140,152 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => { ...@@ -139,7 +140,152 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
useEffect(() => { useEffect(() => {
setHighlight(!!idleInteractivities.length); setHighlight(!!idleInteractivities.length);
}, [idleInteractivities]); }, [idleInteractivities]);
const menuItems = useSnapshot(dropdownMenu);
const [dropdownMenu, setDropdownMenu] = useState({
items: [] as DropdownItem[],
});
// 发动效果
// 1. 下拉菜单里面选择[召唤 / 特殊召唤 /.../效果发动]
// 2. 如果是非效果发动,那么直接选择哪张卡(单张卡直接选择那张)
// 3. 如果是效果发动,那么选择哪张卡,然后选择效果
const handleDropdownMenu = (cards: CardType[], isField: boolean) => {
const map = new Map<Interactivity<number>["interactType"], CardType[]>();
cards.forEach((card) => {
card.idleInteractivities.forEach(({ interactType }) => {
if (!map.has(interactType)) {
map.set(interactType, []);
}
map.get(interactType)?.push(card);
});
});
const actions = [...map.entries()];
const nonEffectActions = actions.filter(
([action]) => action !== InteractType.ACTIVATE
);
const getNonEffectResponse = (action: InteractType, card: CardType) =>
card.idleInteractivities.find((item) => item.interactType === action)!
.response;
const nonEffectItem: DropdownItem[] = nonEffectActions.map(
([action, cards], key) => ({
key,
label: interactTypeToString(action),
onClick: async () => {
if (!isField) {
// 单卡: 直接召唤/特殊召唤/...
const card = cards[0];
sendSelectIdleCmdResponse(getNonEffectResponse(action, card));
} else {
// 场地: 选择卡片
// TODO: hint
const option = await displaySimpleSelectCardsModal({
selectables: cards.map((card) => ({
meta: card.meta,
location: card.location,
response: getNonEffectResponse(action, card),
})),
});
sendSelectIdleCmdResponse(option[0].response!);
}
},
})
);
const hasEffect =
cards.reduce(
(prev, acc) => [
...prev,
...acc.idleInteractivities.filter(
({ interactType }) => interactType === InteractType.ACTIVATE
),
],
[] as Interactivity<number>[]
).length > 0;
const effectItem: DropdownItem = {
key: nonEffectItem.length,
label: interactTypeToString(InteractType.ACTIVATE),
onClick: async () => {
let card: CardType;
if (!isField) {
// 单卡: 直接发动这个卡的效果
card = cards[0];
} else {
// 场地: 选择卡片
// TODO: hint
const option = await displaySimpleSelectCardsModal({
selectables: cards
// 过滤掉不能发效果的卡
.filter(
(card) =>
card.idleInteractivities.find(
({ interactType }) => interactType === InteractType.ACTIVATE
) !== undefined
)
.map((card) => ({
meta: card.meta,
location: card.location,
card,
})),
});
card = option[0].card! as any; // 一定会有的,有输入则定有输出
}
// 选择发动哪个效果
handleEffectActivation(
card.idleInteractivities
.filter(
({ interactType }) => interactType === InteractType.ACTIVATE
)
.map((x) => ({
desc: interactTypeToString(x.interactType),
response: x.response,
effectCode: x.activateIndex,
})),
card.meta
);
},
};
const items = [...nonEffectItem];
hasEffect && items.push(effectItem);
setDropdownMenu({ items });
};
const onClick = () => {
const onCardClick = (card: CardType) => {
// 中央弹窗展示选中卡牌信息
// TODO: 同一张卡片,是否重复点击会关闭CardModal?
displayCardModal(card);
if (card.idleInteractivities.length) handleDropdownMenu([card], false);
// 侧边栏展示超量素材信息
const overlayMaterials = cardStore.findOverlay(
card.location.zone,
card.location.controller,
card.location.sequence
);
if (overlayMaterials.length > 0) {
displayCardListModal({
isZone: false,
monster: card,
});
}
};
const onFieldClick = (card: CardType) => {
displayCardListModal({
isZone: true,
zone: card.location.zone,
controller: card.location.controller,
});
// 收集这个zone的所有交互,并且在下拉菜单之中显示
const cards = cardStore.at(card.location.zone, card.location.controller);
handleDropdownMenu(cards, true);
};
if ([MZONE, SZONE, HAND].includes(state.location.zone)) {
onCardClick(state);
} else if ([EXTRA, GRAVE, REMOVED].includes(state.location.zone)) {
onFieldClick(state);
}
};
// <<< 效果 <<< // <<< 效果 <<<
return ( return (
...@@ -161,23 +307,17 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => { ...@@ -161,23 +307,17 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
"--focus-opacity": styles.focusOpacity, "--focus-opacity": styles.focusOpacity,
} as any as CSSProperties } as any as CSSProperties
} }
onClick={() => { onClick={onClick}
if ([MZONE, SZONE, HAND].includes(state.location.zone)) {
onCardClick(state);
} else if ([EXTRA, GRAVE, REMOVED].includes(state.location.zone)) {
onFieldClick(state);
}
}}
> >
<div className="card-focus" /> <div className="card-focus" />
<div className="card-shadow" /> <div className="card-shadow" />
<Dropdown <Dropdown
menu={{ items: menuItems.value as any }} menu={dropdownMenu}
placement="bottom" placement="bottom"
overlayClassName="card-dropdown" overlayClassName="card-dropdown"
arrow arrow
trigger={["click"]} trigger={["click"]}
// disabled={!highlight} // disabled={!highlight} // TODO: 这里的disable要考虑到field的情况,比如额外卡组
> >
<div className={classnames("card-img-wrap", { focus: classFocus })}> <div className={classnames("card-img-wrap", { focus: classFocus })}>
<YgoCard <YgoCard
...@@ -192,37 +332,6 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => { ...@@ -192,37 +332,6 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
); );
}); });
const onCardClick = (card: CardType) => {
// 中央弹窗展示选中卡牌信息
// TODO: 同一张卡片,是否重复点击会关闭CardModal?
displayCardModal(card);
if (card.idleInteractivities) handleDropdownMenu([card], false);
// 侧边栏展示超量素材信息
const overlayMaterials = cardStore.findOverlay(
card.location.zone,
card.location.controller,
card.location.sequence
);
if (overlayMaterials.length > 0) {
displayCardListModal({
isZone: false,
monster: card,
});
}
};
const onFieldClick = (card: CardType) => {
displayCardListModal({
isZone: true,
zone: card.location.zone,
controller: card.location.controller,
});
// 收集这个zone的所有交互,并且在下拉菜单之中显示
const cards = cardStore.at(card.location.zone, card.location.controller);
handleDropdownMenu(cards, true);
};
// >>> 下拉菜单:点击动作 >>> // >>> 下拉菜单:点击动作 >>>
interface Interactivy { interface Interactivy {
desc: string; desc: string;
...@@ -233,11 +342,10 @@ interface Interactivy { ...@@ -233,11 +342,10 @@ interface Interactivy {
type DropdownItem = NonNullable<MenuProps["items"]>[number] & { type DropdownItem = NonNullable<MenuProps["items"]>[number] & {
onClick: () => void; onClick: () => void;
}; };
const dropdownMenu = proxy({ value: [] as DropdownItem[] });
const handleEffectActivation = ( const handleEffectActivation = (
effectInteractivies: Interactivy[], effectInteractivies: Interactivy[],
meta?: any // FIXME: meta的类型 meta?: CardMeta
) => { ) => {
if (!effectInteractivies.length) return; if (!effectInteractivies.length) return;
else if (effectInteractivies.length === 1) { else if (effectInteractivies.length === 1) {
...@@ -259,103 +367,4 @@ const handleEffectActivation = ( ...@@ -259,103 +367,4 @@ const handleEffectActivation = (
} }
}; };
// 发动效果
// 1. 下拉菜单里面选择[召唤 / 特殊召唤 /.../效果发动]
// 2. 如果是非效果发动,那么直接选择哪张卡(单张卡直接选择那张)
// 3. 如果是效果发动,那么选择哪张卡,然后选择效果
const handleDropdownMenu = (cards: CardType[], isField: boolean) => {
const map = new Map<Interactivity<number>["interactType"], CardType[]>();
cards.forEach((card) => {
card.idleInteractivities.forEach(({ interactType }) => {
if (!map.has(interactType)) {
map.set(interactType, []);
}
map.get(interactType)?.push(card);
});
});
const actions = [...map.entries()];
const nonEffectActions = actions.filter(
([action]) => action !== InteractType.ACTIVATE
);
const getNonEffectResponse = (action: InteractType, card: CardType) =>
card.idleInteractivities.find((item) => item.interactType === action)!
.response;
const nonEffectItem: DropdownItem[] = nonEffectActions.map(
([action, cards], key) => ({
key,
label: interactTypeToString(action),
onClick: async () => {
if (!isField) {
// 单卡: 直接召唤/特殊召唤/...
const card = cards[0];
sendSelectIdleCmdResponse(getNonEffectResponse(action, card));
} else {
// 场地: 选择卡片
// TODO: hint
const option = await displaySimpleSelectCardsModal({
selectables: cards.map((card) => ({
meta: card.meta,
location: card.location,
response: getNonEffectResponse(action, card),
})),
});
sendSelectIdleCmdResponse(option[0].response!);
}
},
})
);
const hasEffect =
cards.reduce(
(prev, acc) => [
...prev,
...acc.idleInteractivities.filter(
({ interactType }) => interactType === InteractType.ACTIVATE
),
],
[] as Interactivity<number>[]
).length > 0;
const effectItem: DropdownItem = {
key: nonEffectItem.length,
label: interactTypeToString(InteractType.ACTIVATE),
onClick: async () => {
let card: CardType;
if (!isField) {
// 单卡: 直接发动这个卡的效果
card = cards[0];
} else {
// 场地: 选择卡片
// TODO: hint
const option = await displaySimpleSelectCardsModal({
selectables: cards
// 过滤掉不能发效果的卡
.filter(
(card) =>
card.idleInteractivities.find(
({ interactType }) => interactType === InteractType.ACTIVATE
) !== undefined
)
.map((card) => ({
meta: card.meta,
location: card.location,
card,
})),
});
card = option[0].card! as any; // 一定会有的,有输入则定有输出
}
// 选择发动哪个效果
handleEffectActivation(
card.idleInteractivities
.filter(({ interactType }) => interactType === InteractType.ACTIVATE)
.map((x) => ({
desc: interactTypeToString(x.interactType),
response: x.response,
effectCode: x.activateIndex,
})),
card.meta
);
},
};
dropdownMenu.value = nonEffectItem;
hasEffect && dropdownMenu.value.push(effectItem);
};
// <<< 下拉菜单 <<< // <<< 下拉菜单 <<<
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment