chore: apply automatic linting

This commit is contained in:
Corbin Crutchley
2022-09-25 05:51:04 -07:00
parent 5d628203d3
commit 3900a13481
92 changed files with 4787 additions and 4694 deletions

View File

@@ -1,46 +1,46 @@
const tsRules = {
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/no-empty-interface": "off"
}
"@typescript-eslint/no-empty-interface": "off",
};
module.exports = {
env: {
node: true,
browser: true,
},
extends: ['eslint:recommended', 'plugin:astro/recommended'],
extends: ["eslint:recommended", "plugin:astro/recommended"],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
ecmaVersion: "latest",
sourceType: "module",
},
rules: {
"no-unused-vars": "off"
"no-unused-vars": "off",
},
overrides: [
{
files: ['*.astro'],
parser: 'astro-eslint-parser',
files: ["*.astro"],
parser: "astro-eslint-parser",
parserOptions: {
parser: '@typescript-eslint/parser',
extraFileExtensions: ['.astro'],
parser: "@typescript-eslint/parser",
extraFileExtensions: [".astro"],
},
rules: {
...tsRules
...tsRules,
},
},
{
files: ['*.ts'],
parser: '@typescript-eslint/parser',
extends: ['plugin:@typescript-eslint/recommended'],
files: ["*.ts"],
parser: "@typescript-eslint/parser",
extends: ["plugin:@typescript-eslint/recommended"],
rules: {
...tsRules
...tsRules,
},
},
{
// Define the configuration for `<script>` tag.
// Script in `<script>` is assigned a virtual file name with the `.js` extension.
files: ['**/*.astro/*.js', '*.astro/*.js'],
parser: '@typescript-eslint/parser',
files: ["**/*.astro/*.js", "*.astro/*.js"],
parser: "@typescript-eslint/parser",
},
],
};

View File

@@ -17,7 +17,7 @@ import { rehypeExcerpt } from "./src/utils/markdown/rehype-excerpt";
import { rehypeUnicornPopulatePost } from "./src/utils/markdown/rehype-unicorn-populate-post";
import { rehypeWordCount } from "./src/utils/markdown/rehype-word-count";
import { rehypeUnicornGetSuggestedPosts } from "./src/utils/markdown/rehype-unicorn-get-suggested-posts";
import copy from 'rollup-plugin-copy'
import copy from "rollup-plugin-copy";
// TODO: Create types
import behead from "remark-behead";
@@ -34,18 +34,18 @@ export default defineConfig({
plugins: [
{
...copy({
hook: 'options',
hook: "options",
flatten: false,
targets: [
{
src: 'content/**/*',
dest: 'public/content'
}
]
src: "content/**/*",
dest: "public/content",
},
],
}),
enforce: 'pre'
}
]
enforce: "pre",
},
],
},
markdown: {
mode: "md",
@@ -103,13 +103,16 @@ export default defineConfig({
{
maxHeight: 768,
maxWidth: 768,
}
},
],
rehypeUnicornElementMap,
[rehypeExcerpt, {
maxLength: 150
}],
rehypeWordCount
[
rehypeExcerpt,
{
maxLength: 150,
},
],
rehypeWordCount,
],
} as AstroUserConfig["markdown"] as never,
});

View File

@@ -13,12 +13,7 @@
"pronouns": "they/themselves",
"profileImg": "./crutchcorn.png",
"color": "#ba68c8",
"roles": [
"devops",
"developer",
"author",
"community"
]
"roles": ["devops", "developer", "author", "community"]
},
{
"id": "fennifith",
@@ -33,11 +28,7 @@
"pronouns": "he",
"profileImg": "./fennifith.jpg",
"color": "#0091EA",
"roles": [
"developer",
"author",
"community"
]
"roles": ["developer", "author", "community"]
},
{
"id": "evelynhathaway",
@@ -52,11 +43,7 @@
"pronouns": "she",
"profileImg": "proud.png",
"color": "#ef5f17",
"roles": [
"developer",
"devops",
"community"
]
"roles": ["developer", "devops", "community"]
},
{
"id": "adueppen",
@@ -72,10 +59,7 @@
"pronouns": "he",
"profileImg": "./adueppen.png",
"color": "#69ffff",
"roles": [
"developer",
"community"
]
"roles": ["developer", "community"]
},
{
"id": "zavukodlak",
@@ -91,9 +75,7 @@
"pronouns": "he",
"profileImg": "./vukashin.png",
"color": "#3485FF",
"roles": [
"designer"
]
"roles": ["designer"]
},
{
"id": "tommyemo",
@@ -108,9 +90,7 @@
"pronouns": "he",
"profileImg": "./tommyemo.jpg",
"color": "#8539EB",
"roles": [
"designer"
]
"roles": ["designer"]
},
{
"id": "edpratti",
@@ -125,10 +105,7 @@
"pronouns": "he",
"profileImg": "./edpratti.jpg",
"color": "#FF3300",
"roles": [
"designer",
"author"
]
"roles": ["designer", "author"]
},
{
"id": "sarsamurmu",
@@ -144,9 +121,7 @@
"pronouns": "he",
"profileImg": "./sarsamurmu.png",
"color": "#7C4DFF",
"roles": [
"developer"
]
"roles": ["developer"]
},
{
"id": "MDutro",
@@ -160,11 +135,7 @@
"pronouns": "he",
"profileImg": "./mdutro.jpg",
"color": "#7C4DFF",
"roles": [
"developer",
"author",
"community"
]
"roles": ["developer", "author", "community"]
},
{
"id": "reikaze",
@@ -179,9 +150,7 @@
"pronouns": "he",
"profileImg": "./reikaze.jpg",
"color": "#ba68c8",
"roles": [
"author"
]
"roles": ["author"]
},
{
"id": "thodges314",
@@ -196,9 +165,7 @@
"pronouns": "he",
"profileImg": "./thodges.png",
"color": "#ba68c8",
"roles": [
"author"
]
"roles": ["author"]
},
{
"id": "skatcat31",
@@ -213,10 +180,7 @@
"color": "#ba68c8",
"profileImg": "./hello.png",
"pronouns": "he",
"roles": [
"author",
"community"
]
"roles": ["author", "community"]
},
{
"id": "seanmiller",
@@ -233,9 +197,7 @@
"pronouns": "he",
"profileImg": "./seanmiller.jpg",
"color": "#551a8b",
"roles": [
"author"
]
"roles": ["author"]
},
{
"id": "pierremtb",
@@ -252,9 +214,7 @@
"pronouns": "he",
"profileImg": "./pierremtb.jpg",
"color": "#FFEB3B",
"roles": [
"author"
]
"roles": ["author"]
},
{
"id": "maisydino",
@@ -270,10 +230,7 @@
"pronouns": "she",
"profileImg": "./maisydino.jpg",
"color": "#FDF6E3",
"roles": [
"author",
"community"
]
"roles": ["author", "community"]
},
{
"id": "bobrossrtx",
@@ -289,10 +246,7 @@
"pronouns": "he",
"profileImg": "./bobrossrtx.jpg",
"color": "#b7e11e",
"roles": [
"developer",
"author"
]
"roles": ["developer", "author"]
},
{
"id": "ljtech",
@@ -309,9 +263,7 @@
"pronouns": "he",
"profileImg": "./ljtechdotca.png",
"color": "#7b61ff",
"roles": [
"author"
]
"roles": ["author"]
},
{
"id": "SkyHawk_0",
@@ -323,9 +275,7 @@
"pronouns": "he",
"profileImg": "./goofy.png",
"color": "#18BBC9",
"roles": [
"author"
]
"roles": ["author"]
},
{
"id": "splatkillwill",
@@ -342,9 +292,7 @@
"pronouns": "they/themselves",
"profileImg": "./splatkillwill.jpg",
"color": "#BF00FF",
"roles": [
"author"
]
"roles": ["author"]
},
{
"id": "fmothe",
@@ -360,9 +308,7 @@
"pronouns": "he",
"profileImg": "./fmothe.jpg",
"color": "#18BBC9",
"roles": [
"translator"
]
"roles": ["translator"]
},
{
"id": "jahirfiquitiva",
@@ -379,9 +325,7 @@
"pronouns": "he",
"profileImg": "./jahirfiquitiva.jpg",
"color": "#3867d6",
"roles": [
"translator"
]
"roles": ["translator"]
},
{
"id": "kaleem",
@@ -397,9 +341,7 @@
"pronouns": "he",
"profileImg": "./kaleem.jpeg",
"color": "#a8b3ba",
"roles": [
"author"
]
"roles": ["author"]
},
{
"id": "qarnax",
@@ -414,12 +356,7 @@
},
"profileImg": "./qarnax.jpg",
"color": "",
"roles": [
"developer",
"author",
"community",
"translator"
]
"roles": ["developer", "author", "community", "translator"]
},
{
"id": "alexchadwick",
@@ -437,10 +374,7 @@
"pronouns": "he",
"profileImg": "./alexchadwick.jpg",
"color": "",
"roles": [
"author",
"translator"
]
"roles": ["author", "translator"]
},
{
"id": "williamcook",
@@ -457,9 +391,7 @@
"pronouns": "he",
"profileImg": "./williamcook.jpg",
"color": "#AF7AC5",
"roles": [
"author"
]
"roles": ["author"]
},
{
"id": "rudy",
@@ -474,9 +406,7 @@
"pronouns": "he",
"profileImg": "./rudy.jpg",
"color": "#ba68c8",
"roles": [
"community"
]
"roles": ["community"]
},
{
"id": "LayZee",
@@ -492,8 +422,6 @@
"pronouns": "he",
"profileImg": "./lars-gyrup-brink-nielsen.jpg",
"color": "#1b9bf0",
"roles": [
"author"
]
"roles": ["author"]
}
]

View File

@@ -1,17 +1,17 @@
/* AFTER CHANGING THIS FILE, PLEASE MANUALLY MINIFY IT AND PUT INTO backbtn.min.js */
/* TODO: Add minifier to build script */
window.onload = () => {
const backBtn = document.querySelector('#backbtn');
const backBtn = document.querySelector("#backbtn");
let hasHistory = false;
window.addEventListener('beforeunload', () => {
window.addEventListener("beforeunload", () => {
hasHistory = true;
})
});
backBtn.addEventListener('click', () => {
backBtn.addEventListener("click", () => {
if (!document.referrer) {
// This is the first page the user has visited on the site in this session
window.location.href = '/';
window.location.href = "/";
return;
}
history.back();
@@ -22,5 +22,5 @@ window.onload = () => {
window.location.href = "/";
}
}, 200);
})
}
});
};

View File

@@ -2,10 +2,10 @@
const LOCAL_STORAGE_KEY = "tabs-selection";
window.addEventListener('DOMContentLoaded', () => {
window.addEventListener("DOMContentLoaded", () => {
const tabLists = document.querySelectorAll('[role="tablist"]');
tabLists.forEach(tabList => {
tabLists.forEach((tabList) => {
/**
* @type {NodeListOf<HTMLElement>}
*/
@@ -13,7 +13,7 @@ window.addEventListener('DOMContentLoaded', () => {
// Add a click event handler to each tab
tabs.forEach((tab) => {
tab.addEventListener('click', e => {
tab.addEventListener("click", (e) => {
/**
* @type {HTMLElement}
*/
@@ -26,21 +26,21 @@ window.addEventListener('DOMContentLoaded', () => {
inline: "center",
});
}, 0);
changeTabs({ target })
changeTabs({ target });
});
});
// Enable arrow navigation between tabs in the tab list
let tabFocus = 0;
tabList.addEventListener('keydown', (_e) => {
tabList.addEventListener("keydown", (_e) => {
/**
* @type {KeyboardEvent}
*/
const e = _e;
// Move right
if (e.keyCode === 39 || e.keyCode === 37) {
tabs[tabFocus].setAttribute('tabindex', `-1`);
tabs[tabFocus].setAttribute("tabindex", `-1`);
if (e.keyCode === 39) {
tabFocus++;
// If we're at the end, go to the start
@@ -56,7 +56,7 @@ window.addEventListener('DOMContentLoaded', () => {
}
}
tabs[tabFocus].setAttribute('tabindex', `0`);
tabs[tabFocus].setAttribute("tabindex", `0`);
tabs[tabFocus].focus();
tabs[tabFocus].click();
}
@@ -84,16 +84,18 @@ window.addEventListener('DOMContentLoaded', () => {
// Remove all current selected tabs
parent
.querySelectorAll('[aria-selected="true"]')
.forEach((t) => t.setAttribute('aria-selected', `false`));
.forEach((t) => t.setAttribute("aria-selected", `false`));
// Set this tab as selected
target.setAttribute('aria-selected', `true`);
target.setAttribute("aria-selected", `true`);
const tabName = target.dataset.tabname;
/**
* @type {NodeListOf<HTMLElement>}
*/
const relatedTabs = document.querySelectorAll(`[role="tab"][data-tabname="${target.dataset.tabname}"]`);
const relatedTabs = document.querySelectorAll(
`[role="tab"][data-tabname="${target.dataset.tabname}"]`
);
localStorage.setItem(LOCAL_STORAGE_KEY, tabName);
@@ -105,12 +107,12 @@ window.addEventListener('DOMContentLoaded', () => {
// Hide all tab panels
grandparent
.querySelectorAll('[role="tabpanel"]')
.forEach((p) => p.setAttribute('hidden', `true`));
.forEach((p) => p.setAttribute("hidden", `true`));
// Show the selected panel
grandparent.parentNode
.querySelector(`#${target.getAttribute('aria-controls')}`)
.removeAttribute('hidden');
.querySelector(`#${target.getAttribute("aria-controls")}`)
.removeAttribute("hidden");
}
/* -------------------- */
@@ -137,18 +139,20 @@ window.addEventListener('DOMContentLoaded', () => {
// If user has linked to a heading that's inside of a tab
const hash = window.location.hash;
if (!hash) return;
const heading = document.querySelector < HTMLElement > (hash);
const heading = document.querySelector < HTMLElement > hash;
if (!heading) return;
const isHidden = checkElementsParents(heading, el =>
el.hasAttribute('hidden') && el.getAttribute('hidden') !== "false"
)
const isHidden = checkElementsParents(
heading,
(el) => el.hasAttribute("hidden") && el.getAttribute("hidden") !== "false"
);
// If it's not hidden, then we can assume that the browser will auto-scroll to it
if (!isHidden) return;
const partialHash = hash.slice(1);
try {
const matchingTab = document.querySelector < HTMLElement > (
`[data-headers*="${partialHash}"`
);
const matchingTab =
document.querySelector <
HTMLElement >
`[data-headers*="${partialHash}"`;
if (!matchingTab) return;
// If header is not in a tab
const tabName = matchingTab.getAttribute("data-tabname");
@@ -162,6 +166,5 @@ window.addEventListener('DOMContentLoaded', () => {
} catch (e) {
console.error("Error finding matching tab", e);
}
})()
})();
});

View File

@@ -1,16 +1,16 @@
/* AFTER CHANGING THIS FILE, PLEASE MANUALLY MINIFY IT AND PUT INTO tabs.min.js */
const COLOR_MODE_STORAGE_KEY = "currentTheme";
const themeToggleBtn = document.querySelector('#theme-toggle-button');
const darkIconEl = document.querySelector('#dark-icon');
const lightIconEl = document.querySelector('#light-icon');
const themeToggleBtn = document.querySelector("#theme-toggle-button");
const darkIconEl = document.querySelector("#dark-icon");
const lightIconEl = document.querySelector("#light-icon");
function toggleButton(theme) {
themeToggleBtn.ariaPressed = `${theme === 'dark'}`;
if (theme === 'light') {
themeToggleBtn.ariaPressed = `${theme === "dark"}`;
if (theme === "light") {
lightIconEl.style.display = null;
darkIconEl.style.display = 'none';
darkIconEl.style.display = "none";
} else {
lightIconEl.style.display = 'none';
lightIconEl.style.display = "none";
darkIconEl.style.display = null;
}
}
@@ -18,11 +18,12 @@ function toggleButton(theme) {
// TODO: Migrate to `classList`
const initialTheme = document.documentElement.className;
toggleButton(initialTheme);
themeToggleBtn.addEventListener('click', () => {
themeToggleBtn.addEventListener("click", () => {
const currentTheme = document.documentElement.className;
document.documentElement.className = currentTheme === 'light' ? 'dark' : 'light';
document.documentElement.className =
currentTheme === "light" ? "dark" : "light";
// TODO: Persist new setting
const newTheme = document.documentElement.className;
toggleButton(newTheme);
localStorage.setItem(COLOR_MODE_STORAGE_KEY, newTheme)
})
localStorage.setItem(COLOR_MODE_STORAGE_KEY, newTheme);
});

View File

@@ -1,10 +1,10 @@
if ("serviceWorker" in navigator) {
navigator.serviceWorker
.register("/sw.js")
.then(serviceWorker => {
.then((serviceWorker) => {
console.log("Service Worker registered: ", serviceWorker);
})
.catch(error => {
.catch((error) => {
console.error("Error registering the Service Worker: ", error);
});
}

View File

@@ -1,5 +1,5 @@
---
import { Icon } from 'astro-icon';
import { Icon } from "astro-icon";
import btnStyles from "./dark-light-button.module.scss";
---
@@ -9,8 +9,20 @@ import btnStyles from "./dark-light-button.module.scss";
aria-pressed="false"
aria-label={"Is dark mode enabled?"}
>
<Icon name="dark" height="36" width="36" id="dark-icon" style="display: none;"/>
<Icon name="light" height="36" width="36" id="light-icon" style="display: none;"/>
<Icon
name="dark"
height="36"
width="36"
id="dark-icon"
style="display: none;"
/>
<Icon
name="light"
height="36"
width="36"
id="light-icon"
style="display: none;"
/>
</button>
<script defer is:inline src="/scripts/themetoggle.min.js"/>
<script defer is:inline src="/scripts/themetoggle.min.js"></script>

View File

@@ -1,7 +1,7 @@
---
import layoutStyles from "./layout.module.scss";
import DarkLightButton from "components/dark-light-button/dark-light-button.astro";
import { Icon } from 'astro-icon';
import { Icon } from "astro-icon";
// const { back } = useHistory();
@@ -10,20 +10,30 @@ const rootPath = `/`;
const isBase = Astro.url.pathname === rootPath;
const isBlogPost = Astro.url.pathname.startsWith(`${rootPath}posts`);
const isCollection = Astro.url.pathname.startsWith(`${rootPath}collections`);
---
<div class={layoutStyles.horizCenter}>
<header class={layoutStyles.header} aria-label={"Toolbar for primary action buttons"}>
<header
class={layoutStyles.header}
aria-label={"Toolbar for primary action buttons"}
>
<div class={layoutStyles.headerInsideContainer}>
{!isBase ? (<>
<button id="backbtn" class={`${layoutStyles.backBtn} baseBtn`} aria-label="Go back">
{
!isBase ? (
<>
<button
id="backbtn"
class={`${layoutStyles.backBtn} baseBtn`}
aria-label="Go back"
>
<Icon height="36" width="36" name="back" />
</button>
<script is:inline defer src="/scripts/backbtn.min.js" />
</>
) : (
<div />
)}
)
}
<div class={layoutStyles.iconList}>
<!-- <AnalyticsLink category={"outbound"} href="https://discord.gg/FMcvc6T" className={"baseBtn"}
aria-label={"Join the Discord"}> -->
@@ -33,7 +43,13 @@ const isCollection = Astro.url.pathname.startsWith(`${rootPath}collections`);
</div>
</div>
</header>
<div class={ isCollection ? "" : !isBlogPost ? "listViewContent" : "postViewContent" }>
<div
class={isCollection
? ""
: !isBlogPost
? "listViewContent"
: "postViewContent"}
>
<slot />
</div>
</div>

View File

@@ -6,23 +6,23 @@ export interface Page {
export const DR = {
ariaLabel: "Go to the next set of pages",
display: '...'
display: "...",
};
export const DL = {
ariaLabel: "Go to previous set of pages",
display: '...'
display: "...",
};
const range = (start: number, end: number): Page[] => {
let length = end - start + 1;
const length = end - start + 1;
return Array.from({ length }, (_, idx) => {
const page = idx + start;
return {
display: String(page),
pageNumber: page,
ariaLabel: `Goto page ${page}`
}
ariaLabel: `Goto page ${page}`,
};
});
};
@@ -37,14 +37,14 @@ export const getPaginationRange = ({
totalCount,
pageSize,
siblingCount = 1,
currentPage
currentPage,
}: GetPaginationRangeProps): Page[] => {
const totalPageCount = Math.ceil(totalCount / pageSize);
const totalPageCountPage: Page = {
display: `${totalPageCount}`,
pageNumber: totalPageCount
}
pageNumber: totalPageCount,
};
const totalPageNumbers = siblingCount + 5;
@@ -63,48 +63,48 @@ export const getPaginationRange = ({
const firstPageIndex: Page = {
display: "1",
pageNumber: 1
pageNumber: 1,
};
const lastPageIndex: Page = {
display: `${totalPageCount}`,
pageNumber: totalPageCount
pageNumber: totalPageCount,
};
if (!shouldShowLeftDots && shouldShowRightDots) {
let leftItemCount = 3 + 2 * siblingCount;
let leftRange = range(1, leftItemCount);
const lastPage = leftRange[leftRange.length - 1]
const leftItemCount = 3 + 2 * siblingCount;
const leftRange = range(1, leftItemCount);
const lastPage = leftRange[leftRange.length - 1];
const DR_Page: Page = {
...DR,
pageNumber: currentPage + 2
}
pageNumber: currentPage + 2,
};
return [...leftRange, DR_Page, totalPageCountPage];
}
if (shouldShowLeftDots && !shouldShowRightDots) {
let rightItemCount = 3 + 2 * siblingCount;
let rightRange = range(
const rightItemCount = 3 + 2 * siblingCount;
const rightRange = range(
totalPageCount - rightItemCount + 1,
totalPageCount
);
const DL_Page: Page = {
...DL,
pageNumber: currentPage - 2
}
pageNumber: currentPage - 2,
};
return [firstPageIndex, DL_Page, ...rightRange];
}
if (shouldShowLeftDots && shouldShowRightDots) {
let middleRange = range(leftSiblingIndex, rightSiblingIndex);
const middleRange = range(leftSiblingIndex, rightSiblingIndex);
const DL_Page: Page = {
...DL,
pageNumber: currentPage - 2
}
pageNumber: currentPage - 2,
};
const DR_Page: Page = {
...DR,
pageNumber: currentPage + 2
}
pageNumber: currentPage + 2,
};
return [firstPageIndex, DL_Page, ...middleRange, DR_Page, lastPageIndex];
}

View File

@@ -1,31 +1,31 @@
---
import { getPaginationRange } from './pagination-logic';
import { Page } from 'astro';
import { PostInfo } from 'types/PostInfo';
import styles from './pagination.module.scss';
import { getPaginationRange } from "./pagination-logic";
import { Page } from "astro";
import { PostInfo } from "types/PostInfo";
import styles from "./pagination.module.scss";
interface PaginationProps {
page: Pick<Page<PostInfo>, 'total' | 'currentPage' | 'size' | 'lastPage' | 'url'>;
page: Pick<
Page<PostInfo>,
"total" | "currentPage" | "size" | "lastPage" | "url"
>;
class: string;
rootURL: string;
}
const {
page,
rootURL,
class: className = ""
} = Astro.props as PaginationProps;
const { page, rootURL, class: className = "" } = Astro.props as PaginationProps;
const paginationRange = getPaginationRange({
currentPage: page.currentPage,
totalCount: page.total,
siblingCount: 0,
pageSize: page.size
pageSize: page.size,
});
const dontShowAnything = page.currentPage === 0 || paginationRange.length < 2;
const getPageHref = (pageNum: number) => pageNum === 0 || pageNum === 1 ? rootURL : `${rootURL}page/${pageNum}`;
const getPageHref = (pageNum: number) =>
pageNum === 0 || pageNum === 1 ? rootURL : `${rootURL}page/${pageNum}`;
const lastPage = paginationRange[paginationRange.length - 1];
const firstPage = paginationRange[0];
@@ -34,27 +34,47 @@ const disablePrevious = !firstPage || page.currentPage === firstPage.pageNumber;
const disableNext = !lastPage || page.currentPage === lastPage.pageNumber;
---
{dontShowAnything ? null : <ul role="navigation" aria-label="Pagination Navigation" class={`${styles.pagination} ${className}`}>
{!disablePrevious && <li class={`${styles.paginationItem} ${styles.previous}`}>
{
dontShowAnything ? null : (
<ul
role="navigation"
aria-label="Pagination Navigation"
class={`${styles.pagination} ${className}`}
>
{!disablePrevious && (
<li class={`${styles.paginationItem} ${styles.previous}`}>
<a href={getPageHref(page.currentPage - 1)} aria-label="Previous">
{"<"}
</a>
</li>}
</li>
)}
{paginationRange.map(pageItem => {
{paginationRange.map((pageItem) => {
const isSelected = pageItem.pageNumber === page.currentPage;
return (
<li class={`${styles.paginationItem} ${ isSelected ? styles.active : '' }`}>
<a href={getPageHref(pageItem.pageNumber)} aria-label={pageItem.ariaLabel} aria-current={isSelected || undefined}>
<li
class={`${styles.paginationItem} ${
isSelected ? styles.active : ""
}`}
>
<a
href={getPageHref(pageItem.pageNumber)}
aria-label={pageItem.ariaLabel}
aria-current={isSelected || undefined}
>
{pageItem.display}
</a>
</li>
);
})}
{!disableNext && <li class={`${styles.paginationItem} ${styles.next}`}>
{!disableNext && (
<li class={`${styles.paginationItem} ${styles.next}`}>
<a href={getPageHref(page.currentPage + 1)} aria-label="Next">
{">"}
</a>
</li>}
</ul>}
</li>
)}
</ul>
)
}

View File

@@ -8,9 +8,9 @@ export interface PostListProps {
showWordCount?: boolean;
numberOfArticles?: number;
wordCount?: number;
unicornData?: PostInfo['authorsMeta'];
unicornData?: PostInfo["authorsMeta"];
listAriaLabel: string;
postsToDisplay: PostInfo[]
postsToDisplay: PostInfo[];
}
/**
* unicornData - The data with the associated post. If present - you're on profile page
@@ -18,12 +18,7 @@ export interface PostListProps {
const { listAriaLabel, postsToDisplay } = Astro.props as PostListProps;
---
<ul
class={listStyle.postsListContainer}
aria-label={listAriaLabel}
role="list"
>
{postsToDisplay.map((post) => (
<PostCard post={post}/>
))}
<ul class={listStyle.postsListContainer} aria-label={listAriaLabel} role="list">
{postsToDisplay.map((post) => <PostCard post={post} />)}
</ul>

View File

@@ -11,20 +11,10 @@ interface PostCardProps {
class?: string; // class to pass to the post card element
}
const {
post,
class: className = ""
} = Astro.props as PostCardProps;
const { post, class: className = "" } = Astro.props as PostCardProps;
const {
published,
slug,
title,
authorsMeta,
tags,
description,
excerpt
} = post;
const { published, slug, title, authorsMeta, tags, description, excerpt } =
post;
const publishedStr = dayjs(published).format("MMMM D, YYYY");
---
@@ -42,7 +32,8 @@ const publishedStr = dayjs(published).format("MMMM D, YYYY");
{authorsMeta[0].name}
</a>
<!-- To avoid having commas on the first author name, we did this -->
{authorsMeta.slice(1).map((author, i) => {
{
authorsMeta.slice(1).map((author, i) => {
return (
<>
<span>, </span>
@@ -51,17 +42,17 @@ const publishedStr = dayjs(published).format("MMMM D, YYYY");
</a>
</>
);
})}
})
}
</p>
<div class={cardStyles.dateTagSubheader}>
<p class={cardStyles.date}>{publishedStr}</p>
{tags.map((tag) => (
<span class={cardStyles.tag}>
{tag}
</span>
))}
{tags.map((tag) => <span class={cardStyles.tag}>{tag}</span>)}
</div>
<p class={cardStyles.excerpt} set:html={description || excerpt} />
<p class={cardStyles.excerpt} set:html={description || excerpt}></p>
</div>
<UserProfilePic authors={authorsMeta} className={cardStyles.authorImagesContainer} />
<UserProfilePic
authors={authorsMeta}
className={cardStyles.authorImagesContainer}
/>
</li>

View File

@@ -1,19 +1,33 @@
---
import {SEOProps} from './shared';
import { SEOProps } from "./shared";
type Props = Pick<SEOProps, 'editedTime' | 'publishedTime' | 'keywords' | 'unicornsData'>;
const { keywords, editedTime, publishedTime, unicornsData } = Astro.props as Props;
const author = unicornsData.map(uni => uni.name).join(",");
type Props = Pick<
SEOProps,
"editedTime" | "publishedTime" | "keywords" | "unicornsData"
>;
const { keywords, editedTime, publishedTime, unicornsData } =
Astro.props as Props;
const author = unicornsData.map((uni) => uni.name).join(",");
---
<>{keywords?.length ? keywords.map(keyword =>
<meta property="article:tag" content={keyword} />) : null}
<>
{
keywords?.length
? keywords.map((keyword) => (
<meta property="article:tag" content={keyword} />
))
: null
}
</>
<meta property="article:section" content="Technology" />
<meta property="article:author" content={author} />
<>{editedTime &&
<meta property="article:modified_time" content={editedTime} />}
<>
{editedTime && <meta property="article:modified_time" content={editedTime} />}
</>
<>{publishedTime &&
<meta property="article:published_time" content={publishedTime} />}
<>
{
publishedTime && (
<meta property="article:published_time" content={publishedTime} />
)
}
</>

View File

@@ -1,13 +1,9 @@
---
import {SEOProps} from './shared';
import { SEOProps } from "./shared";
type Props = Pick<SEOProps, 'publishedTime' | 'isbn' | 'unicornsData'>;
type Props = Pick<SEOProps, "publishedTime" | "isbn" | "unicornsData">;
const {
publishedTime,
unicornsData,
isbn
} = Astro.props as Props;
const { publishedTime, unicornsData, isbn } = Astro.props as Props;
const author = unicornsData!.map((uni) => uni.name).join(",");
---

View File

@@ -1,16 +1,18 @@
---
import { removePrefixLanguageFromPath } from "utils/translations";
import { SEOProps } from "./shared";
import { removePrefixLanguageFromPath } from 'utils/translations';
import {SEOProps} from './shared';
type Props = Pick<SEOProps, 'langData'> & {
type Props = Pick<SEOProps, "langData"> & {
pathName: string;
siteMetadata: {siteUrl: string}
siteMetadata: { siteUrl: string };
};
const { langData, siteMetadata, pathName } = Astro.props as Props;
---
{langData?.currentLang && (
<>
{
langData?.currentLang && (
<link
rel="alternate"
href={
@@ -18,9 +20,13 @@ const {langData, siteMetadata, pathName} = Astro.props as Props;
}
href-lang="x-default"
/>
)}
{langData?.otherLangs?.length ?
langData.otherLangs.map((lang) => (
)
}
</>
<>
{
langData?.otherLangs?.length
? langData.otherLangs.map((lang) => (
<link
rel="alternate"
href={
@@ -30,5 +36,7 @@ const {langData, siteMetadata, pathName} = Astro.props as Props;
}
href-lang={lang}
/>
)) : null}
))
: null
}
</>

View File

@@ -1,14 +1,14 @@
---
import { fileToOpenGraphConverter } from "utils/translations";
import {SEOProps} from './shared';
import { SEOProps } from "./shared";
type Props = Pick<SEOProps, 'title' | 'langData' | 'unicornsData'> & {
type Props = Pick<SEOProps, "title" | "langData" | "unicornsData"> & {
currentPath: string;
metaDescription: string;
metaImage: string;
ogType: string;
siteMetadata: {title: string}
siteMetadata: { title: string };
};
const {
@@ -21,6 +21,7 @@ const {
ogType,
} = Astro.props as Props;
---
{/* Open Graph SEO */}
<meta property="og:url" content={currentPath} />
<meta property="og:site_name" content={siteMetadata.title} />
@@ -28,17 +29,20 @@ const {
<meta
property="og:locale"
content={
langData ? fileToOpenGraphConverter(langData.currentLang) : "en"
}
content={langData ? fileToOpenGraphConverter(langData.currentLang) : "en"}
/>
{langData?.otherLangs?.length ?
langData.otherLangs.map((lang) => (
<>
{
langData?.otherLangs?.length
? langData.otherLangs.map((lang) => (
<meta
property="og:locale:alternate"
content={fileToOpenGraphConverter(lang)}
/>
)) : null}
))
: null
}
</>
<meta property="og:description" content={metaDescription} />
<meta property="og:image" content={metaImage} />
<meta property="og:type" content={ogType} />

View File

@@ -1,7 +1,7 @@
---
import {SEOProps} from './shared';
import { SEOProps } from "./shared";
type Props = Pick<SEOProps, 'unicornsData'>;
type Props = Pick<SEOProps, "unicornsData">;
const { unicornsData } = Astro.props as Props;
---

View File

@@ -40,21 +40,54 @@ const currentPath = siteMetadata.siteUrl + (pathName || "");
<title>
{title ? `${title} | ${siteMetadata.title}` : siteMetadata.title}
</title>
{canonical ?
<link rel="canonical" href={canonical} /> : null}
<>{canonical ? <link rel="canonical" href={canonical} /> : null}</>
<meta property="name" content={siteMetadata.title} />
<meta name="description" content={metaDescription} />
<meta property="keywords" content={metaKeywords} />
<Analytics />
<Twitter title={title} metaDescription={metaDescription} siteMetadata={siteMetadata} metaImage={metaImage}
unicornsData={unicornsData} uniTwitter={uniTwitter} type={type} />
<OpenGraph currentPath={currentPath} siteMetadata={siteMetadata} title={title} langData={langData}
metaDescription={metaDescription} metaImage={metaImage} ogType={ogType} />
<Twitter
title={title}
metaDescription={metaDescription}
siteMetadata={siteMetadata}
metaImage={metaImage}
unicornsData={unicornsData}
uniTwitter={uniTwitter}
type={type}
/>
<OpenGraph
currentPath={currentPath}
siteMetadata={siteMetadata}
title={title}
langData={langData}
metaDescription={metaDescription}
metaImage={metaImage}
ogType={ogType}
/>
<Locale langData={langData} siteMetadata={siteMetadata} pathName={pathName} />
{type === 'article' &&
<Article keywords={keywords} editedTime={editedTime} publishedTime={publishedTime} unicornsData={unicornsData} />}
{type === 'book' &&
<Book publishedTime={publishedTime} unicornsData={unicornsData} isbn={isbn} />}
{type === 'profile' &&
<Profile unicornsData={unicornsData} />}
<>
{
type === "article" && (
<Article
keywords={keywords}
editedTime={editedTime}
publishedTime={publishedTime}
unicornsData={unicornsData}
/>
)
}
</>
<>
{
type === "book" && (
<Book
publishedTime={publishedTime}
unicornsData={unicornsData}
isbn={isbn}
/>
)
}
</>
<>
{type === "profile" && <Profile unicornsData={unicornsData} />}
</>
<slot />

View File

@@ -1,10 +1,10 @@
---
import {SEOProps} from './shared';
import { SEOProps } from "./shared";
type Props = Pick<SEOProps, 'title' | 'unicornsData' | 'type'> & {
type Props = Pick<SEOProps, "title" | "unicornsData" | "type"> & {
metaDescription: string;
metaImage: string;
siteMetadata: {twitterHandle: string}
siteMetadata: { twitterHandle: string };
uniTwitter?: string;
};
@@ -15,15 +15,20 @@ const {
metaImage,
unicornsData,
uniTwitter,
type
type,
} = Astro.props as Props;
---
{/* Twitter SEO */}
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={metaDescription} />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content={siteMetadata.twitterHandle} />
<meta name="twitter:image" content={metaImage} />
{type === "article" && unicornsData?.length === 1 && uniTwitter ? (
<>
{
type === "article" && unicornsData?.length === 1 && uniTwitter ? (
<meta property="twitter:creator" content={`@${uniTwitter}`} />
) : null}
) : null
}
</>

View File

@@ -1,20 +1,23 @@
---
const {headingsToDisplaySlugs} = Astro.props as {headingsToDisplaySlugs: Array<string>}
const { headingsToDisplaySlugs } = Astro.props as {
headingsToDisplaySlugs: Array<string>;
};
---
<script define:vars={{ headingsToDisplaySlugs }}>
window.onload = () => {
const tocListRef = document.querySelector('#tocList');
const tocListRef = document.querySelector("#tocList");
const linkRefs = [...document.querySelectorAll('[data-headingitem="true"]')]
const linkRefs = [
...document.querySelectorAll('[data-headingitem="true"]'),
];
let previousSection = {current: ""}
let previousSection = { current: "" };
const handleObserver = (entries) => {
const highlightFirstActive = () => {
if (!tocListRef) return;
let firstVisibleLink =
tocListRef.querySelector(".toc-is-visible");
let firstVisibleLink = tocListRef.querySelector(".toc-is-visible");
linkRefs.forEach((linkRef) => {
linkRef.classList.remove("toc-is-active");
@@ -64,5 +67,5 @@ const {headingsToDisplaySlugs} = Astro.props as {headingsToDisplaySlugs: Array<s
.forEach((heading) => {
observer.observe(heading);
});
}
};
</script>

View File

@@ -21,30 +21,27 @@ const headingsToDisplay = headings
.map((h) => Object.assign({}, h, { depth: h.depth - minDepth + 1 }))
.filter((headingInfo) => headingInfo.depth <= 3);
const headingsToDisplaySlugs = headingsToDisplay.map(item => item.slug);
const headingsToDisplaySlugs = headingsToDisplay.map((item) => item.slug);
---
<aside aria-label={"Table of Contents"}>
<ol
class={tableOfContentsStyle.tableList}
role="list"
id="tocList"
>
{headingsToDisplay.map((headingInfo, i) => {
<ol class={tableOfContentsStyle.tableList} role="list" id="tocList">
{
headingsToDisplay.map((headingInfo, i) => {
const liClassNames = classnames(tableOfContentsStyle.tocLi, {
[tableOfContentsStyle.tocH1]: headingInfo.depth === 1,
[tableOfContentsStyle.tocH2]: headingInfo.depth === 2,
[tableOfContentsStyle.tocH3]: headingInfo.depth === 3,
});
return (
<li
class={liClassNames}
data-headingitem="true"
>
<li class={liClassNames} data-headingitem="true">
<a href={`#${headingInfo.slug}`}>{headingInfo.value}</a>
</li>
);
})}
})
}
</ol>
</aside>
<HeadingIntersectionObserverScript headingsToDisplaySlugs={headingsToDisplaySlugs} />
<HeadingIntersectionObserverScript
headingsToDisplaySlugs={headingsToDisplaySlugs}
/>

View File

@@ -60,7 +60,9 @@
@extend %headline-uniwidth-2;
}
.tocH2, .tocH3, .tocH4 {
.tocH2,
.tocH3,
.tocH4 {
a {
font-style: italic;
}

View File

@@ -1,7 +1,7 @@
---
import styles from "./user-profile-pic.module.scss";
import { UnicornInfo } from "uu-types";
import {Image} from '@astrojs/image/components';
import { Image } from "@astrojs/image/components";
// TODO: Fix image loading and image 'onClick'
@@ -10,12 +10,14 @@ interface UserProfilePicProps {
className: string;
}
const { authors, className } = Astro.props as UserProfilePicProps
const { authors, className } = Astro.props as UserProfilePicProps;
const hasTwoAuthors = authors.length !== 1;
---
<div class={`${styles.container} ${className || ""}`}>
{authors.map((unicorn, i) => {
{
authors.map((unicorn, i) => {
const classesToApply = hasTwoAuthors ? styles.twoAuthor : "";
return (
@@ -30,10 +32,11 @@ const hasTwoAuthors = authors.length !== 1;
sizes={"85px"}
height={85}
width={85}
format={'png'}
format={"png"}
class={`circleImg ${styles.profilePicImage} ${styles.width50} ${classesToApply}`}
/>
</div>
);
})}
})
}
</div>

View File

@@ -252,20 +252,35 @@
margin-bottom: 0;
}
.formkit-form [data-group="checkboxes"] [data-group="checkbox"] input[type="checkbox"] {
.formkit-form
[data-group="checkboxes"]
[data-group="checkbox"]
input[type="checkbox"] {
display: none;
}
.formkit-form [data-group="checkboxes"] [data-group="checkbox"] input[type="checkbox"]+label::after {
.formkit-form
[data-group="checkboxes"]
[data-group="checkbox"]
input[type="checkbox"]
+ label::after {
content: none;
}
.formkit-form [data-group="checkboxes"] [data-group="checkbox"] input[type="checkbox"]:checked+label::after {
.formkit-form
[data-group="checkboxes"]
[data-group="checkbox"]
input[type="checkbox"]:checked
+ label::after {
border-color: #ffffff;
content: "";
}
.formkit-form [data-group="checkboxes"] [data-group="checkbox"] input[type="checkbox"]:checked+label::before {
.formkit-form
[data-group="checkboxes"]
[data-group="checkbox"]
input[type="checkbox"]:checked
+ label::before {
background: #10bf7a;
border-color: #10bf7a;
}
@@ -361,8 +376,10 @@
opacity: 0.3;
border-radius: 100%;
display: inline-block;
-webkit-animation: formkit-bouncedelay-formkit-form-data-uid-882d42bb6f- 1.4s infinite ease-in-out both;
animation: formkit-bouncedelay-formkit-form-data-uid-882d42bb6f- 1.4s infinite ease-in-out both;
-webkit-animation: formkit-bouncedelay-formkit-form-data-uid-882d42bb6f- 1.4s
infinite ease-in-out both;
animation: formkit-bouncedelay-formkit-form-data-uid-882d42bb6f- 1.4s infinite
ease-in-out both;
}
.formkit-form .formkit-spinner > div:nth-child(1) {
@@ -458,7 +475,6 @@
}
@-webkit-keyframes formkit-bouncedelay-formkit-form-data-uid-882d42bb6f- {
0%,
80%,
100% {
@@ -475,7 +491,6 @@
}
@keyframes formkit-bouncedelay-formkit-form-data-uid-882d42bb6f- {
0%,
80%,
100% {
@@ -550,22 +565,38 @@
margin-right: -5px;
}
.formkit-form[min-width~="700"] .formkit-fields[data-stacked="false"] .formkit-field,
.formkit-form[min-width~="800"] .formkit-fields[data-stacked="false"] .formkit-field,
.formkit-form[min-width~="700"] .formkit-fields[data-stacked="false"] .formkit-submit,
.formkit-form[min-width~="800"] .formkit-fields[data-stacked="false"] .formkit-submit {
.formkit-form[min-width~="700"]
.formkit-fields[data-stacked="false"]
.formkit-field,
.formkit-form[min-width~="800"]
.formkit-fields[data-stacked="false"]
.formkit-field,
.formkit-form[min-width~="700"]
.formkit-fields[data-stacked="false"]
.formkit-submit,
.formkit-form[min-width~="800"]
.formkit-fields[data-stacked="false"]
.formkit-submit {
margin: 0 5px 15px 5px;
}
.formkit-form[min-width~="700"] .formkit-fields[data-stacked="false"] .formkit-field,
.formkit-form[min-width~="800"] .formkit-fields[data-stacked="false"] .formkit-field {
.formkit-form[min-width~="700"]
.formkit-fields[data-stacked="false"]
.formkit-field,
.formkit-form[min-width~="800"]
.formkit-fields[data-stacked="false"]
.formkit-field {
-webkit-flex: 100 1 auto;
-ms-flex: 100 1 auto;
flex: 100 1 auto;
}
.formkit-form[min-width~="700"] .formkit-fields[data-stacked="false"] .formkit-submit,
.formkit-form[min-width~="800"] .formkit-fields[data-stacked="false"] .formkit-submit {
.formkit-form[min-width~="700"]
.formkit-fields[data-stacked="false"]
.formkit-submit,
.formkit-form[min-width~="800"]
.formkit-fields[data-stacked="false"]
.formkit-submit {
-webkit-flex: 1 1 auto;
-ms-flex: 1 1 auto;
flex: 1 1 auto;

View File

@@ -310,7 +310,7 @@ $pendIconMarg: #{$baseUnit}px;
margin-bottom: 1em;
summary {
padding: .5rem 1.5rem;
padding: 0.5rem 1.5rem;
background: var(--darkPrimary);
color: var(--backgroundColor);
cursor: pointer;

View File

@@ -1,5 +1,5 @@
---
import {v4 as uuidV4} from 'uuid';
import { v4 as uuidV4 } from "uuid";
const id = uuidV4();
const clipId = `path-1-inside-1${id}`;
const props = Astro.props;
@@ -18,12 +18,11 @@ const props = Astro.props;
fill-rule="evenodd"
clip-rule="evenodd"
d="M24.6893 7.31065C23.0659 5.6873 20.4339 5.6873 18.8106 7.31066L8.06059 18.0607C5.33267 20.7886 5.33267 25.2114 8.06059 27.9393C10.7885 30.6673 15.2113 30.6673 17.9393 27.9393L30.4393 15.4393C31.0251 14.8535 31.9748 14.8535 32.5606 15.4393C33.1464 16.0251 33.1464 16.9749 32.5606 17.5607L20.0606 30.0607C16.1611 33.9602 9.83876 33.9601 5.93927 30.0607C2.03978 26.1612 2.03977 19.8388 5.93927 15.9393L16.6893 5.18934C19.4842 2.39441 24.0157 2.39441 26.8106 5.18933C29.6055 7.98426 29.6055 12.5157 26.8106 15.3107L16.0606 26.0606C14.3702 27.751 11.6296 27.751 9.93927 26.0607C8.24891 24.3703 8.24891 21.6297 9.93927 19.9393L19.9393 9.93933C20.5251 9.35355 21.4748 9.35355 22.0606 9.93933C22.6464 10.5251 22.6464 11.4749 22.0606 12.0607L12.0606 22.0607C11.5418 22.5794 11.5418 23.4205 12.0606 23.9393C12.5794 24.4581 13.4205 24.4581 13.9393 23.9393L24.6893 13.1893C26.3126 11.566 26.3126 8.93401 24.6893 7.31065Z"
fill="#153E67"
/>
fill="#153E67"></path>
</g>
<defs>
<clipPath id={clipId}>
<rect width="36" height="36" fill="white" />
</clipPath>
<clip-path id={clipId}>
<rect width="36" height="36" fill="white"></rect>
</clip-path>
</defs>
</svg>

View File

@@ -1,5 +1,5 @@
---
import {v4 as uuidV4} from 'uuid';
import { v4 as uuidV4 } from "uuid";
const id = uuidV4();
const clipId = `path-1-inside-1${id}`;
const props = Astro.props;
@@ -13,15 +13,14 @@ const props = Astro.props;
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<g clipPath={`url(#${clipId.current})`}>
<g clip-path={`url(#${clipId})`}>
<path
d="M1.43934 32.4393C0.853553 33.0251 0.853553 33.9749 1.43934 34.5607C2.02513 35.1464 2.97487 35.1464 3.56066 34.5607L1.43934 32.4393ZM24.0607 14.0607C24.6464 13.4749 24.6464 12.5251 24.0607 11.9393C23.4749 11.3536 22.5251 11.3536 21.9393 11.9393L24.0607 14.0607ZM8.67157 14.3284L7.61091 13.2678L8.67157 14.3284ZM14.5607 23.5607L24.0607 14.0607L21.9393 11.9393L12.4393 21.4393L14.5607 23.5607ZM26.5 21H13.5V24H26.5V21ZM3.56066 34.5607L8.56066 29.5607L6.43934 27.4393L1.43934 32.4393L3.56066 34.5607ZM8.56066 29.5607L14.5607 23.5607L12.4393 21.4393L6.43934 27.4393L8.56066 29.5607ZM7.5 30H18.8431V27H7.5V30ZM22.7322 28.3891L31.5607 19.5607L29.4393 17.4393L20.6109 26.2678L22.7322 28.3891ZM9 28.5V17.1569H6V28.5H9ZM9.73223 15.3891L18.5607 6.56066L16.4393 4.43934L7.61091 13.2678L9.73223 15.3891ZM18.5607 6.56066C21.5647 3.55659 26.4353 3.5566 29.4393 6.56066L31.5607 4.43934C27.385 0.263705 20.615 0.263698 16.4393 4.43934L18.5607 6.56066ZM9 17.1569C9 16.4938 9.26339 15.8579 9.73223 15.3891L7.61091 13.2678C6.57946 14.2992 6 15.6982 6 17.1569H9ZM31.5607 19.5607C35.7363 15.385 35.7363 8.61497 31.5607 4.43934L29.4393 6.56066C32.4434 9.56472 32.4434 14.4353 29.4393 17.4393L31.5607 19.5607ZM18.8431 30C20.3018 30 21.7008 29.4205 22.7322 28.3891L20.6109 26.2678C20.1421 26.7366 19.5062 27 18.8431 27V30Z"
fill="#153E67"
/>
fill="#153E67"></path>
</g>
<defs>
<clipPath id={clipId.current}>
<rect width="36" height="36" fill="white" />
</clipPath>
<clip-path id={clipId}>
<rect width="36" height="36" fill="white"></rect>
</clip-path>
</defs>
</svg>

View File

@@ -2,12 +2,12 @@
import ThemeStyle from "../page-components/layouts/theme-style.astro";
import BlockingThemeChangerScript from "../page-components/layouts/blocking-theme-changer-script.astro";
import Layout from "../components/layout/layout.astro";
import '../global.scss';
import "../global.scss";
---
<html class="light">
<head>
<meta name="viewport" content="width=device-width">
<meta name="viewport" content="width=device-width" />
<meta charset="utf-8" />
<link
rel="preload"
@@ -57,7 +57,7 @@ import '../global.scss';
sizes="512x512"
href="/icons/icon-512x512.png?v=2a"
/>
<script defer src="/uninstall-sw.js" is:inline/>
<script defer src="/uninstall-sw.js" is:inline></script>
<ThemeStyle />
<slot name="head" />
</head>

View File

@@ -24,11 +24,15 @@ const publishedStr = dayjs(post.published).format("MMMM D, YYYY");
---
<div class={styles.container}>
<UserProfilePic authors={authorsMeta} class={styles.postMetadataAuthorImagesContainer} />
<UserProfilePic
authors={authorsMeta}
class={styles.postMetadataAuthorImagesContainer}
/>
<div class={styles.textDiv}>
<h2 class={styles.authorName} data-testid="post-meta-author-name">
<span>by</span>
{authorsMeta.map((author, i) => {
{
authorsMeta.map((author, i) => {
return (
<>
{i !== 0 && <span>{", "}</span>}
@@ -37,19 +41,26 @@ const publishedStr = dayjs(post.published).format("MMMM D, YYYY");
</a>
</>
);
})}
})
}
</h2>
<div class={styles.belowName}>
<p>{publishedStr}</p>
<p>{post.wordCount} words</p>
</div>
</div>
{!!post.originalLink && (
{
!!post.originalLink && (
<p class={styles.originalLink}>
Originally posted at&nbsp;
<a href={post.originalLink} target="_blank" rel="nofollow noopener noreferrer">
<a
href={post.originalLink}
target="_blank"
rel="nofollow noopener noreferrer"
>
{originalHost}
</a>
</p>
)}
)
}
</div>

View File

@@ -12,10 +12,6 @@ const { title, tags } = post;
<div class={styles.container}>
<h1 class={styles.title}>{title}</h1>
<ul aria-label="Post tags" role="list" class={styles.tags}>
{tags.map((tag) => (
<li role="listitem">
{tag}
</li>
))}
{tags.map((tag) => <li role="listitem">{tag}</li>)}
</ul>
</div>

View File

@@ -7,13 +7,12 @@ interface TableOfContentsProps {
lang?: Languages;
}
const {
suggestedArticles,
lang,
} = Astro.props as TableOfContentsProps;
const { suggestedArticles, lang } = Astro.props as TableOfContentsProps;
---
{!suggestedArticles.length ? null : <aside aria-label={"Suggested Articles"}>
{
!suggestedArticles.length ? null : (
<aside aria-label={"Suggested Articles"}>
<ol role={"list"} class={suggestedStyle.list}>
<h2 class={suggestedStyle.header}>Related posts</h2>
{suggestedArticles.map((suggestedArticle, i) => {
@@ -21,9 +20,7 @@ const {
.map((author) => author.name)
.join(", ");
return (
<li
class={`${suggestedStyle.card} ${suggestedStyle.localCard}`}
>
<li class={`${suggestedStyle.card} ${suggestedStyle.localCard}`}>
<a
href={`/${lang !== "en" ? `${lang}/` : ""}posts/${
suggestedArticle.slug
@@ -43,4 +40,6 @@ const {
);
})}
</ol>
</aside>}
</aside>
)
}

View File

@@ -1,4 +1,4 @@
---
---
<script is:inline defer src="/scripts/tabs.min.js"/>
<script is:inline defer src="/scripts/tabs.min.js"></script>

View File

@@ -1,9 +1,7 @@
---
import * as Terser from "terser";
import {
COLOR_MODE_STORAGE_KEY,
} from "../../constants";
import { COLOR_MODE_STORAGE_KEY } from "../../constants";
/**
* Much of this code deals with dark mode. It's ripped straight from:
@@ -45,12 +43,14 @@ function setColorsByTheme() {
root.className = colorMode;
}
const boundFn = String(setColorsByTheme)
.replace("COLOR_MODE_STORAGE_KEY", COLOR_MODE_STORAGE_KEY);
const boundFn = String(setColorsByTheme).replace(
"COLOR_MODE_STORAGE_KEY",
COLOR_MODE_STORAGE_KEY
);
let calledFunction = `(${boundFn})()`;
calledFunction = (await Terser.minify(calledFunction)).code!;
---
<script set:html={calledFunction} />
<script set:html={calledFunction}></script>

View File

@@ -1,9 +1,7 @@
---
import {
COLORS
} from "../../constants";
import { COLORS } from "../../constants";
function getThemeStyling(theme: 'light' | 'dark') {
function getThemeStyling(theme: "light" | "dark") {
const CSS_THEME = Object.entries(COLORS).reduce((prev, [key, val]) => {
prev += `\n--${key}: ${val[theme]};`;
return prev;
@@ -14,12 +12,14 @@ function getThemeStyling(theme: 'light' | 'dark') {
const rawStylesCSS = `
html.light, body.light {
${getThemeStyling('light')}
${getThemeStyling("light")}
}
html.dark, body.dark {
${getThemeStyling('dark')}
${getThemeStyling("dark")}
}
`
`;
---
<style set:html={rawStylesCSS} />
<style set:html={rawStylesCSS}>
</style>

View File

@@ -1,6 +1,6 @@
---
import styles from "./post-list-header.module.scss";
import {Image} from '@astrojs/image/components';
import { Image } from "@astrojs/image/components";
import unicornLogo from "../../../assets/unicorn_utterances_logo_512.png";
interface PostListHeaderProps {
@@ -10,10 +10,22 @@ interface PostListHeaderProps {
const { siteDescription } = Astro.props as PostListHeaderProps;
---
<div class={styles.container} role="banner" aria-label={`Banner for Unicorn Utterances`}>
<div
class={styles.container}
role="banner"
aria-label={`Banner for Unicorn Utterances`}
>
<div class={styles.headerPic}>
<Image format={'png'} loading={"eager"} sizes={"300px"} alt={`Smiling cartoon unicorn with a
bowtie`} src={unicornLogo} height={300} width={300} />
<Image
format={"png"}
loading={"eager"}
sizes={"300px"}
alt={`Smiling cartoon unicorn with a
bowtie`}
src={unicornLogo}
height={300}
width={300}
/>
</div>
<div class={styles.noMgContainer}>
<h1 class={styles.title}>Unicorn Utterances</h1>

View File

@@ -14,14 +14,13 @@ import Pagination from "components/pagination/pagination.astro";
export interface PostListTemplateProps {
posts: PostInfo[];
rootURL: string;
page: Pick<Page<PostInfo>, 'total' | 'currentPage' | 'size' | 'lastPage' | 'url'>
page: Pick<
Page<PostInfo>,
"total" | "currentPage" | "size" | "lastPage" | "url"
>;
}
const {
posts,
page,
rootURL
} = Astro.props as PostListTemplateProps;
const { posts, page, rootURL } = Astro.props as PostListTemplateProps;
---
<div>

View File

@@ -1,8 +1,8 @@
---
import styles from "./profile-header.module.scss";
import { UnicornInfo } from "uu-types";
import {Image} from '@astrojs/image/components';
import { Icon } from 'astro-icon';
import { Image } from "@astrojs/image/components";
import { Icon } from "astro-icon";
import SocialBtn from "./social-button.astro";
@@ -17,7 +17,6 @@ interface PicTitleHeaderProps {
const { unicornData } = Astro.props as PicTitleHeaderProps;
const possessiveName = getNamePossessive(unicornData.name);
---
<div
@@ -39,7 +38,7 @@ const possessiveName = getNamePossessive(unicornData.name);
height={192}
width={192}
sizes={"192px"}
format={'png'}
format={"png"}
loading={"eager"}
alt={`${possessiveName} profile picture`}
/>
@@ -57,7 +56,8 @@ const possessiveName = getNamePossessive(unicornData.name);
>
{unicornData.description}
</div>
{unicornData.socials && (
{
unicornData.socials && (
<ul
class={styles.socialsContainer}
aria-label={`${possessiveName} social media links`}
@@ -104,14 +104,12 @@ const possessiveName = getNamePossessive(unicornData.name);
</SocialBtn>
)}
{unicornData.socials.website && (
<SocialBtn
text={"Website"}
url={unicornData.socials.website}
>
<SocialBtn text={"Website"} url={unicornData.socials.website}>
<Icon name="site" height="36" width="36" slot="icon" />
</SocialBtn>
)}
</ul>
)}
)
}
</div>
</div>

View File

@@ -4,7 +4,7 @@ interface SocialBtnProps {
url: string;
}
const { text, url } = Astro.props as SocialBtnProps;
import styles from './social-button.module.scss';
import styles from "./social-button.module.scss";
---
<li class={`baseBtn ${styles.socialBtnLink}`} role="listitem">
@@ -15,12 +15,7 @@ import styles from './social-button.module.scss';
rel="noreferrer"
href={url}
> -->
<a
class="unlink"
target="_blank"
rel="noreferrer"
href={url}
>
<a class="unlink" target="_blank" rel="noreferrer" href={url}>
<span class={styles.svgContainer} aria-hidden={true}>
<slot name="icon" />
</span>

View File

@@ -8,7 +8,6 @@
align-items: center;
}
.socialBtnLink {
&:not(:last-of-type) {
margin-right: 32px;

View File

@@ -3,24 +3,21 @@ import PostList from "components/post-card-list/post-card-list.astro";
import Pagination from "components/pagination/pagination.astro";
import { Page } from "astro";
import { PostInfo } from "types/PostInfo";
import ProfileHeader from './profile-header/profile-header.astro';
import ProfileHeader from "./profile-header/profile-header.astro";
export interface UnicornTemplateProps {
unicorn: any;
posts: PostInfo[];
rootURL: string;
page: Pick<Page<PostInfo>, 'total' | 'currentPage' | 'size' | 'lastPage' | 'url'>
page: Pick<
Page<PostInfo>,
"total" | "currentPage" | "size" | "lastPage" | "url"
>;
}
const {
unicorn,
page,
rootURL,
posts
} = Astro.props as UnicornTemplateProps;
const { unicorn, page, rootURL, posts } = Astro.props as UnicornTemplateProps;
---
<!-- <PostListProvider
posts={authoredPosts}
numberOfPages={numberOfPages}

View File

@@ -1,13 +1,15 @@
---
import Document from '../layouts/document.astro';
import PostListTemplate, { PostListTemplateProps } from '../page-components/post-list/post-list.astro';
import { getAllPostsForListView } from 'utils/api';
import {PostInfo} from 'types/PostInfo';
import Document from "../layouts/document.astro";
import PostListTemplate, {
PostListTemplateProps,
} from "../page-components/post-list/post-list.astro";
import { getAllPostsForListView } from "utils/api";
import { PostInfo } from "types/PostInfo";
import SEO from "components/seo/seo.astro";
const posts = await Astro.glob<PostInfo>('../../content/blog/**/*.md')
const posts = await Astro.glob<PostInfo>("../../content/blog/**/*.md");
const enPosts = getAllPostsForListView(posts, 'en');
const enPosts = getAllPostsForListView(posts, "en");
const postsToDisplay = enPosts.slice(0, 8);
const page = {
@@ -17,12 +19,12 @@ const page = {
lastPage: Math.floor(enPosts.length / postsToDisplay.length),
url: {
current: Astro.url.href,
next: '/page/2'
}
} as PostListTemplateProps['page'];
next: "/page/2",
},
} as PostListTemplateProps["page"];
---
<Document>
<SEO slot="head" title="Homepage" />
<PostListTemplate posts={postsToDisplay} page={page} rootURL={'/'}/>
<PostListTemplate posts={postsToDisplay} page={page} rootURL={"/"} />
</Document>

View File

@@ -1,15 +1,15 @@
---
import Document from '../../layouts/document.astro';
import PostListTemplate from '../../page-components/post-list/post-list.astro';
import { getAllPostsForListView } from 'utils/api';
import {PostInfo} from 'types/PostInfo';
import Document from "../../layouts/document.astro";
import PostListTemplate from "../../page-components/post-list/post-list.astro";
import { getAllPostsForListView } from "utils/api";
import { PostInfo } from "types/PostInfo";
import SEO from "components/seo/seo.astro";
import { Page } from 'astro';
import { Page } from "astro";
export async function getStaticPaths({ paginate }) {
const posts = await Astro.glob<PostInfo>('../../../content/blog/**/*.md')
const posts = await Astro.glob<PostInfo>("../../../content/blog/**/*.md");
const postsToDisplay = getAllPostsForListView(posts, 'en');
const postsToDisplay = getAllPostsForListView(posts, "en");
return paginate(postsToDisplay, { pageSize: 8 });
}
@@ -23,5 +23,5 @@ const SEOTitle = `Post page ${pageIndex}`;
<Document>
<SEO slot="head" title={SEOTitle} />
<PostListTemplate posts={page.data} page={page} rootURL={'/'}/>
<PostListTemplate posts={page.data} page={page} rootURL={"/"} />
</Document>

View File

@@ -1,46 +1,44 @@
---
import Document from '../../layouts/document.astro';
import Document from "../../layouts/document.astro";
import SEO from "components/seo/seo.astro";
import BlogPostLayout from 'components/blog-post-layout/blog-post-layout.astro';
import PostTitleHeader from 'src/page-components/blog-post/post-title-header/post-title-header.astro';
import PostMetadata from 'src/page-components/blog-post/post-metadata/post-metadata.astro';
import TabsScript from 'src/page-components/blog-post/tabs-script/tabs-script.astro';
import SuggestedArticles from 'src/page-components/blog-post/suggested-articles/suggested-articles.astro';
import TableOfContents from 'components/table-of-contents/table-of-contents.astro';
import BlogPostLayout from "components/blog-post-layout/blog-post-layout.astro";
import PostTitleHeader from "src/page-components/blog-post/post-title-header/post-title-header.astro";
import PostMetadata from "src/page-components/blog-post/post-metadata/post-metadata.astro";
import TabsScript from "src/page-components/blog-post/tabs-script/tabs-script.astro";
import SuggestedArticles from "src/page-components/blog-post/suggested-articles/suggested-articles.astro";
import TableOfContents from "components/table-of-contents/table-of-contents.astro";
import type { MarkdownInstance } from 'astro';
import {PostInfo} from 'types/PostInfo';
import { Languages } from 'types/index';
import type { MarkdownInstance } from "astro";
import { PostInfo } from "types/PostInfo";
import { Languages } from "types/index";
export async function getStaticPaths() {
const posts = await Astro.glob<PostInfo>('../../../content/blog/**/*.md')
const posts = await Astro.glob<PostInfo>("../../../content/blog/**/*.md");
return posts.map(post => {
return posts.map((post) => {
return {
params: {
// TODO: Pass locale
postid: post.frontmatter.slug
postid: post.frontmatter.slug,
},
props: {
Content: post.Content,
post: post.frontmatter
}
}
})
post: post.frontmatter,
},
};
});
}
const { Content, post } = Astro.props as {
post: PostInfo,
Content: MarkdownInstance<any>['Content']
}
post: PostInfo;
Content: MarkdownInstance<any>["Content"];
};
const translations = post?.translations || [];
const otherLangs = translations
? (Object.keys(translations).filter(
(t) => t !== post.locale
) as Languages[])
? (Object.keys(translations).filter((t) => t !== post.locale) as Languages[])
: [];
---
@@ -70,7 +68,10 @@ const otherLangs = translations
<TableOfContents headingsWithId={post.headingsWithId} />
</div>
<div slot="right">
<SuggestedArticles suggestedArticles={post.suggestedArticles} lang={"en"} />
<SuggestedArticles
suggestedArticles={post.suggestedArticles}
lang={"en"}
/>
</div>
<header role="banner" class="marginZeroAutoChild">
<PostTitleHeader post={post} />

View File

@@ -1,22 +1,22 @@
---
import Document from '../../../layouts/document.astro';
import Document from "../../../layouts/document.astro";
import SEO from "components/seo/seo.astro";
import UnicornsPage from '../../../page-components/unicorns/unicorn-page.astro';
import { getAllPostsForUnicornListView } from 'utils/api';
import {PostInfo} from 'types/PostInfo';
import UnicornsPage from "../../../page-components/unicorns/unicorn-page.astro";
import { getAllPostsForUnicornListView } from "utils/api";
import { PostInfo } from "types/PostInfo";
import { unicorns } from "utils/data";
import { Page } from 'astro';
import { Page } from "astro";
export async function getStaticPaths() {
return unicorns.map(unicorn => ({ params: {unicornid: unicorn.id}}))
return unicorns.map((unicorn) => ({ params: { unicornid: unicorn.id } }));
}
const params = Astro.params as { unicornid: string };
const unicorn = unicorns.find(unicorn => unicorn.id === params.unicornid);
const unicorn = unicorns.find((unicorn) => unicorn.id === params.unicornid);
const posts = await Astro.glob<PostInfo>('../../../../content/blog/**/*.md')
const enPosts = getAllPostsForUnicornListView(unicorn.id, posts, 'en');
const posts = await Astro.glob<PostInfo>("../../../../content/blog/**/*.md");
const enPosts = getAllPostsForUnicornListView(unicorn.id, posts, "en");
const postsToDisplay = enPosts.slice(0, 8);
const page = {
@@ -26,8 +26,8 @@ const page = {
lastPage: Math.floor(enPosts.length / postsToDisplay.length),
url: {
current: `/unicorns/${unicorn.id}`,
next: `/unicorns/${unicorn.id}/page/2`
}
next: `/unicorns/${unicorn.id}/page/2`,
},
} as Page<PostInfo>;
const rootURL = `/unicorns/${unicorn.id}/`;
@@ -41,5 +41,10 @@ const rootURL = `/unicorns/${unicorn.id}/`;
unicornsData={[unicorn]}
type="profile"
/>
<UnicornsPage unicorn={unicorn} page={page} posts={postsToDisplay} rootURL={rootURL}/>
<UnicornsPage
unicorn={unicorn}
page={page}
posts={postsToDisplay}
rootURL={rootURL}
/>
</Document>

View File

@@ -1,24 +1,33 @@
---
import Document from '../../../../layouts/document.astro';
import Document from "../../../../layouts/document.astro";
import SEO from "components/seo/seo.astro";
import UnicornsPage from '../../../../page-components/unicorns/unicorn-page.astro';
import { getAllPostsForUnicornListView } from 'utils/api';
import {PostInfo} from 'types/PostInfo';
import UnicornsPage from "../../../../page-components/unicorns/unicorn-page.astro";
import { getAllPostsForUnicornListView } from "utils/api";
import { PostInfo } from "types/PostInfo";
import { unicorns } from "utils/data";
import { Page } from 'astro';
import { Page } from "astro";
export async function getStaticPaths({ paginate }) {
const posts = await Astro.glob<PostInfo>('../../../../../content/blog/**/*.md')
return unicorns.map(unicorn => {
const postsToDisplay = getAllPostsForUnicornListView(unicorn.id, posts, 'en');
return paginate(postsToDisplay, { params: {unicornid: unicorn.id}, pageSize: 8 });
})
const posts = await Astro.glob<PostInfo>(
"../../../../../content/blog/**/*.md"
);
return unicorns.map((unicorn) => {
const postsToDisplay = getAllPostsForUnicornListView(
unicorn.id,
posts,
"en"
);
return paginate(postsToDisplay, {
params: { unicornid: unicorn.id },
pageSize: 8,
});
});
}
const { page } = Astro.props as { page: Page<PostInfo> };
const params = Astro.params as { unicornid: string };
const unicorn = unicorns.find(unicorn => unicorn.id === params.unicornid);
const unicorn = unicorns.find((unicorn) => unicorn.id === params.unicornid);
const rootURL = `/unicorns/${unicorn.id}/`;
---
@@ -30,5 +39,10 @@ const rootURL = `/unicorns/${unicorn.id}/`;
unicornsData={[unicorn]}
type="profile"
/>
<UnicornsPage unicorn={unicorn} page={page} posts={page.data} rootURL={rootURL}/>
<UnicornsPage
unicorn={unicorn}
page={page}
posts={page.data}
rootURL={rootURL}
/>
</Document>

View File

@@ -15,7 +15,7 @@ code .line::before {
margin-right: 1.5rem;
display: inline-block;
text-align: right;
color: rgba(115,138,148,.4)
color: rgba(115, 138, 148, 0.4);
}
/* Start of Shiki Twoslash CSS:
@@ -77,7 +77,8 @@ pre.shiki:hover .dim {
pre.shiki div.dim {
opacity: 0.5;
}
pre.shiki div.dim, pre.shiki div.highlight {
pre.shiki div.dim,
pre.shiki div.highlight {
margin: 0;
padding: 0;
}
@@ -163,7 +164,8 @@ pre code a {
}
pre data-err {
/* Extracted from VS Code */
background: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%206%203'%20enable-background%3D'new%200%200%206%203'%20height%3D'3'%20width%3D'6'%3E%3Cg%20fill%3D'%23c94824'%3E%3Cpolygon%20points%3D'5.5%2C0%202.5%2C3%201.1%2C3%204.1%2C0'%2F%3E%3Cpolygon%20points%3D'4%2C0%206%2C2%206%2C0.6%205.4%2C0'%2F%3E%3Cpolygon%20points%3D'0%2C2%201%2C3%202.4%2C3%200%2C0.6'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E") repeat-x bottom left;
background: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%206%203'%20enable-background%3D'new%200%200%206%203'%20height%3D'3'%20width%3D'6'%3E%3Cg%20fill%3D'%23c94824'%3E%3Cpolygon%20points%3D'5.5%2C0%202.5%2C3%201.1%2C3%204.1%2C0'%2F%3E%3Cpolygon%20points%3D'4%2C0%206%2C2%206%2C0.6%205.4%2C0'%2F%3E%3Cpolygon%20points%3D'0%2C2%201%2C3%202.4%2C3%200%2C0.6'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E")
repeat-x bottom left;
padding-bottom: 3px;
}
pre .query {
@@ -178,7 +180,8 @@ pre .query {
/* This sections keeps both of those two in in sync */
pre .error, pre .error-behind {
pre .error,
pre .error-behind {
margin-left: -14px;
margin-top: 8px;
margin-bottom: 4px;
@@ -346,7 +349,6 @@ pre .logger.log-log svg {
margin-right: 9px;
}
blockquote {
position: relative;
margin-left: 1em;

View File

@@ -132,7 +132,6 @@ $robotoMono: "Roboto Mono", monospace;
}
}
%headline-uniwidth-2 {
font-family: $asap;
font-weight: 400;

View File

@@ -20,7 +20,7 @@ export interface RawPostInfo {
export interface PostInfo extends RawPostInfo {
slug: string;
locale: Languages;
Content: MarkdownInstance<never>['Content'];
Content: MarkdownInstance<never>["Content"];
authorsMeta: UnicornInfo[];
licenseMeta: LicenseInfo;
excerpt: string;

View File

@@ -18,7 +18,7 @@ export interface RawUnicornInfo {
pronouns: string;
profileImg: string;
color: string;
roles: Array<RolesEnum['id']>;
roles: Array<RolesEnum["id"]>;
}
export interface UnicornInfo extends RawUnicornInfo {

View File

@@ -2,7 +2,7 @@ import { PostInfo } from "types/PostInfo";
import { Languages } from "types/index";
import { MarkdownInstance } from "astro";
let allPostsCache = new WeakMap<object, MarkdownInstance<PostInfo>[]>();
const allPostsCache = new WeakMap<object, MarkdownInstance<PostInfo>[]>();
export function getAllPosts(
posts: MarkdownInstance<PostInfo>[],
@@ -16,15 +16,14 @@ export function getAllPosts(
if (cacheString) allPostsCache.set(cacheString, posts);
return posts
.filter(post => post.frontmatter.locale === language);
return posts.filter((post) => post.frontmatter.locale === language);
}
const listViewCache = {};
export const getAllPostsForListView = (
posts: MarkdownInstance<PostInfo>[],
language: Languages,
language: Languages
): PostInfo[] => {
let allPosts = getAllPosts(posts, language, listViewCache);
@@ -35,16 +34,17 @@ export const getAllPostsForListView = (
return date1 > date2 ? -1 : 1;
});
return allPosts.map(post => post.frontmatter).filter(post => post.locale === language);
return allPosts
.map((post) => post.frontmatter)
.filter((post) => post.locale === language);
};
export const getAllPostsForUnicornListView = (
authorId: string,
posts: MarkdownInstance<PostInfo>[],
language: Languages,
language: Languages
): PostInfo[] => {
return getAllPostsForListView(posts, language)
.filter(post =>
post.authorsMeta.find(postAuthor => postAuthor.id === authorId)
return getAllPostsForListView(posts, language).filter((post) =>
post.authorsMeta.find((postAuthor) => postAuthor.id === authorId)
);
};

View File

@@ -37,10 +37,8 @@ const fullUnicorns: UnicornInfo[] = unicornsRaw.map((unicorn) => {
/**
* `getFullRelativePath` strips all prefixing `/`, so we must add one manually
*/
const relativeServerPath = '/' + getFullRelativePath(
"content/data/",
unicorn.profileImg
);
const relativeServerPath =
"/" + getFullRelativePath("content/data/", unicorn.profileImg);
const profileImgSize = getImageSize(unicorn.profileImg, dataDirectory);
// Mutation go BRR

View File

@@ -28,7 +28,7 @@ export function getPostSlugs(lang: Languages) {
export const getAllPosts = (lang: Languages): PostInfo[] => {
const slugs = getPostSlugs(lang);
return slugs.map(slug => {
return slugs.map((slug) => {
const file = {
path: path.join(postsDirectory, slug, getIndexPath(lang)),
data: {
@@ -41,8 +41,8 @@ export const getAllPosts = (lang: Languages): PostInfo[] => {
(rehypeUnicornPopulatePost as any)()(undefined, file);
return {
...(file.data.astro.frontmatter as any || {}).frontmatterBackup,
...file.data.astro.frontmatter
...((file.data.astro.frontmatter as any) || {}).frontmatterBackup,
...file.data.astro.frontmatter,
};
});
};
})
}

View File

@@ -56,7 +56,7 @@ export type OrderSuggestPosts = ReturnType<
*/
const howManySimilarBetween = <T>(arr1: T[], arr2: T[]): number => {
let match = 0;
for (let item of arr1) {
for (const item of arr1) {
if (arr2.includes(item)) match++;
}
return match;
@@ -89,19 +89,16 @@ const getOrderRange = (arr: OrderSuggestPosts) => {
};
};
export const getSuggestedArticles = (
postNode: PostInfo,
lang: Languages
) => {
export const getSuggestedArticles = (postNode: PostInfo, lang: Languages) => {
const { suggestedPosts, dateSorted } = getAllPostsByLang(lang);
let extraSuggestedArticles: OrderSuggestPosts = [];
let suggestedArticles: OrderSuggestPosts = [];
let similarTags: Array<{
const extraSuggestedArticles: OrderSuggestPosts = [];
const suggestedArticles: OrderSuggestPosts = [];
const similarTags: Array<{
post: OrderSuggestPosts[number];
howManyTagsSimilar: number;
}> = [];
for (let post of suggestedPosts) {
for (const post of suggestedPosts) {
// Early "return" for value
if (suggestedArticles.length >= 3) break;
// Don't return the same article
@@ -133,7 +130,7 @@ export const getSuggestedArticles = (
if (suggestedArticles.length >= 3) break;
if (extraSuggestedArticles.length === 0) break;
const { largest, smallest } = getOrderRange(suggestedArticles) || {};
for (let suggestedPost of extraSuggestedArticles) {
for (const suggestedPost of extraSuggestedArticles) {
if (
suggestedPost.order === smallest.order! - 1 ||
suggestedPost.order === largest.order! + 1

View File

@@ -30,7 +30,7 @@ export const rehypeAstroImageMd: Plugin<
loader: sharp_service ?? globalThis.astroImage?.loader,
};
let imgNodes: any[] = [];
const imgNodes: any[] = [];
visit(tree, (node: any) => {
if (node.tagName === "img") {
imgNodes.push(node);
@@ -39,9 +39,13 @@ export const rehypeAstroImageMd: Plugin<
await Promise.all(
imgNodes.map(async (node) => {
const slug = path.dirname(file.path).split('/').at(-1);
const slug = path.dirname(file.path).split("/").at(-1);
const filePathDir = path.resolve(__dirname, '../../../public/content/blog', slug)
const filePathDir = path.resolve(
__dirname,
"../../../public/content/blog",
slug
);
// TODO: How should remote images be handled?
const dimensions = getImageSize(node.properties.src, filePathDir) || {

View File

@@ -1,36 +1,36 @@
import { Root } from "hast";
import { Plugin } from "unified";
import {visit} from 'unist-util-visit'
import { visit } from "unist-util-visit";
interface RehypeExcerptProps {
maxLength: number;
}
export const rehypeExcerpt: Plugin<
[RehypeExcerptProps | never],
Root
> = ({maxLength}) => {
export const rehypeExcerpt: Plugin<[RehypeExcerptProps | never], Root> = ({
maxLength,
}) => {
return (tree, file) => {
const getFileExcerpt = () => (file?.data?.astro as any)?.frontmatter?.excerpt as string;
const getFileExcerpt = () =>
(file?.data?.astro as any)?.frontmatter?.excerpt as string;
const setFileExcerpt = (val) => {
(file.data.astro as any).frontmatter.excerpt = val;
}
};
if (!getFileExcerpt()) {
setFileExcerpt("");
}
visit(tree, 'element', node => {
visit(tree, "element", (node) => {
const fileExcerpt = getFileExcerpt();
if (fileExcerpt.length >= maxLength) return;
// Don't get headers or anything other than text
if (node.tagName === 'p') {
visit(node, 'text', textNode => {
if (node.tagName === "p") {
visit(node, "text", (textNode) => {
let newVal = fileExcerpt + textNode.value;
if (newVal.length > maxLength) {
newVal = newVal.slice(0, maxLength - 3) + "...";
}
setFileExcerpt(newVal);
})
});
}
});
};

View File

@@ -26,7 +26,7 @@ export const rehypeHeaderText = () => {
};
if (file.data.astro.frontmatter.headingsWithId) {
file.data.astro.frontmatter.headingsWithId.push(headingWithID)
file.data.astro.frontmatter.headingsWithId.push(headingWithID);
} else {
file.data.astro.frontmatter.headingsWithId = [headingWithID];
}

View File

@@ -9,8 +9,7 @@ import { fromHtml } from "hast-util-from-html";
import path from "path";
interface RehypeUnicornElementMapProps {
}
interface RehypeUnicornElementMapProps {}
function escapeHTML(s) {
if (!s) return s;
@@ -85,7 +84,9 @@ export const rehypeUnicornElementMap: Plugin<
const headerLinkHTML = `
<a
href="#${id}"
aria-label="Permalink for &quot;${escapeHTML(headerText)}&quot;"
aria-label="Permalink for &quot;${escapeHTML(
headerText
)}&quot;"
class="anchor before"
>
<svg

View File

@@ -2,8 +2,7 @@ import { Root } from "hast";
import { Plugin } from "unified";
import { getSuggestedArticles } from "../get-suggested-articles";
interface RehypeUnicornGetSuggestedPostsProps {
}
interface RehypeUnicornGetSuggestedPostsProps {}
export const rehypeUnicornGetSuggestedPosts: Plugin<
[RehypeUnicornGetSuggestedPostsProps | never],
@@ -16,10 +15,10 @@ export const rehypeUnicornGetSuggestedPosts: Plugin<
const post = {
...(file.data.astro as any).frontmatter.frontmatterBackup,
...(file.data.astro as any).frontmatter
}
...(file.data.astro as any).frontmatter,
};
const suggestedArticles = getSuggestedArticles(post, 'en');
const suggestedArticles = getSuggestedArticles(post, "en");
setData("suggestedArticles", suggestedArticles);
};
};

View File

@@ -5,15 +5,13 @@ import { readFileSync } from "fs";
import * as path from "path";
import { licenses, unicorns } from "../data";
interface RehypeUnicornPopulatePostProps {
}
interface RehypeUnicornPopulatePostProps {}
export const rehypeUnicornPopulatePost: Plugin<
[RehypeUnicornPopulatePostProps | never],
Root
> = () => {
return (_, file) => {
function setData(key: string, val: any) {
(file.data.astro as any).frontmatter[key] = val;
}
@@ -29,10 +27,10 @@ export const rehypeUnicornPopulatePost: Plugin<
// Calculate post locale
// index.md or index.es.md
const indexName = directorySplit.at(-1);
const indexSplit = indexName.split('.');
const indexSplit = indexName.split(".");
let locale = indexSplit.at(-2);
if (locale === 'index') {
locale = 'en';
if (locale === "index") {
locale = "en";
}
// // TODO: Add translations
@@ -66,16 +64,14 @@ export const rehypeUnicornPopulatePost: Plugin<
let license;
if (frontmatter.license) {
license = licenses.find(
(l) => l.id === frontmatter.license
);
license = licenses.find((l) => l.id === frontmatter.license);
}
if (!license) license = null;
setData('slug', slug);
setData('locale', locale);
setData('authorsMeta', authorsMeta);
setData('license', license);
setData('frontmatterBackup', frontmatter);
setData("slug", slug);
setData("locale", locale);
setData("authorsMeta", authorsMeta);
setData("license", license);
setData("frontmatterBackup", frontmatter);
};
};

View File

@@ -27,7 +27,7 @@ import { visit } from "unist-util-visit";
import { unified } from "unified";
import english from "retext-english";
import rehypeRetext from 'rehype-retext';
import rehypeRetext from "rehype-retext";
import { validateConfig } from "astro/dist/types/core/config";
interface RemarkCountProps {}
@@ -39,9 +39,12 @@ function count(counts: Record<string, number>) {
visit(tree, visitor);
function visitor(node: Node) {
if (node.type === 'SourceNode') {
const inlineCount = (node as never as {value: string}).value.split(/\b/g).length;
counts["InlineCodeWords"] = (counts["InlineCodeWords"] || 0) + inlineCount;
if (node.type === "SourceNode") {
const inlineCount = (node as never as { value: string }).value.split(
/\b/g
).length;
counts["InlineCodeWords"] =
(counts["InlineCodeWords"] || 0) + inlineCount;
}
counts[node.type] = (counts[node.type] || 0) + 1;
}
@@ -67,6 +70,7 @@ export const rehypeWordCount: Plugin<[RemarkCountProps | never], Root> = () => {
.use(rehypeRetext, unified().use(english).use(count(counts)))
.run(tree);
(file.data.astro as any).frontmatter.wordCount = (counts.InlineCodeWords || 0) + (counts.TextNode || 0);
(file.data.astro as any).frontmatter.wordCount =
(counts.InlineCodeWords || 0) + (counts.TextNode || 0);
};
};

View File

@@ -1 +1 @@
export * from './tabs';
export * from "./tabs";

View File

@@ -14,7 +14,7 @@ const isNodeHeading = (n: ElementNode) =>
const findLargestHeading = (nodes: ElementNode[]) => {
let largestSize = Infinity;
for (let node of nodes) {
for (const node of nodes) {
if (!isNodeHeading(node)) continue;
const size = parseInt(node.tagName.substr(1), 10);
largestSize = Math.min(largestSize, size);
@@ -61,15 +61,15 @@ export const rehypeTabs: Plugin<[RehypeTabsProps | never], Root> = ({
type: "element",
tagName: "div",
properties: {
class: "tabs"
class: "tabs",
},
children: [
{
type: "element",
tagName: "ul",
properties: {
role: 'tablist',
class: "tabs__tab-list"
role: "tablist",
class: "tabs__tab-list",
},
children: [] as ElementNode[],
},
@@ -98,13 +98,13 @@ export const rehypeTabs: Plugin<[RehypeTabsProps | never], Root> = ({
tagName: "li",
children: localNode.children,
properties: {
role: 'tab',
role: "tab",
class: "tabs__tab",
"data-tabname": headerSlug,
"aria-selected": idx === 0 ? "true" : 'false',
"aria-selected": idx === 0 ? "true" : "false",
"aria-controls": `panel-${idx}`,
"id": `tab-${idx}`,
tabIndex: idx === 0 ? "0" : "-1"
id: `tab-${idx}`,
tabIndex: idx === 0 ? "0" : "-1",
},
};
@@ -118,7 +118,7 @@ export const rehypeTabs: Plugin<[RehypeTabsProps | never], Root> = ({
class: "tabs__tab-panel",
tabindex: 0,
"aria-labelledby": `tab-${idx}`,
...(idx === 0 ? {} : {hidden: "true"})
...(idx === 0 ? {} : { hidden: "true" }),
},
};

View File

@@ -14,20 +14,22 @@ export const isRelativePath = (str: string) => {
return true;
};
var pathJoin = function(...pathArr){
return pathArr.map(function(path){
const pathJoin = function (...pathArr) {
return pathArr
.map(function (path) {
if (path[0] === "/") {
path = path.slice(1);
}
if (path.startsWith('./')) {
if (path.startsWith("./")) {
path = path.slice(2);
}
if (path[path.length - 1] === "/") {
path = path.slice(0, path.length - 1);
}
return path;
}).join("/");
}
})
.join("/");
};
export const getFullRelativePath = (...paths: string[]) => {
return isRelativePath(paths[paths.length - 1])

View File

@@ -24,7 +24,7 @@
"components/*": ["./src/components/*"],
"utils/*": ["./src/utils/*"],
"uu-utils": ["./src/utils"],
"assets/*": ["./src/assets/*"],
"assets/*": ["./src/assets/*"]
}
}
}