Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calculator #23

Merged
merged 8 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,28 +30,33 @@
"@headlessui/react": "https://esm.sh/v135/*@headlessui/react@1.7.17",
"@preact/signals-core": "https://esm.sh/v135/@preact/signals-core@1.5.1",
"@preact/signals": "https://esm.sh/v135/*@preact/signals@1.2.2",
"preact": "https://esm.sh/v135/preact@10.19.5",
"preact/": "https://esm.sh/v135/preact@10.19.5/",
"preact": "https://esm.sh/v135/preact@10.19.6",
"preact/": "https://esm.sh/v135/preact@10.19.6/",
"@headlessui/tailwindcss": "npm:@headlessui/tailwindcss@0.2.0",
"@mdx-js/mdx": "npm:@mdx-js/mdx@3.0.1",
"@tailwindcss/forms": "npm:@tailwindcss/forms@0.5.7",
"@tailwindcss/typography": "npm:@tailwindcss/typography@0.5.10",
"rehype-mathjax": "npm:rehype-mathjax@6.0.0",
"remark-frontmatter": "npm:remark-frontmatter@5.0.0",
"remark-math": "npm:remark-math@6.0.0",
"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/typography": "npm:@tailwindcss/typography@0.5.10",
"tailwindcss": "npm:tailwindcss@3.4.1",
"tailwindcss/plugin": "npm:tailwindcss@3.4.1/plugin.js",
"vfile": "npm:vfile@6.0.1",
"unified": "npm:unified@11.0.4",
"vfile-matter": "npm:vfile-matter@5.0.0",
"vfile-reporter": "npm:vfile-reporter@8.1.0",
"unified": "npm:unified@11.0.4"
"vfile": "npm:vfile@6.0.1"
},
"scopes": {
"https://esm.sh/v135/": {
"@preact/signals-react": "https://esm.sh/v135/*@preact/signals@1.2.2",
"client-only": "https://esm.sh/v135/client-only@0.0.1",
"react-dom": "https://esm.sh/v135/preact@10.19.5/compat",
"react": "https://esm.sh/v135/preact@10.19.5/compat"
"react-dom": "https://esm.sh/v135/preact@10.19.6/compat",
"react": "https://esm.sh/v135/preact@10.19.6/compat",
"react/jsx-runtime": "https://esm.sh/v135/preact@10.19.6/jsx-runtime",
"react-dom/test-utils": "https://esm.sh/v135/preact@10.19.6/test-utils"
}
},
"compilerOptions": {
Expand Down
4 changes: 2 additions & 2 deletions src/components/Admonition.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,11 @@ export function Admonition({
}: RenderableProps<AdmonitionProps>): JSX.Element {
return (
<div
class={`w-2/3 flex-col divide-y rounded-lg border-2 *:px-4 ${getAdmonitionStyles(
class={`flex w-2/3 flex-col divide-y rounded-lg border-2 *:px-4 ${getAdmonitionStyles(
type,
)}`}
>
<div class="flex items-center *:mr-4">
<div class="flex flex-row items-center *:mr-4">
<AdmonitionIcon type={type} /> {getTitle(type)}
</div>
<div>{children}</div>
Expand Down
31 changes: 31 additions & 0 deletions src/components/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { JSX } from "preact";

export interface CheckboxProps {
name: string;
id: string;
labelText: string;
required?: boolean;
}

export function Checkbox({
name,
id,
labelText,
required,
}: CheckboxProps): JSX.Element {
return (
<div class="top-16 flex w-72 flex-col items-center gap-4">
<label class="text-lg" for={id}>
{labelText}
</label>
<input
name={name}
type="checkbox"
/* Removal of ring based on https://romansorin.com/blog/disabling-the-tailwind-input-ring */
class="cursor-default rounded border-2 border-gray-500 bg-slate-200 shadow-md focus:outline-none focus:ring-1 focus:ring-offset-0 focus-visible:border-blue-600 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-slate-800"
id={id}
required={required}
/>
</div>
);
}
2 changes: 1 addition & 1 deletion src/components/Cover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export function Cover({
icon = <Logo />,
}: RenderableProps<CoverProps>): JSX.Element {
return (
<div class="flex justify-center bg-green-500 px-4 py-8 dark:bg-green-700">
<div class="flex flex-row justify-center bg-green-500 px-4 py-8 dark:bg-green-700">
<div class="flex max-w-screen-sm flex-col items-center justify-center gap-y-4 text-balance text-center lg:max-w-screen-md">
{icon}
<h1 class="text-4xl font-bold">{title}</h1>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ function RenderAbouts(): JSX.Element {
function Who(): JSX.Element {
return (
<>
<div class="flex items-center gap-1">
<div class="flex flex-row items-center gap-1">
<IconSolarPanel2
class="inline-block size-6 min-w-6"
aria-hidden="true"
Expand Down
6 changes: 3 additions & 3 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ export function Header({ active }: HeaderProps): JSX.Element {
<HomeLink />
<ul class="flex flex-row flex-wrap items-center gap-6">
{menus.map((menu: Menu) => (
<li key={menu.title} class="flex h-8 items-end">
<li key={menu.title} class="flex h-8 flex-row items-end">
<HeaderMenu {...menu} active={active.startsWith(menu.url)} />
</li>
))}
{extraMenus.map(
({ title, url }: BasicMenu): JSX.Element => (
<li key={url} class="flex h-8 items-end">
<li key={url} class="flex h-8 flex-row items-end">
<LinkMenu active={active === url} title={title} url={url} />
</li>
),
Expand All @@ -56,7 +56,7 @@ export function Header({ active }: HeaderProps): JSX.Element {
function HomeLink(): JSX.Element {
return (
<a
class="flex flex-1 items-center text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
class="flex flex-1 flex-row items-center text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
href="/"
>
<Logo aria-hidden="true" class="size-6" />
Expand Down
11 changes: 9 additions & 2 deletions src/content/geothermal/what.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ However, it’s important to remember that geothermal energy isn’t a new conce
Humans have been using it for over 10,000 years for functions such as heating hot springs, cooking, and even space heating in cities such as Pompeii.
Nowadays, it’s still mainly used for heating; however, if you install a converter, it can also be used for electricity.
Due to this many large universities such as Yale, Brown, and Missouri S&T have been installing geothermal systems to power their campuses.
The switch to geothermal is still beginning but it will be a worthy investment for the future.
The switch to geothermal is still beginning, but it will be a worthy investment for the future.

<img src="/images/geothermal_what.avif" alt="Geothermal mountain"/>
<img src="/images/geothermal_what.avif" alt="Geothermal mountain" />

There are four types of geothermal energy solutions.

1. Horizontal loop: Horizontal loop systems are best for properties with more land, as they are bigger and require deeper trenches.
2. Vertical Loop: Vertical loop systems are very narrow and great for residential areas!
3. Closed Loop: Closed loop systems mix water and antifreeze to accommodate cold climates, making them optimal for northern locations.
4. Open Loop: Open loop systems circulate water from an outside source, meaning that they are optimal for properties with a steady water source.
7 changes: 5 additions & 2 deletions src/content/geothermal/worth-it.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ sectionHeader: Is It Worth It?
Deciding to invest in a geothermal energy solution can be tricky.
When comparing it to solar it’s a lot more expensive to start and can take up to 20 years to pay itself off.
The pumps also require electricity to power so it’s not technically _free_.
Similar to solar energy, there are also federal tax credits that range from 22% to 30%, applicable on the installation.
Similar to solar energy, there are also federal tax credits that range from 22% to 30%, applicable on installation.
When considering what solution to buy, keep in mind that estimates are rarely over $4000 off, though the actual prices vary depending on local labor costs and other factors.
Additionally, if your state has high geothermic activity, then you will recoup your cost faster.
However, geothermal can be much more efficient than wind and solar as it isn’t impacted by weather.
It will always run as long as it’s powered.
Also, depending on the loop system you choose and how it corresponds to your environment you can generate a lot more energy.
Overall, investing in geothermal can be a smart choice depending on your location and budget as it will save you money over time and lessen carbon emissions.
Feel free to ask our Why Switch AI chatbot for more information!

<img src="/images/geothermal_worth.avif" alt="Geothermal plant"/>
<img src="/images/geothermal_worth.avif" alt="Geothermal plant" />
4 changes: 4 additions & 0 deletions src/fresh.gen.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

122 changes: 122 additions & 0 deletions src/islands/Selector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
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 { IconCheck, IconChevronDown } from "../utils/icons.ts";
import { tw } from "../utils/tailwind.ts";

export interface SelectorProps<T extends string, U extends T> {
name: string;
question: string;
list: SelectorListObject<T>[];
current?: U | undefined;
required?: boolean;
}

export interface SelectorListObject<T extends string> {
name: string;
value: T;
}

export function Selector<T extends string, U extends T>({
name,
question,
list,
current: currentValue,
required,
}: SelectorProps<T, U>): JSX.Element {
const current = useSignal(list.find((val) => val.name === currentValue));
const query = useSignal("");

const filtered = list.filter((item) =>
item.name
.toLowerCase()
.replace(/\s+/g, "")
.includes(query.value.toLowerCase().replace(/\s+/g, "")),
);

return (
<div class="top-16 flex w-72 flex-col items-center gap-4">
<Combobox
disabled={!IS_BROWSER}
value={current.value}
onChange={(newValue) => {
current.value = newValue;
}}
>
<Combobox.Label class="text-lg">{question}</Combobox.Label>
<div class="relative mt-1 w-min">
<div class="relative w-full cursor-default rounded-lg bg-slate-200 text-left shadow-md focus:outline-none focus-visible:ring-2 focus-visible:ring-white/75 focus-visible:ring-offset-2 focus-visible:ring-offset-teal-300 sm:text-sm dark:bg-slate-800 dark:focus-visible:ring-black/75 dark:focus-visible:ring-offset-teal-700">
<Combobox.Input
name={name}
class="rounded border-2 border-gray-500 bg-slate-200 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-slate-800"
autoComplete="off"
required={required}
displayValue={(state: SelectorListObject<T>) => `${state.name}`}
onChange={(event) => {
if (event.target instanceof HTMLInputElement) {
query.value = event.target.value;
}
}}
/>
<Combobox.Button class="absolute inset-y-0 right-0 flex items-center pr-2">
<IconChevronDown
class="h-5 w-5 text-gray-400"
aria-hidden="true"
/>
</Combobox.Button>
</div>
<Transition
as={Fragment}
enter={tw`transition ease-out duration-100`}
enterFrom={tw`opacity-0`}
enterTo={tw`opacity-100`}
leave={tw`transition ease-in duration-100`}
leaveFrom={tw`opacity-100`}
leaveTo={tw`opacity-0`}
afterLeave={() => {
query.value = "";
}}
>
<Combobox.Options class="absolute z-10 mt-1 max-h-60 w-full max-w-full overflow-auto rounded-md bg-slate-200 text-base shadow-lg ring-1 ring-black/5 focus:outline-none sm:text-sm dark:bg-slate-800">
{filtered.length === 0 && query.value !== "" ? (
<div class="relative cursor-default select-none px-4 py-2 text-gray-700">
No results found
</div>
) : (
filtered.map((item) => (
<Combobox.Option
key={item.name}
class="relative cursor-default select-none rounded-md py-2 pl-10 pr-4 ui-active:bg-green-500 ui-active:text-white ui-not-active:text-gray-900 dark:ui-active:bg-green-700 ui-not-active:dark:text-gray-100"
value={item}
>
{({ selected, active }) => (
<>
<span
class={tw`block truncate text-left ${
selected ? tw`font-medium` : tw`font-normal`
}`}
>
{item.name}
</span>
{selected && (
<span
class={tw`absolute inset-y-0 left-0 flex items-center pl-3 ${
active ? tw`text-white` : tw`text-green-700`
}`}
>
<IconCheck class="h-5 w-5" aria-hidden="true" />
</span>
)}
</>
)}
</Combobox.Option>
))
)}
</Combobox.Options>
</Transition>
</div>
</Combobox>
</div>
);
}
2 changes: 1 addition & 1 deletion src/routes/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { Header } from "../components/Header.tsx";
*/
export default function Layout({ Component, url }: PageProps): JSX.Element {
return (
<div class="flex min-h-screen flex-col">
<div class="flex min-h-screen flex-col place-content-center">
<Partial name="body">
<Header active={url.pathname} />
<Component />
Expand Down
13 changes: 10 additions & 3 deletions src/routes/about.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> => {
const handler = await createHandler(manifest, config);

Expand Down
Loading
Loading