Commit 61035821 authored by timel's avatar timel

feat: card dropdown

parent c082dd4d
Pipeline #22392 failed with stages
in 11 minutes and 37 seconds
import { sendSelectSingleResponse, ygopro } from "@/api"; import { sendSelectSingleResponse, ygopro } from "@/api";
import { useConfig } from "@/config"; import { useConfig } from "@/config";
import { fetchSelectHintMeta, messageStore } from "@/stores"; import { fetchSelectHintMeta } from "@/stores";
import { displaySelectActionsModal } from "@/ui/Duel/Message/SelectActionsModal"; import { displaySelectActionsModal } from "@/ui/Duel/Message/SelectActionsModal";
import { fetchCheckCardMeta } from "../utils"; import { fetchCheckCardMeta } from "../utils";
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { cardStore, matStore } from "@/stores"; import { cardStore } from "@/stores";
import { showWaiting } from "@/ui/Duel/Message"; import { showWaiting } from "@/ui/Duel/Message";
export default (_wait: ygopro.StocGameMessage.MsgWait) => { export default (_wait: ygopro.StocGameMessage.MsgWait) => {
......
import type { CardMeta, ygopro } from "@/api"; import type { CardMeta, ygopro } from "@/api";
type CardLocation = ReturnType<typeof ygopro.CardLocation.prototype.toObject>;
interface Option {
// card id
meta: CardMeta;
location?: CardLocation;
// 效果
effectDesc?: string;
// 作为素材的cost,比如同调召唤的星级
level1?: number;
level2?: number;
response: number;
}
export interface ModalState { export interface ModalState {
// Yes or No弹窗 // Yes or No弹窗
......
...@@ -10,6 +10,7 @@ import { ...@@ -10,6 +10,7 @@ import {
OptionModal, OptionModal,
PositionModal, PositionModal,
SelectActionsModal, SelectActionsModal,
SimpleSelectCardsModal,
SortCardModal, SortCardModal,
YesNoModal, YesNoModal,
} from "./Message"; } from "./Message";
...@@ -32,6 +33,7 @@ const NeosDuel = () => { ...@@ -32,6 +33,7 @@ const NeosDuel = () => {
<CheckCounterModal /> <CheckCounterModal />
<SortCardModal /> <SortCardModal />
<AnnounceModal /> <AnnounceModal />
<SimpleSelectCardsModal />
</> </>
); );
}; };
......
import "./index.scss"; import "./index.scss";
import { MinusOutlined, UpOutlined } from "@ant-design/icons"; import { MinusOutlined, UpOutlined } from "@ant-design/icons";
import { Button, Modal, type ModalProps } from "antd"; import { Modal, type ModalProps } from "antd";
import classNames from "classnames"; import classNames from "classnames";
import { type CSSProperties, type FC, useRef, useState } from "react"; import { useState } from "react";
interface Props extends ModalProps { interface Props extends ModalProps {
canBeMinimized?: boolean; canBeMinimized?: boolean;
} }
export const NeosModal: FC<Props> = (props) => { export const NeosModal: React.FC<Props> = (props) => {
const { canBeMinimized = true } = props; const { canBeMinimized = true } = props;
const [mini, setMini] = useState(false); const [mini, setMini] = useState(false);
return ( return (
......
import "./index.scss"; import "./index.scss";
import { type FC } from "react";
import { INTERNAL_Snapshot as Snapshot, proxy, useSnapshot } from "valtio"; import { INTERNAL_Snapshot as Snapshot, proxy, useSnapshot } from "valtio";
import { sendSelectMultiResponse, sendSelectSingleResponse } from "@/api"; import { sendSelectMultiResponse, sendSelectSingleResponse } from "@/api";
import { type Option, SelectCardsModal } from "../SelectCardsModal"; import {
type Option,
SelectCardsModal,
type SelectCardsModalProps,
} from "../SelectCardsModal";
const CANCEL_RESPONSE = -1; const CANCEL_RESPONSE = -1;
const FINISH_RESPONSE = -1; const FINISH_RESPONSE = -1;
const defaultProps = { const defaultProps: Omit<
SelectCardsModalProps,
"onSubmit" | "onCancel" | "onFinish"
> = {
isOpen: false, isOpen: false,
isChain: false, isChain: false,
min: 0, min: 0,
...@@ -27,7 +33,7 @@ const defaultProps = { ...@@ -27,7 +33,7 @@ const defaultProps = {
const localStore = proxy(defaultProps); const localStore = proxy(defaultProps);
export const SelectActionsModal: FC = () => { export const SelectActionsModal: React.FC = () => {
const { const {
isOpen, isOpen,
isChain, isChain,
...@@ -44,12 +50,13 @@ export const SelectActionsModal: FC = () => { ...@@ -44,12 +50,13 @@ export const SelectActionsModal: FC = () => {
} = useSnapshot(localStore); } = useSnapshot(localStore);
const onSubmit = (options: Snapshot<Option[]>) => { const onSubmit = (options: Snapshot<Option[]>) => {
const values = options.map((option) => option.response); const values = options.map((option) => option.response!);
if (isChain) { if (isChain) {
sendSelectSingleResponse(values[0]); sendSelectSingleResponse(values[0]);
} else { } else {
sendSelectMultiResponse(values); sendSelectMultiResponse(values);
} }
console.log("here");
rs(); rs();
}; };
......
...@@ -2,19 +2,19 @@ import "./index.scss"; ...@@ -2,19 +2,19 @@ import "./index.scss";
import { CheckCard } from "@ant-design/pro-components"; import { CheckCard } from "@ant-design/pro-components";
import { Button, Segmented, Space, Tooltip } from "antd"; import { Button, Segmented, Space, Tooltip } from "antd";
import { type FC, useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { INTERNAL_Snapshot as Snapshot, useSnapshot } from "valtio"; import { INTERNAL_Snapshot as Snapshot, useSnapshot } from "valtio";
import type { CardMeta, ygopro } from "@/api"; import type { CardMeta, ygopro } from "@/api";
import { fetchStrings } from "@/api"; import { fetchStrings } from "@/api";
import { matStore } from "@/stores"; import { CardType, matStore } from "@/stores";
import { YgoCard } from "@/ui/Shared"; import { YgoCard } from "@/ui/Shared";
import { groupBy } from "../../utils"; import { groupBy } from "../../utils";
import { showCardModal } from "../CardModal"; import { showCardModal } from "../CardModal";
import { NeosModal } from "../NeosModal"; import { NeosModal } from "../NeosModal";
export const SelectCardsModal: FC<{ export interface SelectCardsModalProps {
isOpen: boolean; isOpen: boolean;
isChain: boolean; isChain: boolean;
min: number; min: number;
...@@ -30,7 +30,9 @@ export const SelectCardsModal: FC<{ ...@@ -30,7 +30,9 @@ export const SelectCardsModal: FC<{
onSubmit: (options: Snapshot<Option[]>) => void; onSubmit: (options: Snapshot<Option[]>) => void;
onCancel: () => void; onCancel: () => void;
onFinish: () => void; onFinish: () => void;
}> = ({ }
export const SelectCardsModal: React.FC<SelectCardsModalProps> = ({
isOpen, isOpen,
isChain, isChain,
min, min,
...@@ -119,7 +121,7 @@ export const SelectCardsModal: FC<{ ...@@ -119,7 +121,7 @@ export const SelectCardsModal: FC<{
<Button <Button
type="primary" type="primary"
disabled={!submitable} disabled={!submitable}
onClick={() => onSubmit(mustSelects)} onClick={() => onSubmit([...mustSelects, ...result])}
> >
{submitText} {submitText}
</Button> </Button>
...@@ -141,7 +143,10 @@ export const SelectCardsModal: FC<{ ...@@ -141,7 +143,10 @@ export const SelectCardsModal: FC<{
options[0] === selectedZone && ( options[0] === selectedZone && (
<div className="checkcard-container" key={i}> <div className="checkcard-container" key={i}>
<CheckCard.Group <CheckCard.Group
onChange={setResult as any} onChange={(res) => {
console.log("setresult", res);
setResult((single ? [res] : res) as any);
}}
// TODO 考虑如何设置默认值,比如只有一个的,就直接选中 // TODO 考虑如何设置默认值,比如只有一个的,就直接选中
multiple={!single} multiple={!single}
style={{ style={{
...@@ -197,7 +202,7 @@ export const SelectCardsModal: FC<{ ...@@ -197,7 +202,7 @@ export const SelectCardsModal: FC<{
}; };
/** 选择区域 */ /** 选择区域 */
const Selector: FC<{ const Selector: React.FC<{
zoneOptions: { zoneOptions: {
value: ygopro.CardZone; value: ygopro.CardZone;
label: string; label: string;
...@@ -226,5 +231,7 @@ export interface Option { ...@@ -226,5 +231,7 @@ export interface Option {
// 作为素材的cost,比如同调召唤的星级 // 作为素材的cost,比如同调召唤的星级
level1?: number; level1?: number;
level2?: number; level2?: number;
response: number; response?: number;
// 便于直接返回这个信息
card?: CardType;
} }
// import "./index.scss"; // import "./index.scss";
import { type FC } from "react";
import { INTERNAL_Snapshot as Snapshot, proxy, useSnapshot } from "valtio"; import { INTERNAL_Snapshot as Snapshot, proxy, useSnapshot } from "valtio";
import { type Option, SelectCardsModal } from "../SelectCardsModal"; import { type Option, SelectCardsModal } from "../SelectCardsModal";
...@@ -12,7 +10,7 @@ const defaultProps = { ...@@ -12,7 +10,7 @@ const defaultProps = {
const localStore = proxy(defaultProps); const localStore = proxy(defaultProps);
export const SimpleSelectCardsModal: FC = () => { export const SimpleSelectCardsModal: React.FC = () => {
const { isOpen, selectables } = useSnapshot(localStore); const { isOpen, selectables } = useSnapshot(localStore);
return ( return (
<SelectCardsModal <SelectCardsModal
...@@ -28,7 +26,7 @@ export const SimpleSelectCardsModal: FC = () => { ...@@ -28,7 +26,7 @@ export const SimpleSelectCardsModal: FC = () => {
finishable={false} finishable={false}
totalLevels={1} totalLevels={1}
overflow overflow
onSubmit={(options) => rs(options)} onSubmit={rs}
onFinish={() => rs([])} onFinish={() => rs([])}
onCancel={() => rs([])} onCancel={() => rs([])}
/> />
...@@ -37,11 +35,14 @@ export const SimpleSelectCardsModal: FC = () => { ...@@ -37,11 +35,14 @@ export const SimpleSelectCardsModal: FC = () => {
let rs: (options: Snapshot<Option[]>) => void = () => {}; let rs: (options: Snapshot<Option[]>) => void = () => {};
export const displaySimpleSelectActionsModal = async ( export const displaySimpleSelectCardsModal = async (
args: Omit<typeof defaultProps, "isOpen"> args: Omit<typeof defaultProps, "isOpen">
) => { ) => {
localStore.isOpen = true; localStore.isOpen = true;
localStore.selectables = args.selectables; localStore.selectables = args.selectables;
await new Promise<Snapshot<Option[]>>((resolve) => (rs = resolve)); // 等待在组件内resolve const res = await new Promise<Snapshot<Option[]>>(
(resolve) => (rs = resolve)
); // 等待在组件内resolve
localStore.isOpen = false; localStore.isOpen = false;
return res;
}; };
...@@ -8,5 +8,6 @@ export * from "./HintNotification"; ...@@ -8,5 +8,6 @@ export * from "./HintNotification";
export * from "./OptionModal"; export * from "./OptionModal";
export * from "./PositionModal"; export * from "./PositionModal";
export * from "./SelectActionsModal"; export * from "./SelectActionsModal";
export * from "./SimpleSelectCardsModal";
export * from "./SortCardModal"; export * from "./SortCardModal";
export * from "./YesNoModal"; export * from "./YesNoModal";
import "./index.scss"; import "./index.scss";
import classnames from "classnames"; import classnames from "classnames";
import { type CSSProperties, type FC } from "react"; import { type CSSProperties } from "react";
import { type INTERNAL_Snapshot as Snapshot, useSnapshot } from "valtio"; import { type INTERNAL_Snapshot as Snapshot, useSnapshot } from "valtio";
import { sendSelectPlaceResponse, ygopro } from "@/api"; import { sendSelectPlaceResponse, ygopro } from "@/api";
...@@ -31,7 +31,7 @@ const BgDisabledStyle = { ...@@ -31,7 +31,7 @@ const BgDisabledStyle = {
)`, )`,
}; };
const BgExtraRow: FC<{ const BgExtraRow: React.FC<{
meSnap: Snapshot<BlockState[]>; meSnap: Snapshot<BlockState[]>;
opSnap: Snapshot<BlockState[]>; opSnap: Snapshot<BlockState[]>;
}> = ({ meSnap, opSnap }) => { }> = ({ meSnap, opSnap }) => {
...@@ -60,7 +60,7 @@ const BgExtraRow: FC<{ ...@@ -60,7 +60,7 @@ const BgExtraRow: FC<{
); );
}; };
const BgRow: FC<{ const BgRow: React.FC<{
isSzone?: boolean; isSzone?: boolean;
opponent?: boolean; opponent?: boolean;
snap: Snapshot<BlockState[]>; snap: Snapshot<BlockState[]>;
...@@ -82,7 +82,7 @@ const BgRow: FC<{ ...@@ -82,7 +82,7 @@ const BgRow: FC<{
</div> </div>
); );
export const Bg: FC = () => { export const Bg: React.FC = () => {
const snap = useSnapshot(placeStore.inner); const snap = useSnapshot(placeStore.inner);
return ( return (
<div className="mat-bg"> <div className="mat-bg">
...@@ -106,7 +106,7 @@ const onBlockClick = (placeInteractivity: PlaceInteractivity) => { ...@@ -106,7 +106,7 @@ const onBlockClick = (placeInteractivity: PlaceInteractivity) => {
} }
}; };
const DecoTriangles: FC = () => ( const DecoTriangles: React.FC = () => (
<> <>
{Array.from({ length: 4 }).map((_, i) => ( {Array.from({ length: 4 }).map((_, i) => (
<div className="triangle" key={i} /> <div className="triangle" key={i} />
......
import "./index.scss"; import "./index.scss";
import {
DownloadOutlined,
UploadOutlined,
UpOutlined,
} from "@ant-design/icons";
import { animated, to, useSpring } from "@react-spring/web"; import { animated, to, useSpring } from "@react-spring/web";
import { Button, Dropdown, type MenuProps } from "antd"; import { Dropdown, type MenuProps } from "antd";
import classnames from "classnames"; import classnames from "classnames";
import React, { type CSSProperties, type FC, useEffect, useState } from "react"; import React, { type CSSProperties, useEffect, useState } from "react";
import { proxy, useSnapshot } from "valtio"; import { proxy, useSnapshot } from "valtio";
import { getCardStr, ygopro } from "@/api"; import { getCardStr, sendSelectIdleCmdResponse, ygopro } from "@/api";
import { type CardMeta, fetchStrings, sendSelectIdleCmdResponse } from "@/api";
import { useConfig } from "@/config"; import { useConfig } from "@/config";
import { eventbus, Task } from "@/infra"; import { eventbus, Task } from "@/infra";
import { cardStore, CardType, messageStore } from "@/stores"; import { cardStore, CardType, Interactivity, InteractType } from "@/stores";
import { import { showCardModal as displayCardModal } from "@/ui/Duel/Message/CardModal";
closeCardModal,
showCardModal as displayCardModal,
} from "@/ui/Duel/Message/CardModal";
import { YgoCard } from "@/ui/Shared"; import { YgoCard } from "@/ui/Shared";
import { displayCardListModal, displayOptionModal } from "../../Message"; import {
displayCardListModal,
displayOptionModal,
displaySimpleSelectCardsModal,
} from "../../Message";
import { interactTypeToString } from "../../utils"; import { interactTypeToString } from "../../utils";
import { import {
attack, attack,
...@@ -38,7 +33,7 @@ const NeosConfig = useConfig(); ...@@ -38,7 +33,7 @@ const NeosConfig = useConfig();
const { HAND, GRAVE, REMOVED, DECK, EXTRA, MZONE, SZONE, TZONE } = const { HAND, GRAVE, REMOVED, DECK, EXTRA, MZONE, SZONE, TZONE } =
ygopro.CardZone; ygopro.CardZone;
export const Card: FC<{ idx: number }> = React.memo(({ idx }) => { export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
const state = cardStore.inner[idx]; const state = cardStore.inner[idx];
const snap = useSnapshot(state); const snap = useSnapshot(state);
...@@ -83,7 +78,7 @@ export const Card: FC<{ idx: number }> = React.memo(({ idx }) => { ...@@ -83,7 +78,7 @@ export const Card: FC<{ idx: number }> = React.memo(({ idx }) => {
} }
}; };
// 这里后期应该去掉? // 每张卡都需要移动到初始位置
useEffect(() => { useEffect(() => {
move(state.location.zone); move(state.location.zone);
}, []); }, []);
...@@ -183,7 +178,7 @@ export const Card: FC<{ idx: number }> = React.memo(({ idx }) => { ...@@ -183,7 +178,7 @@ export const Card: FC<{ idx: number }> = React.memo(({ idx }) => {
overlayClassName="card-dropdown" overlayClassName="card-dropdown"
arrow arrow
trigger={["click"]} trigger={["click"]}
disabled={!highlight} // disabled={!highlight}
> >
<div className={classnames("card-img-wrap", { focus: classFocus })}> <div className={classnames("card-img-wrap", { focus: classFocus })}>
<YgoCard <YgoCard
...@@ -203,7 +198,7 @@ const onCardClick = (card: CardType) => { ...@@ -203,7 +198,7 @@ const onCardClick = (card: CardType) => {
// TODO: 对方的卡片/未知的卡片,点击应该是没有效果的 // TODO: 对方的卡片/未知的卡片,点击应该是没有效果的
// TODO: 同一张卡片,是否重复点击会关闭CardModal? // TODO: 同一张卡片,是否重复点击会关闭CardModal?
displayCardModal(card); displayCardModal(card);
displaySingleCardDropdown(card); handleDropdownMenu([card], false);
// 侧边栏展示超量素材信息 // 侧边栏展示超量素材信息
const overlayMaterials = cardStore.findOverlay( const overlayMaterials = cardStore.findOverlay(
...@@ -225,13 +220,9 @@ const onFieldClick = (card: CardType) => { ...@@ -225,13 +220,9 @@ const onFieldClick = (card: CardType) => {
zone: card.location.zone, zone: card.location.zone,
controller: card.location.controller, controller: card.location.controller,
}); });
// TODO: 收集这个zone的所有交互,并且在下拉菜单之中显示 // 收集这个zone的所有交互,并且在下拉菜单之中显示
const cards = cardStore.at(card.location.zone, card.location.controller); const cards = cardStore.at(card.location.zone, card.location.controller);
// 所有的交互进行聚类 handleDropdownMenu(cards, true);
const interactivities = cards.map((card) => card.idleInteractivities);
// 构建新的dropdownMenu
// 发动效果 -> 发动哪张卡?(卡选择) -> 效果选择
// 召唤、特殊召唤同理
}; };
// >>> 下拉菜单:点击动作 >>> // >>> 下拉菜单:点击动作 >>>
...@@ -246,35 +237,6 @@ type DropdownItem = NonNullable<MenuProps["items"]>[number] & { ...@@ -246,35 +237,6 @@ type DropdownItem = NonNullable<MenuProps["items"]>[number] & {
}; };
const dropdownMenu = proxy({ value: [] as DropdownItem[] }); const dropdownMenu = proxy({ value: [] as DropdownItem[] });
const displaySingleCardDropdown = (card: CardType) => {
const interactivies = card.idleInteractivities.map((interactivity) => ({
desc: interactTypeToString(interactivity.interactType),
response: interactivity.response,
effectCode: interactivity.activateIndex,
}));
const EFFECT_ACTIVATION_DESC = "发动效果";
const nonEffectInteractivies = interactivies.filter(
(item) => item.desc !== EFFECT_ACTIVATION_DESC
);
const effectInteractivies = interactivies.filter(
(item) => item.desc === EFFECT_ACTIVATION_DESC
);
dropdownMenu.value = nonEffectInteractivies.map(
({ desc, response }, key) => ({
key,
label: desc,
onClick: () => sendSelectIdleCmdResponse(response),
})
);
if (effectInteractivies.length) {
dropdownMenu.value.push({
key: dropdownMenu.value.length,
label: EFFECT_ACTIVATION_DESC,
onClick: () => handleEffectActivation(effectInteractivies, card.meta),
});
}
};
const handleEffectActivation = ( const handleEffectActivation = (
effectInteractivies: Interactivy[], effectInteractivies: Interactivy[],
meta?: any // FIXME: meta的类型 meta?: any // FIXME: meta的类型
...@@ -298,4 +260,95 @@ const handleEffectActivation = ( ...@@ -298,4 +260,95 @@ const handleEffectActivation = (
displayOptionModal(options); // 主动发动效果,所以不需要await,但是以后可能要留心 displayOptionModal(options); // 主动发动效果,所以不需要await,但是以后可能要留心
} }
}; };
// 发动效果
// 1. 下拉菜单里面选择[召唤 / 特殊召唤 /.../效果发动]
// 2. 如果是非效果发动,那么直接选择哪张卡(单张卡直接选择那张)
// 3. 如果是效果发动,那么选择哪张卡,然后选择效果
const handleDropdownMenu = (cards: CardType[], isField: boolean) => {
console.log("here");
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 {
// 场地: 选择卡片
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 {
// 场地: 选择卡片
const option = await displaySimpleSelectCardsModal({
selectables: cards.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);
};
// <<< 下拉菜单 <<< // <<< 下拉菜单 <<<
import "./index.scss"; import "./index.scss";
import type { FC, PropsWithChildren } from "react";
import { useSnapshot } from "valtio"; import { useSnapshot } from "valtio";
import { cardStore } from "@/stores"; import { cardStore } from "@/stores";
import { Bg } from "../Bg"; import { Bg } from "../Bg";
import { Card } from "../Card"; import { Card } from "../Card";
import { matConfig, toCssProperties } from "../utils";
// 后面再改名 // 后面再改名
export const Mat: FC = () => { export const Mat: React.FC = () => {
const snap = useSnapshot(cardStore.inner); const snap = useSnapshot(cardStore.inner);
return ( return (
<section <section
...@@ -31,12 +29,12 @@ export const Mat: FC = () => { ...@@ -31,12 +29,12 @@ export const Mat: FC = () => {
); );
}; };
const Plane: FC<PropsWithChildren> = ({ children }) => ( const Plane: React.FC<React.PropsWithChildren> = ({ children }) => (
<div id="camera"> <div id="camera">
<div id="plane">{children}</div> <div id="plane">{children}</div>
</div> </div>
); );
const CardContainer: FC<PropsWithChildren> = ({ children }) => ( const CardContainer: React.FC<React.PropsWithChildren> = ({ children }) => (
<div className="mat-card-container">{children}</div> <div className="mat-card-container">{children}</div>
); );
import "./index.scss"; import "./index.scss";
import classNames from "classnames"; import classNames from "classnames";
import { CSSProperties, type FC, useMemo } from "react"; import { CSSProperties, useMemo } from "react";
import { useConfig } from "@/config"; import { useConfig } from "@/config";
...@@ -14,7 +14,7 @@ interface Props { ...@@ -14,7 +14,7 @@ interface Props {
onClick?: () => void; onClick?: () => void;
} }
export const YgoCard: FC<Props> = (props) => { export const YgoCard: React.FC<Props> = (props) => {
const { const {
className, className,
code = 0, code = 0,
......
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