diff --git a/components/ClientOnlyPortal.tsx b/components/ClientOnlyPortal.tsx
new file mode 100644
index 0000000..d28aa2a
--- /dev/null
+++ b/components/ClientOnlyPortal.tsx
@@ -0,0 +1,19 @@
+import { useRef, useEffect, useState, ReactNode } from 'react';
+import { createPortal } from 'react-dom';
+
+interface Props {
+ children: ReactNode
+ selector: string
+}
+
+export default function ClientOnlyPortal({ children, selector }: Props) {
+ const ref = useRef();
+ const [mounted, setMounted] = useState(false);
+
+ useEffect(() => {
+ ref.current = document.querySelector(selector);
+ setMounted(true);
+ }, [selector]);
+
+ return mounted ? createPortal(children, ref.current!) : null;
+}
\ No newline at end of file
diff --git a/components/modal.tsx b/components/modal.tsx
new file mode 100644
index 0000000..7e1397d
--- /dev/null
+++ b/components/modal.tsx
@@ -0,0 +1,14 @@
+import { createPortal } from "react-dom";
+
+import ClientOnlyPortal from "./ClientOnlyPortal";
+
+export default function Modal({open}: { open: Function }) {
+ return (
+
+
+ Hello
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/pages/_document.tsx b/pages/_document.tsx
new file mode 100644
index 0000000..b855d0f
--- /dev/null
+++ b/pages/_document.tsx
@@ -0,0 +1,17 @@
+import { Html, Head, Main, NextScript } from 'next/document';
+
+export default function Document() {
+ return (
+
+
+ Magic Card Valuator
+
+
+
+ {/* Here we will mount our modal portal */}
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/pages/index.tsx b/pages/index.tsx
index aff3319..8ea7123 100644
--- a/pages/index.tsx
+++ b/pages/index.tsx
@@ -5,25 +5,31 @@ import styles from '../styles/Home.module.css';
import React, { useEffect, useState, useMemo } from 'react';
import { debounce } from 'lodash';
+import Modal from '../components/modal';
+
export default function Home() {
const [search, setSearch] = useState('');
+ const [selectedCard, setSelectedCard] = useState({});
const [cards, setCards] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(false);
const [queryParams, setQueryParams] = useState('');
+ const [modalOpen, setModalOpen] = useState(false);
const searchForCard = async (event: React.ChangeEvent) => {
event.stopPropagation();
setSearch(event.target.value);
setLoading(true);
- await fetch(`https://api.scryfall.com/cards/search?q=${encodeURIComponent(event.target.value)}`)
+ if( event.target.value !== '' ) {
+ await fetch(`https://api.scryfall.com/cards/search?q=${encodeURIComponent(event.target.value)}`)
.then( res => res.json() )
.then( ({data}) => {
setCards(data);
setLoading(false);
})
- .catch( err => console.log(err) );
+ .catch( err => setError(err) );
+ }
};
const debounceSearch = useMemo( () => debounce(searchForCard, 300), [search] );
@@ -34,39 +40,48 @@ export default function Home() {
return (
-
-
Magic Card Valuator
-
-
-
+
+ {error}
+
+
+
-
-
- {loading && !cards.length ?
-
- :
- cards && cards.map( (card: any) => )
- }
-
+
+ { cards?.length === 0 || !cards ?
+ Please search for a card
+ :
+
+ {loading && search !== "" ?
+
+ :
+ cards && cards.map( (card: any) => {
+ setSelectedCard(card);
+ setModalOpen(true);
+ }} className="cursor-pointer hover:scale-110 transition-transform duration-150 ease-in-out w-full sm:w-1/2 md:w-1/3 lg:w-1/4 p-2" key={card.id} alt={`${card.name} Card`} src={card.image_uris?.png || "https://via.placeholder.com/150" } width={100} height={100} /> )
+ }
+
+ }
+
+ {modalOpen ?
+
+ : null}
+
)
}
diff --git a/styles/Home.module.css b/styles/Home.module.css
index d1eef9f..b75a332 100644
--- a/styles/Home.module.css
+++ b/styles/Home.module.css
@@ -29,5 +29,13 @@
}
@media (prefers-color-scheme: dark) {
-
+
+}
+
+@media only screen and (min-width: 1280px) {
+ .hero {
+ max-width: 1280px;
+ margin: 2rem auto 0 auto;
+ border-radius: 2rem;
+ }
}
diff --git a/styles/globals.css b/styles/globals.css
index c56afd8..c833a58 100644
--- a/styles/globals.css
+++ b/styles/globals.css
@@ -12,11 +12,18 @@
}
}
+.modal {
+ background: #1d1d1c;
+ width: 100%;
+ height: 100%;
+}
+
.spinner {
display: inline-block;
width: 80px;
height: 80px;
}
+
.spinner:after {
content: " ";
display: block;
@@ -28,6 +35,7 @@
border-color: #fff transparent #fff transparent;
animation: lds-dual-ring 1.2s linear infinite;
}
+
@keyframes lds-dual-ring {
0% {
transform: rotate(0deg);