diff --git a/src/App/App.css b/src/App/App.css new file mode 100644 index 0000000000000000000000000000000000000000..6a6f7f330af17dbdbede2a156d8b32d4d7815562 --- /dev/null +++ b/src/App/App.css @@ -0,0 +1,689 @@ +/* * * * * * * */ +/* * COLOURS * */ +/* * * * * * * */ +:root { + /* our colours*/ + --text-primary: #850F78; + --mediumpurple: #bc15aa; + /*--purple: #B85BD1; */ + --accen-secondary: #F57D22; + --accent-primary: #F4CC1E; + --lightyellow: #fae99e; + --lightblue: #A0A7F3 ; + --offblack: #32232C ; + --cebitecgray: #8295A4; + /*--offwhite: #e9dff1; */ + --ourbeige: #FFF6F2; + --background: white; + /*igem colours*/ + --igemdarkgreen: #006530; + --igemmediumgreen: #019968; + --igemlightgreen: #99cb9a; +} +/* * * * * * * */ +/* * * BODY* * */ +/* * * * * * * */ +body { + padding-top: 56px; + background-color: var(--background); + color: #493843; +} +body.dark-mode { + background-color: var(--offblack); + color: white; +} +p { + text-align: justify; +} +a { + color: var(--lightblue) !important; + text-decoration: none !important; +} +a:active { + background-color: yellow; +} +/* * * * * * * */ +/* *SIDEBAR* * */ +/* * * * * * * */ + +.sidebar{ + border-left: 6px solid; + border-left-color: var(--text-primary); + border-color: var(--accent-primary); + color: var(--text-primary); + list-style-type: none; + line-height: 280%; + margin: 0px 0px; + padding: 0px 0px; +} +.sidebar>div>a>span:hover{ + text-shadow: 5px 5px 15px black; + transition: all 0.1s linear; +} +.sidebar>div{ +overflow: hidden; +text-align: center; +cursor: pointer; +} +.sidebar>div>a>span{ +padding: 1rem; +color: var(--text-primary); +} +.active-scroll-spy{ + background-color: yellowgreen !important; +} + +/* * * * * * * */ +/* * GENERAL * */ +/* * * * * * * */ +.left-aligned {margin-left: auto;} +.align-items-center{align-items:center!important} +.zweirem{padding: 2rem;} +.left{float: left;} +.right{float: right;} +.sticky-top { + position: -webkit-sticky; + position: sticky !important; + top: 0; + z-index: 1020; + top: 80px !important; + overflow-wrap: break-word; +} +.fullsize{ + max-height: 100% !important; + max-width: 100% !important; +} +.null{ + padding: 0% !important; + margin: 0% !important; +} +.v-align-m{ + vertical-align: middle !important; +} +.fit-cont{ + max-width: fit-content; +} +.center{ + display: flex !important; + align-items: center !important; +} +/* * * * * * * */ +/* * NAVBAR * */ +/* * * * * * * */ +.nav-link{ + color: var(--text-primary) !important; +} +.nav-link:hover { + color: white !important; + background-color: var(--text-primary) !important; + border-radius: 7px; +} +.navbar{ + backdrop-filter: blur(5px); + transition: visibility 0s, 0.6s, opacity 0.6s linear, transform 1s; +} + nav.navbar { + padding-top: 1rem; + padding-bottom: 1rem; +} +.dropdown-item{ + color: var(--text-primary) !important; +} +.dropdown-item:hover{ + color: white !important; + background-color: var(--text-primary) !important; +} +.nav-item.dropdown:hover .dropdown-menu { + display: block; + background-color: white; + border-color: var(--text-primary); + border-radius: 7px; +} +.navbar-brand{ + color: var(--text-primary) !important; +} + + + +/* * * * * * * */ +/* BACKGROUND */ +/* * * * * * * */ +.bg-d{ + background-color: var(--text-primary) !important; +} +.bg-l{ + background-color: var(--text-primary) !important; + color: white; +} +.bg-transp{ + background:transparent; + color: var(--text-primary); +} + +/* * * * * * * */ +/* *HEADINGS * */ +/* * * * * * * */ +h2{ + font-size: 3rem !important; + -webkit-background-clip: text !important; + -webkit-text-stroke-width: 2px !important; + -webkit-text-stroke-color: var(--text-primary) !important; + background-clip: text !important; + color: transparent !important; + padding-top: 15px !important; + background-image: repeating-linear-gradient(-45deg, var(--text-primary) 0, var(--text-primary) 2px, white 2px, white 4px) !important; +} + +.underline--magical { + background-image: linear-gradient(120deg, var(--lightblue) 0%, var(--mediumpurple) 100%); + background-repeat: no-repeat; + color: black; + background-size: 100% 0.2em; + background-position: 0 105%; + transition: background-size 0.25s ease-in; +} +.underline--magical:hover { + background-size: 100% 100%; + color: black !important; + text-decoration: none !important; +} + + +/* * * * * * * */ +/* * CALLOUT * */ +/* * * * * * * */ +.bd-callout { + padding: 1.25rem; + margin-top: 1.25rem; + margin-bottom: 1.25rem; + border: 1px solid #e9ecef; + border-left-width: 0.25rem; + border-radius: 0.25rem; +} + +.bd-callout h4 { + margin-bottom: 0.25rem; +} + +.bd-callout p:last-child { + margin-bottom: 0; +} + +.bd-callout code { + border-radius: 0.25rem; +} + +.bd-callout + .bd-callout { + margin-top: -0.25rem; +} + +.bd-callout-info { + border-left-color: #ABD2FA; +} + +.bd-callout-warning { + border-left-color: #f0ad4e; +} + +.bd-callout-danger { + border-left-color: #d9534f; +} + +/* * * * * * * */ +/* * FOOTER * */ +/* * * * * * * */ +footer{ + background-color: var(--background); +} +footer a { + color: white; + font-weight: bold; + text-decoration: none; +} +footer a:hover { + color: white; + text-decoration: underline; +} + + +/* * * * * * * * */ +/*VILLAGE BUTTONS*/ +/* * * * * * * * */ +.bottom-buttons{ + margin-top: 50px !important; + margin-bottom: 20px; + padding-left: 30px; +} +.village-style-button{ + box-shadow: 5px 5px 15px gray !important; + border-radius: 2rem !important; + padding: 0 !important; + max-width: 12% !important; + aspect-ratio: 2 / 3 !important; + margin-left: 1vw; +} +.village-style-button:hover{ + box-shadow: 5px 5px 15px black !important; +} +.village-style-button h3{ + text-align: center !important; + font-size: 10px; + font-weight: bold; + color: #000; +} +.village-style-button img{ + max-width: 70% !important; + max-height: 70% !important; + padding-top: 20px !important; +} + +/* * * * * * * * * */ +/* * * IMAGES * * */ +/* * * * * * * * * */ +img, +picture, +svg { + max-width: 100%; + display: block; +} +img .middle{ + vertical-align:middle; +} +.socials{ + height: 1.5rem; + width: auto; + margin: 0.5rem; +} +.img-sponsor{ + max-width: 70%; + max-height: 150px; +} +.img-right{ + float: right !important; + padding-left: 1vw !important; + padding-bottom: 1vw !important; + padding-top: 1vw !important; +} +.img-half{ + max-width: 50% !important; +} + +.hexagon{ + clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%); +} + +/* * * * * * * */ +/* * * SVG * * */ +/* * * * * * * */ +svg{ + background:transparent +} +svg text{ + font-size: 7vw !important; + stroke-width:2px !important; + fill:var(--accent-primary) ; + stroke:var(--text-primary); + /*letter-spacing:4px;*/ + animation:effect 3s, dash 3s; +} +svg text:hover{ + fill: var(--text-primary) +} + + + + +/* * * * * * * */ +/* MEDIA RULES */ +/* * * * * * * */ +/*For tablet or bigger*/ +@media screen and (min-width: 992px) { + /* navbar opens on hover*/ + .dropdown:hover .dropdown-menu { + display: block; + } +} +/*For Tablet and smaller*/ +@media screen and (max-width: 992px){ + +} +/*For Smartphones*/ +@media screen and (max-width: 768px){ + svg text{ + font-size: 9vw; + stroke-width:1px; + } + .village-style-button h3{ + display: none !important; + } + .village-style-button{ + box-shadow: 1px 1px 1px gray; + border-radius: 10px; + border-color: black; + } + .village-style-button:hover{ + box-shadow: none; + } + .village-style-button img{ + max-width: 90%; + max-height: 90%; + padding-top: 10px; + padding-bottom: 5px; + } + .img-half{ + max-width: 100% !important; + } +} +/*Bigger than smartphones*/ +@media only screen and (min-width: 768px) { + .col-1 {width: 8.33%;} + .col-2 {width: 16.66%;} + .col-3 {width: 25%;} + .col-4 {width: 33.33%;} + .col-5 {width: 41.66%;} + .col-6 {width: 50%;} + .col-7 {width: 58.33%;} + .col-8 {width: 66.66%;} + .col-9 {width: 75%;} + .col-10 {width: 83.33%;} + .col-11 {width: 91.66%;} + .col-12 {width: 100%;} + + +} + + +/* * * * * * * */ +/* * EFFECTS * */ +/* * * * * * * */ +@keyframes effect{ + 0%{ + stroke-dasharray:0 70%; + } + 100%{ + stroke-dasharray:10% 0%; + stroke-dashoffset:20%; + } +} +@keyframes dash { + 0% { + stroke-dashoffset: 1300; + } + 35% { + fill-opacity: 0; + } + 50% { + stroke-dashoffset: 0; + } + 100% { + stroke-dashoffset: 0; + fill-opacity: 1; + } +} + + + + +/* TIMELINE EINS */ + +.timeline-container { + display: flex; + flex-direction: column; + position: relative; + margin: 40px 0; +} + +.timeline-container::after { + background-color: var(--text-primary); + position: absolute; + left: calc(50% - 2px); + content: ""; + width: 4px; + height: 100%; +} +.timeline-item { + display: flex; + justify-content: flex-end; + padding-right: 30px; + position: relative; + margin: 10px 0; + width: 50%; +} + +.timeline-item:nth-child(odd) { + align-self: flex-end; + justify-content: flex-start; + padding-left: 30px; + padding-right: 0; +} +.timeline-item-content { + box-shadow: 0 0 5px rgba(0, 0, 0, 0.3); + border-radius: 5px; + background-color: #fff; + display: flex; + flex-direction: column; + align-items: flex-end; + padding: 15px; + position: relative; + text-align: right; +} + +.timeline-item-content::after { + background-color: #fff; + box-shadow: 1px -1px 1px rgba(0, 0, 0, 0.2); + position: absolute; + right: -7.5px; + top: calc(50% - 7.5px); + transform: rotate(45deg); + width: 15px; + height: 15px; +} + +.timeline-item:nth-child(odd) .timeline-item-content { + text-align: left; + align-items: flex-start; +} + +.timeline-item:nth-child(odd) .timeline-item-content::after { + right: auto; + left: -7.5px; + box-shadow: -1px 1px 1px rgba(0, 0, 0, 0.2); +} +.timeline-item-content .tag { + color: #fff; + font-size: 12px; + font-weight: bold; + top: 5px; + left: 5px; + letter-spacing: 1px; + padding: 5px; + position: absolute; + text-transform: uppercase; +} + +.timeline-item:nth-child(odd) .timeline-item-content .tag { + left: auto; + right: 5px; +} + +.timeline-item-content time { + color: var(--lightblue); + font-size: 12px; + font-weight: bold; +} + +.timeline-item-content p { + font-size: 16px; + line-height: 24px; + margin: 15px 0; + max-width: 250px; +} + +.timeline-item-content a { + font-size: 14px; + font-weight: bold; +} + +.timeline-item-content a::after { + font-size: 12px; +} + +.timeline-item-content .circle { + background-color: #fff; + border: 3px solid var(--text-primary); + border-radius: 50%; + position: absolute; + top: calc(50% - 10px); + right: -40px; + width: 20px; + height: 20px; + z-index: 100; +} + +.timeline-item:nth-child(odd) .timeline-item-content .circle { + right: auto; + left: -40px; +} +@media only screen and (max-width: 1023px) { + .timeline-item-content { + max-width: 100%; + } +} + +@media only screen and (max-width: 767px) { + .timeline-item-content, + .timeline-item:nth-child(odd) .timeline-item-content { + padding: 15px 10px; + text-align: center; + align-items: center; + } + + .timeline-item-content .tag { + width: calc(100% - 10px); + text-align: center; + } + + .timeline-item-content time { + margin-top: 20px; + } + + .timeline-item-content a { + text-decoration: underline; + } + + .timeline-item-content a::after { + display: none; + } +} + + +.timeline { + background-color: inherit; + font-size: 1rem; + } + +.card { + border-radius: 4px; + background-color: #fff; + color: #333; + padding: 10px; + box-shadow: 0 4px 6px 0 hsla(0, 0%, 0%, 0.2); + width: 100%; + max-width: 560px; + +} +.date { + background-color: var(--text-primary) !important; + padding: 4px !important; + color: #fff !important; + border-radius: 4px !important; + font-weight: 500; + font-size: .85rem; +} +.imageAtom{ + object-fit: cover; + overflow: hidden; + width: 100%; + max-height: 400px; +} +.imageCredit { margin-top: 10px; font-size: 0.85rem } +.imageText { margin-bottom: 10px; font-size: 1rem } +.events{ padding: 10px } +.event { margin-bottom: 20px } + +.date-col{ + position: relative; + background-color: #fff ; + padding: 10px; + width: 10%; + border-right: #000; + border-right-width: 2px; +} + +.card-col{ + width: 100%; + display: flex; + flex-direction: column; + justify-content: flex-start; + flex-basis: 50%; + flex-grow: 1; + align-items: flex-end; +} + +.img-text{ + margin-bottom:10px; + font-size: 1rem; +} +.img-credit{ + margin-top: 10px; + font-size: 0.85rem; +} +.event{ + position: relative; + display: flex; + margin-top: 20px; +} +.img-timeline{ + overflow: hidden; + object-fit: cover; + width: 100%; + max-height: 400px; +} + + +/*PDF*/ +.container_document{ + max-width: 40%; +} + + +.download-butt{ + background-color: var(--text-primary); + padding: 0.5vw; + border-radius: 5px; + margin: auto !important; + width: fit-content !important; +} +.download-col{ + height: 5vw !important; + display: flex; + align-items: center !important; +} +.small-i{ + width: 80%; +} + + +/* SHAPES */ +.circle { + display: flex; + width: 10vw; + height: 10vw; + background-color: var(--lightblue) !important; + border-radius: 50%; + margin: 1vw; +} +.shape-text { + margin: auto; + text-align: center; +} \ No newline at end of file diff --git a/src/App/App.tsx b/src/App/App.tsx new file mode 100644 index 0000000000000000000000000000000000000000..3525633d51515258c52290e24266a5712f3389db --- /dev/null +++ b/src/App/App.tsx @@ -0,0 +1,100 @@ +import "./App.css"; +import "bootstrap/dist/css/bootstrap.min.css"; +import { Route, Routes } from "react-router-dom"; +import { Footer } from "../components/Footer.tsx"; +import { Header } from "../components/Header.tsx"; +import { NotFound } from "../components/NotFound.tsx"; +import { Navbar } from "../components/Navbar.tsx"; +import { getPathMapping } from "../utils/getPathMapping.ts"; +import { stringToSlug } from "../utils/stringToSlug.ts"; +import { useEffect} from "react"; +import Villbuttonrow from "../components/Villagebuttons.tsx"; +import Sidebar from "../components/Sidebar.tsx"; +import "../../utils/highlight.js"; +/* import Sidebar from "../../components/Sidebar.tsx"; */ + +const App = () => { + /* // Dark Mode toggle + const [isDarkMode, setIsDarkMode] = useState(true); + useEffect(() => { + document.body.className = isDarkMode ? 'dark-mode' : ''; + }, [isDarkMode]); */ + + const pathMapping = getPathMapping(); + const currentPath = + location.pathname + .split(`${stringToSlug(import.meta.env.VITE_TEAM_NAME)}`) + .pop() || "/"; + + // Set Page Title + const title = + currentPath in pathMapping ? pathMapping[currentPath].title : "Not Found"; + + useEffect(() => { + document.title = `${title || ""} | ${import.meta.env.VITE_TEAM_NAME} - iGEM ${import.meta.env.VITE_TEAM_YEAR}`; + }, [title]); + + return ( + <> + {/* Navigation */} + <Navbar /* isDarkMode={isDarkMode} setIsDarkMode={setIsDarkMode} */ /> + + {/* Header and PageContent */} + <Routes> + {Object.entries(pathMapping).map( + ([path, { navlist, title, lead, component: Component}]) => ( + <Route + key={path} + path={path} + element={ + <> + <Header title={title || ""} lead={lead || ""} /> + {/* Page content */} + <div className="container-fluid"> + <div className="row "> + <div className="col-2 d-none d-lg-block"> + <div className="sticky-top sidebar"> + <Sidebar nums={navlist || [""]}></Sidebar> + </div> + </div> + <div className="col"> + <Component /> + <Villbuttonrow/> + </div> + <div className="col-1 d-none d-lg-block" > + {/* <!-- empty so far --> */} + + </div> + </div> + </div> + + {/* End page content */} + + + </> + } + /> + ), + )} + <Route + path="*" + element={ + <> + <Header + title="Not Found" + lead="The requested URL was not found on this server." + /> + <NotFound /> + </> + } + /> + </Routes> + + {/* Footer */} + {/* MUST mention license AND have a link to team wiki's repository on gitlab.igem.org */} + <Footer /> + </> + ); +}; + +export default App; diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx new file mode 100644 index 0000000000000000000000000000000000000000..57e6f69f3eed06f2f22990b8b736045a8734e901 --- /dev/null +++ b/src/components/Footer.tsx @@ -0,0 +1,186 @@ +import { stringToSlug } from "../utils"; + + +export function Footer() { + const teamYear = import.meta.env.VITE_TEAM_YEAR; + const teamName = import.meta.env.VITE_TEAM_NAME; + const teamSlug = stringToSlug(teamName); + + + return ( + <footer className="pt-5 pb-5 footer py-5 mt-5 bg-d text-white"> + <div className="container"> + <div className="col zweirem"> + <span className="left"> + <a href="{{ url_for('pages', page='impressum') }}" className="middle">Impressum</a> + </span> + <span className="right"> + <a href="https://www.linkedin.com/in/igem-bielefeld-cebitec"> + <img className="socials" src="https://static.igem.wiki/teams/5247/design/icons/linkedin-hell.png"/> + </a> + <a href="https://www.instagram.com/igem.bielefeld/"> + <img className="socials" src="https://static.igem.wiki/teams/5247/design/icons/instagram-1.png"/> + </a> + <a href="http://www.youtube.com/@iGemBielefeld2024"> + <img className="socials" src="https://static.igem.wiki/teams/5247/design/icons/youtube-hell.png"/> + </a> + <a href="https://x.com/iGEM_Bielefeld"> + <img className="socials" src="https://static.igem.wiki/teams/5247/design/icons/twitter-hell.png"/> + </a> + </span> + </div> + <hr/> + <div className="row align-items-center"> + <div className="col-sm-4"> + <h6>Contact</h6> + </div> + <div className="col-sm-8"> + <div className="row align-items-center"> + <div className="col"> + <a href="https://2024.igem.wiki/bielefeld-cebitec/partners"> + <h6 className="alink">Spornsors and partners</h6> + </a> + </div> + <div className="col"> + <h6>Former iGem Bielefeld teams</h6> + </div> + </div> + </div> + </div> + <div className="row align-items-center"> + <div className="col-sm-4"> + <div className="col"> + info@igem-bielefeld.de + </div> + <div className="col"> </div> + <div className="col"> + iGEM2024 + Universitätsstraße 25, 33613 Bielefeld + Centrum für Biotechnologie (CeBiTec) + Universität Bielefeld + </div> + </div> + <div className="col-4"> + <div className="slick-carousel align-items-center"> + <a href="https://www.plasmidfactory.com/"> + <img className="img-sponsor" src="https://static.igem.wiki/teams/5247/sponsors/plasmidfactory.png"/> + </a> + <a href="http://www.zeiss.de/naturwissenschaften"> + <img className="img-sponsor" src="https://static.igem.wiki/teams/5247/sponsors/zeiss.png" /> + </a> + <a href="https://www.jenabioscience.com/"> + <img className="img-sponsor" src="https://static.igem.wiki/teams/5247/sponsors/jbs-dunkelgruen-text.png"/> + </a> + <a href="https://www.gip.com/home/"> + <img className="img-sponsor" src="https://static.igem.wiki/teams/5247/sponsors/gip.png" /> + </a> + <a href="https://www.integra-biosciences.com"> + <img className="img-sponsor" src="https://static.igem.wiki/teams/5247/sponsors/integra-hinterlegt.jpeg" /> + </a> + <a href="https://www.uni-bielefeld.de/"> + <img className="img-sponsor" src="https://static.igem.wiki/teams/5247/logos-team/uni-bielefeld-dunkel.png"/> + </a> + <a href="www.snapgene.com"> + <img className="img-sponsor" src="https://static.igem.wiki/teams/5247/sponsors/snapgene.png"/> + </a> + <a href="www.promega.com"> + <img className="img-sponsor" src="https://static.igem.wiki/teams/5247/sponsors/promega-gelb.png"/> + </a> + <a href="https://www.carlroth.de/"> + <img className="img-sponsor" src="https://static.igem.wiki/teams/5247/sponsors/roth.jpg"/> + </a> + <a href="https://bts-ev.de/"> + <img className="img-sponsor" src="https://static.igem.wiki/teams/5247/sponsors/bts.png"/> + </a> + <a href="https://www.uni-bielefeld.de/fakultaeten/technische-fakultaet/arbeitsgruppen/multiscale-bioengineering/campusbrauerei/"> + <img className="img-sponsor" src="https://static.igem.wiki/teams/5247/sponsors/campus-brauerei-hinterlegt.jpeg"/> + </a> + </div> + </div> + <div className="col-4"> + <div className="row align-items-center"> + + <div className="col"> + <div className="row align-items-center"> + <div className="col"> + <div className="col"> + <a href="https://2023.igem.wiki/bielefeld-cebitec/home"> Team 2023</a> + </div> + <div className="col"> + <a href="https://2021.igem.org/Team:Bielefeld-CeBiTec"> Team 2021</a> + </div> + <div className="col"> + <a href="https://2020.igem.org/Team:Bielefeld-CeBiTec"> Team 2020</a> + </div> + <div className="col"> + <a href="https://2019.igem.org/Team:Bielefeld-CeBiTec"> Team 2019</a> + </div> + <div className="col"> + <a href="https://2018.igem.org/Team:Bielefeld-CeBiTec"> Team 2018</a> + </div> + <div className="col"> + <a href="https://2017.igem.org/Team:Bielefeld-CeBiTec"> Team 2017</a> + </div> + <div className="col"> + <a href="https://2016.igem.org/Team:Bielefeld-CeBiTec"> Team 2016</a> + </div> + </div> + <div className="col"> + <div className="col"> + <a href="https://2015.igem.org/Team:Bielefeld-CeBiTec" className="alink"> Team 2015</a> + </div> + <div className="col"> + <a href="https://2014.igem.org/Team:Bielefeld-CeBiTec"> Team 2014</a> + </div> + <div className="col"> + <a href="https://2013.igem.org/Team:Bielefeld-Germany"> Team 2013</a> + </div> + <div className="col"> + <a href="https://2012.igem.org/Team:Bielefeld-Germany"> Team 2012</a> + </div> + <div className="col"> + <a href="https://2011.igem.org/Team:Bielefeld-Germany"> Team 2011</a> + </div> + <div className="col"> + <a href="https://2010.igem.org/Team:Bielefeld-Germany"> Team 2010</a> + </div> + </div> + </div> + </div> + </div> + </div> + </div> + <hr/> + + <hr /> + {/* The following MUST be on every page: license information and link to the repository on gitlab.igem.org */} + <div className="row mt-4"> + <div className="col"> + <p className="mb-0"> + <small> + © 2024 - Content on this site is licensed under a{" "} + <a + className="subfoot" + href="https://creativecommons.org/licenses/by/4.0/" + rel="license" + > + Creative Commons Attribution 4.0 International license + </a> + . + </small> + </p> + <p> + <small> + The repository used to create this website is available at{" "} + <a href={`https://gitlab.igem.org/${teamYear}/${teamSlug}`}> + gitlab.igem.org/{teamYear}/{teamSlug} + </a> + . + </small> + </p> + </div> + </div> + </div> + </footer> + ); +} diff --git a/src/components/Header.tsx b/src/components/Header.tsx new file mode 100644 index 0000000000000000000000000000000000000000..dbb8bb19c39f2572d6c550642a7350ecc8232ab7 --- /dev/null +++ b/src/components/Header.tsx @@ -0,0 +1,20 @@ +import { H1 } from './headings.tsx'; +interface HeaderProps { + title: string; + lead: string; +} + +export function Header({ title, lead }: HeaderProps) { + return ( + <header className="py-5 mb-5"> + <div className="container h-100"> + <div className="row h-100 align-items-center"> + <div className="col-lg-12"> + <H1 text={title}></H1> + <p className="lead mb-5">{lead}</p> + </div> + </div> + </div> + </header> + ); +} diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx new file mode 100644 index 0000000000000000000000000000000000000000..d554cb4ee61b2b2d7a8bedb12d2c5794abe53e05 --- /dev/null +++ b/src/components/Navbar.tsx @@ -0,0 +1,66 @@ +import Container from "react-bootstrap/Container"; +import Nav from "react-bootstrap/Nav"; +import BootstrapNavbar from "react-bootstrap/Navbar"; +import NavDropdown from "react-bootstrap/NavDropdown"; +import { Link } from "react-router-dom"; +import Pages from "../pages.ts"; + +export function Navbar(/* {isLightMode, setIsLightMode}:any */) { + + /* const handleToggle = () => { + setIsLightMode(!isLightMode); + }; */ + + const pages = Pages.map((item, pageIndex) => { + if ("folder" in item && item.folder) { + const folderItems = item.folder.map((subpage, subpageIndex) => { + if (subpage.path) { + return ( + <NavDropdown.Item + key={`subpage-${pageIndex}-${subpageIndex}`} + as={Link} + to={subpage.path} + > + {subpage.name} + </NavDropdown.Item> + ); + } + }); + return ( + <NavDropdown + key={`page-${pageIndex}`} + title={item.name} + renderMenuOnMount={true} + id="basic-nav-dropdown" + > + {folderItems} + </NavDropdown> + ); + } else if ("path" in item && item.path) { + return ( + <Nav.Link key={`page-${pageIndex}`} as={Link} to={item.path}> + {item.name} + </Nav.Link> + ); + } + }); + + return ( + <BootstrapNavbar className="navbar-custom" expand="lg" bg="bg-transparent" variant="light"/* bg={isLightMode ? "d" : "l"} variant={isLightMode ? "dark" : "light"} */fixed="top"> + <Container> + <BootstrapNavbar.Brand> + {import.meta.env.VITE_TEAM_NAME} + </BootstrapNavbar.Brand> + <BootstrapNavbar.Toggle aria-controls="basic-navbar-nav"/> + <BootstrapNavbar.Collapse id="basic-navbar-nav"> + <Nav className="left-aligned"> + {/* <button id="theme-toggle" className="btn btn-outline-secondary ml-auto" onClick={handleToggle}> + {isLightMode ? '🌙' : '🔆' } + </button> */} + {pages} + </Nav> + </BootstrapNavbar.Collapse> + </Container> + </BootstrapNavbar> + ); +} diff --git a/src/components/NotFound.tsx b/src/components/NotFound.tsx new file mode 100644 index 0000000000000000000000000000000000000000..e98efdc19d580a9b5dbd5ca9d8714d77ca17fa2c --- /dev/null +++ b/src/components/NotFound.tsx @@ -0,0 +1,16 @@ +import { Link } from "react-router-dom"; + +export function NotFound() { + return ( + <div className="d-flex flex-column justify-content-center align-items-center"> + <h1 className="not-found-title" style={{ fontSize: "100pt" }}> + 404 + </h1> + <div className="my-5"> + <Link to="/" className="btn btn-secondary btn-lg"> + Back to Home + </Link> + </div> + </div> + ); +} diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx new file mode 100644 index 0000000000000000000000000000000000000000..b214463cb817b8d3eef0d6c393d33152a2ce9efa --- /dev/null +++ b/src/components/Sidebar.tsx @@ -0,0 +1,109 @@ + + +import { useEffect } from 'react'; + +function SideItem({hesh, num}:{hesh: string; num: number}){ + let link = "#" + hesh + let subdi = "subtitle" + num + return( + <div> + <a href={link}> + <span id={subdi}> + {hesh} + </span> + </a> + </div> + ) +} + + + +/* LOading too slow... +function Sidebar(){ + const allWithClass = Array.from( + document.querySelectorAll('section') + ); + console.log(allWithClass); + let content = []; + for(let idx in allWithClass){ + const item = allWithClass[idx]; + const id = item.id; + console.log("idx: " + idx); + console.log("item: " + item); + console.log("id: " + id); + content.push(<SideItem key={id} hesh={id}></SideItem>); + } + return ( + <nav className="sidebar"> + {content} + </nav> + ) + +} */ + + + function Sidebar({nums}:{nums: Array<string>}){ + + + useEffect(() => { + window.addEventListener('scroll', handleScroll); + return () => window.removeEventListener('scroll', handleScroll); + }, []); + + const handleScroll = () => { + for(let idx in nums){ + const item = nums[idx]; + let ind = nums.findIndex((e) => e == item) + let subdi = "subtitle" + ind + Highlight({el: document.getElementById(item)!}, {subtitle: document.getElementById(subdi)!}); + } + + console.log("function HighlightCheck") + } + + + console.log(nums) + let content = []; + for(let idx in nums){ + const item = nums[idx]; + /* console.log("idx: " + idx); + console.log("item: " + item); */ + console.log("Type: " + typeof(document.getElementById(item))) + let ind = nums.findIndex((e) => e == item) + content.push(<SideItem key={item} hesh={item} num={ind}></SideItem>); + } + + return ( + <nav className="sidebar"> + {content} + </nav> + ) + + } + + + + function Highlight({el}: {el: HTMLElement | null}, {subtitle}:{subtitle: HTMLElement | null}){ + let TopDistance = 100; + if (el != null && subtitle != null){ + if (el.getBoundingClientRect().top < TopDistance + 1 && el.getBoundingClientRect().bottom > TopDistance){ + subtitle.style.color = "#FFF6F2"; + subtitle.style.backgroundColor = "#850F78"; + subtitle.style.borderRadius = "15px"; + subtitle.style.borderWidth = "15px"; + subtitle.style.borderColor = "#850F78"; +/* console.log("subtitle: ",subtitle) + console.log("style: ", subtitle.style) + console.log("color: ",subtitle.style.color) + console.log("backcolor: ",subtitle.style.backgroundColor) */ + } + else{ + subtitle.style.color = "#850F78"; + subtitle.style.backgroundColor = ""; + } + } + + } + + +export default Sidebar; \ No newline at end of file diff --git a/src/components/Villagebuttons.tsx b/src/components/Villagebuttons.tsx new file mode 100644 index 0000000000000000000000000000000000000000..1133d06f59f05621a2892f9eae31955630d91e12 --- /dev/null +++ b/src/components/Villagebuttons.tsx @@ -0,0 +1,27 @@ +import { Link } from "react-router-dom"; + +export function Villagebutton({ title, source, page }: { title: string; source: string; page:string}) { + return ( + <Link className="btn village-style-button" role="button" to={page}> + <img src={source} className="d-block mx-auto mb-2" alt=""></img> + <h3>{title}</h3> + </Link> + ) +} + +export function Villbuttonrow(){ + return( + <div className="row align-items-center bottom-buttons"> + <Villagebutton page="/human-practices" source="https://static.igem.wiki/teams/5247/design/icons/humanpractices.png" title="Human Practice"></Villagebutton> + <Villagebutton page="/team" source="https://static.igem.wiki/teams/5247/design/icons/team.png" title="Team"></Villagebutton> + <Villagebutton page="/experiments" source="https://static.igem.wiki/teams/5247/design/icons/lab.png" title="Experiments"></Villagebutton> + <Villagebutton page="/description" source="https://static.igem.wiki/teams/5247/design/icons/dna.png" title="Description"></Villagebutton> + <Villagebutton page="/engineering" source="https://static.igem.wiki/teams/5247/design/icons/genetic-engineering.png" title="Engineering"></Villagebutton> + <Villagebutton page="/safety" source="https://static.igem.wiki/teams/5247/design/icons/safety.png" title="Safety"></Villagebutton> + <Villagebutton page="/results" source="https://static.igem.wiki/teams/5247/design/icons/results.png" title="Results"></Villagebutton> + + </div> + ) +} + +export default Villbuttonrow; // Don’t forget to use export default! \ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000000000000000000000000000000000000..47cbbc68cd51c118a175e14ae15482dc3b4bd5a2 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,12 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App/App"; +import { BrowserRouter } from "react-router-dom"; + +ReactDOM.createRoot(document.getElementById("root")!).render( + <React.StrictMode> + <BrowserRouter basename={import.meta.env.BASE_URL}> + <App /> + </BrowserRouter> + </React.StrictMode>, +); diff --git a/src/utils/getPathMapping.ts b/src/utils/getPathMapping.ts new file mode 100644 index 0000000000000000000000000000000000000000..0e0415a6fc24463bda2aad34a7796df74ac68002 --- /dev/null +++ b/src/utils/getPathMapping.ts @@ -0,0 +1,36 @@ +import pages from "../pages.ts"; + +export const getPathMapping = () => { + return pages.reduce<{ + [key: string]: { + name: string | undefined; + title: string | undefined; + component: React.FC; + lead: string | undefined; + navlist: Array<string> | undefined; + }; + }>((map, item) => { + if ("path" in item && item.path && item.component) { + map[item.path] = { + name: item.name, + title: item.title, + component: item.component, + lead: item.lead, + navlist: item.navlist, + }; + } else if ("folder" in item && item.folder) { + item.folder.forEach((page) => { + if (page.path && page.component) { + map[page.path] = { + name: page.name, + title: page.title, + component: page.component, + lead: page.lead, + navlist: page.navlist, + }; + } + }); + } + return map; + }, {}); +}; diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..91290f5249e80844f03035c7eb4b7da5bd5f7243 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,2 @@ +export * from "./getPathMapping"; +export * from "./stringToSlug"; \ No newline at end of file diff --git a/src/utils/stringToSlug.ts b/src/utils/stringToSlug.ts new file mode 100644 index 0000000000000000000000000000000000000000..3fb7b55ea0fd10e2cdd901c982a323e6b5aa2c1a --- /dev/null +++ b/src/utils/stringToSlug.ts @@ -0,0 +1,9 @@ +export function stringToSlug(string: string): string { + let slug = String(string).toLowerCase(); + slug = slug.replace(/[^a-z0-9-]/g, "-"); + slug = slug.replace(/-+/g, "-"); + // remove dashes at start and end + const start = slug.search(/[^-]/); // find index of first non-dash + const end = slug.search(/-+$/); // find index of first end dash + return slug.substring(start, end === -1 ? undefined : end); +} diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..8b741f11b6fb76ed53d6daf408c7a360969c3883 --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1,13 @@ +/// <reference types="vite/client" /> + +interface ImportMetaEnv { + readonly VITE_TEAM_ID: string; + readonly VITE_TEAM_NAME: string; + readonly VITE_TEAM_YEAR: string; + } + + interface ImportMeta { + readonly env: ImportMetaEnv; + } + + \ No newline at end of file