-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The (Editor and Collection) Merge (#108)
- Loading branch information
Showing
16 changed files
with
465 additions
and
494 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
# Environment variables | ||
*.env | ||
.vscode | ||
.vs | ||
|
||
# MacOS | ||
.DS_Store | ||
|
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"javascript.validate.enable": false, | ||
"typescript.validate.enable": false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
56 changes: 56 additions & 0 deletions
56
packages/webapp/src/components/collection/cardCollectionDisplay.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import React from 'react' | ||
import Image from 'next/image' | ||
import { Card } from 'src/store/types' | ||
import { MintDeckModal } from 'src/components/modals/mintDeckModal' | ||
import { testCards } from 'src/utils/card-list' | ||
|
||
interface CardCollectionDisplayProps { | ||
cards: Card[] | ||
isHydrated: boolean | ||
setSelectedCard: (card: Card | null) => void | ||
onCardToggle: (card: Card) => void | ||
selectedCards: Card[] | ||
isEditing: boolean | ||
} | ||
|
||
const CardCollectionDisplay: React.FC<CardCollectionDisplayProps> = ({ cards, isHydrated, setSelectedCard, selectedCards, onCardToggle, isEditing }) => { | ||
return ( | ||
<> | ||
<div className="col-span-7 flex rounded-xl border overflow-y-auto"> | ||
{isHydrated && cards.length === 0 && ( | ||
<div className="flex flex-row w-full justify-center items-center"> | ||
<MintDeckModal/> | ||
</div> | ||
)} | ||
|
||
{isHydrated && cards.length > 0 && ( | ||
<div className="flex flex-wrap justify-around overflow-y-auto pb-4"> | ||
{cards.map((card, index) => ( | ||
<div | ||
key={card.id} | ||
className={`m-4 bg-slate-900/50 ${ | ||
selectedCards.some(c => c.id === card.id) ? 'shadow-highlight shadow-orange-300' : '' | ||
} hover:bg-slate-800 rounded-lg p-4 border-4 border-slate-900 grow w-[220px] max-w-[330px]`} | ||
onMouseEnter={() => setSelectedCard(card)} | ||
onClick={() => { | ||
if (isEditing) { | ||
onCardToggle(card) | ||
} | ||
}} | ||
> | ||
<Image className="aspect-square" src={testCards.find(tc => Number(tc.id) === index + 1)?.image || ""} alt={card.lore.name} width={256} height={256} /> | ||
<div className="text-center">{card.lore.name}</div> | ||
<div className="flex items-end justify-between p-2 relative"> | ||
<div className="flex items-center justify-center h-8 w-8 rounded-full bg-yellow-400 text-gray-900 font-bold text-lg absolute bottom-[-16px]">{card.stats.attack}</div> | ||
<div className="flex items-center justify-center h-8 w-8 rounded-full bg-red-600 text-gray-900 font-bold text-lg absolute bottom-[-16px] right-3">{card.stats.defense}</div> | ||
</div> | ||
</div> | ||
))} | ||
</div> | ||
)} | ||
</div> | ||
</> | ||
) | ||
} | ||
|
||
export default CardCollectionDisplay |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import React from 'react' | ||
import Link from "src/components/link" | ||
import { Deck } from 'src/store/types' | ||
import { Button } from "src/components/ui/button" | ||
|
||
interface DeckCollectionDisplayProps { | ||
decks: Deck[] | ||
onDeckSelect: (deckID: number) => void | ||
} | ||
|
||
const DeckCollectionDisplay: React.FC<DeckCollectionDisplayProps> = ({ decks, onDeckSelect }) => { | ||
return ( | ||
<div className="w-full flex flex-col items-center p-3"> | ||
{/* New Deck Button */} | ||
<div> | ||
<Button variant="secondary" className="border-2 border-yellow-500 normal-case hover:scale-105 font-fable text-xl hover:border-yellow-400"> | ||
<Link href={"/collection?newDeck=true"}> | ||
New Deck → | ||
</Link> | ||
</Button> | ||
</div> | ||
|
||
{/* Deck Buttons */} | ||
{decks.map((deck, deckID) => ( | ||
<Button variant="secondary" width="full" className="border-2 border-yellow-500 normal-case hover:scale-105 font-fable text-xl hover:border-yellow-400" | ||
key={deckID} | ||
onClick={() => onDeckSelect(deckID)} | ||
> | ||
{deck.name} | ||
</Button> | ||
))} | ||
</div> | ||
) | ||
} | ||
|
||
export default DeckCollectionDisplay |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import React, { useState } from 'react' | ||
import { Deck, Card } from 'src/store/types' | ||
import Image from 'next/image' | ||
import { testCards } from 'src/utils/card-list' | ||
import { Button } from "src/components/ui/button" | ||
|
||
interface DeckConstructionPanelProps { | ||
deck: Deck | ||
selectedCards: Card[] | ||
onCardSelect: (card: Card) => void | ||
onSave: (deck: Deck) => void | ||
onCancel: () => void | ||
} | ||
|
||
|
||
const DeckConstructionPanel : React.FC<DeckConstructionPanelProps> = ({ deck, selectedCards = [], onCardSelect, onSave, onCancel }) => { | ||
const [ deckName, setDeckName ] = useState(deck.name) | ||
const [ deckNameValid, setIsDeckNameValid ] = useState(false) | ||
|
||
const nameValid = (name: string) => name.trim().length > 0 | ||
|
||
const handleDeckNameChange = (event: React.ChangeEvent<HTMLInputElement>) => { | ||
const newName = event.target.value | ||
setDeckName(event.target.value) | ||
setIsDeckNameValid(nameValid(newName)) | ||
} | ||
|
||
const handleSave = () => { | ||
if(!nameValid(deckName)) return | ||
|
||
const newDeck = { | ||
name: deckName.trim(), | ||
cards: selectedCards | ||
} | ||
|
||
onSave(newDeck) | ||
} | ||
|
||
return ( | ||
<div className="flex flex-col items-center w-full p-3 overflow-y-auto overflow-x-hidden"> | ||
{/* Deck Name Input */} | ||
<div className="flex flex-wrap gap-2 justify-center w-full"> | ||
<input | ||
type="text" | ||
value={deckName} | ||
onChange={handleDeckNameChange} | ||
style={{ outline: deckNameValid ? "none" : "2px solid red" }} | ||
className="flex-shrink min-w-0 px-2 py-2 border rounded-md text-black bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent m-1.5 placeholder-gray-700 flex-basis[auto] max-w-full" | ||
placeholder="Deck name" | ||
/> | ||
</div> | ||
|
||
{/* Save and Cancel Buttons */} | ||
<div className="flex flex-wrap gap-2 justify-center w-full"> | ||
<Button variant="secondary" className="border-2 border-yellow-500 normal-case hover:scale-105 font-fable text-xl hover:border-yellow-400" onClick={handleSave}> | ||
✓Save | ||
</Button> | ||
<Button variant="secondary" className="border-2 border-yellow-500 normal-case hover:scale-105 font-fable text-xl hover:border-yellow-400" onClick={onCancel}> | ||
✕Cancel | ||
</Button> | ||
</div> | ||
|
||
{/* List of Cards in the Deck */} | ||
<div className="mt-4 w-full"> | ||
{selectedCards.length > 0 ? ( | ||
selectedCards.map((card, index) => ( | ||
<div | ||
key={index} | ||
className="p-2 cursor-pointer hover:bg-gray-100" | ||
onClick={() => onCardSelect(card)} | ||
> | ||
<div className="flex items-center space-x-3"> | ||
<Image src={testCards.find(tc => tc.id === Number(card.id))?.image || '/card_art/1.jpg'} alt="Card art" width={40} height={40} className="object-cover rounded-full" /> | ||
<span className="font-medium">{card.lore.name}</span> | ||
</div> | ||
</div> | ||
)) | ||
) : ( | ||
<div className="p-4 text-center text-gray-300"> | ||
Click on cards to add them to the deck. | ||
</div> | ||
)} | ||
</div> | ||
</div> | ||
) | ||
} | ||
|
||
export default DeckConstructionPanel |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import React from 'react' | ||
import Image from 'next/image' | ||
import { Card } from 'src/store/types' | ||
|
||
interface FilterPanelProps { | ||
effects: string[] | ||
types: string[] | ||
effectMap: { [key: string]: boolean } | ||
typeMap: { [key: string]: boolean } | ||
handleEffectClick: (index: number) => void | ||
handleTypeClick: (index: number) => void | ||
handleInputChange: (event: React.ChangeEvent<HTMLInputElement>) => void | ||
selectedCard: Card | null | ||
} | ||
|
||
const FilterPanel: React.FC<FilterPanelProps> = ({ | ||
effects, | ||
types, | ||
effectMap, | ||
typeMap, | ||
handleEffectClick, | ||
handleTypeClick, | ||
handleInputChange, | ||
selectedCard | ||
}) => { | ||
const cardName = selectedCard?.lore.name || "Select a card" | ||
const cardFlavor = selectedCard?.lore.flavor || "Select a card to see its details" | ||
|
||
return ( | ||
<div className="flex col-span-3 rounded-xl border overflow-y-auto"> | ||
<div className="overflow-y-auto"> | ||
{/* Search */} | ||
<h2 className="text-2xl font-bold text-white m-1.5">Search</h2> | ||
<div> | ||
<input | ||
type="text" | ||
onChange={handleInputChange} | ||
className="px-4 py-2 border rounded-md text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent m-1.5" | ||
placeholder="Search by name" /> | ||
</div> | ||
|
||
{/* Effects */} | ||
<h3 className="text-xl font-bold text-white m-1.5">Effects</h3> | ||
<div className="flex flex-wrap gap-2"> | ||
{effects.map((effect, index) => ( | ||
<button | ||
key={index} | ||
onClick={() => handleEffectClick(index)} | ||
className={`text-white font-bold py-2 px-2 rounded m-1.5 ${effectMap[effect] ? 'bg-purple-900' : 'bg-gray-500'}`}> | ||
{effect} | ||
</button>) | ||
)} | ||
</div> | ||
|
||
{/* Types */} | ||
<h3 className="text-xl font-bold text-white m-1">Types</h3> | ||
<div className="flex flex-wrap gap-2"> | ||
{types.map((type, index) => ( | ||
<button | ||
key={index} | ||
onClick={() => handleTypeClick(index)} | ||
className={`text-white font-bold py-2 px-2 rounded m-1 ${typeMap[type] ? 'bg-purple-900' : 'bg-gray-500'}`}> | ||
{type} | ||
</button>) | ||
)} | ||
</div> | ||
|
||
{/* todo @eviterin: makes sense to add a filter for the card collection display to only show one of each card. */} | ||
|
||
{/* Selected Card Display */} | ||
<div className="pb-5"> | ||
<h2 className="text-3xl font-bold text-white m-1.5">Card details</h2> | ||
<div className="m-4 bg-slate-900/50 rounded-lg p-4 border-4 border-slate-900"> | ||
<Image src="/card_art/0.jpg" alt={cardName} width={256} height={256} className="m-auto" /> | ||
<div className="text-center">{cardName}</div> | ||
</div> | ||
<div className="text-center m-2">{cardFlavor}</div> | ||
</div> | ||
</div> | ||
</div> | ||
) | ||
} | ||
|
||
export default FilterPanel |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import React from "react" | ||
import { useRouter } from "next/router" | ||
import Link from "next/link" | ||
|
||
interface QueryParamLinkProps { | ||
children: React.ReactNode | ||
href: string | ||
} | ||
|
||
/** | ||
* A Link component wrapper that appends a 'index' query parameter to the URL in development mode. | ||
* This is used to persist state across navigation during testing. | ||
*/ | ||
const QueryParamLink : React.FC<QueryParamLinkProps> = ({ children, href }) => { | ||
const router = useRouter() | ||
|
||
let url = href | ||
|
||
if (process.env.NODE_ENV === "development") { | ||
const index = parseInt(router.query.index as string) | ||
if (index !== undefined && !isNaN(index) && 0 <= index && index <= 9) | ||
url += (url.includes("?") ? "&" : "?") + `index=${index}` | ||
} | ||
return ( | ||
<Link href={url}> | ||
{children} | ||
</Link> | ||
) | ||
} | ||
|
||
export default QueryParamLink |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.