Commit c92e6d78 authored by zhangshize's avatar zhangshize

feat: add mouse event on deck

parent 5ebda8bb
Pipeline #23372 canceled with stages
in 3 minutes and 2 seconds
......@@ -15,6 +15,7 @@ import {
type MenuProps,
Pagination,
Space,
message,
} from "antd";
import { isEqual } from "lodash-es";
import { type OverlayScrollbarsComponentRef } from "overlayscrollbars-react";
......@@ -26,12 +27,13 @@ import { proxy, useSnapshot } from "valtio";
import { subscribeKey } from "valtio/utils";
import { type CardMeta, searchCards } from "@/api";
import { isToken } from "@/common";
import { isExtraDeckCard, isToken } from "@/common";
import { FtsConditions } from "@/middleware/sqlite/fts";
import { deckStore, type IDeck, initStore } from "@/stores";
import {
Background,
DeckCard,
DeckCardMouseUpEvent,
DeckZone,
IconFont,
Loading,
......@@ -159,6 +161,47 @@ export const DeckEditor: React.FC<{
useEffect(() => {
iDeckToEditingDeck(deck).then(editDeckStore.set);
}, [deck]);
const handleSwitchCard = (type: Type, card: CardMeta) => {
const cardType = card.data.type ?? 0;
const isSide = type === 'side';
const targetType = isSide ?
(isExtraDeckCard(cardType) ? 'extra' : 'main') : 'side';
const { result, reason } = editDeckStore.canAdd(card, targetType, type);
if (result) {
editDeckStore.remove(type, card);
editDeckStore.add(targetType, card);
} else {
message.error(reason);
}
}
const showSelectedCard = (card: CardMeta) => {
selectedCard.id = card.id;
selectedCard.open = true;
}
const handleMouseUp = (type: 'main' | 'extra' | 'side', payload: DeckCardMouseUpEvent) => {
const { event, card } = payload;
switch (event.button) {
// 左键
case 0:
showSelectedCard(card);
break;
// 中键
case 1:
handleSwitchCard(type, card);
break;
// 右键
case 2:
editDeckStore.remove(type, card);
break;
default:
break;
}
event.preventDefault();
}
return (
<div className={styles.container}>
<Space className={styles.title}>
......@@ -215,11 +258,7 @@ export const DeckEditor: React.FC<{
editDeckStore.remove(source, card);
}
}}
onElementClick={(card) => {
selectedCard.id = card.id;
selectedCard.open = true;
}}
onElementRightClick={(card) => editDeckStore.remove(type, card)}
onElementMouseUp={(event) => handleMouseUp(type, event)}
/>
))}
</ScrollableArea>
......@@ -423,6 +462,54 @@ const SearchResults: React.FC<{
const endIndex = startIndex + itemsPerPage;
const currentData = results.slice(startIndex, endIndex);
const showSelectedCard = (card: CardMeta) => {
selectedCard.id = card.id;
selectedCard.open = true;
};
const handleAddCardToMain = (card: CardMeta) => {
const cardType = card.data.type ?? 0;
const isExtraCard = isExtraDeckCard(cardType);
const type = isExtraCard ? "extra" : "main";
const { result, reason } = editDeckStore.canAdd(card, type, "search");
if (result) {
editDeckStore.add(type, card);
} else {
message.error(reason);
}
}
const handleAddCardToSide = (card: CardMeta) => {
const { result, reason } = editDeckStore.canAdd(card, "side", "search");
if (result) {
editDeckStore.add("side", card);
} else {
message.error(reason);
}
}
/** safari 不支持 onAuxClick,所以使用 mousedown 模拟 */
const handleMouseUp = (payload: DeckCardMouseUpEvent) => {
const { event, card } = payload;
switch (event.button) {
// 左键
case 0:
showSelectedCard(card);
break;
// 中键
case 1:
handleAddCardToSide(card);
break;
// 右键
case 2:
handleAddCardToMain(card);
break;
default:
break;
}
}
return (
<>
<div className={styles["search-cards"]}>
......@@ -431,10 +518,8 @@ const SearchResults: React.FC<{
value={card}
key={card.id}
source="search"
onClick={() => {
selectedCard.id = card.id;
selectedCard.open = true;
}}
onMouseUp={handleMouseUp}
onMouseEnter={() => showSelectedCard(card)}
/>
))}
</div>
......
......@@ -10,13 +10,18 @@ import styles from "./index.module.scss";
const { assetsPath } = useConfig();
export interface DeckCardMouseUpEvent {
event: React.MouseEvent;
card: CardMeta;
}
/** 组卡页和Side页使用的单张卡片,增加了文字和禁限数量 */
export const DeckCard: React.FC<{
value: CardMeta;
source: Type | "search";
onRightClick?: () => void;
onClick?: () => void;
}> = memo(({ value, source, onRightClick, onClick }) => {
onMouseUp?: (event: DeckCardMouseUpEvent) => void;
onMouseEnter?: () => void;
}> = memo(({ value, source, onMouseUp, onMouseEnter }) => {
const ref = useRef<HTMLDivElement>(null);
const [{ isDragging }, drag] = useDrag({
type: "Card",
......@@ -33,10 +38,13 @@ export const DeckCard: React.FC<{
className={styles.card}
ref={ref}
style={{ opacity: isDragging && source !== "search" ? 0 : 1 }}
onClick={onClick}
onContextMenu={(e) => {
onMouseUp={event => onMouseUp?.({
event,
card: value,
})}
onMouseEnter={onMouseEnter}
onContextMenu={e => {
e.preventDefault();
onRightClick?.();
}}
>
{showText && <div className={styles.cardname}>{value.text.name}</div>}
......
......@@ -5,7 +5,7 @@ import { useDrop } from "react-dnd";
import { CardMeta } from "@/api";
import { DeckCard } from "../DeckCard";
import { DeckCard, DeckCardMouseUpEvent } from "../DeckCard";
import styles from "./index.module.scss";
/** 正在组卡的zone,包括main/extra/side
......@@ -24,66 +24,59 @@ export const DeckZone: React.FC<{
source: Type | "search",
destination: Type,
) => void;
onElementClick: (card: CardMeta) => void;
onElementRightClick?: (card: CardMeta) => void;
onElementMouseUp: (event: DeckCardMouseUpEvent) => void;
}> = ({
type,
cards,
canAdd,
onChange,
onElementClick,
onElementRightClick,
onElementMouseUp: onElementMouseUp,
}) => {
const { message } = App.useApp();
const [allowToDrop, setAllowToDrop] = useState(false);
const [{ isOver }, dropRef] = useDrop({
accept: ["Card"], // 指明该区域允许接收的拖放物。可以是单个,也可以是数组
// 里面的值就是useDrag所定义的type
// 当拖拽物在这个拖放区域放下时触发,这个item就是拖拽物的item(拖拽物携带的数据)
drop: ({ value, source }: { value: CardMeta; source: Type | "search" }) => {
if (type === source) return;
const { result, reason } = canAdd(value, type, source);
if (result) {
onChange(value, source, type);
} else {
message.error(reason);
}
},
hover: ({ value, source }) => {
setAllowToDrop(
type !== source ? canAdd(value, type, source).result : true,
);
},
collect: (monitor) => ({
isOver: monitor.isOver(),
}),
});
return (
<div
className={classNames(styles[type], {
[styles.over]: isOver,
[styles["not-allow-to-drop"]]: isOver && !allowToDrop,
})}
ref={dropRef}
>
<div className={styles["card-continer"]}>
{cards.map((card, i) => (
<DeckCard
value={card}
key={card.id + i + type}
source={type}
onClick={() => {
onElementClick(card);
}}
onRightClick={() => {
onElementRightClick?.(card);
}}
/>
))}
<div className={styles["editing-zone-name"]}>
{`${type.toUpperCase()}: ${cards.length}`}
const { message } = App.useApp();
const [allowToDrop, setAllowToDrop] = useState(false);
const [{ isOver }, dropRef] = useDrop({
accept: ["Card"], // 指明该区域允许接收的拖放物。可以是单个,也可以是数组
// 里面的值就是useDrag所定义的type
// 当拖拽物在这个拖放区域放下时触发,这个item就是拖拽物的item(拖拽物携带的数据)
drop: ({ value, source }: { value: CardMeta; source: Type | "search" }) => {
if (type === source) return;
const { result, reason } = canAdd(value, type, source);
if (result) {
onChange(value, source, type);
} else {
message.error(reason);
}
},
hover: ({ value, source }) => {
setAllowToDrop(
type !== source ? canAdd(value, type, source).result : true,
);
},
collect: (monitor) => ({
isOver: monitor.isOver(),
}),
});
return (
<div
className={classNames(styles[type], {
[styles.over]: isOver,
[styles["not-allow-to-drop"]]: isOver && !allowToDrop,
})}
ref={dropRef}
>
<div className={styles["card-continer"]}>
{cards.map((card, i) => (
<DeckCard
value={card}
key={card.id + i + type}
source={type}
onMouseUp={onElementMouseUp}
/>
))}
<div className={styles["editing-zone-name"]}>
{`${type.toUpperCase()}: ${cards.length}`}
</div>
</div>
</div>
</div>
);
};
);
};
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