first commit

This commit is contained in:
Joshua Seigler 2022-10-11 00:08:38 -04:00
commit c0216d7149
16 changed files with 10324 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
node_modules
dist
.cache

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load diff

BIN
.parcel-cache/data.mdb Normal file

Binary file not shown.

BIN
.parcel-cache/lock.mdb Normal file

Binary file not shown.

4618
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

57
package.json Normal file
View file

@ -0,0 +1,57 @@
{
"name": "seigler.github.io",
"version": "1.0.0",
"description": "GitHub personal site, to showcase projects in a more personalized way",
"source": "src/index.html",
"browserslist": "> 0.5%, last 2 versions, not dead",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "parcel",
"build": "parcel build"
},
"repository": {
"type": "git",
"url": "git+https://github.com/seigler/seigler.github.io.git"
},
"keywords": [
"websites"
],
"author": "Joshua Seigler",
"license": "MIT",
"bugs": {
"url": "https://github.com/seigler/seigler.github.io/issues"
},
"homepage": "https://github.com/seigler/seigler.github.io#readme",
"prettier": {
"arrowParens": "always",
"bracketSameLine": true,
"bracketSpacing": true,
"embeddedLanguageFormatting": "auto",
"endOfLine": "lf",
"htmlWhitespaceSensitivity": "css",
"insertPragma": false,
"jsxSingleQuote": false,
"printWidth": 80,
"proseWrap": "preserve",
"quoteProps": "as-needed",
"requirePragma": false,
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "none",
"useTabs": false,
"vueIndentScriptAndStyle": false
},
"devDependencies": {
"@types/http-link-header": "^1.0.3",
"@types/parse-link-header": "^2.0.0",
"buffer": "^6.0.3",
"parcel": "^2.7.0",
"typescript": "^4.8.4"
},
"dependencies": {
"@preact/signals": "^1.1.1",
"http-link-header": "^1.0.5",
"preact": "^10.11.1"
}
}

651
src/index.css Normal file
View file

@ -0,0 +1,651 @@
/*
Copied from https://github.com/kevquirk/simple.css/blob/a73f11a864cea3aab1e8d1c9807de2be8f428568/simple.css
My updates:
- use `#root` instead of `body`
- add .isActive to links
*/
/* Global variables. */
:root {
/* Set sans-serif & mono fonts */
--sans-font: -apple-system, BlinkMacSystemFont, 'Avenir Next', Avenir,
'Nimbus Sans L', Roboto, 'Noto Sans', 'Segoe UI', Arial, Helvetica,
'Helvetica Neue', sans-serif;
--mono-font: Consolas, Menlo, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
/* Default (light) theme */
--bg: #fff;
--accent-bg: #f5f7ff;
--text: #212121;
--text-light: #585858;
--border: #898ea4;
--accent: #0d47a1;
--code: #d81b60;
--preformatted: #444;
--marked: #ffdd33;
--disabled: #efefef;
}
/* Dark theme */
@media (prefers-color-scheme: dark) {
:root {
color-scheme: dark;
--bg: #212121;
--accent-bg: #2b2b2b;
--text: #dcdcdc;
--text-light: #ababab;
--accent: #ffb300;
--code: #f06292;
--preformatted: #ccc;
--disabled: #111;
}
/* Add a bit of transparency so light media isn't so glaring in dark mode */
img,
video {
opacity: 0.8;
}
}
/* Reset box-sizing */
:root {
box-sizing: border-box;
}
*,
::before,
::after {
box-sizing: inherit;
}
/* Reset default appearance */
textarea,
select,
input,
progress {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
}
html {
/* Set the font globally */
font-family: var(--sans-font);
scroll-behavior: smooth;
}
body {
margin: 0;
background-color: var(--bg);
}
/* Make the body a nice central block */
#root {
color: var(--text);
font-size: 1.15rem;
line-height: 1.5;
display: grid;
grid-template-columns: 1fr min(45rem, 90%) 1fr;
margin: 0;
}
#root > * {
grid-column: 2;
}
/* Make the header bg full width, but the content inline with body */
#root > header {
background-color: var(--accent-bg);
border-bottom: 1px solid var(--border);
text-align: center;
padding: 0 0.5rem 2rem 0.5rem;
grid-column: 1 / -1;
}
#root > header h1 {
max-width: 1200px;
margin: 1rem auto;
}
#root > header p {
max-width: 40rem;
margin: 1rem auto;
}
/* Add a little padding to ensure spacing is correct between content and header > nav */
main {
padding-top: 1.5rem;
}
#root > footer {
margin-top: 4rem;
padding: 2rem 1rem 1.5rem 1rem;
color: var(--text-light);
font-size: 0.9rem;
text-align: center;
border-top: 1px solid var(--border);
}
/* Format headers */
h1 {
font-size: 3rem;
}
h2 {
font-size: 2.6rem;
margin-top: 3rem;
}
h3 {
font-size: 2rem;
margin-top: 3rem;
}
h4 {
font-size: 1.44rem;
}
h5 {
font-size: 1.15rem;
}
h6 {
font-size: 0.96rem;
}
/* Prevent long strings from overflowing container */
p,
h1,
h2,
h3,
h4,
h5,
h6 {
overflow-wrap: break-word;
}
/* Fix line height when title wraps */
h1,
h2,
h3 {
line-height: 1.1;
}
/* Reduce header size on mobile */
@media only screen and (max-width: 720px) {
h1 {
font-size: 2.5rem;
}
h2 {
font-size: 2.1rem;
}
h3 {
font-size: 1.75rem;
}
h4 {
font-size: 1.25rem;
}
}
/* Format links & buttons */
a,
a:visited {
color: var(--accent);
}
a:hover {
text-decoration: none;
}
button,
[role='button'],
input[type='submit'],
input[type='reset'],
input[type='button'],
label[type='button'] {
border: none;
border-radius: 5px;
background-color: var(--accent);
font-size: 1rem;
color: var(--bg);
padding: 0.7rem 0.9rem;
margin: 0.5rem 0;
}
button[disabled],
[role='button'][aria-disabled='true'],
input[type='submit'][disabled],
input[type='reset'][disabled],
input[type='button'][disabled],
input[type='checkbox'][disabled],
input[type='radio'][disabled],
select[disabled] {
opacity: 0.5;
cursor: not-allowed;
}
input:disabled,
textarea:disabled,
select:disabled {
cursor: not-allowed;
background-color: var(--disabled);
}
input[type='range'] {
padding: 0;
}
/* Set the cursor to '?' on an abbreviation and style the abbreviation to show that there is more information underneath */
abbr[title] {
cursor: help;
text-decoration-line: underline;
text-decoration-style: dotted;
}
button:focus,
button:enabled:hover,
[role='button']:focus,
[role='button']:not([aria-disabled='true']):hover,
input[type='submit']:focus,
input[type='submit']:enabled:hover,
input[type='reset']:focus,
input[type='reset']:enabled:hover,
input[type='button']:focus,
input[type='button']:enabled:hover,
label[type='button']:focus,
label[type='button']:hover {
filter: brightness(1.4);
cursor: pointer;
}
/* Format navigation */
header > nav {
font-size: 1rem;
line-height: 2;
padding: 1rem 0 0 0;
}
/* Use flexbox to allow items to wrap, as needed */
header > nav ul,
header > nav ol {
align-content: space-around;
align-items: center;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
list-style-type: none;
margin: 0;
padding: 0;
}
/* List items are inline elements, make them behave more like blocks */
header > nav ul li,
header > nav ol li {
display: inline-block;
}
header > nav a,
header > nav a:visited {
margin: 0 0.5rem 1rem 0.5rem;
border: 1px solid var(--border);
border-radius: 5px;
color: var(--text);
display: inline-block;
padding: 0.1rem 1rem;
text-decoration: none;
}
header > nav a:hover {
border-color: var(--accent);
color: var(--accent);
cursor: pointer;
}
/* Reduce nav side on mobile */
@media only screen and (max-width: 720px) {
header > nav a {
border: none;
padding: 0;
text-decoration: underline;
line-height: 1;
}
}
/* Consolidate box styling */
aside,
details,
pre,
progress {
background-color: var(--accent-bg);
border: 1px solid var(--border);
border-radius: 5px;
margin-bottom: 1rem;
}
aside {
font-size: 1rem;
width: 30%;
padding: 0 15px;
margin-left: 15px;
float: right;
}
/* Make aside full-width on mobile */
@media only screen and (max-width: 720px) {
aside {
width: 100%;
float: none;
margin-left: 0;
}
}
article,
fieldset {
border: 1px solid var(--border);
padding: 1rem;
border-radius: 5px;
margin-bottom: 1rem;
}
article h2:first-child,
section h2:first-child {
margin-top: 1rem;
}
section {
border-top: 1px solid var(--border);
border-bottom: 1px solid var(--border);
padding: 2rem 1rem;
margin: 3rem 0;
}
/* Don't double separators when chaining sections */
section + section,
section:first-child {
border-top: 0;
padding-top: 0;
}
section:last-child {
border-bottom: 0;
padding-bottom: 0;
}
details {
padding: 0.7rem 1rem;
}
summary {
cursor: pointer;
font-weight: bold;
padding: 0.7rem 1rem;
margin: -0.7rem -1rem;
word-break: break-all;
}
details[open] > summary + * {
margin-top: 0;
}
details[open] > summary {
margin-bottom: 0.5rem;
}
details[open] > :last-child {
margin-bottom: 0;
}
/* Format tables */
table {
border-collapse: collapse;
display: block;
margin: 1.5rem 0;
overflow: auto;
width: 100%;
}
td,
th {
border: 1px solid var(--border);
text-align: left;
padding: 0.5rem;
}
th {
background-color: var(--accent-bg);
font-weight: bold;
}
tr:nth-child(even) {
/* Set every other cell slightly darker. Improves readability. */
background-color: var(--accent-bg);
}
table caption {
font-weight: bold;
margin-bottom: 0.5rem;
}
/* Format forms */
textarea,
select,
input {
font-size: inherit;
font-family: inherit;
padding: 0.5rem;
margin-bottom: 0.5rem;
color: var(--text);
background-color: var(--bg);
border: 1px solid var(--border);
border-radius: 5px;
box-shadow: none;
max-width: 100%;
display: inline-block;
}
label {
display: block;
}
textarea:not([cols]) {
width: 100%;
}
/* Add arrow to drop-down */
select:not([multiple]) {
background-image: linear-gradient(45deg, transparent 49%, var(--text) 51%),
linear-gradient(135deg, var(--text) 51%, transparent 49%);
background-position: calc(100% - 15px), calc(100% - 10px);
background-size: 5px 5px, 5px 5px;
background-repeat: no-repeat;
padding-right: 25px;
}
/* checkbox and radio button style */
input[type='checkbox'],
input[type='radio'] {
vertical-align: middle;
position: relative;
width: min-content;
}
input[type='checkbox'] + label,
input[type='radio'] + label {
display: inline-block;
}
input[type='radio'] {
border-radius: 100%;
}
input[type='checkbox']:checked,
input[type='radio']:checked {
background-color: var(--accent);
}
input[type='checkbox']:checked::after {
/* Creates a rectangle with colored right and bottom borders which is rotated to look like a check mark */
content: ' ';
width: 0.18em;
height: 0.32em;
border-radius: 0;
position: absolute;
top: 0.05em;
left: 0.17em;
background-color: transparent;
border-right: solid var(--bg) 0.08em;
border-bottom: solid var(--bg) 0.08em;
font-size: 1.8em;
transform: rotate(45deg);
}
input[type='radio']:checked::after {
/* creates a colored circle for the checked radio button */
content: ' ';
width: 0.25em;
height: 0.25em;
border-radius: 100%;
position: absolute;
top: 0.125em;
background-color: var(--bg);
left: 0.125em;
font-size: 32px;
}
/* Makes input fields wider on smaller screens */
@media only screen and (max-width: 720px) {
textarea,
select,
input {
width: 100%;
}
}
/* Set a height for color input */
input[type='color'] {
height: 2.5rem;
padding: 0.2rem;
}
/* do not show border around file selector button */
input[type='file'] {
border: 0;
}
/* Misc body elements */
hr {
border: none;
height: 1px;
background: var(--border);
margin: 1rem auto;
}
mark {
padding: 2px 5px;
border-radius: 4px;
background-color: var(--marked);
}
img,
video {
max-width: 100%;
height: auto;
border-radius: 5px;
}
figure {
margin: 0;
text-align: center;
}
figcaption {
font-size: 0.9rem;
color: var(--text-light);
margin-bottom: 1rem;
}
blockquote {
margin: 2rem 0 2rem 2rem;
padding: 0.4rem 0.8rem;
border-left: 0.35rem solid var(--accent);
color: var(--text-light);
font-style: italic;
}
cite {
font-size: 0.9rem;
color: var(--text-light);
font-style: normal;
}
dt {
color: var(--text-light);
}
/* Use mono font for code elements */
code,
pre,
pre span,
kbd,
samp {
font-family: var(--mono-font);
color: var(--code);
}
kbd {
color: var(--preformatted);
border: 1px solid var(--preformatted);
border-bottom: 3px solid var(--preformatted);
border-radius: 5px;
padding: 0.1rem 0.4rem;
}
pre {
padding: 1rem 1.4rem;
max-width: 100%;
overflow: auto;
color: var(--preformatted);
}
/* Fix embedded code within pre */
pre code {
color: var(--preformatted);
background: none;
margin: 0;
padding: 0;
}
/* Progress bars */
/* Declarations are repeated because you */
/* cannot combine vendor-specific selectors */
progress {
width: 100%;
}
progress:indeterminate {
background-color: var(--accent-bg);
}
progress::-webkit-progress-bar {
border-radius: 5px;
background-color: var(--accent-bg);
}
progress::-webkit-progress-value {
border-radius: 5px;
background-color: var(--accent);
}
progress::-moz-progress-bar {
border-radius: 5px;
background-color: var(--accent);
transition-property: width;
transition-duration: 0.3s;
}
progress:indeterminate::-moz-progress-bar {
background-color: var(--accent-bg);
}
a.isActive {
background-color: var(--accent);
color: var(--bg) !important;
}

14
src/index.html Normal file
View file

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Joshua Seigler&rsquo;s GitHub Overview</title>
<link rel="stylesheet" href="index.css" />
</head>
<body>
<div id="root"></div>
<script src="./index.tsx" type="module"></script>
</body>
</html>

78
src/index.tsx Normal file
View file

@ -0,0 +1,78 @@
import { h, Fragment, render } from 'preact'
import { useEffect } from 'preact/hooks'
import { computed, signal } from '@preact/signals'
import { RepoCard } from './repoCard'
import * as LinkHeader from 'http-link-header'
export type GithubRepo = {
id: number
name: string
full_name: string
html_url: string
created_at: string
homepage: string | null
stargazers_count: number
topics: string[]
fork: boolean
}
const repos = signal<GithubRepo[]>([])
const topics = computed(() => {
const map = repos.value.reduce((map, repo) => {
repo.topics.forEach((topic) => map.set(topic, 1 + (map.get(topic) ?? 0)))
return map
}, new Map<string, number>())
return [...map.entries()]
.map(([topic, count]) => ({ topic, count }))
.filter(({ count }) => count > 1)
})
const isLoading = signal(true)
function fetchDataUntilNoNext(uri: string) {
fetch(uri)
.then((response) => {
const link = LinkHeader.parse(response.headers.get('Link'))
const [rel] = link.rel('next')
if (rel) {
const { uri } = rel
fetchDataUntilNoNext(uri)
} else {
isLoading.value = false
}
return response.json()
})
.then((data) => {
repos.value = repos.value.concat(data)
})
.catch((reason) => console.error("Couldn't fetch repos because:", reason))
}
function App() {
useEffect(
() =>
fetchDataUntilNoNext(
'https://api.github.com/users/seigler/repos?per_page=30'
),
[]
)
return (
<>
<header>
<h1>Joshua Seigler's github repos</h1>
</header>
<main>
{topics.value.map(({ topic, count }) => (
<div>
{topic}: {count}
</div>
))}
{repos.value.map((repo) => (
<RepoCard repo={repo} />
))}
</main>
</>
)
}
render(<App />, document.getElementById('root'))

6
src/repoCard.tsx Normal file
View file

@ -0,0 +1,6 @@
import { h, Fragment, render } from 'preact'
import { GithubRepo } from '.'
export function RepoCard({ repo }: { repo: GithubRepo }) {
return <div>{repo.full_name}</div>
}

14
tsconfig.json Normal file
View file

@ -0,0 +1,14 @@
{
"compilerOptions": {
"outDir": "./dist",
"sourceMap": true,
"noImplicitAny": true,
"module": "es6",
"moduleResolution": "node",
"target": "es6",
"jsx": "react",
"jsxFactory": "h",
"jsxFragmentFactory": "Fragment"
},
"include": ["./src/**/*"]
}