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