onClick && onClick(card.id)}
+ >
{card.name}
@@ -28,4 +38,3 @@ export const Card = ({ card }: { card: CardProps }) => {
);
};
-
diff --git a/src/cards/Cards.tsx b/src/cards/Cards.tsx
index 5f2bdb7..d18cd95 100644
--- a/src/cards/Cards.tsx
+++ b/src/cards/Cards.tsx
@@ -2,7 +2,9 @@ import * as React from "react";
import { useState, useEffect, useRef } from "react";
import { Loading } from "./Loading";
import { Card } from "./Card";
-import { useInfiniteScroll } from "./infiniteScroll";
+import { JustifiedLayout, GridLayout } from "@egjs/react-infinitegrid";
+import ReactModal from "react-modal";
+import { FullCard, fullcardStyles } from "./FullCard";
import {
CardProps,
CardRequestProps,
@@ -16,54 +18,85 @@ const firstUrl = `https://api.elderscrollslegends.io/v1/cards?pageSize=${PAGE_SI
const emptyCards: CardState = {
cards: [],
- next: firstUrl
+ next: firstUrl,
+ maxCards: -1
};
export const Cards = () => {
const [cards, setCards]: CardStateHandler = useState(emptyCards);
- const [cardPage, setCardPage]: [string | null, Function] = useState(null);
const [loading, setLoading]: [boolean, Function] = useState(false);
+ const [currentCard, setCurrentCard]: [CardProps | null, Function] = useState(
+ null
+ );
- const getNextPage = () => {
- setCardPage(cards.next);
- setLoading(true);
- };
-
- let pageBottomRef = useRef(null);
- useInfiniteScroll(pageBottomRef, getNextPage);
-
- const fetchMoreCards = async () => {
- if (cards.next === null || cardPage === null) {
- return cards;
+ const loadItems = () => {
+ if (cards.next === null || cards.cards.length === cards.maxCards) {
+ return;
}
- const request = new Request(cardPage!);
- const response = await fetch(request);
- const data: CardRequestProps = await response.json();
- setCards({
- cards: cards.cards.concat(data.cards),
- next: data._links && data._links.next ? data._links.next : null
- });
- setLoading(false);
+ setLoading(true);
+ const request = new Request(cards.next);
+ fetch(request)
+ .then(response => response.json())
+ .then((data: CardRequestProps) => {
+ setCards({
+ cards: [...cards.cards, ...data.cards],
+ next: data._links && data._links.next ? data._links.next : null,
+ maxcards: data._totalCount
+ });
+ setLoading(false);
+ });
};
- if (cards.next && cardPage === null) {
- getNextPage();
- }
+ const loadMoreCards = (options: any) => {
+ if (options.startLoading !== null) {
+ options.startLoading();
+ }
+ loadItems();
+ };
- useEffect(() => {
- fetchMoreCards();
- }, [cardPage]);
+ const onLayoutComplete = (options: any) => {
+ !options.isLayout && options.endLoading();
+ };
+
+ const handleCloseCard = () => {
+ setCurrentCard(null);
+ };
+
+ const handleOpenCard = (id: string) => {
+ const card = cards.cards.find(card => card.id === id);
+ if (card) {
+ setCurrentCard(card);
+ }
+ };
return (
-
+
+
+
+
{cards.cards.map((card: CardProps, count: number) => (
-
+
))}
-
- {loading &&
}
-
+
+ {loading ?
: ""}
);
};
diff --git a/src/cards/FullCard.tsx b/src/cards/FullCard.tsx
new file mode 100644
index 0000000..b83e606
--- /dev/null
+++ b/src/cards/FullCard.tsx
@@ -0,0 +1,111 @@
+import * as React from "react";
+import { CardProps } from "./types";
+
+export const fullcardStyles = {
+ content: {
+ backgroundColor: "#0c0c0c",
+ color: "#f8f8f8"
+ }
+};
+
+export const FullCard = ({ card }: { card: CardProps | null }) => {
+ if (card === null) {
+ return <>>;
+ }
+
+ return (
+
+
+
+
{card.name}
+
{card.text}
+
+
+
+
+ Type
+ |
+ {card.type} |
+
+ Cost
+ |
+ {card.cost} |
+
+
+
+ Subtypes
+ |
+ {card.subtypes.join(", ")} |
+
+ Power
+ |
+ {card.power} |
+
+
+
+ Rarity
+ |
+ {card.rarity} |
+
+ Health
+ |
+ {card.health} |
+
+
+
+ Set
+ |
+ {card.set.name} |
+
+ Soul Summon
+ |
+ {card.soulSummon} |
+
+
+
+ Attributes
+ |
+ {card.attributes.join(", ")} |
+
+ Soul Trap
+ |
+ {card.soulTrap} |
+
+
+
+ Keywords
+ |
+ {card.keywords} |
+ |
+
+
+
+
+
+
+
+
+ Set Name:
+ |
+ {card.set.name} |
+
+
+
+ Type:
+ |
+ {card.type} |
+
+
+
+ {card.collectible ?
This card is considered collectible.
: ""}
+ {card.unique ? (
+
+ This card is unique. Unique cards may only be used once per deck.
+
+ ) : (
+ ""
+ )}
+
+
+ );
+};
diff --git a/src/cards/infiniteScroll.ts b/src/cards/infiniteScroll.ts
deleted file mode 100644
index 3f49403..0000000
--- a/src/cards/infiniteScroll.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import * as React from "react";
-import { useEffect, useCallback } from "react";
-
-// From the Smashing Magazine article,
-// https://www.smashingmagazine.com/2020/03/infinite-scroll-lazy-image-loading-react/
-
-// As this is new to me, I'm going to try to explain it;
-// IntersectionObserver takes a callback to be called when it's
-// triggred. It's trigged when the node it's been asked to observe
-// changes its relationship with another object or, without a
-// specified object, with the viewport. When the `intersectionRatio`
-// exceed zero, it is _intersecting_ the viewport, i.e. it is now
-// visible.
-
-// useCallback is a memoizer; it only updates the contents of the
-// function its wrapped if the included function changes, which
-// it shouldn't.
-
-export const useInfiniteScroll = (scrollRef: React.RefObject
, stateUpdate: Function) => {
- const scrollObserver = (node: HTMLElement) => {
- new IntersectionObserver(entries => {
- if (entries.some(en => en.intersectionRatio > 0)) {
- stateUpdate();
- }
- }).observe(node);
- };
-
- useEffect(() => {
- if (scrollRef.current) {
- scrollObserver(scrollRef.current);
- }
- }, [scrollObserver, scrollRef]);
-}
diff --git a/src/cards/types.ts b/src/cards/types.ts
index 254c764..840bbf7 100644
--- a/src/cards/types.ts
+++ b/src/cards/types.ts
@@ -9,11 +9,14 @@ export interface CardProps {
rarity: string;
type: string;
cost: number;
+ power: number;
+ health: number;
set: SetProps;
collectible: boolean;
soulSummon: number;
soulTrap: number;
text: string;
+ subtypes: string[];
attributes: string[];
keywords: string[];
unique: boolean;
@@ -33,11 +36,10 @@ export interface CardRequestProps {
_totalCount: number;
}
-
export interface CardState {
cards: CardProps[];
next: string | null;
+ maxCards: number;
}
export type CardStateHandler = [CardState, Function];
-
diff --git a/yarn.lock b/yarn.lock
index a844027..75feefb 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1166,6 +1166,40 @@
resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-10.1.0.tgz#f0950bba18819512d42f7197e56c518aa491cf18"
integrity sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg==
+"@egjs/component@^2.1.2":
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/@egjs/component/-/component-2.1.2.tgz#c466a0a6fc6ba2d479814dcbe6ee4643c25a2e5b"
+ integrity sha512-7tnPiqxbSZ0porzlm0+/O3qZdanMj0zOq0sb17wQXuaRG49XKKKJaO+SacGnZDqf308N5hzJ0m9fZ4+j+VBvXA==
+
+"@egjs/infinitegrid@^3.6.3":
+ version "3.6.3"
+ resolved "https://registry.yarnpkg.com/@egjs/infinitegrid/-/infinitegrid-3.6.3.tgz#f12f60b4d7d983a6cc0371735df69991101618cc"
+ integrity sha512-GELg3eeOjIvk6DkUB61sab2eyV8yJHB2npR7mHmp0rjGf3nJoEBR6A1FqjIrAtT+NjseBxTP2EqYRyI9CwhrIA==
+ dependencies:
+ "@egjs/component" "^2.1.2"
+ "@egjs/lazyloaded" "0.0.2"
+ "@egjs/list-differ" "^1.0.0"
+
+"@egjs/lazyloaded@0.0.2":
+ version "0.0.2"
+ resolved "https://registry.yarnpkg.com/@egjs/lazyloaded/-/lazyloaded-0.0.2.tgz#1658a2188239e1c5e9dee71734830bcca6625388"
+ integrity sha512-UWgJCDHxsEP/sF+ztwl8F79rP1RHmlwG5ur0fFUT6RgaqKN6aQTnuYm9G/MQB48mWpw/tsY80Ha7xhvhryl+FA==
+ dependencies:
+ "@egjs/component" "^2.1.2"
+
+"@egjs/list-differ@^1.0.0":
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/@egjs/list-differ/-/list-differ-1.0.0.tgz#2277aff52e3e4bd9318d5c30ffc3ba3b6216f05e"
+ integrity sha512-HsbMKc0ZAQH+EUeCmI/2PvTYSybmkaWwakU8QGDYYgMVIg9BQ5sM0A0Nnombjxo2+JzXHxmH+jw//yGX+y6GYw==
+
+"@egjs/react-infinitegrid@^3.0.5":
+ version "3.0.5"
+ resolved "https://registry.yarnpkg.com/@egjs/react-infinitegrid/-/react-infinitegrid-3.0.5.tgz#be3759b219c67d7f8e487a2857ccd7d453949b26"
+ integrity sha512-Lf90xlCuThKeg9JfZEd9gdn4a5WgHaL/DmsiS/qWe3dUs7XmEolvfh3tzCP6j8APt6ezQsblr0q/DpUnG/7xkg==
+ dependencies:
+ "@egjs/infinitegrid" "^3.6.3"
+ "@egjs/list-differ" "^1.0.0"
+
"@hapi/address@2.x.x":
version "2.1.4"
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
@@ -1671,6 +1705,13 @@
dependencies:
"@types/react" "*"
+"@types/react-modal@^3.10.6":
+ version "3.10.6"
+ resolved "https://registry.yarnpkg.com/@types/react-modal/-/react-modal-3.10.6.tgz#76717220f32bc72769190692147814a540053c7e"
+ integrity sha512-XpshhwVYir1TRZ2HS5EfmNotJjB8UEC2IkT3omNtiQzROOXSzVLz5xsjwEpACP8U+PctkpfZepX+WT5oDf0a9g==
+ dependencies:
+ "@types/react" "*"
+
"@types/react@*", "@types/react@^16.9.49":
version "16.9.49"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.49.tgz#09db021cf8089aba0cdb12a49f8021a69cce4872"
@@ -4528,6 +4569,11 @@ execa@^1.0.0:
signal-exit "^3.0.0"
strip-eof "^1.0.0"
+exenv@^1.2.0:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d"
+ integrity sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=
+
exit@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
@@ -8856,7 +8902,7 @@ prompts@^2.0.1:
kleur "^3.0.3"
sisteransi "^1.0.4"
-prop-types@^15.6.2, prop-types@^15.7.2:
+prop-types@^15.5.10, prop-types@^15.6.2, prop-types@^15.7.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@@ -9082,6 +9128,21 @@ react-is@^16.12.0, react-is@^16.8.1, react-is@^16.8.4:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
+react-lifecycles-compat@^3.0.0:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
+ integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
+
+react-modal@^3.11.2:
+ version "3.11.2"
+ resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.11.2.tgz#bad911976d4add31aa30dba8a41d11e21c4ac8a4"
+ integrity sha512-o8gvvCOFaG1T7W6JUvsYjRjMVToLZgLIsi5kdhFIQCtHxDkA47LznX62j+l6YQkpXDbvQegsDyxe/+JJsFQN7w==
+ dependencies:
+ exenv "^1.2.0"
+ prop-types "^15.5.10"
+ react-lifecycles-compat "^3.0.0"
+ warning "^4.0.3"
+
react-scripts@3.4.3:
version "3.4.3"
resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-3.4.3.tgz#21de5eb93de41ee92cd0b85b0e1298d0bb2e6c51"
@@ -10990,6 +11051,13 @@ walker@^1.0.7, walker@~1.0.5:
dependencies:
makeerror "1.0.x"
+warning@^4.0.3:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
+ integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==
+ dependencies:
+ loose-envify "^1.0.0"
+
watchpack-chokidar2@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz#9948a1866cbbd6cb824dea13a7ed691f6c8ddff0"