diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..adab642 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["wallabyjs.quokka-vscode"] +} diff --git a/src/App.tsx b/src/App.tsx index 175f226..ad25371 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,17 +5,17 @@ import Deck from './gameSession/deck/Deck'; import Btns from './gameSession/footer/Btns'; import About from './about/About'; import DebugVisual from './dev/debugVisual/DebugVisual'; -import type { CardType, DragsStatusType, DevSpeedControlType } from './types/types'; +import type { CardType, DragsStatusType, DevSpeedControlType, BtnType } from './types/types'; import './App.css'; function App() { - const [btns, btnsSet] = useState<{ left: boolean; right: boolean; back: boolean; flip: boolean }>({ - left: false, - right: false, - back: false, - flip: false, - }); + const btnHndlr = (action: BtnType): void => { + deckBtnHndlrRef.current?.(action); + }; + + const deckBtnHndlrRef = useRef<((action: BtnType) => void) | null>(null); + const [devDeckRest, devDeckRestSet] = useState(0); const [devDragsStatus, setDevDragsStatus] = useState([]); const [devDeckVisible, devDeckVisibleSet] = useState([]); @@ -29,8 +29,9 @@ function App() {
{ + deckBtnHndlrRef.current = hndlr; + }} devDeckRestSet={devDeckRestSet} devDeckVisibleSet={devDeckVisibleSet} setDevDragsStatus={setDevDragsStatus} @@ -38,7 +39,7 @@ function App() { />
- +
diff --git a/src/components/draggable/Draggable.tsx b/src/components/draggable/Draggable.tsx index 474ab63..0c4560f 100644 --- a/src/components/draggable/Draggable.tsx +++ b/src/components/draggable/Draggable.tsx @@ -63,7 +63,7 @@ const Draggable = ({ const xyMove = useRef({ x: 0, y: 0 }); const xyPrev = useRef({ x: 0, y: 0 }); - const [xy, setXy] = useState(card.comeBack ? card.lastPos : { x: 0, y: 0 }); + const [xy, setXy] = useState(card.lastPos ? card.lastPos : { x: 0, y: 0 }); const xyRef = useRef(xy); const dragHistory = React.useRef<{ pos: XYType; time: number }[]>([]); @@ -142,7 +142,7 @@ const Draggable = ({ if ( Math.abs(xyRef.current.x) > window.innerWidth * 0.5 - window.innerWidth * 0.1 && !card?.btnLR && - !card?.comeBack + !card?.lastPos ) { if (isCardOut) { if (outFlag.current) return; @@ -177,7 +177,7 @@ const Draggable = ({ const fresh = [...prev]; for (let i = 0; i < fresh.length; i++) { if (fresh[i].id === card.id) { - delete fresh[i]?.comeBack; + delete fresh[i]?.lastPos; delete fresh[i]?.btnLR; } } @@ -208,7 +208,7 @@ const Draggable = ({ const fresh = [...prev]; for (let i = 0; i < fresh.length; i++) { if (fresh[i].id === card.id) { - delete fresh[i]?.comeBack; + delete fresh[i]?.lastPos; delete fresh[i]?.btnLR; } } @@ -225,7 +225,7 @@ const Draggable = ({ return { x: prev.x * speed, y: prev.y * speed }; }); - if (comeBackFlag.current && !card?.comeBack) { + if (comeBackFlag.current && !card?.lastPos) { //? DEV devChangeStatus(setDevDragsStatus, card, 'backToDeck', dragCountRef); @@ -289,7 +289,6 @@ const Draggable = ({ delete deckRef.current[cardIndex]?.btnLR; delete deckRef.current[cardIndex]?.lastPos; - delete deckRef.current[cardIndex]?.comeBack; const pickedToTop = () => { const deck = deckRef.current; diff --git a/src/gameSession/deck/Deck.module.css b/src/gameSession/deck/Deck.module.css index 89cea0d..9405154 100644 --- a/src/gameSession/deck/Deck.module.css +++ b/src/gameSession/deck/Deck.module.css @@ -12,7 +12,6 @@ -webkit-user-select: none; -webkit-tap-highlight-color: transparent; /* 🧽 убирает синюю подсветку при тапе */ -webkit-touch-callout: none; /* ❌ запрещает контекстное меню на iOS */ - z-index: 1; } diff --git a/src/gameSession/deck/Deck.tsx b/src/gameSession/deck/Deck.tsx index 9e1bf50..bb2c0ea 100644 --- a/src/gameSession/deck/Deck.tsx +++ b/src/gameSession/deck/Deck.tsx @@ -12,17 +12,17 @@ import type { import gameDeck from '../../features/gameDeck'; import { nameFromImg, devChangeStatus } from '../../dev/debugVisual/features'; import { dragCountUpdate } from '../../components/draggable/features/actions'; +import type { BtnType } from '../../types/types'; type DeckProps = { - btns: { left: boolean; right: boolean; back: boolean; flip: boolean }; - btnsSet: React.Dispatch>; + setBtnHndlr: (handler: (btn: BtnType) => void) => void; devDeckRestSet: React.Dispatch>; devDeckVisibleSet: React.Dispatch>; setDevDragsStatus: React.Dispatch>; devSpeed: React.RefObject; }; -const Deck = ({ btns, btnsSet, devDeckRestSet, devDeckVisibleSet, setDevDragsStatus, devSpeed }: DeckProps) => { +const Deck = ({ setBtnHndlr, devDeckRestSet, devDeckVisibleSet, setDevDragsStatus, devSpeed }: DeckProps) => { const dragCountLimit = useRef(5); const [dragCount, setDragCount] = useState(5); const dragCountRef = useRef(5); @@ -36,9 +36,6 @@ const Deck = ({ btns, btnsSet, devDeckRestSet, devDeckVisibleSet, setDevDragsSta const draggedId = useRef>(new Set([])); - // Я могу вынести glow(right/wrong) на урвовень deck и управлять им by max и min value of xyDragged(всех карт в !== sleep) - const xyDragged = useRef([]); - // Удаление карточки: //* I. Clear // 0. Add field lastPos @@ -54,6 +51,7 @@ const Deck = ({ btns, btnsSet, devDeckRestSet, devDeckVisibleSet, setDevDragsSta }; const counter1Ref: React.RefObject<(n?: number) => number> = useRef(closure()); const counter2Ref: React.RefObject<(n?: number) => number> = useRef(closure()); + const counter3Ref: React.RefObject<(n?: number) => number> = useRef(closure()); // console.log('🍒', counter2Ref.current(), 'Deck Re-Render'); const cardOutHndlr = (cardId: number, direction: 'string', lastPos: XYType, codePoint: string) => { @@ -97,7 +95,7 @@ const Deck = ({ btns, btnsSet, devDeckRestSet, devDeckVisibleSet, setDevDragsSta const prevStatus = prevJ.find((j) => j.id === card.id)?.status; let status: DragsStatusStatusType = 'sleep'; - if (card.comeBack) status = 'comeBack'; + if (card?.lastPos) status = 'comeBack'; else if (prevStatus) status = prevStatus; return { id: card.id, dragNum: i, card: nameFromImg([card], 0), status }; }); @@ -107,14 +105,47 @@ const Deck = ({ btns, btnsSet, devDeckRestSet, devDeckVisibleSet, setDevDragsSta }); }; + const btnSwipeHndlr = (direction: 'l' | 'r') => { + let notRLDraged: number = 0; + + for (let i = 0; i < draggedId.current.size; i++) { + if (!deckRef.current[deckRef.current.length - 1 - i]?.btnLR) notRLDraged++; + } + + const indexTop = deckRef.current.length - 1 - (draggedId.current.size - notRLDraged); + const topCard = deckRef.current[indexTop]; + + if (!topCard || topCard?.btnLR || topCard?.lastPos) return; + + draggedId.current.add(topCard.id); + topCard.btnLR = direction; + setDeck(deckRef.current); + + dragCountUpdate(draggedId, dragCountLimit, dragCountRef, setDragCount); + + //? DEV + setDevDragsStatus((prevJ) => { + const fresh: DragsStatusType[] = deckRef.current.slice(-dragCountRef.current).map((card, i) => { + const prevStatus = prevJ.find((j) => j.id === card.id)?.status; + + let status: DragsStatusStatusType = 'sleep'; + if (card.lastPos) status = 'comeBack'; + else if (card.id === topCard.id) status = 'fling'; + else if (prevStatus) status = prevStatus; + return { id: card.id, dragNum: i, card: nameFromImg([card], 0), status }; + }); + return fresh; + }); + }; + const hasInitialized = useRef(false); useEffect(() => { if (hasInitialized.current) return; hasInitialized.current = true; + // deck Initialization const deckFirst = gameDeck(); setDeck(deckFirst); - for (let i = 0; i < dragCountLimit.current; i++) { const indexTop = deckFirst.length - dragCountLimit.current + i; const cardName = nameFromImg(deckFirst, indexTop); @@ -122,97 +153,66 @@ const Deck = ({ btns, btnsSet, devDeckRestSet, devDeckVisibleSet, setDevDragsSta return [...prev, { id: deckFirst[indexTop].id, dragNum: prev.length, card: cardName, status: 'sleep' }]; }); } - }, []); - - useEffect(() => { - if (deck.length === 0) return; - const vis = deck.slice(-(dragCountLimit.current + 15)); - setDeckVisible(vis); - devDeckVisibleSet(vis); - - deckRef.current = deck; + // btns action + const btnHndlr = (btn: BtnType) => { + if (btn === 'left') btnSwipeHndlr('l'); + else if (btn === 'right') btnSwipeHndlr('r'); + else if (btn === 'back') { + if (deckHistory.current.length === 0) { + deckHistoryTop.current = null; + return; + } + if (deckRef.current.some((card) => card.id === deckHistory.current[deckHistory.current.length - 1].id)) { + console.warn('🔁 Карта уже в колоде, повторное добавление пропущено'); + return; + } + if (draggedId.current.size >= dragCountLimit.current) return; - // Dev - devDeckRestSet(deck.length); - }, [deck, dragCountLimit.current]); + deckHistoryTop.current = deckHistory.current.pop() ?? null; + const comeBackCard = { ...deckHistoryTop.current }; - useEffect(() => { - if (!btns.back) return; + setDeck((prev) => { + const freshDeck = [...prev, comeBackCard]; + deckRef.current = freshDeck; - if (deckHistory.current.length === 0) { - deckHistoryTop.current = null; - return; - } - if (deckRef.current.some((card) => card.id === deckHistory.current[deckHistory.current.length - 1].id)) { - console.warn('🔁 Карта уже в колоде, повторное добавление пропущено'); - return; - } - if (draggedId.current.size >= dragCountLimit.current) return; + draggedId.current.add(comeBackCard.id); + dragCountUpdate(draggedId, dragCountLimit, dragCountRef, setDragCount); - deckHistoryTop.current = deckHistory.current.pop() ?? null; - const comeBackCard = { ...deckHistoryTop.current, comeBack: true }; + setDevDragsStatus((prevJ) => { + const fresh: DragsStatusType[] = freshDeck.slice(-dragCountRef.current).map((card, i) => { + const prevStatus = prevJ.find((j) => j.id === card.id)?.status; - setDeck((prev) => { - const freshDeck = [...prev, comeBackCard]; - deckRef.current = freshDeck; + let status: DragsStatusStatusType = 'sleep'; + if (card?.lastPos) status = 'comeBack'; + else if (prevStatus) status = prevStatus; + return { id: card.id, dragNum: i, card: nameFromImg([card], 0), status }; + }); + return fresh; + }); - draggedId.current.add(comeBackCard.id); - dragCountUpdate(draggedId, dragCountLimit, dragCountRef, setDragCount); - - setDevDragsStatus((prevJ) => { - const fresh: DragsStatusType[] = freshDeck.slice(-dragCountRef.current).map((card, i) => { - const prevStatus = prevJ.find((j) => j.id === card.id)?.status; - - let status: DragsStatusStatusType = 'sleep'; - if (card.comeBack) status = 'comeBack'; - else if (prevStatus) status = prevStatus; - return { id: card.id, dragNum: i, card: nameFromImg([card], 0), status }; + return freshDeck; }); - return fresh; - }); - - return freshDeck; - }); + } else if (btn === 'flip') { + // console.log('flip'); + } + }; - btnsSet((prev) => ({ ...prev, back: false })); - }, [btns.back]); + setBtnHndlr(btnHndlr); + }, []); useEffect(() => { - if (!btns.left && !btns.right) return; - btnsSet((prev) => ({ ...prev, left: false, right: false })); - - let notRLDraged: number = 0; - - for (let i = 0; i < draggedId.current.size; i++) { - if (!deckRef.current[deckRef.current.length - 1 - i]?.btnLR) notRLDraged++; - } - - const indexTop = deckRef.current.length - 1 - (draggedId.current.size - notRLDraged); - const topCard = deckRef.current[indexTop]; - - if (!topCard || topCard?.btnLR || topCard?.comeBack) return; - - draggedId.current.add(topCard.id); - topCard.btnLR = btns.left ? 'l' : 'r'; - setDeck(deckRef.current); + if (deck.length === 0) return; + const vis = deck.slice(-(dragCountLimit.current + 15)); - dragCountUpdate(draggedId, dragCountLimit, dragCountRef, setDragCount); + setDeckVisible(vis); + devDeckVisibleSet(vis); - //? DEV - setDevDragsStatus((prevJ) => { - const fresh: DragsStatusType[] = deckRef.current.slice(-dragCountRef.current).map((card, i) => { - const prevStatus = prevJ.find((j) => j.id === card.id)?.status; + deckRef.current = deck; - let status: DragsStatusStatusType = 'sleep'; - if (card.comeBack) status = 'comeBack'; - else if (card.id === topCard.id) status = 'fling'; - else if (prevStatus) status = prevStatus; - return { id: card.id, dragNum: i, card: nameFromImg([card], 0), status }; - }); - return fresh; - }); - }, [btns.left, btns.right]); + // Dev + devDeckRestSet(deck.length); + }, [deck, dragCountLimit.current]); const [imgHasError, setImgHasError] = useState(false); diff --git a/src/gameSession/footer/Btns.tsx b/src/gameSession/footer/Btns.tsx index abe6e34..f754d64 100644 --- a/src/gameSession/footer/Btns.tsx +++ b/src/gameSession/footer/Btns.tsx @@ -1,20 +1,16 @@ -import { useEffect, useRef, useState } from 'react'; - +import type { BtnType } from '../../types/types'; import BtnsCss from './Btns.module.css'; type BtnsProps = { - btns: { left: boolean; right: boolean; back: boolean; flip: boolean }; - btnsSet: React.Dispatch>; + btnHndlr: (btn: BtnType) => void; }; -function Btns({ btns, btnsSet }: BtnsProps) { - // setBtns({ left: false, right: false, back: false, flip: false }); - +function Btns({ btnHndlr }: BtnsProps) { return (