From 8a3186a8c1156d06d42b6b02f6173c9c426a1532 Mon Sep 17 00:00:00 2001 From: yashsinghcodes Date: Mon, 6 May 2024 13:28:49 +0000 Subject: [PATCH 1/6] fresh look for docs --- frontend/src/codeeditor-index.css | 14 + frontend/src/index.css | 20 +- frontend/src/views/Docs.jsx | 1650 ++++++++++++++--------------- 3 files changed, 809 insertions(+), 875 deletions(-) diff --git a/frontend/src/codeeditor-index.css b/frontend/src/codeeditor-index.css index 6a8b757f..c87341f5 100644 --- a/frontend/src/codeeditor-index.css +++ b/frontend/src/codeeditor-index.css @@ -90,3 +90,17 @@ code { color: #fff; font-size: 14px; } + +::-webkit-scrollbar { + width: 8px; +} + +::-webkit-scrollbar-thumb { + background-color: #494949; + border-radius: 5px; +} + +::-webkit-scrollbar-track { + background-color: rgb(26,26,26); +} + diff --git a/frontend/src/index.css b/frontend/src/index.css index 4188b885..9b1fc96c 100755 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -18,6 +18,11 @@ code { margin-right: -13px; } +.toc:hover { + background-color: gray; + color: white; +} + /* .cm-string{ z-index: -1; } @@ -30,4 +35,17 @@ code { .CodeMirror-selectedtext{ background-color: rgba(28, 47, 69, 0.6) !important; color: rgb(255, 255, 255) !important; -} \ No newline at end of file +} + +::-webkit-scrollbar { + width: 8px; +} + +::-webkit-scrollbar-thumb { + background-color: #494949; + border-radius: 5px; +} + +::-webkit-scrollbar-track { + background-color: rgb(26,26,26); +} diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index 4ae1cfbe..9835d947 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -11,104 +11,106 @@ import { useParams, useNavigate, Link } from "react-router-dom"; import { validateJson, GetIconInfo } from "../views/Workflows.jsx"; import { - Grid, - TextField, - IconButton, - Tooltip, - Divider, - Button, - Menu, - MenuItem, - Typography, - Paper, - List, - Collapse, - ListItemButton, - ListItemText + Grid, + TextField, + IconButton, + Tooltip, + Divider, + Button, + Menu, + MenuItem, + Typography, + Paper, + List, + Collapse, + ListItemButton, + ListItemText } from "@mui/material"; import { - Link as LinkIcon, - Edit as EditIcon, - KeyboardArrowRight as KeyboardArrowRightIcon, - ExpandMore as ExpandMoreIcon, - FileCopy as FileCopyIcon + Link as LinkIcon, + Edit as EditIcon, + KeyboardArrowRight as KeyboardArrowRightIcon, + ExpandMore as ExpandMoreIcon, + FileCopy as FileCopyIcon } from "@mui/icons-material"; +import { fontGrid } from "@mui/material/styles/cssUtils.js"; const Body = { - //maxWidth: 1000, - //minWidth: 768, - maxWidth: "100%", - minWidth: "100%", - display: "flex", - height: "100%", - color: "white", - position: "relative", - //textAlign: "center", + //maxWidth: 1000, + //minWidth: 768, + maxWidth: "100%", + minWidth: "100%", + 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", + color: "rgba(255, 255, 255, 0.40)", + textDecoration: "none", }; + const hrefStyle2 = { - color: "#f86a3e", - textDecoration: "none", + color: "#f86a3e", + textDecoration: "none", }; const innerHrefStyle = { - color: "rgba(255, 255, 255, 0.75)", - textDecoration: "none", + color: "rgba(255, 255, 255, 0.75)", + textDecoration: "none", }; export const CopyToClipboard = (props) => { - const {text, style, onCopy} = props; - const parsedstyle = style !== undefined ? style : { - position: "absolute", - right: 0, - top: -10, - } + const { text, style, onCopy } = props; + const parsedstyle = style !== undefined ? style : { + position: "absolute", + right: 0, + top: -10, + } - return ( -
- { - navigator.clipboard.writeText(text); - toast("Copied to clipboard") - }} - > - - -
- ) + return ( +
+ { + navigator.clipboard.writeText(text); + toast("Copied to clipboard") + }} + > + + +
+ ) } export const OuterLink = (props) => { if (props.href.includes("http") || props.href.includes("mailto")) { - return ( - - {props.children} - - ); + return ( + + {props.children} + + ); } return ( - - {props.children} - + + {props.children} + ); - } +} export const Img = (props) => { @@ -120,850 +122,750 @@ export const CodeHandler = (props) => { const validate = validateJson(propvalue) - var newprop = propvalue - if (validate.valid === false) { - // Check if https://shuffler.io in the url - // if so, then we change it for the current url - if (propvalue.includes("https://shuffler.io")) { - newprop = propvalue.replace("https://shuffler.io", window.location.origin) - } + var newprop = propvalue + if (validate.valid === false) { + // Check if https://shuffler.io in the url + // if so, then we change it for the current url + if (propvalue.includes("https://shuffler.io")) { + newprop = propvalue.replace("https://shuffler.io", window.location.origin) + } - // Check if it contains Bearer APIKEY - // If so, replace apikey - //if (newprop.includes("Bearer APIKEY")) { - // newprop = newprop.replace("Bearer APIKEY", "Bearer API - //} - } + // Check if it contains Bearer APIKEY + // If so, replace apikey + //if (newprop.includes("Bearer APIKEY")) { + // newprop = newprop.replace("Bearer APIKEY", "Bearer API + //} + } - // Need to check if it's singletick or multi + // Need to check if it's singletick or multi console.log("PROP: ", propvalue, props) - if (props.inline === true) { - // Show it inline - return ( - - {newprop} - - ) - } + if (props.inline === true) { + // Show it inline + return ( + + {newprop} + + ) + } return ( -
- {validate.valid === true ? - - : -
- - {newprop} - - -
- } -
- ) +
+ {validate.valid === true ? + + : +
+ + {newprop} + + +
+ } +
+ ) } const Docs = (defaultprops) => { - const { globalUrl, selectedDoc, serverside, serverMobile } = defaultprops; + const { globalUrl, selectedDoc, serverside, serverMobile } = defaultprops; - let navigate = useNavigate(); + let navigate = useNavigate(); - // Quickfix for react router 5 -> 6 - const params = useParams(); - //var props = JSON.parse(JSON.stringify(defaultprops)) - var props = Object.assign({ selected: false }, defaultprops); - props.match = {} - props.match.params = params + // Quickfix for react router 5 -> 6 + const params = useParams(); + //var props = JSON.parse(JSON.stringify(defaultprops)) + var props = Object.assign({ selected: false }, defaultprops); + props.match = {} + props.match.params = params - useEffect(() => { - //if (params["key"] === undefined) { - // navigate("/docs/about") - // return - //} - }, []) - //console.log("PARAMS: ", params) - - const [mobile, setMobile] = useState(serverMobile === true || isMobile === true ? true : false); - const [data, setData] = useState(""); - const [firstrequest, setFirstrequest] = useState(true); - const [list, setList] = useState([]); - const [isopen, setOpen] = useState(-1); - 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 handleCollapse = (index) => { - setOpen(isopen === index ? -1 : index) - }; - - const SidebarPaperStyle = { - backgroundColor: theme.palette.surfaceColor, - overflowX: "hidden", - position: "relative", - paddingLeft: 15, - paddingRight: 15, - paddingTop: 15, - marginTop: 15, - minHeight: "80vh", - //height: "50vh", - }; - - 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 = ( -
-
- {isMobile ? null : ( - - - - - - )} - {isMobile ? null : ( -
- )} - - {selectedMeta.read_time} minute - {selectedMeta.read_time === 1 ? "" : "s"} to read - -
-
- {isMobile || - selectedMeta.contributors === undefined || - selectedMeta.contributors === null ? ( - "" - ) : ( -
- {selectedMeta.contributors.slice(0, 7).map((data, index) => { - return ( - - - {data.url} - - - ); - })} -
- )} -
-
- ); - } - - if (extraInfo !== "" && props.level === 1 && props.children !== undefined && props.children !== null && props.children.length > 0) { - if (props.children[0].toLowerCase().includes("privacy") || props.children[0].toLowerCase().includes("terms")) { - extraInfo = "" - } - } - - return ( - { - setHover(true); - }} - > - {props.level !== 1 ? ( - - ) : null} - {element} - {extraInfo} - - ) - } - - const SideBar = { - minWidth: 300, - width: "20%", - left: 0, - position: "sticky", - top: 50, - minHeight: "90vh", - maxHeight: "90vh", - overflowX: "hidden", - overflowY: "auto", - zIndex: 1000, - //borderRight: "1px solid rgba(255,255,255,0.3)", - }; - - 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.",]); - toast("Failed loading documentation. Please reload the window") - } - 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 === false) { - //toast("Failed loading documentation. Please reload the UI") - } - - if (responseJson.success && responseJson.reason !== undefined) { - // Find tags and translate them into ![]() format - const imgRegex = / 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(); - - //const propkey = props.match.params.key - //if (propkey === undefined) { + useEffect(() => { + //if (params["key"] === undefined) { // navigate("/docs/about") - // return null + // return //} - // - if (props.match.params.key === undefined) { - - } else { - console.log("DOCID: ", props.match.params.key) - 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(" ") - .split("?")[0] - - //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.90)", - overflow: "hidden", - paddingBottom: 100, - margin: "auto", - maxWidth: "100%", - minWidth: "100%", - overflow: "hidden", - fontSize: isMobile ? "1.3rem" : "1.1rem", - }; - - - - - const CustomButton = (props) => { - const { title, icon, link } = props - - const [hover, setHover] = useState(false) - - return ( - -
{ - if (link === "" || link === undefined) { - event.preventDefault() - console.log("IN CLICK!") - if (window.drift !== undefined) { - window.drift.api.startInteraction({ interactionId: 340043 }) - } else { - console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift) - } - } else { - console.log("Link defined: ", link) - } - }} onMouseOver={() => { - setHover(true) - }} - onMouseOut={() => { - setHover(false); - }} - > - {icon} - - {title} - -
-
- ) - } - - - const DocumentationButton = (props) => { - const { item, link } = props + }, []) + //console.log("PARAMS: ", params) + const [mobile, setMobile] = useState(serverMobile === true || isMobile === true ? true : false); + const [data, setData] = useState(""); + const [firstrequest, setFirstrequest] = useState(true); + const [list, setList] = useState([]); + const [isopen, setOpen] = useState(-1); const [hover, setHover] = useState(false); + 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 + ); - if (link === undefined || link === null) { - return null + function handleClick(event) { + setAnchorEl(event.currentTarget); } - return ( - -
{ - setHover(true) - }} - onMouseOut={() => { - setHover(false); - }} - > - - {item} - -
- - ) - } + function handleMouseOver() { + setHover(!hover); + } - const headerStyle = { - marginTop: 25, - } + function handleClose() { + setAnchorEl(null); + } - const mainpageInfo = -
- - Documentation - -
- /> - link="https://discord.gg/B2CBzUm" /> -
-
- Tutorial - - Dive in. Hands-on is the best approach to see how Shuffle can transform your security operations. Our set of tutorials and videos teach you how to build your skills. Check out the getting started section to give it a go! - + const SidebarPaperStyle = { + backgroundColor: "rgb(26,26,26)", + backgroundImage: "none", + overflowX: "hidden", + position: "relative", + paddingLeft: 15, + paddingRight: 15, + minHeight: "80vh", + }; - Why Shuffle? - - Security first. We incentivize trying before buying, and give you the full set of tools you need to automate your operations. What's more is we also help you find usecases that fit your unique needs. Accessibility is key, and we intend to help every SOC globally use and share their usecases. - - Get help - - Our promise is to make it easier and easier to automate your operations. In some cases however, it may be good with a helping hand. That's where Shuffle's consultancy and support services come in handy. We help you build and automate your operational processes to a level you haven't seen before with the help of our usecases. - + const Heading = (props) => { + const element = React.createElement( + `h${props.level}`, + { style: { marginTop: props.level === 1 ? 20 : 50 } }, + props.children + ); - APIs - - Learn. We're all about learning, and are continuously creating documentation and video tutorials to better understand how to get started. APIs are an extremely important part of how the internet works today, and our goal is helping every security professional learn about them. - + var extraInfo = ""; - Workflow building - - Build. Creating workflows has never been easier. Jump into things with our getting Started section and build to your hearts content. Workflows make it all come together, with an easy to use area. - + if (extraInfo !== "" && props.level === 1 && props.children !== undefined && props.children !== null && props.children.length > 0) { + if (props.children[0].toLowerCase().includes("privacy") || props.children[0].toLowerCase().includes("terms")) { + extraInfo = "" + } + } - Managing Shuffle - - Organize. Whether an organization of 1000 or 1, management tools are necessary. In Shuffle we offer full user management, MFA and single-signon options, multi-tenancy and a lot more - for free! - -
-
+ return ( + { + setHover(true); + }} + > + {props.level !== 1 ? ( + + ) : null} + {element} + {extraInfo} + + ) + } - const markdownComponents = { - img: Img, - code: CodeHandler, - h1: Heading, - h2: Heading, - h3: Heading, - h4: Heading, - h5: Heading, - h6: Heading, - a: OuterLink, - } - // PostDataBrowser Section - const postDataBrowser = - list === undefined || list === null ? null : ( -
-
- - - {list.map((data, index) => { - const item = data.name; - if (item === undefined) { - return null; + const SideBar = { + width: "17%", + position: "sticky", + top: 50, + minHeight: "90vh", + maxHeight: "90vh", + overflowX: "hidden", + overflowY: "auto", + zIndex: 1000, + //borderRight: "1px solid rgba(255,255,255,0.3)", + }; + + const IndexBar = { + alignSelf: "flex-start", + position: "sticky", + top: 80, + overflow: "auto", + marginTop: 70, + } + + 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.",]); + toast("Failed loading documentation. Please reload the window") + } + 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 === false) { + //toast("Failed loading documentation. Please reload the UI") } - const path = "/docs/" + item; - const newname = - item.charAt(0).toUpperCase() + - item.substring(1).split("_").join(" ").split("-").join(" "); + if (responseJson.success && responseJson.reason !== undefined) { + // Find tags and translate them into ![]() format + const imgRegex = / - { - setTocLines([]); - fetchDocs(item); - handleCollapse(index); - }} - > - - {newname} - - {isopen === index ? : } - - {itemMatching && - tocLines !== null && - tocLines !== undefined && - tocLines.length > 0 ? ( - - {tocLines.map((data, index) => { + if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.includes("404: Not Found")) { + navigate("/docs") + return + } - return ( - - - {data.text} - - - ); - })} - - ) : null} - - ); - })} - - -
-
- {props.match.params.key === undefined ? - mainpageInfo - : -
- - {data} - -
- } -
-
- ); + if (responseJson.meta !== undefined) { + setSelectedMeta(responseJson.meta); + } - const mobileStyle = { - color: "white", - marginLeft: 25, - marginRight: 25, - paddingBottom: 50, - backgroundColor: "inherit", - display: "flex", - flexDirection: "column", - }; + //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; + } - const postDataMobile = - list === undefined || list === null ? null : ( -
-
- - - {list.map((data, index) => { - const item = data.name; - if (item === undefined) { - return null; - } + 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(); - const path = "/docs/" + item; - const newname = - item.charAt(0).toUpperCase() + - item.substring(1).split("_").join(" ").split("-").join(" "); - return ( - { - window.location.pathname = path; - }} - > - {newname} - - ); - })} - -
- {props.match.params.key === undefined ? - mainpageInfo - : -
- - {data} - -
+ 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(); + + //const propkey = props.match.params.key + //if (propkey === undefined) { + // navigate("/docs/about") + // return null + //} + // + if (props.match.params.key === undefined) { + + } else { + console.log("DOCID: ", props.match.params.key) + 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.length) + .toLowerCase() + .split("%20") + .join(" ") + .split("_") + .join(" ") + .split("-") + .join(" ") + .split("?")[0] + + //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.90)", + overflow: "hidden", + paddingBottom: 100, + margin: "auto", + maxWidth: "100%", + minWidth: "100%", + overflow: "hidden", + fontSize: isMobile ? "1.3rem" : "1.1rem", + }; + + + + + const CustomButton = (props) => { + const { title, icon, link } = props + + return ( + +
{ + if (link === "" || link === undefined) { + event.preventDefault() + console.log("IN CLICK!") + if (window.drift !== undefined) { + window.drift.api.startInteraction({ interactionId: 340043 }) + } else { + console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift) + } + } else { + console.log("Link defined: ", link) + } + }} onMouseOver={() => { + setHover(true) + }} + onMouseOut={() => { + setHover(false); + }} + > + {icon} + + {title} + +
+
+ ) + } + + + const DocumentationButton = (props) => { + const { item, link } = props + + + if (link === undefined || link === null) { + return null + } + + return ( + +
{ + setHover(true) + }} + onMouseOut={() => { + setHover(false); + }} + > + + {item} + +
+ + ) + } + + const headerStyle = { + marginTop: 25, + } + + const mainpageInfo = +
+ + Documentation + +
+ /> + link="https://discord.gg/B2CBzUm" /> +
+ +
+ Tutorial + + Dive in. Hands-on is the best approach to see how Shuffle can transform your security operations. Our set of tutorials and videos teach you how to build your skills. Check out the getting started section to give it a go! + + + Why Shuffle? + + Security first. We incentivize trying before buying, and give you the full set of tools you need to automate your operations. What's more is we also help you find usecases that fit your unique needs. Accessibility is key, and we intend to help every SOC globally use and share their usecases. + + + Get help + + Our promise is to make it easier and easier to automate your operations. In some cases however, it may be good with a helping hand. That's where Shuffle's consultancy and support services come in handy. We help you build and automate your operational processes to a level you haven't seen before with the help of our usecases. + + + APIs + + Learn. We're all about learning, and are continuously creating documentation and video tutorials to better understand how to get started. APIs are an extremely important part of how the internet works today, and our goal is helping every security professional learn about them. + + + Workflow building + + Build. Creating workflows has never been easier. Jump into things with our getting Started section and build to your hearts content. Workflows make it all come together, with an easy to use area. + + + Managing Shuffle + + Organize. Whether an organization of 1000 or 1, management tools are necessary. In Shuffle we offer full user management, MFA and single-signon options, multi-tenancy and a lot more - for free! + +
+
+ + const markdownComponents = { + img: Img, + code: CodeHandler, + h1: Heading, + h2: Heading, + h3: Heading, + h4: Heading, + h5: Heading, + h6: Heading, + a: OuterLink, + } + + + // PostDataBrowser Section + 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(); + return ( +
  • + + + {newname} + + +
  • + ); + })} +
    +
    +
    +
    + {props.match.params.key === undefined ? + mainpageInfo + : +
    + + {data} + +
    + } +
    + +
    + ); + + 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} + + ); + })} +
    + {props.match.params.key === undefined ? + mainpageInfo + : +
    + + {data} + +
    + } + + +
    + ); + + // Padding and zIndex etc set because of footer in cloud. + const loadedCheck = ( +
    + {postDataBrowser} + {postDataMobile} +
    ); - // Padding and zIndex etc set because of footer in cloud. - const loadedCheck = ( -
    - {postDataBrowser} - {postDataMobile} -
    - ); - - return
    {loadedCheck}
    ; + return
    {loadedCheck}
    ; }; export default Docs; From 2278b98e81a89d99b50cc548569e52fae37878a7 Mon Sep 17 00:00:00 2001 From: yashsinghcodes Date: Wed, 8 May 2024 08:05:28 +0000 Subject: [PATCH 2/6] sub-contents --- frontend/src/views/Docs.jsx | 177 ++++++++++++++++++++++++------------ 1 file changed, 117 insertions(+), 60 deletions(-) diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index 9835d947..e2b7e489 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -54,6 +54,28 @@ const hrefStyle = { textDecoration: "none", }; +const hrefStyleToc = { + color: "rgba(255, 255, 255, 0.6)", + textDecoration: "none", + fontSize: "14px", + fontWeight: 400, + padding: "4px 0", + paddingLeft: "8px", + paddingRight: "8px", + lineHeight: "20px", +}; + + +const hrefStyleToc2 = { + color: "rgba(255, 255, 255, 0.6)", + textDecoration: "none", + fontSize: "14px", + fontWeight: 400, + padding: "4px 0", + paddingLeft: "12px", + paddingRight: "12px", + lineHeight: "20px", +}; const hrefStyle2 = { color: "#f86a3e", @@ -66,6 +88,9 @@ const innerHrefStyle = { }; + + + export const CopyToClipboard = (props) => { const { text, style, onCopy } = props; const parsedstyle = style !== undefined ? style : { @@ -232,6 +257,10 @@ const Docs = (defaultprops) => { setAnchorEl(event.currentTarget); } + function handleCollapse(index) { + setOpen(isopen === index ? -1 : index) + } + function handleMouseOver() { setHover(!hover); } @@ -240,6 +269,42 @@ const Docs = (defaultprops) => { setAnchorEl(null); } + function tocvalue(markdown) { + const items = []; + let currentMainItem = null; + + const lines = markdown.split('\n'); + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + if (line.startsWith('* [')) { + const matches = line.match(/^\* \[([^)]+)\]\(#([^)]+)\)/); + if (matches) { + currentMainItem = { + id: matches[2], + title: matches[1], + items: [], + }; + items.push(currentMainItem); + } + } else if (line.startsWith(' * [')) { + if (currentMainItem) { + const matches = line.match(/^ \* \[([^)]+)\]\(#([^)]+)\)/); + if (matches) { + currentMainItem.items.push({ + id: matches[2], + title: matches[1], + }); + } + } + } else if (line.startsWith('##')) { + continue + } + } + + return items + } + const SidebarPaperStyle = { backgroundColor: "rgb(26,26,26)", @@ -293,8 +358,8 @@ const Docs = (defaultprops) => { width: "17%", position: "sticky", top: 50, - minHeight: "90vh", - maxHeight: "90vh", + minHeight: "100vh", + maxHeight: "100vh", overflowX: "hidden", overflowY: "auto", zIndex: 1000, @@ -350,7 +415,6 @@ const Docs = (defaultprops) => { const tocRegex = /^## Table of contents[\s\S]*?(?=^##\s|\Z)|^\* \[[^\]]+\]\([^)]+\)\n?(?![^\n]+\]\([^)]+\))/gm; const newdata = responseJson.reason.replace(imgRegex, '![]($1)') .replace(tocRegex, ""); - console.log(newdata) setData(newdata); if (docId === undefined) { document.title = "Shuffle documentation introduction"; @@ -372,50 +436,10 @@ const Docs = (defaultprops) => { 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); + const values = tocvalue(responseJson.reason.match(tocRegex) + .join() + .toString()); + setTocLines(values); } } else { setData("# Error\nThis page doesn't exist."); @@ -758,19 +782,52 @@ const Docs = (defaultprops) => {
    } - - +
    +

    Table Of Content

    + +
    + + ); const mobileStyle = { From 7dc322c85c99cef23a85efb7e71786e378b3c736 Mon Sep 17 00:00:00 2001 From: yashsinghcodes Date: Wed, 8 May 2024 17:21:12 +0000 Subject: [PATCH 3/6] link --- frontend/src/views/Docs.jsx | 183 ++++++++++++++++++------------------ 1 file changed, 92 insertions(+), 91 deletions(-) diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index e2b7e489..a79e26c4 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -301,11 +301,9 @@ const Docs = (defaultprops) => { continue } } - return items } - const SidebarPaperStyle = { backgroundColor: "rgb(26,26,26)", backgroundImage: "none", @@ -316,8 +314,12 @@ const Docs = (defaultprops) => { minHeight: "80vh", }; - const Heading = (props) => { + var id = props.children[0].toLowerCase().toString() + if (props.level <= 3) { + id = props.children[0].toLowerCase().toString().replaceAll(" ", "-"); + } + const element = React.createElement( `h${props.level}`, { style: { marginTop: props.level === 1 ? 20 : 50 } }, @@ -337,6 +339,7 @@ const Docs = (defaultprops) => { onMouseOver={() => { setHover(true); }} + id={id} > {props.level !== 1 ? ( { 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 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.length) + // .toLowerCase() + // .split("%20") + // .join(" ") + // .split("_") + // .join(" ") + // .split("-") + // .join(" ") + // .split("?")[0] + // + // //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(); + // }; - const name = window.location.hash - .slice(1, window.location.hash.length) - .toLowerCase() - .split("%20") - .join(" ") - .split("_") - .join(" ") - .split("-") - .join(" ") - .split("?")[0] - - //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(); - } + // if (serverside !== true && window.location.hash.length > 0) { + // parseElementScroll(); + // } const markdownStyle = { color: "rgba(255, 255, 255, 0.90)", @@ -789,13 +792,12 @@ const Docs = (defaultprops) => { return (
    ( handleCollapse(index) )} + href={`#${data.id}`} > {data.title} {data.items.length > 0 ? ( @@ -809,10 +811,9 @@ const Docs = (defaultprops) => { {data.items.map((d, i) => { return ( {d.title} From 42f472996934543426f76e09c44475efc4719187 Mon Sep 17 00:00:00 2001 From: yashsinghcodes Date: Fri, 10 May 2024 09:37:40 +0000 Subject: [PATCH 4/6] scoll view for toc --- frontend/src/views/Docs.jsx | 233 +++++++++++++++++++++++++----------- 1 file changed, 166 insertions(+), 67 deletions(-) diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index a79e26c4..9ad90b28 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useLayoutEffect, useRef, useState } from "react"; import { toast } from 'react-toastify'; import Markdown from 'react-markdown' @@ -35,6 +35,7 @@ import { FileCopy as FileCopyIcon } from "@mui/icons-material"; import { fontGrid } from "@mui/material/styles/cssUtils.js"; +import { active } from "d3"; const Body = { //maxWidth: 1000, @@ -54,17 +55,6 @@ const hrefStyle = { textDecoration: "none", }; -const hrefStyleToc = { - color: "rgba(255, 255, 255, 0.6)", - textDecoration: "none", - fontSize: "14px", - fontWeight: 400, - padding: "4px 0", - paddingLeft: "8px", - paddingRight: "8px", - lineHeight: "20px", -}; - const hrefStyleToc2 = { color: "rgba(255, 255, 255, 0.6)", @@ -87,8 +77,38 @@ const innerHrefStyle = { textDecoration: "none", }; +// Emma Goto +const useIntersectionObserver = (setActiveId) => { + const headingElementsRef = useRef({}) + const callback = (headings) => { + headingElementsRef.current = headings.reduce((map, headingElemet) => { + if (headingElemet.target.id != undefined || headingElemet.target.id != "") { + map[headingElemet.target.id] = headingElemet + return map + } + }, headingElementsRef.current) + const visibleHeadings = []; + Object.keys(headingElementsRef.current).forEach((key) => { + const headingElemet = headingElementsRef.current[key]; + if (headingElemet.isIntersecting) visibleHeadings.push(headingElemet) + }) + if (visibleHeadings.length > 0) { + setActiveId(visibleHeadings[0].target.id) + } + } + + const observer = new IntersectionObserver(callback, { + rootMargin: "10%", + }); + + const headingElements = Array.from(document.querySelectorAll("h2")) + if (headingElements.length != 0) { + headingElements.forEach((element) => observer.observe(element)); + return () => observer.disconnect() + } +} export const CopyToClipboard = (props) => { @@ -227,22 +247,18 @@ const Docs = (defaultprops) => { props.match = {} props.match.params = params - useEffect(() => { - //if (params["key"] === undefined) { - // navigate("/docs/about") - // return - //} - }, []) //console.log("PARAMS: ", params) const [mobile, setMobile] = useState(serverMobile === true || isMobile === true ? true : false); const [data, setData] = useState(""); const [firstrequest, setFirstrequest] = useState(true); const [list, setList] = useState([]); + const [activeId, setActiveId] = useState(); const [isopen, setOpen] = useState(-1); const [hover, setHover] = useState(false); const [, setListLoaded] = useState(false); const [anchorEl, setAnchorEl] = React.useState(null); + const [anchorElToc, setAnchorElToc] = React.useState(null); const [headingSet, setHeadingSet] = React.useState(false); const [selectedMeta, setSelectedMeta] = React.useState({ link: "hello", @@ -253,10 +269,23 @@ const Docs = (defaultprops) => { serverside === true ? "" : window.location.href ); + useEffect(() => { + //if (params["key"] === undefined) { + // navigate("/docs/about") + // return + //} + }, []) + + useIntersectionObserver(setActiveId); + function handleClick(event) { setAnchorEl(event.currentTarget); } + function handleClickToc(event) { + setAnchorElToc(event.currentTarget); + } + function handleCollapse(index) { setOpen(isopen === index ? -1 : index) } @@ -269,6 +298,21 @@ const Docs = (defaultprops) => { setAnchorEl(null); } + function handleCloseToc() { + setAnchorElToc(null); + } + + function scrollToHash() { + const hash = window.location.hash.replace("#", "") + if (hash) { + const element = document.getElementById(hash) + if (element) { + element.scrollIntoView({ behavior: 'smooth' }) + } + } + + } + function tocvalue(markdown) { const items = []; let currentMainItem = null; @@ -322,7 +366,7 @@ const Docs = (defaultprops) => { const element = React.createElement( `h${props.level}`, - { style: { marginTop: props.level === 1 ? 20 : 50 } }, + { style: { marginTop: props.level === 1 ? 20 : 50, scrollPaddingTop: "50px" }, id: `${id}` }, props.children ); @@ -361,8 +405,8 @@ const Docs = (defaultprops) => { width: "17%", position: "sticky", top: 50, - minHeight: "100vh", - maxHeight: "100vh", + minHeight: "93vh", + maxHeight: "93vh", overflowX: "hidden", overflowY: "auto", zIndex: 1000, @@ -399,6 +443,8 @@ const Docs = (defaultprops) => { }; const fetchDocs = (docId) => { + setActiveId("") + setTocLines([]) fetch(`${globalUrl}/api/v1/docs/${docId}`, { method: "GET", headers: { @@ -571,9 +617,9 @@ const Docs = (defaultprops) => { // //$(".parent").find("h2:contains('Statistics')").parent(); // }; - // if (serverside !== true && window.location.hash.length > 0) { - // parseElementScroll(); - // } + if (serverside !== true && window.location.hash.length > 0) { + scrollToHash() + } const markdownStyle = { color: "rgba(255, 255, 255, 0.90)", @@ -786,14 +832,27 @@ const Docs = (defaultprops) => { }
    -

    Table Of Content

    + {tocLines.length > 0 ? + ( +

    Table Of Content

    + + ) : null} -
    + ); @@ -841,7 +902,6 @@ const Docs = (defaultprops) => { flexDirection: "column", }; - const postDataMobile = list === undefined || list === null ? null : (
    @@ -856,28 +916,67 @@ const Docs = (defaultprops) => { >
    More docs
    - {list.map((data, index) => { - const item = data.name; - if (item === undefined) { - return 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 path = "/docs/" + item; + const newname = + item.charAt(0).toUpperCase() + + item.substring(1).split("_").join(" ").split("-").join(" "); + return ( + { + window.location.pathname = path; + }} + > + {newname} + + ); + })} + + + + {tocLines.map((data, index) => { + return ( + + {data.title} + + ) + })} +
    {props.match.params.key === undefined ? mainpageInfo From 199058652d1d8a773dc3683de142765f0d8443f8 Mon Sep 17 00:00:00 2001 From: yashsinghcodes Date: Fri, 10 May 2024 10:47:57 +0000 Subject: [PATCH 5/6] edit button (scoll view stopped working) --- frontend/src/views/Docs.jsx | 101 +++++++++++++++++++++++++++++++----- 1 file changed, 88 insertions(+), 13 deletions(-) diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index 9ad90b28..e6d8523d 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -95,12 +95,12 @@ const useIntersectionObserver = (setActiveId) => { }) if (visibleHeadings.length > 0) { + console.log(visibleHeadings) setActiveId(visibleHeadings[0].target.id) } } const observer = new IntersectionObserver(callback, { - rootMargin: "10%", }); const headingElements = Array.from(document.querySelectorAll("h2")) @@ -277,6 +277,7 @@ const Docs = (defaultprops) => { }, []) useIntersectionObserver(setActiveId); + console.log(activeId) function handleClick(event) { setAnchorEl(event.currentTarget); @@ -307,7 +308,7 @@ const Docs = (defaultprops) => { if (hash) { const element = document.getElementById(hash) if (element) { - element.scrollIntoView({ behavior: 'smooth' }) + element.scrollIntoView({ behavior: "instant" }) } } @@ -366,18 +367,98 @@ const Docs = (defaultprops) => { const element = React.createElement( `h${props.level}`, - { style: { marginTop: props.level === 1 ? 20 : 50, scrollPaddingTop: "50px" }, id: `${id}` }, + { style: { marginTop: props.level === 1 ? 20 : 50 }, id: `${id}` }, 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} + + + ); + })} +
    + )} +
    +
    + ); + } if (extraInfo !== "" && props.level === 1 && props.children !== undefined && props.children !== null && props.children.length > 0) { if (props.children[0].toLowerCase().includes("privacy") || props.children[0].toLowerCase().includes("terms")) { extraInfo = "" } } - return ( { @@ -443,8 +524,6 @@ const Docs = (defaultprops) => { }; const fetchDocs = (docId) => { - setActiveId("") - setTocLines([]) fetch(`${globalUrl}/api/v1/docs/${docId}`, { method: "GET", headers: { @@ -632,9 +711,6 @@ const Docs = (defaultprops) => { fontSize: isMobile ? "1.3rem" : "1.1rem", }; - - - const CustomButton = (props) => { const { title, icon, link } = props @@ -783,7 +859,6 @@ const Docs = (defaultprops) => { if (item === undefined) { return null; } - const path = "/docs/" + item; const newname = item.charAt(0).toUpperCase() + @@ -843,6 +918,7 @@ const Docs = (defaultprops) => {
    { paddingRight: "8px", lineHeight: "20px", }} - onClick={() => ( + onClick={(e) => { handleCollapse(index) - )} - href={`#${data.id}`} + }} > {data.title} {data.items.length > 0 ? ( From 3765ff461a86d35808c356a508a5a936faa0ed0b Mon Sep 17 00:00:00 2001 From: yashsinghcodes Date: Fri, 10 May 2024 12:07:08 +0000 Subject: [PATCH 6/6] each heading embedded by link --- frontend/src/views/Docs.jsx | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index e6d8523d..d7c6b35f 100755 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -367,8 +367,8 @@ const Docs = (defaultprops) => { const element = React.createElement( `h${props.level}`, - { style: { marginTop: props.level === 1 ? 20 : 50 }, id: `${id}` }, - props.children + { id: `${id}` }, + props.children, ); const [hover, setHover] = useState(false); @@ -475,7 +475,18 @@ const Docs = (defaultprops) => { }} /> ) : null} - {element} +
    + {element} + +
    + {extraInfo} )