From 1cc630da4247e75a18629d960768d06239b0175b Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Tue, 24 Aug 2021 18:40:19 +0200 Subject: details and gallery --- src/views/App.js | 42 +++++++++++++++++++++++++++++ src/views/Detail.js | 65 ++++++++++++++++++++++++++++++++------------- src/views/Gallery.js | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+), 19 deletions(-) create mode 100644 src/views/App.js create mode 100644 src/views/Gallery.js (limited to 'src/views') diff --git a/src/views/App.js b/src/views/App.js new file mode 100644 index 0000000..c5a7f83 --- /dev/null +++ b/src/views/App.js @@ -0,0 +1,42 @@ +/** + * Main React app logic + */ + +import React, { useState, useEffect, useCallback } from "react"; + +import Detail from "./Detail.js"; +import buildGraph from "../graph.js"; + +export default function App() { + const [db, setDb] = useState(null); + const [node, setNode] = useState(null); + const [detailVisible, setDetailVisible] = useState(null); + + useEffect(async () => { + const newDb = await loadDB(); + setDb(newDb); + buildGraph(newDb, { + click: handleClick, + }); + }, []); + + const handleClick = useCallback((node) => { + setNode(node); + setDetailVisible(true); + }); + + const handleClose = useCallback((node) => { + setDetailVisible(false); + }); + + return ( +
+ +
+ ); +} + +async function loadDB() { + const request = await fetch("/assets/db.json"); + return await request.json(); +} diff --git a/src/views/Detail.js b/src/views/Detail.js index 51598c1..0a756c9 100644 --- a/src/views/Detail.js +++ b/src/views/Detail.js @@ -2,30 +2,57 @@ * Detail view, displaying text plus media */ -export default function Detail({ node }) { - const index = id + 1; +import React from "react"; + +import Gallery from "./Gallery.js"; + +export default function Detail({ node, visible, onClose }) { + if (!node) { + return
; + } + const { id, data } = node; + const index = id + 1; return ( -
+
-
- {index} -
- {data.author} -
- {data.title} -
+
+
+
{pad(index)}
+ {data.author && ( +
+ )} +
+
+
+
+
+
+
+
+
-
-
+ {node.type === "video" ? ( + "video" + ) : ( + + )}
-
); } + +const pad = (value) => (value < 10 ? "0" + value : value); +const capitalizeWord = (text = "") => + text ? text.charAt(0).toUpperCase() + text.slice(1) : ""; +const capitalize = (text = "") => + String(text || "") + .split(" ") + .map(capitalizeWord) + .join(" "); diff --git a/src/views/Gallery.js b/src/views/Gallery.js new file mode 100644 index 0000000..7bdf18b --- /dev/null +++ b/src/views/Gallery.js @@ -0,0 +1,75 @@ +/** + * Detail view, displaying text plus media + */ + +import React, { useState, useEffect } from "react"; + +export default function Gallery({ images, visible }) { + const hasItems = !!images?.length; + const oneItem = images?.length === 1; + + const [index, setIndex] = useState(0); + const [opacity, setOpacity] = useState(0); + useEffect(() => { + setIndex(0); + setOpacity(0); + setTimeout(() => setOpacity(1), 500); + }, [images]); + + function previous() { + setOpacity(0); + setTimeout(() => setIndex(Math.max(0, index - 1)), 100); + // setTimeout(() => setOpacity(1), 500); + } + function next() { + setOpacity(0); + setTimeout(() => setIndex(Math.min(images.length - 1, index + 1)), 100); + // setTimeout(() => setOpacity(1), 500); + } + function nextOrWrap() { + if (oneItem) return; + setOpacity(0); + setTimeout(() => setIndex(mod(index + 1, images.length)), 100); + // setTimeout(() => setOpacity(1), 500); + } + function appear() { + setOpacity(1); + } + + if (!hasItems) { + return
; + } + + return ( +
+
+ {visible && !!images[index] && ( + + )} +
+
+ {!oneItem && ( + 0 ? 1 : 0 }} + /> + )} + {!oneItem && ( + + )} +
+
+ ); +} + +const mod = (n, m) => n - m * Math.floor(n / m); -- cgit v1.2.3-70-g09d2