From 8d095b097dca78fbb6496b4f6ba67f9fa5521a83 Mon Sep 17 00:00:00 2001 From: Eli <88557639+lishaduck@users.noreply.github.com> Date: Fri, 1 Mar 2024 12:23:21 -0600 Subject: [PATCH 1/8] feat: add solar pricing calculator * Add Tailwind form reset and the headlessui plugin * Add a new page for the solar pricing calculator * Add a new island for comboboxes * Add a TODO comment about testing ;) * Add a new utility for grabbing geo data from IP addresses Co-authored-by: Matthew Wasser <71449347+MattsAttack@users.noreply.github.com> --- deno.json | 1 + src/fresh.gen.ts | 4 + src/islands/StateSelector.tsx | 110 +++++++++ src/routes/about.test.ts | 13 +- src/routes/calculator.tsx | 128 ++++++++++ src/tailwind.config.ts | 10 +- src/utils/calc.ts | 435 ++++++++++++++++++++++++++++++++++ src/utils/headless.ts | 50 ++++ src/utils/icons.ts | 6 + src/utils/ip.ts | 62 +++++ src/utils/zod.ts | 3 + 11 files changed, 818 insertions(+), 4 deletions(-) create mode 100644 src/islands/StateSelector.tsx create mode 100644 src/routes/calculator.tsx create mode 100644 src/utils/calc.ts create mode 100644 src/utils/headless.ts create mode 100644 src/utils/ip.ts create mode 100644 src/utils/zod.ts diff --git a/deno.json b/deno.json index 0671ab03..03b9faff 100644 --- a/deno.json +++ b/deno.json @@ -39,6 +39,7 @@ "remark-mdx-frontmatter": "npm:remark-mdx-frontmatter@4.0.0", "remark-preset-lint-consistent": "npm:remark-preset-lint-consistent@5.1.2", "remark-preset-lint-recommended": "npm:remark-preset-lint-recommended@6.1.3", + "@tailwindcss/forms": "npm:@tailwindcss/forms@0.5.7", "@tailwindcss/typography": "npm:@tailwindcss/typography@0.5.10", "tailwindcss": "npm:tailwindcss@3.4.1", "tailwindcss/plugin": "npm:tailwindcss@3.4.1/plugin.js", diff --git a/src/fresh.gen.ts b/src/fresh.gen.ts index 2083a31f..7fa38fc8 100644 --- a/src/fresh.gen.ts +++ b/src/fresh.gen.ts @@ -7,10 +7,12 @@ import * as $_500 from "./routes/_500.tsx"; import * as $_app from "./routes/_app.tsx"; import * as $_layout from "./routes/_layout.tsx"; import * as $about from "./routes/about.tsx"; +import * as $calculator from "./routes/calculator.tsx"; import * as $index from "./routes/index.tsx"; import * as $solutions_category_slug_ from "./routes/solutions/[category]/[[slug]].tsx"; import * as $solutions_category_index from "./routes/solutions/[category]/index.tsx"; import * as $HeaderMenu from "./islands/HeaderMenu.tsx"; +import * as $StateSelector from "./islands/StateSelector.tsx"; import { type Manifest } from "$fresh/server.ts"; const manifest = { @@ -20,12 +22,14 @@ const manifest = { "./routes/_app.tsx": $_app, "./routes/_layout.tsx": $_layout, "./routes/about.tsx": $about, + "./routes/calculator.tsx": $calculator, "./routes/index.tsx": $index, "./routes/solutions/[category]/[[slug]].tsx": $solutions_category_slug_, "./routes/solutions/[category]/index.tsx": $solutions_category_index, }, islands: { "./islands/HeaderMenu.tsx": $HeaderMenu, + "./islands/StateSelector.tsx": $StateSelector, }, baseUrl: import.meta.url, } satisfies Manifest; diff --git a/src/islands/StateSelector.tsx b/src/islands/StateSelector.tsx new file mode 100644 index 00000000..e7d2fe1a --- /dev/null +++ b/src/islands/StateSelector.tsx @@ -0,0 +1,110 @@ +import { IS_BROWSER } from "$fresh/runtime.ts"; +import { Combobox, Transition } from "@headlessui/react"; +import { useSignal } from "@preact/signals"; +import { Fragment, type JSX } from "preact"; +import { type State, states } from "../utils/calc.ts"; +import { IconCheck, IconChevronDown } from "../utils/icons.ts"; +import { tw } from "../utils/tailwind.ts"; + +export interface StateSelectorProps { + currentState: State; +} + +export function StateSelector({ + currentState, +}: StateSelectorProps): JSX.Element { + const state = useSignal(currentState); + const query = useSignal(""); + + const filteredStates = + query.value === "" + ? states + : states.filter((state) => + state + .toLowerCase() + .replace(/\s+/g, "") + .includes(query.value.toLowerCase().replace(/\s+/g, "")), + ); + + return ( +
+ { + state.value = newState; + }} + > + + What state are you from? + +
+
+ { + if (event.target instanceof HTMLInputElement) { + query.value = event.target.value; + } + }} + /> + + +
+ { + query.value = ""; + }} + > + + {filteredStates.length === 0 && query.value !== "" ? ( +
+ Nothing found. +
+ ) : ( + filteredStates.map((state) => ( + + {({ selected, active }) => ( + <> + + {state} + + {selected ? ( + + + ) : undefined} + + )} + + )) + )} +
+
+
+
+
+ ); +} diff --git a/src/routes/about.test.ts b/src/routes/about.test.ts index 8e7cc1da..f0e33480 100644 --- a/src/routes/about.test.ts +++ b/src/routes/about.test.ts @@ -10,9 +10,16 @@ const connInfo = { remoteAddr: { hostname: "127.0.0.1", port: 53496, transport: "tcp" }, } as const satisfies ServeHandlerInfo; -/** - * Tests for the about page. - */ +/* + TODO(lishaduck): write a testing library for Fresh + Plan: + - fork `fresh_marionette`, + - make it use Astral instead of Puppeteer, and + - add in the useful functions from `fresh_testing_library` + - Not including the parts from `testing-library`, of course. + - Use Astral or Storybook for integration and unit tests, respectively. + - Note that Storybook doesn't yet support Deno well. +*/ Deno.test("HTTP assert test.", async (t: Deno.TestContext): Promise => { const handler = await createHandler(manifest, config); diff --git a/src/routes/calculator.tsx b/src/routes/calculator.tsx new file mode 100644 index 00000000..0ea5f5b8 --- /dev/null +++ b/src/routes/calculator.tsx @@ -0,0 +1,128 @@ +import { Head } from "$fresh/runtime.ts"; +import type { Handlers, PageProps } from "$fresh/server.ts"; +import type { JSX } from "preact"; +import { Cover } from "../components/Cover.tsx"; +import { Meta } from "../components/Meta.tsx"; +import { StateSelector } from "../islands/StateSelector.tsx"; +import { type State, type StateData, stateData } from "../utils/calc.ts"; +import type { FreshContextHelper } from "../utils/handlers.ts"; +import { getIPLocation } from "../utils/ip.ts"; +import { isKey } from "../utils/type-helpers.ts"; + +export type CalculatorProps = CalculatorSearchProps | CalculatorShowProps; + +export interface CalculatorSearchProps { + state: "search"; + region: State; +} + +export interface CalculatorShowProps { + state: "display"; + regionData: StateData; +} + +/** + * The page title. + */ +const pageTitle = "Calculator" as const; + +export const handler: Handlers = { + async GET( + req: Request, + ctx: FreshContextHelper, + ): Promise { + const visitor = await getIPLocation(ctx.remoteAddr.hostname); + + const url = new URL(req.url); + const state = url.searchParams.get("region"); + if (state === null) { + return ctx.render({ + state: "search", + region: visitor.region, + }); + } + + if (!isKey(stateData, state)) { + return ctx.renderNotFound(); + } + + return ctx.render({ + state: "display", + regionData: stateData[state], + }); + }, +}; + +/** + * Render an about page. + * + * @returns The rendered about page. + */ +export default function Calculator({ + data, +}: PageProps): JSX.Element { + return ( + <> + + + + +
+ +
+ + ); +} + +function CalculatorPages(data: CalculatorProps): JSX.Element { + switch (data.state) { + case "search": + return ; + + case "display": + return ; + } +} + +function CalculatorDisplay(data: CalculatorShowProps): JSX.Element { + return ( + + + + + + + + + + + + + + + + + + + + + + +
Monies
Energy FormTime to PayoffSavings per MonthInstall CostRebateEmissions per Month
Solar{data.regionData.payoff.toFixed(2)} Years${data.regionData.savings.toFixed(2)}${data.regionData.install.toFixed(2)}${data.regionData.rebate.toFixed(2)}{data.regionData.emissions.toFixed(2)} lbs of Carbon per kWh
+ ); +} + +function CalculatorSearch(data: CalculatorSearchProps): JSX.Element { + return ( +
+ + + + + ); +} diff --git a/src/tailwind.config.ts b/src/tailwind.config.ts index e18b2907..91837fe6 100644 --- a/src/tailwind.config.ts +++ b/src/tailwind.config.ts @@ -1,9 +1,12 @@ +import forms from "@tailwindcss/forms"; import typography from "@tailwindcss/typography"; import type { Config } from "tailwindcss"; +// import headless from "@headlessui/tailwindcss"; +import headless from "./utils/headless.ts"; export default { content: ["{routes,islands,components}/**/*.{ts,tsx}"], - plugins: [typography], + plugins: [typography, forms, headless], theme: { extend: { gridTemplateColumns: { @@ -17,4 +20,9 @@ export default { }, }, }, + variants: { + extend: { + borderWidth: ["first"], + }, + }, } satisfies Config; diff --git a/src/utils/calc.ts b/src/utils/calc.ts new file mode 100644 index 00000000..fc3e46ab --- /dev/null +++ b/src/utils/calc.ts @@ -0,0 +1,435 @@ +import { z } from "zod"; + +export interface StateData { + /** Years */ + payoff: number; + + /** Per month */ + savings: number; + install: number; + /** A percentage */ + rebate: number; + emissions: number; +} + +export type State = z.infer; + +export const stateData = { + Alabama: { + payoff: 4.97, + savings: 2123 / 12, + install: 10542, + rebate: (10542 / 70) * 100 * 0.3, + emissions: 1178 * 0.86, + }, + + Alaska: { + payoff: 6.56, + savings: 1669 / 12, + install: 10941, + rebate: (10941 / 70) * 100 * 0.3, + emissions: 6.56 * 0.86, + }, + + Arizona: { + payoff: 5.76, + savings: 1849 / 12, + install: 10654, + rebate: (10654 / 70) * 100 * 0.3, + emissions: 5.76 * 0.86, + }, + + Arkansas: { + payoff: 6.95, + savings: 1693 / 12, + install: 11771, + rebate: (11771 / 70) * 100 * 0.3, + emissions: 6.95 * 0.86, + }, + + California: { + payoff: 6.8, + savings: 1715 / 12, + install: 11666, + rebate: (11666 / 70) * 100 * 0.3, + emissions: 6.8 * 0.86, + }, + + Colorado: { + payoff: 10.17, + savings: 1207 / 12, + install: 12278, + rebate: (12278 / 70) * 100 * 0.3, + emissions: 10.17 * 0.86, + }, + + Connecticut: { + payoff: 5.05, + savings: 2487 / 12, + install: 12558, + rebate: (12558 / 70) * 100 * 0.3, + emissions: 5.05 * 0.86, + }, + + Delaware: { + payoff: 5.79, + savings: 1972 / 12, + install: 11414, + rebate: (11414 / 70) * 100 * 0.3, + emissions: 5.79 * 0.86, + }, + + "District of Columbia": { + payoff: 8.17, + savings: 1481 / 12, + install: 12100, + rebate: (12100 / 70) * 100 * 0.3, + emissions: 8.17 * 0.86, + }, + + Florida: { + payoff: 5.71, + savings: 2064 / 12, + install: 11788, + rebate: (11788 / 70) * 100 * 0.3, + emissions: 5.71 * 0.86, + }, + + Georgia: { + payoff: 6.43, + savings: 1808 / 12, + install: 11634, + rebate: (11634 / 70) * 100 * 0.3, + emissions: 6.43 * 0.86, + }, + + Hawaii: { + payoff: 4.35, + savings: 2638 / 12, + install: 11466, + rebate: (11466 / 70) * 100 * 0.3, + emissions: 4.35 * 0.86, + }, + + Idaho: { + payoff: 8.26, + savings: 1465 / 12, + install: 12107, + rebate: (12107 / 70) * 100 * 0.3, + emissions: 8.26 * 0.86, + }, + + Illinois: { + payoff: 9.61, + savings: 1368 / 12, + install: 13146, + rebate: (13146 / 70) * 100 * 0.3, + emissions: 9.61 * 0.86, + }, + + Indiana: { + payoff: 7.65, + savings: 1729 / 12, + install: 13230, + rebate: (13230 / 70) * 100 * 0.3, + emissions: 7.65 * 0.86, + }, + + Iowa: { + payoff: 8.67, + savings: 1425 / 12, + install: 12348, + rebate: (12348 / 70) * 100 * 0.3, + emissions: 8.67 * 0.86, + }, + + Kansas: { + payoff: 7.31, + savings: 1528 / 12, + install: 11172, + rebate: (11172 / 70) * 100 * 0.3, + emissions: 7.31 * 0.86, + }, + + Kentucky: { + payoff: 6.55, + savings: 1705 / 12, + install: 11172, + rebate: (11172 / 70) * 100 * 0.3, + emissions: 6.55 * 0.86, + }, + + Louisiana: { + payoff: 6.76, + savings: 1761 / 12, + install: 11907, + rebate: (11907 / 70) * 100 * 0.3, + emissions: 6.76 * 0.86, + }, + + Maine: { + payoff: 6.61, + savings: 2038 / 12, + install: 13461, + rebate: (13461 / 70) * 100 * 0.3, + emissions: 6.61 * 0.86, + }, + + Maryland: { + payoff: 5.8, + savings: 2137 / 12, + install: 12401, + rebate: (12401 / 70) * 100 * 0.3, + emissions: 5.8 * 0.86, + }, + + Massachusetts: { + payoff: 7.82, + savings: 2137 / 12, + install: 15162, + rebate: (15162 / 70) * 100 * 0.3, + emissions: 7.82 * 0.86, + }, + + Michigan: { + payoff: 8.97, + savings: 1491 / 12, + install: 13377, + rebate: (13377 / 70) * 100 * 0.3, + emissions: 8.97 * 0.86, + }, + + Minnesota: { + payoff: 9.23, + savings: 1418 / 12, + install: 13083, + rebate: (13083 / 70) * 100 * 0.3, + emissions: 9.23 * 0.86, + }, + + Mississippi: { + payoff: 5.84, + savings: 1946 / 12, + install: 11361, + rebate: (11361 / 70) * 100 * 0.3, + emissions: 5.84 * 0.86, + }, + + Missouri: { + payoff: 7.35, + savings: 1605 / 12, + install: 11792, + rebate: (11792 / 70) * 100 * 0.3, + emissions: 7.35 * 0.86, + }, + + Montana: { + payoff: 7.84, + savings: 1412 / 12, + install: 11067, + rebate: (11067 / 70) * 100 * 0.3, + emissions: 7.84 * 0.86, + }, + + Nebraska: { + payoff: 8.42, + savings: 1472 / 12, + install: 12390, + rebate: (12390 / 70) * 100 * 0.3, + emissions: 8.42 * 0.86, + }, + + Nevada: { + payoff: 6.52, + savings: 1649 / 12, + install: 10752, + rebate: (10752 / 70) * 100 * 0.3, + emissions: 6.52 * 0.86, + }, + + "New Hampshire": { + payoff: 7.17, + savings: 1926 / 12, + install: 13818, + rebate: (13818 / 70) * 100 * 0.3, + emissions: 7.17 * 0.86, + }, + + "New Jersey": { + payoff: 6.58, + savings: 1858 / 12, + install: 12222, + rebate: (12222 / 70) * 100 * 0.3, + emissions: 6.58 * 0.86, + }, + + "New Mexico": { + payoff: 10.52, + savings: 1147 / 12, + install: 12065, + rebate: (12065 / 70) * 100 * 0.3, + emissions: 10.52 * 0.86, + }, + + "New York": { + payoff: 8.11, + savings: 1613 / 12, + install: 13083, + rebate: (13083 / 70) * 100 * 0.3, + emissions: 8.11 * 0.86, + }, + + "North Carolina": { + payoff: 6.06, + savings: 1897 / 12, + install: 11487, + rebate: (11487 / 70) * 100 * 0.3, + emissions: 6.06 * 0.86, + }, + + "North Dakota": { + payoff: 6.6, + savings: 1566 / 12, + install: 10332, + rebate: (10332 / 70) * 100 * 0.3, + emissions: 6.6 * 0.86, + }, + + Ohio: { + payoff: 6.85, + savings: 1679 / 12, + install: 11498, + rebate: (11498 / 70) * 100 * 0.3, + emissions: 6.85 * 0.86, + }, + + Oklahoma: { + payoff: 6.04, + savings: 1839 / 12, + install: 11102, + rebate: (11102 / 70) * 100 * 0.3, + emissions: 6.04 * 0.86, + }, + + Oregon: { + payoff: 8.17, + savings: 1503 / 12, + install: 12285, + rebate: (12285 / 70) * 100 * 0.3, + emissions: 8.17 * 0.86, + }, + + Pennsylvania: { + payoff: 6.48, + savings: 1889 / 12, + install: 12233, + rebate: (12233 / 70) * 100 * 0.3, + emissions: 6.48 * 0.86, + }, + + "Rhode Island": { + payoff: 5.98, + savings: 2246 / 12, + install: 13419, + rebate: (13419 / 70) * 100 * 0.3, + emissions: 5.98 * 0.86, + }, + + "South Carolina": { + payoff: 6.1, + savings: 1889 / 12, + install: 11519, + rebate: (11519 / 70) * 100 * 0.3, + emissions: 6.1 * 0.86, + }, + + "South Dakota": { + payoff: 6.18, + savings: 1649 / 12, + install: 10192, + rebate: (10192 / 70) * 100 * 0.3, + emissions: 6.18 * 0.86, + }, + + Tennessee: { + payoff: 6.73, + savings: 1792 / 12, + install: 12054, + rebate: (12054 / 70) * 100 * 0.3, + emissions: 6.73 * 0.86, + }, + + Texas: { + payoff: 5.33, + savings: 2079 / 12, + install: 11088, + rebate: (11088 / 70) * 100 * 0.3, + emissions: 5.33 * 0.86, + }, + + Utah: { + payoff: 10.71, + savings: 1054 / 12, + install: 11288, + rebate: (11288 / 70) * 100 * 0.3, + emissions: 10.71 * 0.86, + }, + + Vermont: { + payoff: 8.57, + savings: 1507 / 12, + install: 12915, + rebate: (12915 / 70) * 100 * 0.3, + emissions: 8.57 * 0.86, + }, + + Virginia: { + payoff: 6.54, + savings: 1860 / 12, + install: 12170, + rebate: (12170 / 70) * 100 * 0.3, + emissions: 6.54 * 0.86, + }, + + Washington: { + payoff: 9.05, + savings: 1379 / 12, + install: 12485, + rebate: (12485 / 70) * 100 * 0.3, + emissions: 9.05 * 0.86, + }, + + "West Virginia": { + payoff: 6.08, + savings: 1942 / 12, + install: 11813, + rebate: (11813 / 70) * 100 * 0.3, + emissions: 6.08 * 0.86, + }, + + Wisconsin: { + payoff: 9.17, + savings: 1400 / 12, + install: 12842, + rebate: (12842 / 70) * 100 * 0.3, + emissions: 9.17 * 0.86, + }, + + Wyoming: { + payoff: 8.29, + savings: 1333 / 12, + install: 11046, + rebate: (11046 / 70) * 100 * 0.3, + emissions: 8.29 * 0.86, + }, +} as const satisfies Record; + +export const states = Object.keys(stateData); + +export const regionSchema = z.custom( + (region) => + z + .string() + .refine((region2) => states.includes(region2)) + .safeParse(region).success, +); diff --git a/src/utils/headless.ts b/src/utils/headless.ts new file mode 100644 index 00000000..a92891d0 --- /dev/null +++ b/src/utils/headless.ts @@ -0,0 +1,50 @@ +/** + * Vendor of @headlessui/tailwindcss b/c it doesn't support ESM. + * @module + */ + +import plugin from "tailwindcss/plugin"; + +interface Options { + /** + * The prefix used for the variants. This defaults to `ui`. + * + * Usage example: + * ```html + *
+ * ``` + **/ + prefix?: string; +} + +export default plugin.withOptions(({ prefix = "ui" } = {}) => { + return ({ addVariant }) => { + for (const state of ["open", "checked", "selected", "active", "disabled"]) { + // TODO(tailwindlabs): Once `:has()` is properly supported, then we can switch to this version: + // addVariant(`${prefix}-${state}`, [ + // `&[data-headlessui-state~="${state}"]`, + // `:where([data-headlessui-state~="${state}"]):not(:has([data-headlessui-state])) &`, + // ]) + + // But for now, this will do: + addVariant(`${prefix}-${state}`, [ + `&[data-headlessui-state~="${state}"]`, + `:where([data-headlessui-state~="${state}"]) &`, + ]); + + addVariant(`${prefix}-not-${state}`, [ + `&[data-headlessui-state]:not([data-headlessui-state~="${state}"])`, + `:where([data-headlessui-state]:not([data-headlessui-state~="${state}"])) &:not([data-headlessui-state])`, + ]); + } + + addVariant( + `${prefix}-focus-visible`, + ":where([data-headlessui-focus-visible]) &:focus", + ); + addVariant( + `${prefix}-not-focus-visible`, + "&:focus:where(:not([data-headlessui-focus-visible] &))", + ); + }; +}); diff --git a/src/utils/icons.ts b/src/utils/icons.ts index 1dbd9951..ffa194ad 100644 --- a/src/utils/icons.ts +++ b/src/utils/icons.ts @@ -5,6 +5,7 @@ import IconBoltComponent from "$tabler_icons/bolt.tsx"; import IconBrandDenoComponent from "$tabler_icons/brand-deno.tsx"; import IconBrandReactComponent from "$tabler_icons/brand-react.tsx"; import IconBrandTailwindComponent from "$tabler_icons/brand-tailwind.tsx"; +import IconCheckComponent from "$tabler_icons/check.tsx"; import IconChevronDownComponent from "$tabler_icons/chevron-down.tsx"; import IconFlameComponent from "$tabler_icons/flame.tsx"; import IconInfoCircleComponent from "$tabler_icons/info-circle.tsx"; @@ -82,3 +83,8 @@ export const IconAlertTriangle: Icon = IconAlertTriangleComponent; * An info flame icon. */ export const IconFlame: Icon = IconFlameComponent; + +/** + * An icon of a checkmark. + */ +export const IconCheck: Icon = IconCheckComponent; diff --git a/src/utils/ip.ts b/src/utils/ip.ts new file mode 100644 index 00000000..4ded342e --- /dev/null +++ b/src/utils/ip.ts @@ -0,0 +1,62 @@ +/** + * Vendored version of `https://deno.land/x/ip_location@v1.0.0` + * @module + */ + +import { z } from "zod"; +import { regionSchema } from "./calc.ts"; +import type { ZodTypeUnknown } from "./zod.ts"; + +type Geo = z.infer; +type Ip = z.infer; + +const geoSchema = z.object({ region: regionSchema }); +const ipSchema = z.object({ ip: z.string() }); + +const ipEndpoint = "https://api.ipify.org"; +const geoEndpoint = "https://ipapi.co"; + +let serverIp: string | undefined; + +/** + * Get details about an IP from the API. + */ +export async function getIPLocation(ip?: string): Promise { + let currentIP: string | undefined; + if (["127.0.0.1", undefined].includes(ip)) { + if (serverIp === undefined) { + serverIp = (await getIp()).ip; + } + currentIP = serverIp; + } else { + currentIP = ip; + } + + return await makeRequest(`${geoEndpoint}/${currentIP}/json/`, geoSchema); +} + +/** + * Query a JSON API and validate the response. + * + * @param endpoint - The API to query (GET). + * @param schema - The Zod schema to validate the data against. + * @returns - The validated API content. + */ +export async function makeRequest( + endpoint: string, + schema: T, +): Promise> { + // Send the request. + const json = await (await fetch(endpoint)).json(); + + // Validate the response. + return schema.parse(json); +} + +/** + * Get the current device's IP from the https://ipify.org API. + */ +export function getIp(): Promise { + // make http request and return the IP as json + return makeRequest(`${ipEndpoint}?format=json`, ipSchema); +} diff --git a/src/utils/zod.ts b/src/utils/zod.ts new file mode 100644 index 00000000..1a0a612e --- /dev/null +++ b/src/utils/zod.ts @@ -0,0 +1,3 @@ +import type { z } from "zod"; + +export type ZodTypeUnknown = z.ZodType; From 01f6802a0a604e0263b962a3495368f62c6e4dbe Mon Sep 17 00:00:00 2001 From: Eli <88557639+lishaduck@users.noreply.github.com> Date: Mon, 4 Mar 2024 23:59:42 -0600 Subject: [PATCH 2/8] chore: minor tweaks * Fix flexboxes * Center page content by default * Clarify vendored tailwind plugin by renaming to `headless-tailwind.ts` --- src/components/Admonition.tsx | 4 ++-- src/components/Cover.tsx | 2 +- src/components/Footer.tsx | 2 +- src/components/Header.tsx | 6 +++--- src/routes/_layout.tsx | 2 +- src/tailwind.config.ts | 2 +- src/utils/{headless.ts => headless-tailwind.ts} | 0 7 files changed, 9 insertions(+), 9 deletions(-) rename src/utils/{headless.ts => headless-tailwind.ts} (100%) diff --git a/src/components/Admonition.tsx b/src/components/Admonition.tsx index b9d31fd4..eaf9634b 100644 --- a/src/components/Admonition.tsx +++ b/src/components/Admonition.tsx @@ -82,11 +82,11 @@ export function Admonition({ }: RenderableProps): JSX.Element { return (
-
+
{getTitle(type)}
{children}
diff --git a/src/components/Cover.tsx b/src/components/Cover.tsx index 261dbbeb..b85ba65e 100644 --- a/src/components/Cover.tsx +++ b/src/components/Cover.tsx @@ -33,7 +33,7 @@ export function Cover({ icon = , }: RenderableProps): JSX.Element { return ( -
+
{icon}

{title}

diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx index 961e9358..eb3e803c 100644 --- a/src/components/Footer.tsx +++ b/src/components/Footer.tsx @@ -198,7 +198,7 @@ function RenderAbouts(): JSX.Element { function Who(): JSX.Element { return ( <> -
+