Add zustand
This commit is contained in:
+3
-1
@@ -16,8 +16,10 @@
|
||||
"next": "13.4.19",
|
||||
"prettier": "^3.0.3",
|
||||
"react": "18.2.0",
|
||||
"react-div-100vh": "^0.7.0",
|
||||
"react-dom": "18.2.0",
|
||||
"three": "0.152.2"
|
||||
"three": "0.152.2",
|
||||
"zustand": "^4.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "20.6.0",
|
||||
|
||||
@@ -41,33 +41,15 @@ export const BanknoteInstance = ({ obverse, reverse }: CoinProps) => {
|
||||
<>
|
||||
<boxGeometry attach="geometry" args={[width, 2, 0.005]} />
|
||||
|
||||
<meshStandardMaterial
|
||||
color={"#888"}
|
||||
metalness={0}
|
||||
roughness={0.5}
|
||||
attach="material-0"
|
||||
/>
|
||||
|
||||
<meshStandardMaterial
|
||||
color={"#888"}
|
||||
metalness={0}
|
||||
roughness={0.5}
|
||||
attach="material-1"
|
||||
/>
|
||||
|
||||
<meshStandardMaterial
|
||||
color={"#888"}
|
||||
metalness={0}
|
||||
roughness={0.5}
|
||||
attach="material-2"
|
||||
/>
|
||||
|
||||
<meshStandardMaterial
|
||||
color={"#888"}
|
||||
metalness={0}
|
||||
roughness={0.5}
|
||||
attach="material-3"
|
||||
/>
|
||||
{Array.from({ length: 4 }, (_, index) => (
|
||||
<meshStandardMaterial
|
||||
key={index}
|
||||
color={"#888"}
|
||||
metalness={0}
|
||||
roughness={0.5}
|
||||
attach={"material-" + index}
|
||||
/>
|
||||
))}
|
||||
|
||||
<meshStandardMaterial
|
||||
color={"#888"}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.canvas {
|
||||
height: 100%;
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import { CoinInstances } from "./coin-instances";
|
||||
import { CoinProps } from "@/types/coin";
|
||||
import { PlaneProps, Physics, usePlane } from "@react-three/cannon";
|
||||
import { Mesh } from "three";
|
||||
import styles from "./index.module.css";
|
||||
|
||||
type Props = {
|
||||
coins: CoinProps[];
|
||||
@@ -31,8 +32,7 @@ const Lightbulb = (props: PointLightProps) => (
|
||||
);
|
||||
|
||||
export const Pile = ({ coins }: Props) => (
|
||||
<Canvas camera={{ position: [0, 16, 16] }}>
|
||||
<color attach="background" args={["#111"]} />
|
||||
<Canvas camera={{ position: [0, 16, 16] }} className={styles.canvas}>
|
||||
<ambientLight />
|
||||
|
||||
<Lightbulb position={[0, 16, 0]} />
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
.wrapper {
|
||||
position: absolute;
|
||||
inset: -25% 0 0;
|
||||
}
|
||||
|
||||
.canvas {
|
||||
height: 100%;
|
||||
}
|
||||
@@ -1,173 +1,61 @@
|
||||
import styles from "./index.module.css";
|
||||
import { CoinProps } from "@/types/coin";
|
||||
import { Canvas, MeshProps, useFrame } from "@react-three/fiber";
|
||||
import { Suspense, useCallback, useEffect, useRef, useState } from "react";
|
||||
import { CoinInstance } from "@/components/coin";
|
||||
import { Mesh } from "three";
|
||||
import { BanknoteInstance } from "@/components/banknote";
|
||||
import { Canvas } from "@react-three/fiber";
|
||||
import { useEffect } from "react";
|
||||
import { InfoBox } from "./info-box";
|
||||
import { OrbitControls } from "@react-three/drei";
|
||||
import { OrbitControls as OrbitControlsType } from "three-stdlib";
|
||||
import { Row } from "./row";
|
||||
import { useShowcaseStore } from "./store";
|
||||
|
||||
type Props = {
|
||||
category: "coins" | "banknotes";
|
||||
types: CoinProps[];
|
||||
};
|
||||
|
||||
const SpinninType = ({
|
||||
isSelected,
|
||||
isSpinning,
|
||||
category,
|
||||
typeProps,
|
||||
...props
|
||||
}: {
|
||||
typeProps: CoinProps;
|
||||
isSelected?: boolean;
|
||||
isSpinning?: boolean;
|
||||
} & MeshProps &
|
||||
Pick<Props, "category">) => {
|
||||
const ref = useRef<Mesh>(null!);
|
||||
const Instance = category === "banknotes" ? BanknoteInstance : CoinInstance;
|
||||
|
||||
useFrame((_state, delta) => {
|
||||
if (!isSelected || !isSpinning) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (category === "banknotes") {
|
||||
ref.current.rotation.x += delta;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (typeProps.orientation) {
|
||||
case "medal":
|
||||
ref.current.rotation.x += delta;
|
||||
break;
|
||||
|
||||
default:
|
||||
ref.current.rotation.z += delta;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<mesh {...props}>
|
||||
<mesh
|
||||
ref={ref}
|
||||
{...(category !== "banknotes"
|
||||
? {
|
||||
scale: [
|
||||
(typeProps.size ?? 16) / 16,
|
||||
(typeProps.thickness ?? 2) / 16,
|
||||
(typeProps.size ?? 16) / 16,
|
||||
],
|
||||
}
|
||||
: { rotation: [Math.PI / 2, Math.PI, Math.PI / 2] })}
|
||||
>
|
||||
<Instance {...typeProps} />
|
||||
</mesh>
|
||||
</mesh>
|
||||
);
|
||||
};
|
||||
|
||||
const Row = ({
|
||||
category,
|
||||
types,
|
||||
selectedIndex,
|
||||
isSpinning,
|
||||
}: Props & { selectedIndex: number; isSpinning?: boolean }) => {
|
||||
const ref = useRef<Mesh>(null!);
|
||||
const spacing = category === "banknotes" ? 8 : 6;
|
||||
|
||||
useFrame(() => {
|
||||
const xPosition = Math.round(10 * ref.current.position.x) / 10;
|
||||
const target = -selectedIndex * spacing;
|
||||
|
||||
if (xPosition > target) {
|
||||
ref.current.position.x -= Math.abs(xPosition - target) / 16;
|
||||
}
|
||||
|
||||
if (xPosition < target) {
|
||||
ref.current.position.x += Math.abs(xPosition - target) / 16;
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<mesh ref={ref}>
|
||||
{types.map(
|
||||
(type, index) =>
|
||||
Math.abs(index - selectedIndex) <= 1 && (
|
||||
<Suspense key={type.id} fallback={null}>
|
||||
<SpinninType
|
||||
category={category}
|
||||
isSpinning={isSpinning}
|
||||
isSelected={selectedIndex === index}
|
||||
typeProps={type}
|
||||
position={[index * spacing, 1, 0]}
|
||||
rotation={[Math.PI / 2, Math.PI / 2, 0]}
|
||||
/>
|
||||
</Suspense>
|
||||
),
|
||||
)}
|
||||
</mesh>
|
||||
);
|
||||
};
|
||||
|
||||
export const Showcase = (props: Props) => {
|
||||
const [selectedIndex, selectIndex] = useState(0);
|
||||
const [isSpinning, setIsSpinning] = useState(true);
|
||||
const orbitControlsRef = useRef<OrbitControlsType>(null!);
|
||||
const toggleSpin = useCallback(() => {
|
||||
setIsSpinning((value) => {
|
||||
if (!value) {
|
||||
orbitControlsRef.current?.setAzimuthalAngle(0);
|
||||
orbitControlsRef.current?.setPolarAngle(Math.PI / 2);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}, []);
|
||||
const isSpinning = useShowcaseStore((state) => state.isSpinning);
|
||||
const nextIndex = useShowcaseStore((state) => state.nextIndex);
|
||||
const previousIndex = useShowcaseStore((state) => state.previousIndex);
|
||||
const init = useShowcaseStore((state) => state.init);
|
||||
const toggleSpin = useShowcaseStore((state) => state.toggleSpin);
|
||||
const setOrbitControlsRef = useShowcaseStore(
|
||||
(state) => state.setOrbitControlsRef,
|
||||
);
|
||||
|
||||
useEffect(() => init(props), [init, props]);
|
||||
|
||||
useEffect(() => {
|
||||
const onKeyDown = ({ key }: KeyboardEvent) => {
|
||||
switch (key) {
|
||||
case "ArrowLeft":
|
||||
return selectIndex((value) => Math.max(value - 1, 0));
|
||||
return previousIndex();
|
||||
case "ArrowRight":
|
||||
return selectIndex((value) =>
|
||||
Math.min(value + 1, props.types.length - 1),
|
||||
);
|
||||
return nextIndex();
|
||||
case " ":
|
||||
return toggleSpin();
|
||||
}
|
||||
};
|
||||
document.addEventListener("keydown", onKeyDown);
|
||||
return () => document.removeEventListener("keydown", onKeyDown);
|
||||
}, [props.types.length, toggleSpin]);
|
||||
}, [nextIndex, previousIndex, toggleSpin]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Canvas>
|
||||
<color attach="background" args={["#111"]} />
|
||||
<div className={styles.wrapper}>
|
||||
<Canvas className={styles.canvas}>
|
||||
<ambientLight />
|
||||
<directionalLight position={[0, 0.125, 1]} />
|
||||
<directionalLight position={[0, 0.125, -1]} />
|
||||
<Row {...props} selectedIndex={selectedIndex} isSpinning={isSpinning} />
|
||||
<Row />
|
||||
<OrbitControls
|
||||
ref={orbitControlsRef}
|
||||
ref={setOrbitControlsRef}
|
||||
enableRotate={!isSpinning}
|
||||
enablePan={false}
|
||||
minDistance={2}
|
||||
maxDistance={10}
|
||||
/>
|
||||
</Canvas>
|
||||
|
||||
<InfoBox
|
||||
category={props.category}
|
||||
type={props.types[selectedIndex]}
|
||||
selectIndex={selectIndex}
|
||||
selectedIndex={selectedIndex}
|
||||
isSpinning={isSpinning}
|
||||
toggleSpin={toggleSpin}
|
||||
maxIndex={props.types.length - 1}
|
||||
/>
|
||||
</>
|
||||
<InfoBox />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,23 +1,31 @@
|
||||
import { CoinProps } from "@/types/coin";
|
||||
import { Dispatch, SetStateAction } from "react";
|
||||
import { useShowcaseStore } from "./store";
|
||||
|
||||
export const InfoBox = () => {
|
||||
const category = useShowcaseStore((state) => state.category);
|
||||
const isSpinning = useShowcaseStore((state) => state.isSpinning);
|
||||
const getType = useShowcaseStore((state) => state.getType);
|
||||
const toggleSpin = useShowcaseStore((state) => state.toggleSpin);
|
||||
const currentIndex = useShowcaseStore((state) => state.currentIndex);
|
||||
const nextIndex = useShowcaseStore((state) => state.nextIndex);
|
||||
const previousIndex = useShowcaseStore((state) => state.previousIndex);
|
||||
const total = useShowcaseStore((state) => state.types.length);
|
||||
|
||||
const currentType = getType();
|
||||
if (!currentType) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
issuer,
|
||||
title,
|
||||
min_year,
|
||||
max_year,
|
||||
count = 1,
|
||||
weight,
|
||||
size,
|
||||
thickness,
|
||||
} = currentType;
|
||||
|
||||
export const InfoBox = ({
|
||||
category,
|
||||
type,
|
||||
maxIndex,
|
||||
selectIndex,
|
||||
selectedIndex,
|
||||
isSpinning,
|
||||
toggleSpin,
|
||||
}: {
|
||||
category: "banknotes" | "coins";
|
||||
type: CoinProps;
|
||||
maxIndex: number;
|
||||
selectIndex: Dispatch<SetStateAction<number>>;
|
||||
selectedIndex: number;
|
||||
isSpinning: boolean;
|
||||
toggleSpin: () => void;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
@@ -25,7 +33,7 @@ export const InfoBox = ({
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
position: "absolute",
|
||||
bottom: 96,
|
||||
bottom: 32,
|
||||
left: 0,
|
||||
right: 0,
|
||||
}}
|
||||
@@ -42,19 +50,15 @@ export const InfoBox = ({
|
||||
color: "white",
|
||||
}}
|
||||
>
|
||||
<small>{type.issuer.name}</small>
|
||||
<h1 style={{ textAlign: "center" }}>{type.title}</h1>
|
||||
<p>
|
||||
{type.min_year !== type.max_year
|
||||
? type.min_year + " - " + type.max_year
|
||||
: type.min_year}
|
||||
</p>
|
||||
<small>{(type.count ?? 1) + " in collection"}</small>
|
||||
<small>{issuer.name}</small>
|
||||
<h1 style={{ textAlign: "center" }}>{title}</h1>
|
||||
<p>{min_year !== max_year ? min_year + " - " + max_year : min_year}</p>
|
||||
<small>{(count ?? 1) + " in collection"}</small>
|
||||
{category === "coins" && (
|
||||
<>
|
||||
{type.weight && <p>{type.weight + "g"}</p>}
|
||||
{type.size && <p>{type.size + "mm"}</p>}
|
||||
{type.thickness && <p>{type.thickness + "mm"}</p>}
|
||||
{weight && <p>{weight + "g"}</p>}
|
||||
{size && <p>{size + "mm"}</p>}
|
||||
{thickness && <p>{thickness + "mm"}</p>}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
@@ -74,19 +78,19 @@ export const InfoBox = ({
|
||||
}}
|
||||
>
|
||||
<button
|
||||
disabled={selectedIndex <= 0}
|
||||
disabled={currentIndex <= 0}
|
||||
style={{ flex: 1 }}
|
||||
onClick={() => selectIndex((value) => Math.max(value - 1, 0))}
|
||||
onClick={previousIndex}
|
||||
>
|
||||
{"<-"}
|
||||
</button>
|
||||
|
||||
<span>{selectedIndex + 1 + "/" + (maxIndex + 1)}</span>
|
||||
<span>{currentIndex + 1 + "/" + total}</span>
|
||||
|
||||
<button
|
||||
disabled={selectedIndex >= maxIndex}
|
||||
disabled={currentIndex >= total - 1}
|
||||
style={{ flex: 1 }}
|
||||
onClick={() => selectIndex((value) => Math.min(value + 1, maxIndex))}
|
||||
onClick={nextIndex}
|
||||
>
|
||||
{"->"}
|
||||
</button>
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
import { MeshProps, useFrame } from "@react-three/fiber";
|
||||
import { Suspense, useRef } from "react";
|
||||
import { CoinInstance } from "@/components/coin";
|
||||
import { Mesh } from "three";
|
||||
import { BanknoteInstance } from "@/components/banknote";
|
||||
import { useShowcaseStore } from "./store";
|
||||
import { CoinProps } from "@/types/coin";
|
||||
|
||||
const SpinninType = ({
|
||||
typeProps,
|
||||
isSelected,
|
||||
...props
|
||||
}: MeshProps & {
|
||||
typeProps?: CoinProps;
|
||||
isSelected?: boolean;
|
||||
}) => {
|
||||
const ref = useRef<Mesh>(null!);
|
||||
const category = useShowcaseStore((state) => state.category);
|
||||
const isSpinning = useShowcaseStore((state) => state.isSpinning);
|
||||
const isTransitioning = useShowcaseStore((state) => state.isTransitioning);
|
||||
const Instance = category === "banknotes" ? BanknoteInstance : CoinInstance;
|
||||
|
||||
useFrame((_state, delta) => {
|
||||
if (!isSelected || !isSpinning || !typeProps) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (category === "banknotes") {
|
||||
ref.current.rotation.x += delta;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (typeProps.orientation) {
|
||||
case "medal":
|
||||
ref.current.rotation.x += delta;
|
||||
break;
|
||||
|
||||
default:
|
||||
ref.current.rotation.z += delta;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
if (!typeProps) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { size = 16, thickness = 2 } = typeProps;
|
||||
|
||||
return (
|
||||
<mesh {...props}>
|
||||
<mesh
|
||||
ref={ref}
|
||||
{...(category !== "banknotes"
|
||||
? {
|
||||
scale: [size / 16, thickness / 16, size / 16],
|
||||
}
|
||||
: { rotation: [Math.PI / 2, Math.PI, Math.PI / 2] })}
|
||||
{...(!isSelected &&
|
||||
!isTransitioning && {
|
||||
scale: [0, 0, 0],
|
||||
})}
|
||||
>
|
||||
<Instance {...typeProps} />
|
||||
</mesh>
|
||||
</mesh>
|
||||
);
|
||||
};
|
||||
|
||||
export const Row = () => {
|
||||
const ref = useRef<Mesh>(null!);
|
||||
const category = useShowcaseStore((state) => state.category);
|
||||
const currentIndex = useShowcaseStore((state) => state.currentIndex);
|
||||
const getType = useShowcaseStore((state) => state.getType);
|
||||
const isTransitioning = useShowcaseStore((state) => state.isTransitioning);
|
||||
const endTransition = useShowcaseStore((state) => state.endTransition);
|
||||
const types = useShowcaseStore((state) => state.types);
|
||||
const spacing = category === "banknotes" ? 8 : 6;
|
||||
|
||||
useFrame(() => {
|
||||
if (!isTransitioning) {
|
||||
return;
|
||||
}
|
||||
|
||||
const target = -currentIndex * spacing;
|
||||
const diff = ref.current.position.x - target;
|
||||
|
||||
if (Math.abs(diff) > 1 / 32) {
|
||||
ref.current.position.x -= diff / 16;
|
||||
} else {
|
||||
console.log("ending transition");
|
||||
endTransition();
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<mesh ref={ref}>
|
||||
{types.map(
|
||||
(type, index) =>
|
||||
Math.abs(index - currentIndex) <= 1 && (
|
||||
<Suspense key={type.id} fallback={null}>
|
||||
<SpinninType
|
||||
typeProps={getType(index)}
|
||||
isSelected={currentIndex === index}
|
||||
position={[index * spacing, 0, 0]}
|
||||
rotation={[Math.PI / 2, Math.PI / 2, 0]}
|
||||
/>
|
||||
</Suspense>
|
||||
),
|
||||
)}
|
||||
</mesh>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,78 @@
|
||||
import { create } from "zustand";
|
||||
import { OrbitControls } from "three-stdlib";
|
||||
import { CoinProps } from "@/types/coin";
|
||||
import { MutableRefObject, createRef } from "react";
|
||||
|
||||
type Props = { category: "coins" | "banknotes"; types: CoinProps[] };
|
||||
|
||||
interface ShowcaseStore {
|
||||
currentIndex: number;
|
||||
endTransition: () => void;
|
||||
isSpinning: boolean;
|
||||
isTransitioning: boolean;
|
||||
nextIndex: () => void;
|
||||
previousIndex: () => void;
|
||||
orbitControlsRef: MutableRefObject<OrbitControls | null>;
|
||||
setOrbitControlsRef: (ref: OrbitControls | null) => void;
|
||||
toggleSpin: () => void;
|
||||
types: CoinProps[];
|
||||
category: Props["category"] | null;
|
||||
init: (props: Props) => void;
|
||||
getType: (index?: number) => CoinProps | undefined;
|
||||
}
|
||||
|
||||
export const useShowcaseStore = create<ShowcaseStore>((set, get) => ({
|
||||
isSpinning: true,
|
||||
isTransitioning: false,
|
||||
currentIndex: 0,
|
||||
types: [],
|
||||
category: null,
|
||||
orbitControlsRef: createRef(),
|
||||
nextIndex: () =>
|
||||
set((state) => {
|
||||
const currentIndex = Math.min(
|
||||
state.currentIndex + 1,
|
||||
state.types.length - 1,
|
||||
);
|
||||
return {
|
||||
currentIndex,
|
||||
isTransitioning: true,
|
||||
currentType: state.types[currentIndex],
|
||||
};
|
||||
}),
|
||||
previousIndex: () =>
|
||||
set((state) => {
|
||||
const currentIndex = Math.max(state.currentIndex - 1, 0);
|
||||
return {
|
||||
currentIndex,
|
||||
isTransitioning: true,
|
||||
currentType: state.types[currentIndex],
|
||||
};
|
||||
}),
|
||||
toggleSpin: () =>
|
||||
set((state) => {
|
||||
if (!state.isSpinning) {
|
||||
state.orbitControlsRef.current?.setAzimuthalAngle(0);
|
||||
state.orbitControlsRef.current?.setPolarAngle(Math.PI / 2);
|
||||
return { isSpinning: true };
|
||||
}
|
||||
return { isSpinning: false };
|
||||
}),
|
||||
endTransition: () => set(() => ({ isTransitioning: false })),
|
||||
init: (props) =>
|
||||
set((state) => {
|
||||
if (state.types.length) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return props;
|
||||
}),
|
||||
getType: (index) => {
|
||||
const { types, currentIndex } = get();
|
||||
return types[index ?? currentIndex];
|
||||
},
|
||||
setOrbitControlsRef: (instance) =>
|
||||
set(() => ({
|
||||
orbitControlsRef: { current: instance },
|
||||
})),
|
||||
}));
|
||||
+21
-3
@@ -1,6 +1,24 @@
|
||||
import "@/styles/globals.css";
|
||||
import type { AppProps } from "next/app";
|
||||
import type { AppContext, AppProps } from "next/app";
|
||||
import App from "next/app";
|
||||
import Div100vh from "react-div-100vh";
|
||||
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
return <Component {...pageProps} />;
|
||||
export default function MyApp({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
<Div100vh>
|
||||
<Component {...pageProps} />
|
||||
</Div100vh>
|
||||
);
|
||||
}
|
||||
|
||||
MyApp.getInitialProps = async (appContext: AppContext) => {
|
||||
const appProps = await App.getInitialProps(appContext);
|
||||
|
||||
if (appContext.ctx.res?.statusCode === 404) {
|
||||
appContext.ctx.res.writeHead(302, { Location: "/" });
|
||||
appContext.ctx.res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
return { ...appProps };
|
||||
};
|
||||
|
||||
@@ -28,16 +28,17 @@ const BanknotesPage: NextPage<Props> = ({ banknotes, ...props }) => (
|
||||
|
||||
<Header route={"banknotes"} {...props} />
|
||||
|
||||
<main id={"scene-root"} style={{ position: "relative" }}>
|
||||
<Showcase category="banknotes" types={banknotes} />
|
||||
</main>
|
||||
<Showcase category="banknotes" types={banknotes} />
|
||||
</>
|
||||
);
|
||||
|
||||
export const getStaticProps: GetStaticProps<Props> = async ({ params }) => {
|
||||
if (typeof params?.country !== "string") {
|
||||
return {
|
||||
notFound: true,
|
||||
redirect: {
|
||||
destination: "/",
|
||||
permanent: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -28,16 +28,17 @@ const CoinsPage: NextPage<Props> = ({ coins, ...props }) => (
|
||||
|
||||
<Header route={"coins"} {...props} />
|
||||
|
||||
<main id={"scene-root"} style={{ position: "relative" }}>
|
||||
<Showcase category="coins" types={coins} />
|
||||
</main>
|
||||
<Showcase category="coins" types={coins} />
|
||||
</>
|
||||
);
|
||||
|
||||
export const getStaticProps: GetStaticProps<Props> = async ({ params }) => {
|
||||
if (typeof params?.country !== "string") {
|
||||
return {
|
||||
notFound: true,
|
||||
redirect: {
|
||||
destination: "/",
|
||||
permanent: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ const CountriesPage: NextPage<Props> = ({ countsByCountry }) => {
|
||||
position: "relative",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
minHeight: "100vh",
|
||||
gap: 32,
|
||||
padding: "32px 16px",
|
||||
}}
|
||||
|
||||
@@ -28,16 +28,17 @@ const PilePage: NextPage<Props> = ({ coins, ...props }) => (
|
||||
|
||||
<Header route={"pile"} {...props} />
|
||||
|
||||
<main id={"scene-root"}>
|
||||
<Pile coins={coins} />
|
||||
</main>
|
||||
<Pile coins={coins} />
|
||||
</>
|
||||
);
|
||||
|
||||
export const getStaticProps: GetStaticProps<Props> = async ({ params }) => {
|
||||
if (typeof params?.country !== "string") {
|
||||
return {
|
||||
notFound: true,
|
||||
redirect: {
|
||||
destination: "/",
|
||||
permanent: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,3 @@ body {
|
||||
background-color: #111;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#scene-root > div:first-child {
|
||||
height: 100vh !important;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
cd /mnt/bigdisk/home/miguel/Documents/projects/react/coins
|
||||
rm -rf packages/site/.next
|
||||
yarn
|
||||
yarn build
|
||||
yarn start
|
||||
Reference in New Issue
Block a user