This commit is contained in:
Camerin 2022-01-07 18:15:13 -05:00
commit e7159f2bb2
16 changed files with 338 additions and 88 deletions

File diff suppressed because one or more lines are too long

View File

@ -41,4 +41,16 @@
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
<!-- Hotjar Tracking Code for https://www.camscode.com -->
<script>
(function(h,o,t,j,a,r){
h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
h._hjSettings={hjid:2739788,hjsv:6};
a=o.getElementsByTagName('head')[0];
r=o.createElement('script');r.async=1;
r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;
a.appendChild(r);
})(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');
</script>
</html>

View File

@ -4,6 +4,7 @@ import "./css/App.css";
import Navigation from './Navigation';
import Github from './Github';
import Articles from './Articles';
import ArticleEditor from './ArticleEditor';
import About from './About';
import Intro from './Intro';
import Bai from './Bai';
@ -20,6 +21,7 @@ const App = (props) => {
<Route path="/about" component={About} />
<Route path="/articles" component={Articles} />
<Route path="/bai" component={Bai} />
<Route path="/articleEditor" component={ArticleEditor} />
</Switch>
</div>
</div>

View File

@ -1,36 +1,147 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import Theater from './subcomponents/Theater';
import './css/Articles.css';
const Article = ({article}) => {
const [show, setShow] = useState("");
const [currArticle, setCurrArticle] = useState("");
useEffect(()=>{
if (currArticle === ""){ // If no articles have been opened
if (show === "") {
// Set the current article and initiate the open animation
setCurrArticle(article);
setShow("open");
// Once the animation runs, open the entire article
setTimeout(() =>{
setShow("show");
}, 1024)
}
} else if (currArticle !== article) { // If we've changed articles
// Change currArticle to the actual current article and initiate close animation
setCurrArticle(article);
setShow("close");
// Step through the close animation, open animation, and fully opening the article
setTimeout(()=>{
setShow("");
setTimeout(()=>{
setShow("open");
setTimeout(() =>{
setShow("show");
}, 1024);
}, 1024);
}, 24);
}
}, [currArticle, article, show]);
let linkProcessor = (text) => {
/**
* Given some text, processes and returns jsx with any link represented as an anchor
*/
let output = [""]; // Stores all text in a list
let loc = 0; // Stores the current location in output that we're working with
let tmp;
let i = 0;
console.log(text);
while (i < text.length) { // Iterate through the entire text string
if (text.slice(i, i+4) === "http"){ // slice from i to 4 chars plus and check for http
let x = i; // store i in x so the location is not modified
for (let y = i; ![" ", "\n"].includes(text[y]) && y < text.length; y++){
i=y;
} // iterate until we find the end of the link denoted by a space
if (output[loc] !== "") { // if the current output location isn't empty, increment loc
loc++;
}
tmp = text.slice(x, i+1);
// Put anchor for link into output list
output[loc] = <a key={i} href={tmp}>{tmp}</a>;
output[++loc] = ""; // Create new location in output with empty string
} else {
// Append current char to output
output[loc] += text[i];
}
i++;
}
// Return the output
return output;
};
let newLineProcessor = (text) => {
/**
* Given some text, processes and returns jsx with line breaks
*/
let tmp;
let output = [""]; // Stores all text in a list
let loc = 0; // Stores the current location in output that we're working with
for (let i = 0; i < text.length; i++) { // Iterate through the entire text string
if (!React.isValidElement(text[i])) {
tmp = [""];
loc = 0;
for (let j = 0; j < text[i].length; j++){
if (text[i].slice(j, j+1) === "\n"){ // slice from i to 4 chars plus and check for http
if (tmp[loc] !== "") { // if the current output location isn't empty, increment loc
loc++;
}
tmp[loc] = <br key={i+j}/>;
tmp[++loc] = "";
} else {
// Append current char to output
tmp[loc] += text[i][j];
}
}
tmp[++loc] = "";
} else {
tmp = text[i]
}
output.push(tmp)
}
// Return the output
return output;
};
let articleFormatter = (text) => {
let output = [];
let type = [];
let ind = 0;
let tick=false;
let delimiters = ['', '`', '*'];
let output = [""]; // Used to store separate formatted text
let type = []; // Parallel to output list to signify format type
let ind = 0; // Denote index of output
let tick=false; // used to check if we're currently in formatted text.
let delimiters = ['', '`', '*', '~']; // Denotes characters used to format
let tmp;
for (let i = 0; i < text.length; i++) { // Iterate through input
console.log(text[i]);
if (delimiters.indexOf(text[i]) !== -1) { // Detect Code Delimiter
if (delimiters.indexOf(text[i]) >= 0) { // Detect Code Delimiter
console.log(text[i])
if (tick) { // Close the code section
console.log(1);
output[++ind] = ""
tick = false;
tmp = "";
} else { // Start a new code section
console.log(2);
} else { // Start a new code section
type.push(delimiters.indexOf(text[i]));
if (!output[ind]) {
if (output.length < ind) {
output[ind] = "";
} else if (output.length < type.length) {
output[++ind] = "";
}
tmp = "";
tick = true;
}
@ -40,36 +151,41 @@ const Article = ({article}) => {
if (output.length > type.length) { // If this is the beggining of a default text type
type.push(0);
output[ind] = ""
//output[ind] = text[i]
} else if (output.length < type.length) {
output[++ind] = ""
}
console.log(3);
output[ind] += text[i]
tmp = text[i]
}
}
console.log(output);
console.log(type);
output[ind] += tmp
}
return [...output.keys()].map((i)=>{ // Format text and return as jsx
let text = linkProcessor(output[i]); // Process links
text = newLineProcessor(text);
if (type[i] === 0){ // Return default text type
return <div key={i}>{output[i]}</div>;
return <div key={i}>{text}</div>;
} else if(type[i] === 1) { // Return Code text type
return <div className="code" key={i}>{output[i]}</div>;
return <div className="code" key={i}>{text}</div>;
} else if (type[i] === 2) {
return <div className="h1" key={i}>{output[i]}</div>;
return <div className="h1" key={i}>{text}</div>;
}else {
} else if (type[i] === 3) {
return <li className="li" key={i}>{text}</li>;
} else {
return <div key={i}></div>;
}
});
@ -77,7 +193,7 @@ const Article = ({article}) => {
};
return (
<div className="article">
<div className={"article " + show}>
<Theater
title={article.title}
description={article.desc}

View File

@ -0,0 +1,34 @@
import React, { useState } from 'react';
import Article from './Article';
import "./css/ArticleEditor.css";
const ArticleEditor = (props) => {
const [content, setContent] = useState("Hello World");
let article = {
"id":"0",
"title": "Article Editor",
"desc":"This is a place to edit articles",
"contents": content
};
let copyToClipboard = () => {
navigator.clipboard.writeText(content).then(function() {
console.log('Async: Copying to clipboard was successful!');
}, function(err) {
console.error('Async: Could not copy text: ', err);
});
};
return (
<div className="ArticleEditor">
<div id="toolbar" className="toolbar">
<div className="btn" onClick={copyToClipboard()}>Copy to Clipboard</div>
</div>
<Article article={article}/>
<textarea onInput={e=>{setContent(e.target.value)}}></textarea>
</div>
);
};
export default ArticleEditor;

View File

@ -51,6 +51,8 @@ class Github extends React.Component {
if (this.props.repos.length > 0) {
// Render each repo
const render = this.props.repos.map((repo) =>{
let updated = (new Date (repo.updated_at)).toLocaleString();
let created = (new Date (repo.created_at)).toLocaleString();
return (
<div className="repo" key={repo.id}>
@ -74,6 +76,11 @@ class Github extends React.Component {
this.renderLanguages(repo.name) // Render each language for the repo
}
</div>
<div className="times">
Last Updated: {updated}
<br/>
Created: {created}
</div>
</div>
);

View File

@ -1,7 +1,6 @@
import React from 'react';
import { connect } from 'react-redux';
import './css/Intro.css';
import {hideNavigation, showNavigation} from '../actions';
import Topic from './subcomponents/Topic';
import { ChevronDoubleUp, ChevronDoubleDown } from 'react-bootstrap-icons';
@ -108,8 +107,6 @@ class Intro extends React.Component {
} else if (input === "down" && this.state.nextLoc >= this.topics.length) {
// Set something to happen when you reach the end
this.props.showNavigation();
localStorage.setItem('intro', true);
}
};
@ -123,11 +120,6 @@ class Intro extends React.Component {
document.title = "HomePage"; // Set document title
// Hide the navigation
if (!localStorage.getItem('intro')) {
this.props.hideNavigation();
}
}
// Topics to display in intro
@ -137,7 +129,7 @@ class Intro extends React.Component {
<div>
This website helps you access the projects that I've worked on. You can navigate
at the top to different locations in the site. Within you can find information about me,
my github repositories, and some youtube videos I've posted. This website is coded with
my github repositories, and articles I've posted. This website is coded with
React/Redux and hosted over Vercel. You can email me at <a href="mailto:cam@camscode.com">cam@camscode.com</a>.
</div>
</Topic>,
@ -178,4 +170,4 @@ const mapStateToProps = (state) => {
}
export default connect(mapStateToProps, {hideNavigation, showNavigation})(Intro);
export default connect(mapStateToProps, {})(Intro);

View File

@ -7,39 +7,36 @@ import { HouseDoor, FileEarmarkPerson, Github, Envelope, Book } from 'react-boot
import ContactModal from './subcomponents/ContactModal';
const Navigation = (props) => {
if (props.enable){
return (
<div className="Navigation">
<Link to="/">
Home
<HouseDoor />
</Link>
<Link to="/github">
Github
<Github />
</Link>
<Link to="/articles">
Articles
<Book />
</Link>
<Link to="/about">
About
<FileEarmarkPerson />
</Link>
<button className="end" onClick={()=>props.toggleContactModal()}>
Contact Me
<Envelope />
</button>
<ContactModal show={props.modal}/>
</div>
);
}
return (
<div className="Navigation">
<Link to="/">
Home
<HouseDoor />
</Link>
<Link to="/github">
Github
<Github />
</Link>
<Link to="/articles">
Articles
<Book />
</Link>
<Link to="/about">
About
<FileEarmarkPerson />
</Link>
<button className="end" onClick={()=>props.toggleContactModal()}>
Contact Me
<Envelope />
</button>
<ContactModal show={props.modal}/>
</div>
);
return <div></div>;
}
const mapStateToProps = (state) => {
return {modal: state.contactModal.contactModal, enable: state.navigation.enable};
return {modal: state.contactModal.contactModal};
}
export default connect(mapStateToProps, {toggleContactModal})(Navigation);
export default connect(mapStateToProps, {toggleContactModal})(Navigation);

View File

@ -31,7 +31,7 @@
display:flex;
flex-direction:row;
align-content:center;
justify-content:flex-start;
justify-content:space-evenly;
align-items:center;
flex-wrap: wrap;
width:85vw;
@ -44,4 +44,10 @@
.About .links * {
margin: 10px;
}
@media only screen and (max-width: 600px) {
.About .links .link img {
width: 25vw;
}
}

View File

@ -0,0 +1,42 @@
.ArticleEditor textarea {
width:100%;
height: 35vh;
border-style: solid;
border-color: lightgray;
border-radius: 2px;
border-width: 2px;
padding: 0.4em 0.4em 0.4em 0;
text-align: left;
}
.ArticleEditor .toolbar {
width: 100%;
height: 56px;
display: flex;
}
.ArticleEditor .toolbar .btn {
color: white;
background-color: darkgreen;
text-align: center;
align-items:center;
justify-content: center;
display: flex;
padding-left: 3px;
padding-right: 3px;
}
.ArticleEditor .toolbar .btn:hover{
background-color: #005400
}
.ArticleEditor .article .open {
max-height:100vh;
}
.ArticleEditor .article .Close {
max-height:100vh;
}
.ArticleEditor .article {
max-height:100vh;
}

View File

@ -3,24 +3,51 @@
align-items: center;
display: flex;
flex-direction: column;
max-height:0;
overflow: hidden;
transition: max-height 1s ease;
}
.article.open {
max-height:100vh;
transition: max-height 1s ease;
}
.article.close {
max-height:100vh;
}
.article.show {
max-height:none;
}
.article .content {
width: 80vw;
text-align:left;
line-height: 1.5;
}
.article .code {
padding: 10px;
background-color: #3A3B3B;
color: gray;
color: lightgray;
border-radius: 5px;
margin: 10px;
margin: 20px;
}
.article .h1 {
font-size: 24px;
font-weight:bolder;
margin-top: 10px;
margin-top: 20px;
margin-bottom: 10px;
}
.article .li {
margin-left: 10px;
}
.article a {
color: #ACACFF;
}

View File

@ -61,7 +61,7 @@
.repo .description {
flex-grow: 1;
padding:10px;
padding:5px;
}
.repo .languages {
@ -82,6 +82,14 @@
color: #DAD4DF;
}
.repo .times {
padding: 5px;
margin: 2px;
margin-left: 5px;
width: 100%;
text-align: start;
}
.content {
margin: 25px;
text-align: center;
@ -95,4 +103,11 @@
border-radius: 10rem;
height: 10rem;
width: 10rem;
}
@media only screen and (max-width: 600px) {
.repo .content {
flex-direction: column;
}
}

View File

@ -15,7 +15,7 @@
.theater-bg {
height: 100%;
height: inherit;
width:100%;
background-size: cover;
background-image: url('../../img/background.webp');

View File

@ -1,4 +1,5 @@
import React from 'react';
import { Link } from 'react-router-dom';
import '../css/Listing.css';
const Listing = (props) => {
@ -11,12 +12,12 @@ const Listing = (props) => {
*/
return (
<div className="listing">
<a href={props.link}>
<Link to={props.link}>
<div className="title">{props.title}</div>
<div className="content">
{props.children}
</div>
</a>
</Link>
</div>
);
};

View File

@ -3,7 +3,6 @@ import { combineReducers } from "redux";
import githubReducer from './githubReducer';
import contactModalReducer from "./contactModalReducer";
import introReducer from "./introReducer";
import navigationReducer from "./navigationReducer";
import articlesReducer from "./articlesReducer";
import modelReducer from "./modelReducer";
@ -11,7 +10,6 @@ export default combineReducers({
github: githubReducer,
contactModal: contactModalReducer,
intro: introReducer,
navigation: navigationReducer,
articles: articlesReducer,
model: modelReducer,
});

View File

@ -1,11 +0,0 @@
let navigationReducer = (state={enable: true}, action) => {
switch(action.type) {
case 'SET_NAVIGATION':
return { ...state, enable: action.payload };
default:
return state;
}
};
export default navigationReducer;