Skip to content

Commit

Permalink
chore: fixes
Browse files Browse the repository at this point in the history
Simplify code in a manner after God's own heart.
Or maybe just mine.
This fixes a lot of type holes.
We now use template literal types, custom type literal functions, and don't use casts.
In addition, a lot more things are `readonly` or `const` now. Down with mutability!

UI-wise, I removed the link to the homepage in the header, because you can just click the "logo."
This simplifies quite a bit of code.
  • Loading branch information
lishaduck committed Jan 9, 2024
1 parent 3075dc8 commit abe549b
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 26 deletions.
6 changes: 4 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
"quickfix.biome": "always",
"source.organizeImports.biome": "always"
},
"css.customData": [
".vscode/tailwind.json"
"css.customData": [".vscode/tailwind.json"],
"tailwindCSS.experimental.classRegex": [
"tw`([^`]*)",
["tw\\(([^\\)]*)\\)", "`([^`]*)`"]
]
}
8 changes: 5 additions & 3 deletions src/components/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import IconLemon2 from "$tabler_icons/lemon-2.tsx";
import IconSolarPanel2 from "$tabler_icons/solar-panel-2.tsx";
import type { VNode } from "preact";
import { siteName, slogan } from "../site.ts";
import { tw } from "../utils/tailwind.ts";

export interface FooterProps {
readonly class?: string;
Expand All @@ -27,7 +28,8 @@ const menus = [
{ name: "Pricing", href: "pricing/" },
],
},
];
] as const;

const icons = [
{
icon: IconLemon2,
Expand All @@ -49,9 +51,9 @@ const icons = [
href: "https://tailwindcss.com/",
name: "Tailwind",
},
];
] as const;

export function Footer({ class: classes = "" }: FooterProps): VNode {
export function Footer({ class: classes = tw`` }: FooterProps): VNode {
return (
<footer
class={`max-w-screen-xlg grid w-full grid-flow-col grid-cols-footer-mobile grid-rows-footer-mobile gap-x-2 gap-y-16 bg-white p-8 text-sm dark:bg-black sm:grid-cols-footer-desktop sm:grid-rows-footer-desktop sm:gap-x-8 md:gap-16 ${classes}`}
Expand Down
18 changes: 6 additions & 12 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import IconSolarPanel2 from "$tabler_icons/solar-panel-2.tsx";
import type { VNode } from "preact";
import { HeaderMenu } from "../islands/HeaderMenu.tsx";
import { HeaderMenu, isMenuWithItems } from "../islands/HeaderMenu.tsx";
import { siteName } from "../site.ts";

export interface HeaderProps {
readonly active: string; // TODO(lishaduck): https://deno.com/blog/fresh-1.5#easier-active-link-styling
readonly active: string;
}

const menus = [
{
name: "Home",
href: "/",
},
{
name: "Going Green!",
href: "/green/",
Expand All @@ -32,7 +28,8 @@ const menus = [
name: "About",
href: "/about/",
},
];
] as const;

export function Header({ active }: HeaderProps): VNode {
return (
<header class="max-w-screen-xlg flex h-20 w-full flex-col gap-4 bg-white px-8 py-6 dark:bg-black sm:flex-row">
Expand All @@ -45,12 +42,9 @@ export function Header({ active }: HeaderProps): VNode {
<li key={menu.name} class="flex h-8 items-end">
<HeaderMenu
title={menu.name}
active={
active === menu.href ||
(active.startsWith(menu.href) && menu.href !== "/")
}
active={active === menu.href || active.startsWith(menu.href)}
href={menu.href}
{...(menu.items !== undefined ? { items: menu.items } : {})}
{...(isMenuWithItems(menu) ? { items: menu.items } : {})}
/>
</li>
))}
Expand Down
38 changes: 31 additions & 7 deletions src/islands/HeaderMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,57 @@
import IconChevronDown from "$tabler_icons/chevron-down.tsx";
import { Popover } from "@headlessui/react";
import type { VNode } from "preact";
import { tw } from "../utils/tailwind.ts";

export interface HeaderMenuProps {
readonly title: string;
readonly active: boolean;
readonly items?: MenuItem[];
readonly href?: string;
readonly items?: readonly MenuItem[];
readonly href?: `/${string}/`;
}

export interface MenuItem {
readonly url: string;
readonly url: `${string}/`;
readonly name: string;
}

function makeTextStyle(active: boolean): string {
return `text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 py-1 whitespace-nowrap ${
return tw`text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 py-1 whitespace-nowrap ${
active ? "font-bold" : ""
}`;
}

function makeBorderStyle(active: boolean): string {
return `border-gray-500 hover:border-gray-700 dark:border-gray-400 dark:hover:border-gray-200 ${
return tw`border-gray-500 hover:border-gray-700 dark:border-gray-400 dark:hover:border-gray-200 data-[current]:border-b-2${
active ? "border-b-2" : ""
}`;
}

const prettyFocus =
"rounded-sm focus:outline-none focus-visible:ring-2 focus-visible:ring-white/75";
const prettyFocus = tw`rounded-sm focus:outline-none focus-visible:ring-2 focus-visible:ring-white/75`;

/**
* Checks if the menu has items.
*
* @param menu The menu to check.
* @returns True if the menu has items, false otherwise.
*
* @todo Replace with zod.
*/
export function isMenuWithItems(
menu: unknown,
): menu is { readonly items: readonly MenuItem[] } {
return isMenuWithItemsHelper(menu) && Array.isArray(menu.items);
}

function isMenuWithItemsHelper(
menu: unknown,
): menu is { readonly items: unknown } {
return isMenuWithItemsHelper2(menu) && Object.hasOwn(menu, "items");
}

function isMenuWithItemsHelper2(menu: unknown): menu is object {
return typeof menu === "object" && menu !== null;
}

export function HeaderMenu({
title,
Expand Down
4 changes: 2 additions & 2 deletions src/utils/posts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const dir = "src/content";
export const solutions = await getPosts();

/** Get all solutions. */
export async function getPosts(): Promise<SolutionPage[]> {
export async function getPosts(): Promise<readonly SolutionPage[]> {
const files = Deno.readDir(dir);
const promises = [];
for await (const file of files) {
Expand All @@ -25,7 +25,7 @@ export async function getPosts(): Promise<SolutionPage[]> {

const solutions = await Promise.all(promises);

return solutions as SolutionPage[];
return solutions.filter((val): val is SolutionPage => val !== null);
}

/** Get a solution. */
Expand Down
6 changes: 6 additions & 0 deletions src/utils/tailwind.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export function tw(
strings: TemplateStringsArray,
...values: readonly unknown[]
) {
return strings.map((string, i) => string + (values[i] || "")).join("");
}

0 comments on commit abe549b

Please sign in to comment.