import React, { useEffect, useState } from "react"; import { useTheme } from "@material-ui/core/styles"; import ReactMarkdown from "react-markdown"; import { BrowserView, MobileView } from "react-device-detect"; import { useParams, useNavigate, Link } from "react-router-dom"; import { Tooltip, Divider, Button, Menu, MenuItem, Typography, Paper, List, } from "@material-ui/core"; import { Link as LinkIcon, Edit as EditIcon } from "@material-ui/icons"; const Body = { maxWidth: 1000, minWidth: 768, margin: "auto", display: "flex", height: "100%", color: "white", position: "relative", //textAlign: "center", }; const dividerColor = "rgb(225, 228, 232)"; const hrefStyle = { color: "rgba(255, 255, 255, 0.40)", textDecoration: "none", }; const innerHrefStyle = { color: "rgba(255, 255, 255, 0.75)", textDecoration: "none", }; const Docs = (defaultprops) => { const { globalUrl, selectedDoc, serverside, isMobile } = defaultprops; let navigate = useNavigate(); const theme = useTheme(); // Quickfix for react router 5 -> 6 const params = useParams(); var props = JSON.parse(JSON.stringify(defaultprops)) props.match = {} props.match.params = params useEffect(() => { if (params["key"] === undefined) { navigate("/docs/about") return } }, []) //console.log("PARAMS: ", params) const [mobile, setMobile] = useState(isMobile === true ? true : false); const [data, setData] = useState(""); const [firstrequest, setFirstrequest] = useState(true); const [list, setList] = useState([]); const [, setListLoaded] = useState(false); const [anchorEl, setAnchorEl] = React.useState(null); const [headingSet, setHeadingSet] = React.useState(false); const [selectedMeta, setSelectedMeta] = React.useState({ link: "hello", read_time: 2, }); const [tocLines, setTocLines] = React.useState([]); const [baseUrl, setBaseUrl] = React.useState( serverside === true ? "" : window.location.href ); function handleClick(event) { setAnchorEl(event.currentTarget); } function handleClose() { setAnchorEl(null); } const SidebarPaperStyle = { backgroundColor: theme.palette.surfaceColor, overflowX: "hidden", position: "relative", padding: 30, paddingTop: 15, marginTop: 15, minHeight: "50vh", //height: "50vh", }; const SideBar = { maxWidth: 250, flex: 1, position: "sticky", top: 100, maxHeight: "83vh", overflowX: "hidden", overflowY: "auto", zIndex: 1000, }; const fetchDocList = () => { fetch(globalUrl + "/api/v1/docs", { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", }, }) .then((response) => response.json()) .then((responseJson) => { if (responseJson.success) { setList(responseJson.list); } else { setList([ "# Error loading documentation. Please contact us if this persists.", ]); } setListLoaded(true); }) .catch((error) => {}); }; const fetchDocs = (docId) => { fetch(globalUrl + "/api/v1/docs/" + docId, { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", }, }) .then((response) => response.json()) .then((responseJson) => { if (responseJson.success) { setData(responseJson.reason); document.title = "Shuffle " + docId + " documentation"; if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.includes("404: Not Found")) { navigate("/docs") return } if (responseJson.meta !== undefined) { setSelectedMeta(responseJson.meta); } //console.log("TOC list: ", responseJson.reason) if ( responseJson.reason !== undefined && responseJson.reason !== null ) { const splitkey = responseJson.reason.split("\n"); var innerTocLines = []; var record = false; for (var key in splitkey) { const line = splitkey[key]; //console.log("Line: ", line) if (line.toLowerCase().includes("table of contents")) { record = true; continue; } if (record && line.length < 3) { record = false; } if (record) { const parsedline = line.split("]("); if (parsedline.length > 1) { parsedline[0] = parsedline[0].replaceAll("*", ""); parsedline[0] = parsedline[0].replaceAll("[", ""); parsedline[0] = parsedline[0].replaceAll("]", ""); parsedline[0] = parsedline[0].replaceAll("(", ""); parsedline[0] = parsedline[0].replaceAll(")", ""); parsedline[0] = parsedline[0].trim(); parsedline[1] = parsedline[1].replaceAll("*", ""); parsedline[1] = parsedline[1].replaceAll("[", ""); parsedline[1] = parsedline[1].replaceAll("]", ""); parsedline[1] = parsedline[1].replaceAll(")", ""); parsedline[1] = parsedline[1].replaceAll("(", ""); parsedline[1] = parsedline[1].trim(); //console.log(parsedline[0], parsedline[1]) innerTocLines.push({ text: parsedline[0], link: parsedline[1], }); } else { console.log("Bad line for parsing: ", line); } } } setTocLines(innerTocLines); } } else { setData("# Error\nThis page doesn't exist."); } }) .catch((error) => {}); }; if (firstrequest) { setFirstrequest(false); if (!serverside) { if (window.innerWidth < 768) { setMobile(true); } } if (selectedDoc !== undefined) { setData(selectedDoc.reason); setList(selectedDoc.list); setListLoaded(true); } else { if (!serverside) { fetchDocList(); console.log("PROPS: ", props) //const propkey = props.match.params.key //if (propkey === undefined) { // navigate("/docs/about") // return null //} fetchDocs(props.match.params.key) } } } // Handles search-based changes that origin from outside this file if (serverside !== true && window.location.href !== baseUrl) { setBaseUrl(window.location.href); fetchDocs(props.match.params.key); } const parseElementScroll = () => { const offset = 45; var parent = document.getElementById("markdown_wrapper_outer"); if (parent !== null) { //console.log("IN PARENT") var elements = parent.getElementsByTagName("h2"); const name = window.location.hash .slice(1, window.location.hash.lenth) .toLowerCase() .split("%20") .join(" ") .split("_") .join(" ") .split("-") .join(" "); //console.log(name) var found = false; for (var key in elements) { const element = elements[key]; if (element.innerHTML === undefined) { continue; } // Fix location.. if (element.innerHTML.toLowerCase() === name) { //console.log(element.offsetTop) element.scrollIntoView({ behavior: "smooth" }); //element.scrollTo({ // top: element.offsetTop+offset, // behavior: "smooth" //}) found = true; //element.scrollTo({ // top: element.offsetTop-100, // behavior: "smooth" //}) } } // H# if (!found) { elements = parent.getElementsByTagName("h3"); //console.log("NAMe: ", name) found = false; for (key in elements) { const element = elements[key]; if (element.innerHTML === undefined) { continue; } // Fix location.. if (element.innerHTML.toLowerCase() === name) { element.scrollIntoView({ behavior: "smooth" }); //element.scrollTo({ // top: element.offsetTop-offset, // behavior: "smooth" //}) found = true; //element.scrollTo({ // top: element.offsetTop-100, // behavior: "smooth" //}) } } } } //console.log(element) //console.log("NAME: ", name) //console.log(document.body.innerHTML) // parent = document.getElementById(parent); //var descendants = parent.getElementsByTagName(tagname); // this.scrollDiv.current.scrollIntoView({ behavior: 'smooth' }); //$(".parent").find("h2:contains('Statistics')").parent(); }; if (serverside !== true && window.location.hash.length > 0) { parseElementScroll(); } const markdownStyle = { color: "rgba(255, 255, 255, 0.65)", flex: "1", maxWidth: mobile ? "100%" : 750, overflow: "hidden", paddingBottom: 100, marginLeft: mobile ? 0 : 50, }; function OuterLink(props) { if (props.href.includes("http") || props.href.includes("mailto")) { return ( {props.children} ); } return ( {props.children} ); } function Img(props) { return {props.alt}; } function CodeHandler(props) { return (
        {props.value}
      
); } const Heading = (props) => { const element = React.createElement( `h${props.level}`, { style: { marginTop: props.level === 1 ? 20 : 50 } }, props.children ); const [hover, setHover] = useState(false); var extraInfo = ""; if (props.level === 1) { extraInfo = (
{mobile ? null : ( )} {mobile ? null : (
)} {selectedMeta.read_time} minute {selectedMeta.read_time === 1 ? "" : "s"} to read
{mobile || selectedMeta.contributors === undefined || selectedMeta.contributors === null ? ( "" ) : (
{selectedMeta.contributors.slice(0, 7).map((data, index) => { return ( {data.url} ); })}
)}
); } return ( { setHover(true); }} > {props.level !== 1 ? ( ) : null} {element} {/*hover ? {setHover(true)}} style={{cursor: "pointer", display: "inline", }} onClick={() => { window.location.href += "#hello" console.log(window.location) //window.history.pushState('page2', 'Title', '/page2.php'); //window.history.replaceState('page2', 'Title', '/page2.php'); }} /> : "" */} {extraInfo} ); }; //React.createElement("p", {style: {color: "red", backgroundColor: "blue"}}, this.props.paragraph) //function unicodeToChar(text) { // return text.replace(/\\u[\dA-F]{4}/gi, // function (match) { // return String.fromCharCode(parseInt(match.replace(/\\u/g, ''), 16)); // } // ); //} const postDataBrowser = list === undefined || list === null ? null : (
{list.map((data, index) => { const item = data.name; if (item === undefined) { return null; } const path = "/docs/" + item; const newname = item.charAt(0).toUpperCase() + item.substring(1).split("_").join(" ").split("-").join(" "); const itemMatching = props.match.params.key === undefined ? false : props.match.params.key.toLowerCase() === item.toLowerCase(); //const [tocLines, setTocLines] = React.useState([]); return (
  • { setTocLines([]); fetchDocs(item); }} > > {newname} {itemMatching && tocLines !== null && tocLines !== undefined && tocLines.length > 0 ? (
    {tocLines.map((data, index) => { //console.log(data) return ( {}} > - {data.text} ); })}
    ) : null}
  • ); })}
    ); const mobileStyle = { color: "white", marginLeft: 25, marginRight: 25, paddingBottom: 50, backgroundColor: "inherit", display: "flex", flexDirection: "column", }; const postDataMobile = list === undefined || list === null ? null : (
    {list.map((data, index) => { const item = data.name; if (item === undefined) { return null; } const path = "/docs/" + item; const newname = item.charAt(0).toUpperCase() + item.substring(1).split("_").join(" ").split("-").join(" "); return ( { window.location.pathname = path; }} > {newname} ); })}
    ); //const imageModal = // // {imageModal} // Padding and zIndex etc set because of footer in cloud. const loadedCheck = (
    {postDataBrowser} {postDataMobile}
    ); return
    {loadedCheck}
    ; }; export default Docs;