This commit is contained in:
Daniel Kluge 2022-06-15 00:35:49 +02:00
parent 01b8c4eaf6
commit 761b43efc2
7 changed files with 101 additions and 12 deletions

View File

@ -1,11 +1,13 @@
import type { NextPage } from "next"; import type { NextPage } from "next";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState, isValidElement } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import styles from "../styles/ProjectModal.module.css"; import styles from "../styles/ProjectModal.module.css";
import type { Project, Diary } from "../lib/content/types"; import type { Project, Diary } from "../lib/content/types";
import { useCommands } from "./contexts/CommandInterface"; import { useCommands } from "./contexts/CommandInterface";
import { generateContent, projectEmpty } from "../lib/content/generate"; import { generateContent, projectEmpty } from "../lib/content/generate";
import { useModalFunctions } from "./contexts/ModalFunctions"; import { useModalFunctions } from "./contexts/ModalFunctions";
import Spinner from "./Spinner";
import { renderToStaticMarkup } from "react-dom/server";
// Code Highlighting // Code Highlighting
import hljs from "highlight.js"; import hljs from "highlight.js";
@ -17,25 +19,45 @@ hljs.registerLanguage("console", bash);
hljs.registerLanguage("shell", bash); hljs.registerLanguage("shell", bash);
const ProjectModal: NextPage = () => { const ProjectModal: NextPage = () => {
const [visible, setVisible] = useState<boolean>(false); const [visible, _setVisible] = useState<boolean>(false);
const [currentContent, setCurrentContent] = useState<Project | Diary | undefined>(undefined); const [currentContent, _setCurrentContent] = useState<Project | Diary | undefined>(undefined);
const [currentPage, setCurrentPage] = useState<number>(0); const [currentPage, setCurrentPage] = useState<number>(0);
const [HTMLContent, setHTMLContent] = useState<string>(projectEmpty); const [HTMLContent, _setHTMLContent] = useState<string>(projectEmpty);
const router = useRouter(); const router = useRouter();
const { updateCallbacks: updateCmdCallbacks, cmdContext } = useCommands(); const { updateCallbacks: updateCmdCallbacks, cmdContext } = useCommands();
const { updateCallbacks: updateModalCallbacks } = useModalFunctions(); const { updateCallbacks: updateModalCallbacks } = useModalFunctions();
const setHTMLContent = (html: any) => {
switch (true) {
case typeof html === "string": {
_setHTMLContent(html);
return;
}
case isValidElement(html): {
_setHTMLContent(renderToStaticMarkup(html));
return;
}
default: {
try {
_setHTMLContent(html.toString());
} catch {}
return;
}
}
};
const setModalContent = async (content: Project | Diary, selectedPage?: number) => { const setModalContent = async (content: Project | Diary, selectedPage?: number) => {
setCurrentContent(content); _setCurrentContent(content);
router.replace("#", `#/${content.type}/${content.name}${content.type === "diary" && selectedPage ? `/${selectedPage}`: ""}`, {shallow: true}); router.replace("#", `#/${content.type}/${content.name}${content.type === "diary" && selectedPage ? `/${selectedPage}`: ""}`, {shallow: true});
if (content.type === "diary") setCurrentPage(selectedPage === undefined ? 0 : selectedPage); if (content.type === "diary") setCurrentPage(selectedPage === undefined ? 0 : selectedPage);
setHTMLContent(<Spinner size={200} />);
setHTMLContent(await generateContent(content, selectedPage)); setHTMLContent(await generateContent(content, selectedPage));
}; };
const setModalVisible = async (visible: boolean) => { const setVisible = async (visible: boolean) => {
if (!visible) router.replace("#", undefined, {shallow: true}); if (!visible) router.replace("#", undefined, {shallow: true});
setVisible(visible); _setVisible(visible);
}; };
const onContentReady = () => { const onContentReady = () => {
@ -43,8 +65,8 @@ const ProjectModal: NextPage = () => {
if (selected[2]) cmdContext.executeCommand(`project ${selected[2]}`); if (selected[2]) cmdContext.executeCommand(`project ${selected[2]}`);
}; };
updateCmdCallbacks({ setModalVisible, setModalContent, setModalHTML: setHTMLContent }); updateCmdCallbacks({ setModalVisible: setVisible, setModalContent, setModalHTML: setHTMLContent });
updateModalCallbacks({ setVisible: setModalVisible, setContent: setModalContent, setHtml: setHTMLContent, onContentReady }); updateModalCallbacks({ setVisible, setContent: setModalContent, setHtml: setHTMLContent, onContentReady });
useEffect(() => { useEffect(() => {
hljs.highlightAll(); hljs.highlightAll();
@ -71,7 +93,7 @@ const ProjectModal: NextPage = () => {
})(); })();
return <div className={styles.modal}> return <div className={styles.modal}>
<a onClick={() => setModalVisible(false)} className={"fake-link"}> <a onClick={() => setVisible(false)} className={"fake-link"}>
<div className={styles.modalClose}><div className={styles.modalCloseAlign}>X</div></div> <div className={styles.modalClose}><div className={styles.modalCloseAlign}>X</div></div>
</a> </a>
<div className={styles.modalContainer}> <div className={styles.modalContainer}>

19
components/Spinner.tsx Normal file
View File

@ -0,0 +1,19 @@
import type { NextPage } from "next";
import styles from "../styles/Spinner.module.scss";
const Spinner: NextPage<{size: number}> = ({ size }) => {
const diameterY = 300;
const padding = 25;
const rad = (angle: number) => angle * (Math.PI / 180);
const side = (diameterY / 2) / Math.sin(rad(60));
const x0 = side * Math.sin(rad(30));
const vbSizeX = (2 * x0) + side + (2 * padding);
const vbSizeY = diameterY + (2 * padding);
return <div style={{height: size, width: size}} className={styles.spinnerContainer}><svg height={"100%"} width={"100%"} viewBox={`-${padding} -${padding} ${vbSizeX} ${vbSizeY}`} className={styles.spinner}>
<polygon points={`${x0},${diameterY} 0,${diameterY/2} ${x0},0 ${x0+side},0 ${2*x0 + side},${diameterY/2} ${x0+side},${diameterY}`} className={styles.spinnerPath} />
</svg></div>;
};
export default Spinner;

View File

@ -6,7 +6,7 @@ import type { Diary, Project, ContentList } from "../../lib/content/types";
interface CommandInterfaceCallbacks { interface CommandInterfaceCallbacks {
setModalVisible?: (visible: boolean) => void; setModalVisible?: (visible: boolean) => void;
setModalContent?: (content: Project | Diary, selectedPage?: number) => void; setModalContent?: (content: Project | Diary, selectedPage?: number) => void;
setModalHTML?: (html: string) => void; setModalHTML?: (html: any) => void;
} }
const commandInterface = new CommandInterface(); const commandInterface = new CommandInterface();

View File

@ -5,7 +5,7 @@ import type { Project, Diary } from "../../lib/content/types";
interface ModalFunctions { interface ModalFunctions {
setVisible?: CallableFunction; setVisible?: CallableFunction;
setContent?: (content: Project| Diary) => void; setContent?: (content: Project| Diary) => void;
setHtml?: (html: string) => void; setHtml?: (html: any) => void;
onContentReady?: () => void; onContentReady?: () => void;
} }

19
package-lock.json generated
View File

@ -20,6 +20,7 @@
"@types/color": "^3.0.3", "@types/color": "^3.0.3",
"@types/node": "16.11.11", "@types/node": "16.11.11",
"@types/react": "17.0.37", "@types/react": "17.0.37",
"@types/react-dom": "^18.0.5",
"eslint": "7.32.0", "eslint": "7.32.0",
"eslint-config-next": "12.0.4", "eslint-config-next": "12.0.4",
"sass": "^1.49.7", "sass": "^1.49.7",
@ -520,6 +521,15 @@
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
}, },
"node_modules/@types/react-dom": {
"version": "18.0.5",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.5.tgz",
"integrity": "sha512-OWPWTUrY/NIrjsAPkAk1wW9LZeIjSvkXRhclsFO8CZcZGCOg2G0YZy4ft+rOyYxy8B7ui5iZzi9OkDebZ7/QSA==",
"dev": true,
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/scheduler": { "node_modules/@types/scheduler": {
"version": "0.16.2", "version": "0.16.2",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
@ -3948,6 +3958,15 @@
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
}, },
"@types/react-dom": {
"version": "18.0.5",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.5.tgz",
"integrity": "sha512-OWPWTUrY/NIrjsAPkAk1wW9LZeIjSvkXRhclsFO8CZcZGCOg2G0YZy4ft+rOyYxy8B7ui5iZzi9OkDebZ7/QSA==",
"dev": true,
"requires": {
"@types/react": "*"
}
},
"@types/scheduler": { "@types/scheduler": {
"version": "0.16.2", "version": "0.16.2",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",

View File

@ -22,6 +22,7 @@
"@types/color": "^3.0.3", "@types/color": "^3.0.3",
"@types/node": "16.11.11", "@types/node": "16.11.11",
"@types/react": "17.0.37", "@types/react": "17.0.37",
"@types/react-dom": "^18.0.5",
"eslint": "7.32.0", "eslint": "7.32.0",
"eslint-config-next": "12.0.4", "eslint-config-next": "12.0.4",
"sass": "^1.49.7", "sass": "^1.49.7",

View File

@ -0,0 +1,28 @@
.spinnerPath {
stroke: var(--repl-color);
stroke-width: 25;
fill: none;
}
@keyframes spin {
0% { transform: rotate(0deg); }
8.33%, 16.67% { transform: rotate(60deg); }
25%, 33.33% { transform: rotate(120deg); }
41.67%, 50% { transform: rotate(180deg); }
58.33%, 66.67% { transform: rotate(240deg); }
75%, 83.33% { transform: rotate(300deg); }
91.67%, 100% { transform: rotate(360deg); }
}
.spinnerContainer {
margin: 0 auto;
animation: spin 7s linear infinite;
position: relative;
}
.spinner {
margin: 0 auto;
position: absolute;
top: 50%;
transform: translateY(-50%);
}